From 9aa507790e8a75ab8f52e267e29059a080d55122 Mon Sep 17 00:00:00 2001 From: Paul Spooren Date: Sat, 6 Jul 2019 01:25:07 +0200 Subject: [PATCH] luci-app-bmx7: transfer from routing The Makefile is minified as the LuCi build system does most of the job. Signed-off-by: Paul Spooren --- applications/luci-app-bmx7/Makefile | 14 + .../luci-app-bmx7/root/etc/config/luci-bmx7 | 7 + .../root/usr/lib/lua/luci/controller/bmx7.lua | 101 ++++ .../view/admin_status/index/bmx7_nodes.htm | 40 ++ .../usr/lib/lua/luci/view/bmx7/nodes_j.htm | 155 +++++ .../usr/lib/lua/luci/view/bmx7/status_j.htm | 130 ++++ .../usr/lib/lua/luci/view/bmx7/topology.htm | 54 ++ .../usr/lib/lua/luci/view/bmx7/tunnels_j.htm | 76 +++ .../luci-app-bmx7/root/www/cgi-bin/bmx7-info | 133 ++++ .../luci-static/resources/bmx7/bmx7logo.png | Bin 0 -> 5121 bytes .../resources/bmx7/css/netjsongraph-theme.css | 59 ++ .../resources/bmx7/css/netjsongraph.css | 62 ++ .../resources/bmx7/js/netjsongraph.js | 568 ++++++++++++++++++ .../luci-static/resources/bmx7/js/polling.js | 86 +++ .../www/luci-static/resources/bmx7/world.png | Bin 0 -> 1885 bytes .../resources/bmx7/world_small.png | Bin 0 -> 923 bytes 16 files changed, 1485 insertions(+) create mode 100644 applications/luci-app-bmx7/Makefile create mode 100755 applications/luci-app-bmx7/root/etc/config/luci-bmx7 create mode 100644 applications/luci-app-bmx7/root/usr/lib/lua/luci/controller/bmx7.lua create mode 100644 applications/luci-app-bmx7/root/usr/lib/lua/luci/view/admin_status/index/bmx7_nodes.htm create mode 100644 applications/luci-app-bmx7/root/usr/lib/lua/luci/view/bmx7/nodes_j.htm create mode 100644 applications/luci-app-bmx7/root/usr/lib/lua/luci/view/bmx7/status_j.htm create mode 100644 applications/luci-app-bmx7/root/usr/lib/lua/luci/view/bmx7/topology.htm create mode 100644 applications/luci-app-bmx7/root/usr/lib/lua/luci/view/bmx7/tunnels_j.htm create mode 100755 applications/luci-app-bmx7/root/www/cgi-bin/bmx7-info create mode 100644 applications/luci-app-bmx7/root/www/luci-static/resources/bmx7/bmx7logo.png create mode 100644 applications/luci-app-bmx7/root/www/luci-static/resources/bmx7/css/netjsongraph-theme.css create mode 100644 applications/luci-app-bmx7/root/www/luci-static/resources/bmx7/css/netjsongraph.css create mode 100644 applications/luci-app-bmx7/root/www/luci-static/resources/bmx7/js/netjsongraph.js create mode 100644 applications/luci-app-bmx7/root/www/luci-static/resources/bmx7/js/polling.js create mode 100644 applications/luci-app-bmx7/root/www/luci-static/resources/bmx7/world.png create mode 100644 applications/luci-app-bmx7/root/www/luci-static/resources/bmx7/world_small.png diff --git a/applications/luci-app-bmx7/Makefile b/applications/luci-app-bmx7/Makefile new file mode 100644 index 000000000..d614327e3 --- /dev/null +++ b/applications/luci-app-bmx7/Makefile @@ -0,0 +1,14 @@ +# See /LICENSE for more information. +# This is free software, licensed under the GNU General Public License v2. + +include $(TOPDIR)/rules.mk + +LUCI_TITLE:=LuCI support for BMX7 +LUCI_DEPENDS:=+luci-lib-json +luci-mod-admin-full +bmx7 +bmx7-json +PKG_MAINTAINER:= Roger Pueyo \ + Pau Escrich +PKG_LICENSE:=GPL-2.0-or-later + +include ../../luci.mk + +# call BuildPackage - OpenWrt buildroot signature diff --git a/applications/luci-app-bmx7/root/etc/config/luci-bmx7 b/applications/luci-app-bmx7/root/etc/config/luci-bmx7 new file mode 100755 index 000000000..46a77272f --- /dev/null +++ b/applications/luci-app-bmx7/root/etc/config/luci-bmx7 @@ -0,0 +1,7 @@ +config 'bmx7' 'luci' + option ignore '0' + option place 'admin network BMX7' + #option place 'qmp Mesh' + option position '3' + #option json 'http://127.0.0.1/cgi-bin/bmx7-info?' + option json 'exec:/www/cgi-bin/bmx7-info -s' diff --git a/applications/luci-app-bmx7/root/usr/lib/lua/luci/controller/bmx7.lua b/applications/luci-app-bmx7/root/usr/lib/lua/luci/controller/bmx7.lua new file mode 100644 index 000000000..482fb5db5 --- /dev/null +++ b/applications/luci-app-bmx7/root/usr/lib/lua/luci/controller/bmx7.lua @@ -0,0 +1,101 @@ +--[[ + Copyright (C) 2011 Pau Escrich + Contributors Jo-Philipp Wich + Roger Pueyo Centelles + + 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". +--]] + +module("luci.controller.bmx7", package.seeall) + +function index() + local place = {} + local ucim = require "luci.model.uci" + local uci = ucim.cursor() + + -- checking if ignore is on + if uci:get("luci-bmx7","luci","ignore") == "1" then + return nil + end + + -- getting value from uci database + local uci_place = uci:get("luci-bmx7","luci","place") + + -- default values + if uci_place == nil then + place = {"bmx7"} + else + local util = require "luci.util" + place = util.split(uci_place," ") + end + + -- getting position of menu + local uci_position = uci:get("luci-bmx7","luci","position") + + + --------------------------- + -- Placing the pages in the menu + --------------------------- + + -- Status (default) + entry(place,call("action_status_j"),place[#place],tonumber(uci_position)) + + table.insert(place,"Status") + entry(place,call("action_status_j"),"Status",0) + table.remove(place) + + -- Topology + table.insert(place,"Topology") + entry(place,call("topology"),"Topology",1) + table.remove(place) + + -- Nodes + table.insert(place,"Nodes") + entry(place,call("action_nodes_j"),"Nodes",2) + table.remove(place) + + -- Tunnels + table.insert(place,"Gateways") + entry(place,call("action_tunnels_j"),"Gateways",3) + table.remove(place) + + -- Integrate bmx7-mdns if present + if nixio.fs.stat("/usr/lib/lua/luci/model/cbi/bmx7-mdns.lua","type") ~= nil then + table.insert(place,"mDNS") + entry(place, cbi("bmx7-mdns"), "mesh DNS", 1).dependent=false + table.remove(place) + end + +end + + +function action_status_j() + luci.template.render("bmx7/status_j", {}) +end + +function action_tunnels_j() + luci.template.render("bmx7/tunnels_j", {}) +end + +function topology() + luci.template.render("bmx7/topology", {}) +end + +function action_nodes_j() + luci.template.render("bmx7/nodes_j", {}) +end diff --git a/applications/luci-app-bmx7/root/usr/lib/lua/luci/view/admin_status/index/bmx7_nodes.htm b/applications/luci-app-bmx7/root/usr/lib/lua/luci/view/admin_status/index/bmx7_nodes.htm new file mode 100644 index 000000000..8a6aefaa2 --- /dev/null +++ b/applications/luci-app-bmx7/root/usr/lib/lua/luci/view/admin_status/index/bmx7_nodes.htm @@ -0,0 +1,40 @@ +
+
+ <%:Bmx7 mesh nodes%> +
+
+
+
<%:Name%>
+
<%:Short ID%>
+
<%:S/s/T/t%>
+
<%:Primary IPv6%>
+
<%:Via Neighbour%>
+
<%:Device%>
+
<%:Metric%>
+
<%:Last Ref%>
+
+
+
+
+
+ + + diff --git a/applications/luci-app-bmx7/root/usr/lib/lua/luci/view/bmx7/nodes_j.htm b/applications/luci-app-bmx7/root/usr/lib/lua/luci/view/bmx7/nodes_j.htm new file mode 100644 index 000000000..a631c93e8 --- /dev/null +++ b/applications/luci-app-bmx7/root/usr/lib/lua/luci/view/bmx7/nodes_j.htm @@ -0,0 +1,155 @@ +<%# + Copyright © 2011 Pau Escrich + Contributors Lluis Esquerda + Roger Pueyo Centelles + + 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". +-%> + +<%+header%> + + + + + + +
+ +

Mesh nodes

+
+
+
+
+ Tip: click the icon to see individual node information. +
+
+ + +
+ <%:Originators%> +
+
+
+
+
<%:Name%>
+
<%:Short ID%>
+
<%:S/s/T/t%>
+
<%:Primary IPv6%>
+
<%:Via Neighbour%>
+
<%:Metric%>
+
<%:Last Desc%>
+
<%:Last Ref%>
+
<%: %>
+
+
+
+
+ +
+ + + +<%+footer%> diff --git a/applications/luci-app-bmx7/root/usr/lib/lua/luci/view/bmx7/status_j.htm b/applications/luci-app-bmx7/root/usr/lib/lua/luci/view/bmx7/status_j.htm new file mode 100644 index 000000000..b7609d7a5 --- /dev/null +++ b/applications/luci-app-bmx7/root/usr/lib/lua/luci/view/bmx7/status_j.htm @@ -0,0 +1,130 @@ +<%+header%> + + + +
+
+ +
+
+ A mesh routing protocol for Linux devices.
+ Visit bmx6.net for more information.
+
+
+ +
+ +
+ <%:Node configuration%> +
+
+
+
<%:Short ID%>
+
<%:Node name%>
+
<%:Primary IPv6 address%>
+
<%:Node key%>
+
<%:Short DHash%>
+
<%:BMX7 revision%>
+
+
+
+
+ + +
+ <%:Node status%> +
+
+
+
<%:Nodes seen%>
+
<%:Neighbours%>
+
<%:Tunnelled IPv6 address%>
+
<%:Tunnelled IPv4 address%>
+
<%:Uptime%>
+
<%:CPU usage%>
+
<%:Memory usage%>
+
<%:Tx queue%>
+
+
+
+
+ +
+ <%:Network interfaces%> +
+
+
+
<%:Interface%>
+
<%:State%>
+
<%:Type%>
+
<%:Max rate%>
+
<%:LinkLocal Ipv6%>
+
<%:RX BpP%>
+
<%:TX BpP%>
+
+
+
+
+ + +
+ <%:Links%> +
+ +
+
+ +
+ + + +<%+footer%> diff --git a/applications/luci-app-bmx7/root/usr/lib/lua/luci/view/bmx7/topology.htm b/applications/luci-app-bmx7/root/usr/lib/lua/luci/view/bmx7/topology.htm new file mode 100644 index 000000000..58ce9fdbf --- /dev/null +++ b/applications/luci-app-bmx7/root/usr/lib/lua/luci/view/bmx7/topology.htm @@ -0,0 +1,54 @@ +<%+header%> + + + + + + +<%+footer%> + diff --git a/applications/luci-app-bmx7/root/usr/lib/lua/luci/view/bmx7/tunnels_j.htm b/applications/luci-app-bmx7/root/usr/lib/lua/luci/view/bmx7/tunnels_j.htm new file mode 100644 index 000000000..aaa79a8f4 --- /dev/null +++ b/applications/luci-app-bmx7/root/usr/lib/lua/luci/view/bmx7/tunnels_j.htm @@ -0,0 +1,76 @@ +<%# + Copyright (C) 2011 Pau Escrich + Contributors Lluis Esquerda + + 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. + + The full GNU General Public License is included in this distribution in + the file called "COPYING". +-%> + + +<%+header%> + + + +
+

Gateway announcements

+
Networks announced by mesh nodes
+ +
+ <%:Announcements%> +
+
+
+
<%:Status%>
+
<%:Name%>
+
<%:Node%>
+
<%:Network%>
+
<%:Bandwith%>
+
<%:Local net%>
+
<%:Path Metric%>
+
<%:Tun Metric%>
+
<%:Rating%>
+
+
+
+
+ +
+ + + +<%+footer%> diff --git a/applications/luci-app-bmx7/root/www/cgi-bin/bmx7-info b/applications/luci-app-bmx7/root/www/cgi-bin/bmx7-info new file mode 100755 index 000000000..7388ed12c --- /dev/null +++ b/applications/luci-app-bmx7/root/www/cgi-bin/bmx7-info @@ -0,0 +1,133 @@ +#!/bin/sh +# Copyright © 2011 Pau Escrich +# Contributors Jo-Philipp Wich +# Roger Pueyo Centelles +# +# 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. +# +# The full GNU General Public License is included in this distribution in +# the file called "COPYING". +# +# This script gives information about bmx7 +# Can be executed from a linux shell: ./bmx7-info -s links +# Or from web interfae (with cgi enabled): http://host/cgi-bin/bmx7-info?links +# If you ask for a directory you wil get the directory contents in JSON forman + +BMX7_DIR="$(uci get bmx7.general.runtimeDir 2>/dev/null)" || BMX7_DIR="/var/run/bmx7/json" + +#Checking if shell mode or cgi-bin mode +if [ "$1" == "-s" ]; then + QUERY="$2" +else + QUERY="${QUERY_STRING%%=*}" + echo "Content-type: application/json" + echo "" +fi + +check_path() { + [ -d "$1" ] && path=$(cd $1; pwd) + [ -f "$1" ] && path=$(cd $1/..; pwd) + [ $(echo "$path" | grep -c "^$BMX7_DIR") -ne 1 ] && exit 1 +} + +print_mem() { + echo -n '{ "memory": { "bmx7": "' + cat /proc/$(cat /var/run/bmx7/pid)/status |grep -i VmSize | tr -s " " | cut -d " " -f 2,3 | tr -d "\n" + echo '"}}' +} + +print_query() { + # If the query is a directory + [ -d "$BMX7_DIR/$1" ] && + { + # If /all has not been specified + [ -z "$QALL" ] && + { + total=$(ls $BMX7_DIR/$1 | wc -w) + i=1 + echo -n "{ \"$1\": [ " + for f in $(ls $BMX7_DIR/$1); do + echo -n "{ \"name\": \"$f\" }" + [ $i -lt $total ] && echo -n ',' + i=$(( $i + 1 )) + done + echo -n " ] }" + + # If /all has been specified, printing all the files together + } || { + comma="" + echo -n "[ " + for entry in "$BMX7_DIR/$1/"*; do + [ -f "$entry" ] && + { + ${comma:+echo "$comma"} + tr -d '\n' < "$entry" + comma="," + } + done + echo -n " ]" + } + } + + # If the query is a file, just printing the file + [ -f "$BMX7_DIR/$1" ] && [ -s "$BMX7_DIR/$1" ] && cat "$BMX7_DIR/$1" && return 0 || return 1 +} + +if [ "${QUERY##*/}" == "all" ]; then + QUERY="${QUERY%/all}" + QALL=1 +fi + +if [ "$QUERY" == '$info' ]; then + echo '{ "info": [ ' + print_query status + echo -n "," + print_query interfaces && echo -n "," || echo -n '{ "interfaces": "" },' + print_query links && echo -n "," || echo -n '{ "links": "" },' + print_mem + echo "] }" +fi + +if [ "$QUERY" == '$neighbours' ]; then + QALL=1 + echo '{ "neighbours": [ ' + echo '{ "originators": ' + print_query originators + echo '}, ' + echo '{ "descriptions": ' + print_query descriptions + echo "} ] }" + exit 0 + +else if [ "$QUERY" == '$tunnels' ]; then + bmx7 -c --jshow tunnels /r=0 + exit 0 + + else if [ "$QUERY" == '$originators' ]; then + bmx7 -c --jshow originators /r=0 + exit 0 + + else + check_path "$BMX7_DIR/$QUERY" + print_query $QUERY + exit 0 + fi + fi +fi +fi + +ls -1F "$BMX7_DIR" +exit 0 + diff --git a/applications/luci-app-bmx7/root/www/luci-static/resources/bmx7/bmx7logo.png b/applications/luci-app-bmx7/root/www/luci-static/resources/bmx7/bmx7logo.png new file mode 100644 index 0000000000000000000000000000000000000000..c7d9ceafaeb7e11706f9363f4b97f4cd4d4ada2d GIT binary patch literal 5121 zcmV+c6#napP)EF+^ne+&Qs6OKtl zK~#9!?Ol6RROQ5YTcgZ!1rl#{;?4G_^eHNe#LPcC(wH>pOg> zbx!3eEnQ$KV;Ab=0ux12F#XWaLOT`e5s(7Bq@jRt4ude)JQUvF{-YvMny#hY&IJJV8Vn62n`MW)nFiG+_-iS5D^3ca5}{x zkV>TkDnv?33NkY@-L4%uas=zwuSaNTDCW(?VUHy38Jxkq^l3kzX18gGh;KK|~zsM)zwbX{dIAmPIg!Eqco&mN*= zzyxb(Xz&UE9c?o+Ghs9uk&%(%)ib`X{G_BLj2SZqCX=a00CdQcmX_Aibc@P^K8n9x;3QU|h5&8M~eU`&)HX|b=14g3} zCX>l0d5VgPkeZr$!-L?bFTaGNrG>G+B?#~0l5#`@77hjq1o`>-Zj%@j6NAXeNUxtS zEiJ{v4?m2gq@=5^+MakAjYh0kv7*;JGBYzpHp7iZXZG%8UcdF8d!P;u{$;LRE@A;? zz(BBW-8#JW)?28mssa(Ayu2LM)zv62F7`>Am`o-N88QT~zy7+{tGK471_=oX7&B%J zYHDh{?k_Ph5oKj%C@U*NNl6L*{O3O-GBOe=DJcUsfsKugFq_S=TCM0!P`Bm61zbLP zvPXlk%Ss$ZQYi@d<;QWLz(5rZ1l8Bqd%PDD6XPa_x4&7lW_9xI%K!ZN^PT@LE-og6 z!QeCRn3x!{TCE~ktyU{#WMmA;7(M^|^Q2HHi065d%jLvz97&~8N>5Msn#YOFn`wJ| zyy#c($3K$bbh@>@<~D)BVCZqh^>&L%NlAg(Y<63n>FMdHuC7LEYAO;E5_+Y+d$;->bd+)smyWI|(%?5|V0hh}Ky)i@ZdolJb1A4nog%f zW@cuOTZX}4z~aS=q0{M5QBi^N@^aMF)WBx5T~k-Es;UZ)KKdxy+S=URIh6|e`S}<$ zXpmQYYL6U&y}6kU0#=S9Iy#)W9fXAF=~`1&Rh35-``vfnkxr-cN`-sduH%=Toh|CG zu&|IOPo7LAB_-G7-CJ8*DIy|*IF56ZO{3A!+O=za;&o`%D&|)3ziDaA_L8WmFsiBf z5rW_zQXV`w5a-V~-cZ(VQc@C3CKHTCBO)UsQCeE+X5eW7Oa@R0AQ-?10L5NtpOTW2 z>;7&JJ@gRv?%fNk)#|q8+<*W5*t2Jk*L<#AyokbO%a~D~ackBf^xk{LTR&?%7)bA+ zFBBUai*@VPp`@e)IXO8fEiJ|3#fyI#000F5oB%E{0AMf}ke!{~Y2}X}KOXbu&AaaJ zwPC{s?Ay1`4FD2}1VKST*tTt(PrS|+7BT_A`}nVWY^j->#27>{`#l>!ksr>wWl$V!Z*LOO4^e9qOQ<-)^VPRpXP2>%^oKBqG zw~u*m=pX(7PR1@D>A^dY-f$p@jEodn>O9XQD=Q15N00XS9MAK}$;lCACJhDyCQqJx z-QK66p#d{z&J+PatyUv5GZQ06j`WG&#jn4H?ce_vT_*q$Jru3nR3PYSm9Jd6(&PQ) zwBzLF*bc6?Rj|U( zJkJkkWUsgM$}6wnKvaw)>X!mxSsW}o@B)z_o$+i%$s!s*j{e71);!L`u@ zPNx&u+1YM>yq}*R7A{-}iNyO%rcS3rc6K(VOqtTji|2V{WMuf1o46K~pP!FSn>I1+ zfW3S7GJ{iB)dEFD%=^`}G{Ln3!GQw@ker3~uIoMQE zQi6{^{upMn8HtIBH(UWob#*lsFJ8>F13vxqQ$$2W_{`HK2srBj1Vg7ygH$f}T@y$o zfJ}C6K;Uw@!1KJ@-l)-Npi-&a+I{uaSJ=02AF8UVaPHhWXti358Z`=|MvcPUxpSdb zs{sH;qtRoO@5?X0#Noq-QCV4u>gsCTa?354G-*=jGk$)4qGy_#n()I9KZq8H(P;E} zp;#;yBqSuDwY60QfTx~%3NvQR=+zk3efuq}moKxog6_$pzHSFZgyYAL;qc*uxOC|v z>gz9|uI>`lYAx=%%ZL#pVh|l2gUHC?kY5FTU3s#A(J`sn+1W@-OG9&WGg@0);p$Qh zxo_V-Xti3bSg``Hyz&ZOdg&!*zaM`1Azpp;Rn*tl!(y?(*`Z{neabu@E9b4Ff1q-ls>sFs8Ysr!&sI9Gadk48(j<~outX;dd*9+;ihhS20 zTpawosm2Nm3l@cqWirs%u_HUfilDF6yq}MCR-(N*M&yif--c~LLX*6;Q z3eu2HCnG;UDcy0$P+Gr!Ew!|?^cdnSC@3JkUhmewTrQ`&x;k3Ccrj_UTCYOCYPFgY z5)#PaaCGvv*=#g_{(RDCG+y!0Xf(8Z`Eqi0$+G?Q(@!G#I{M4a&GkBzn~{;he3!t$ zK&q>&>vg!ft)YRoCnPY##Ao;K_lgIG3<)BR6N6y<_`9gN`9JjRv&p2>$w;l1iQ0Mk zNhIw&0s_<&9IT;t-`(g@fZe-yiw0RDk#v#5tJP{ceE4vmat;-re8L34uDNr`X|s6; zg3!=FCJ082x}EO4Gm3ONo;mp(2a-y0RbWV^pnw1+&7C`wnwy(N0_@tgi@DPL{QTVh z&+|O#^?KrYo^(2$M-8FTXy}VCzM$pHmx~78TOKNvioXB;dp9|sdFC0BMv+RT6dM~$ zcDvo<05>!=FzXmJ8V$Yi#v6SCfXn5g&;Rr%CJ2sYW_ryBx7`-N1c62a(rOiMt6Qs8 zP;jueQ{@jEHkkbVdD3Vk6cnT(yyr>qoFNZwusv1m>_6Bg(Sz8 zEg$%l&g_UWY}~jRayf_Y;RZnfEEYRVruRI?2~;W-UU=aJ1O^6r)z`S6&(wA2k(!$7 zmFykCpYHLC%F0S+`%RlRAtWTkEx+P)I#E_uhE=OpF#|$Mr4n=I%n`*QdP78nvxS9h zm;e6zA@ecp>@ka|)$(}o!8!Qd@22!h(2$T2{OxaRq0^~E_pV&AASdS^9zmc`C~z;M z0>%@ChlewH+;h)8Uh@bG3lm-QRF1mIu)I*z8MCMF1+ zPABfT;|{O;iHL|0MG74b2cn{)yyh`<=ulDSw6?a^WBU;!M&O-y-hoD=al3A_*(vpf8;Kka2kdq`^F2DMhx(JwcrXZuOql|C4;raQ5U{OJz+!3Z7oo3QxdK6O zF}x26gA-+&mRVDN2yVL2wEw|jfAUJmH7*?-d&5Q?h*9J$99Kk#9ywj)t>@6+$ z>F{B;7Pv|cVXwUdo>!r)>_nfKH#Rn+vGFpxD;th$j}OGgjzs(S5pFIZT)A=uGiJ;X zxq=#v1`8K1bSpsVXrGprhU3SN_qxv?4;+B2OHpd4LCEtMGG$8NLBN^=v$+jvX)l7c zU3g3B>8l}=DMkFHQsBP(rhw=9n^F@jS+YbF<4`CR7&mSlOePc7tXU(9A6P6F%$zw> zq&fP!6@=b*9~9mC>KDTh0PJ=bYHCg+D{E7)7?+lQi|p*JXlW7q+J1g2ELrl{&-Ta7 z1%mhAe;-9fMWPsjpPwIca&iCwt5&Uo(P(swI}i~X8ykCd{J!zjDVROjXnipVueu$y zYXb|?(q6_l-~7`jrWY<;z+;asg2f_;wgS0ag7ENg%$_~(rUOA`WhI_|`f1T>R;g6T z%gaMhP*A7mvwiz^Xti3AHYg}4Kz4R^zi0v7(4hz#F=AjrAP7K9ixtzR-HR7qc;>3A zjEG<|y@xyRj6zM#S=j9^ku9K7$?*2unVrEERT2%{!q!$?ICN;hv;g3t+L+@2KR+eRW-IJ=7o1KPOr|Z! z%ge#++4B(>Hxkj&BM=c0iA$F*qO$T7Dl02cSXh9nsw$YxR=1<>9WJ3x$0IfMzc6Xi zWH;d4477ata$LN4(alDZ$z+I&i$i*PI`f&SQ>P+1IT@LmnQrF{I_!fJCr(`Ti?Qd6 ziy<&Ru))dY2%9>!e?Y)g4;&x}cIfp=G&ft}askcFR*SNPFDqw0-+_GxwFS3d#}Cr8ov1A z3zU_WLHK!P2!ep~=g;H$=buMLMnIsJ{3i*U%#GIDwU|9#8)0X&x?4R zIdg_txujC5m?dQ_I(P0IlZQs5p@M<}y57VA1gR8mKaOip;+RYyT! z%YmNGskVRI0+7kt_kNvDj^F=&3cmX4TP$6g=+Tb2k!j}4nIbztrBdPX#~(+6mw%%m zEG!Hkee@C2K3KG9QI9F>YeBN<(;q}dMH%DeX9EBd5~ic9tQt8v|GBj^p5Rx!jI``3ek~Oa>zA zQn{l>Mr5(