# vim: set et ts=4 sw=4: #***************************************************************************** # # Copyright (c) 2013-2014 Matteo Pasotti # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License version 2, as # published by the Free Software Foundation. # # 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. # #***************************************************************************** package AdminPanel::Module::Proxy; use Modern::Perl '2011'; use autodie; use Moose; use POSIX qw(ceil); use English; use utf8; use yui; use AdminPanel::Shared qw(trim); use AdminPanel::Shared::GUI; use AdminPanel::Shared::Proxy; # TODROP but provides network::network use lib qw(/usr/lib/libDrakX); use network::network; use MDK::Common::System qw(getVarsFromSh); extends qw( AdminPanel::Module ); has '+icon' => ( default => "/usr/share/mcc/themes/default/drakproxy-mdk.png" ); has '+name' => ( default => "Proxymanager", ); =head1 VERSION Version 1.0.0 =cut our $VERSION = '1.0.0'; has 'dialog' => ( is => 'rw', init_arg => undef ); has 'table' => ( is => 'rw', init_arg => undef ); has 'proxy' => ( is => 'rw', isa => 'HashRef', builder => "init_proxy" ); has 'sh_gui' => ( is => 'rw', init_arg => undef, builder => '_SharedUGUIInitialize' ); has 'loc' => ( is => 'rw', init_arg => undef, builder => '_localeInitialize' ); sub _localeInitialize { my $self = shift(); # TODO fix domain binding for translation $self->loc(AdminPanel::Shared::Locales->new(domain_name => 'drakx-net') ); # TODO if we want to give the opportunity to test locally add dir_name => 'path' } sub _SharedUGUIInitialize { my $self = shift(); $self->sh_gui( AdminPanel::Shared::GUI->new() ); } #============================================================= =head2 init_proxy =head3 DESCRIPTION =over 4 =item This method does initialize the proxy attribute provided by this class. =item $self->proxy is structured as follows: =over 6 =item B the string with the list of the excluded domains/addresses =item B the url of the http proxy =item B the url of the https proxy =item B the url for the ftp proxy =back =back =cut #============================================================= sub init_proxy { my %p = ( 'no_proxy' => '', 'http_proxy' => '', 'https_proxy' => '', 'ftp_proxy' => '', ); return \%p; } #============================================================= =head2 start =head3 INPUT $self: this object =head3 DESCRIPTION This method extends Module::start and is invoked to start proxy manager =cut #============================================================= sub start { my $self = shift; if ($EUID != 0) { $self->sh_gui->warningMsgBox({ title => $self->name, text => $self->loc->N("root privileges required"), }); return; } $self->_manageProxyDialog(); }; #============================================================= =head2 ask_for_X_restart =head3 INPUT $self: this object =head3 DESCRIPTION This method shows a message box warning the user that a X server restart is required =cut #============================================================= sub ask_for_X_restart { my $self = shift; $self->sh_gui->warningMsgBox({title=>$self->loc->N("X Restart Required"),text=>$self->loc->N("You need to log out and back in again for changes to take effect"),richtext=>1}); } #============================================================= =head2 validate =head3 INPUT $self: this object $proxy: the hash containing what returns from getVarFromSh eventually modified by the user =head3 DESCRIPTION This method returns true if the each value match certain conditions like the leading http:// for http proxy or https:// for the https proxy, etc. $proxy is passed by reference thus $proxy->{no_proxy} value is sanitized (trimmed). =cut #============================================================= sub validate { my $self = shift; my $proxy = shift; my $retval = 1; $proxy->{no_proxy} =~ s/\s//g; # using commas rather than slashes if($proxy->{http_proxy} !~ m,^($|http://),) { $self->sh_gui->warningMsgBox({title=>'Error',text=>$self->loc->N("Proxy should be http://..."),richtext=>0}); $retval = 0; } if($proxy->{https_proxy} !~ m,^($|https?://),) { $self->sh_gui->warningMsgBox({title=>'Error',text=>$self->loc->N("Proxy should be http://... or https://..."),richtext=>0}); $retval = 0; } if($proxy->{ftp_proxy} !~ m,^($|ftp://|http://),) { $self->sh_gui->warningMsgBox({title=>'Error',text=>$self->loc->N("URL should begin with 'ftp:' or 'http:'"),richtext=>0}); $retval = 0; } return $retval; } sub _manageProxyDialog { my $self = shift; ## TODO fix for adminpanel my $appTitle = yui::YUI::app()->applicationTitle(); my $appIcon = yui::YUI::app()->applicationIcon(); ## set new title to get it in dialog my $newTitle = $self->loc->N("Proxies configuration"); yui::YUI::app()->setApplicationTitle($newTitle); my $factory = yui::YUI::widgetFactory; my $optional = yui::YUI::optionalWidgetFactory; my $label_width = 25; my $inputfield_width = 45; # getVarsFromSh returns an empty hash if no vars are defined # possible alternatives: # . Config::Auto::parse my $proxy_curr_settings = { getVarsFromSh('/etc/profile.d/proxy.sh') }; my $httpsProxyEqualToHttpProxy = 0; if((defined($proxy_curr_settings->{http_proxy}) && defined($proxy_curr_settings->{https_proxy}))&& (($proxy_curr_settings->{http_proxy} eq $proxy_curr_settings->{https_proxy}) && ($proxy_curr_settings->{http_proxy} ne ""))){ $httpsProxyEqualToHttpProxy = 1; } # # @layout # # +------------------------------+ # | +------------+-------------+ | # | |LABELS | VALUES | | # | | | | | # | | | | | # | | | | | # | +------------+-------------+ | # +------------------------------+ $self->dialog($factory->createMainDialog()); my $layout = $factory->createVBox($self->dialog); my $hbox_header = $factory->createHBox($layout); my $headLeft = $factory->createHBox($factory->createLeft($hbox_header)); my $headRight = $factory->createHBox($factory->createRight($hbox_header)); my $logoImage = $factory->createImage($headLeft, $appIcon); my $labelAppDescription = $factory->createLabel($headRight,$newTitle); $logoImage->setWeight($yui::YD_HORIZ,0); $labelAppDescription->setWeight($yui::YD_HORIZ,3); # app description my $hbox_content = $factory->createHBox($layout); $factory->createLabel($hbox_content, $self->loc->N("Here you can set up your proxies configuration (eg: http://my_caching_server:8080)")); $hbox_content = $factory->createHBox($layout); my $vbox_labels_flags = $factory->createVBox($hbox_content); my $vbox_inputfields = $factory->createVBox($hbox_content); # http proxy section my $httpproxy_label = $factory->createLabel($vbox_labels_flags, $self->loc->N("HTTP proxy")); my $http_proxy = $factory->createInputField($factory->createHBox($vbox_inputfields),"",0); $http_proxy->setValue($proxy_curr_settings->{http_proxy}) if(defined($proxy_curr_settings->{http_proxy})); $http_proxy->setWeight($yui::YD_HORIZ, 30); # flag to setup the https proxy with the same value of the http proxy my $ckbHttpEqHttps = $factory->createCheckBox($vbox_labels_flags, $self->loc->N("Use HTTP proxy for HTTPS connections"),$httpsProxyEqualToHttpProxy); $ckbHttpEqHttps->setNotify(1); # add a spacing as we have $factory->createLabel($factory->createHBox($vbox_inputfields)," "); # https proxy $factory->createLabel($vbox_labels_flags, $self->loc->N("HTTPS proxy")); my $https_proxy = $factory->createInputField($factory->createHBox($vbox_inputfields),"",0); $https_proxy->setValue($proxy_curr_settings->{https_proxy}) if(defined($proxy_curr_settings->{https_proxy})); $https_proxy->setWeight($yui::YD_HORIZ, 30); # ftp proxy $factory->createLabel($vbox_labels_flags, $self->loc->N("FTP proxy")); my $ftp_proxy = $factory->createInputField($factory->createHBox($vbox_inputfields),"",0); $ftp_proxy->setValue($proxy_curr_settings->{ftp_proxy}) if(defined($proxy_curr_settings->{ftp_proxy})); $ftp_proxy->setWeight($yui::YD_HORIZ, 30); # no-proxy list $factory->createLabel($vbox_labels_flags, $self->loc->N("No proxy for (comma separated list):")); my $no_proxy = $factory->createInputField($factory->createHBox($vbox_inputfields),"",0); $no_proxy->setValue($proxy_curr_settings->{no_proxy}) if(defined($proxy_curr_settings->{no_proxy})); $no_proxy->setWeight($yui::YD_HORIZ, 30); my $hbox_filler = $factory->createHBox($layout); $factory->createSpacing($hbox_filler,$yui::YD_VERT,2); my $hbox_foot = $factory->createHBox($layout); my $vbox_foot_left = $factory->createVBox($factory->createLeft($hbox_foot)); my $vbox_foot_right = $factory->createVBox($factory->createRight($hbox_foot)); my $aboutButton = $factory->createPushButton($vbox_foot_left,$self->loc->N("About")); my $cancelButton = $factory->createPushButton($vbox_foot_right,$self->loc->N("Cancel")); my $okButton = $factory->createPushButton($vbox_foot_right,$self->loc->N("OK")); # main loop while(1) { my $event = $self->dialog->waitForEvent(); my $eventType = $event->eventType(); #event type checking if ($eventType == $yui::YEvent::CancelEvent) { last; } elsif ($eventType == $yui::YEvent::WidgetEvent) { ### Buttons and widgets ### my $widget = $event->widget(); if ($widget == $cancelButton) { last; }elsif ($widget == $aboutButton) { $self->sh_gui->AboutDialog({ name => $appTitle, version => $VERSION, credits => "Copyright (c) 2013-2014 by Matteo Pasotti", license => "GPLv2", description => $self->loc->N("Graphical manager for proxies"), authors => "Matteo Pasotti <matteo.pasotti\@gmail.com>" } ); }elsif ($widget == $okButton) { # setup proxy attribute my %_proxy = ( no_proxy => $no_proxy->value(), http_proxy => $http_proxy->value(), https_proxy => $https_proxy->value(), ftp_proxy => $ftp_proxy->value() ); if($self->validate(\%_proxy)) { # validation succeded $self->proxy(\%_proxy); # save changes network::network::proxy_configure($self->proxy); $self->ask_for_X_restart(); last; } # validation failed next; }elsif ($widget == $ckbHttpEqHttps){ $https_proxy->setEnabled(!$ckbHttpEqHttps->isChecked()); } } } $self->dialog->destroy() ; #restore old application title yui::YUI::app()->setApplicationTitle($appTitle); } 1; jFt]\Y!S;r3Ę:ϵ )%ں:lZR 6)zYxۊXQk_]-I([ʡMB+I?9 !JBA|1mͩ 77wx<Ȃ2.]T툰rb[&awN0:2 .`@mI>̱0-m u*ƤE+0yvBuHY䬢 2cbTsaD`Gi1Ю>J@AތK {_MHc$ack<K]&<;IR|W^ v;4Mz0kqpF# 'PC켤ӛ`s8MzI6#]faykxδE s>B'9ί0wB$ D2xr*7ԋ>w9&Lw>50ϩ5| A:^vEYyUF앛R{O1K[A*u> /WrXy_Yr0E0uA#"*OĒ=1G"xDm>Ob?^ʷ~I̍7lP ,!XaeG u ]S2fF&^hW~6jal`-(DqSyR3m}ul!zn?oD\݅Y=iTn2nxxFױ/9ܗ4e%C D ¬IΈe'P{P<~d:a7^j |{韵#AL͙ޒ3^5#>*s,ͻy:*Ew.ߥ83Lo `ˬlw_Hċd4T=DZCn9N뭈OܼO˅`L]bL74rt;Ar[ZegQ4:DP'5GgaY18̦i=UӖ0q2piUʹG-G1+}葇k䣌c5_Ю7K\?"w!j@ɡAKj1>^Iɵ.)05):ѡ-FDR}7bw uˇ o?P i쉴H ZI47QqSh/8!T^rYCs#tx>0X7I'®(bڵL]s͹PFKRVp^F/G2?їnTVWN&i<;Fh1߇{|y@YG^:=wq1bL)wM"] =@4C p|&Z(S;IZ V\1FHxc 0'? "!quBI;W35؇FGyN򘇟1$~vUHaw<; 7'1J5vC&j7ߑ#kV.xzCst2AEZlv@#~0ѓr6pI0fy@ucBW8gx~-R^ŠF|m\'@Jz9pђ*_H ̓.zpW&GE8^S2'xGin堨47@P/(bmFxhO}V~D$z~\ԷQgyX[;R.vG%X3KHɸ>]?]SJDG<"ߘlG(jT,F-ɕG$62 h"& .Wnu_FhԶ[L",7'Qͳ!h WCgA`Q {. o~P[xĔNkt;ޘb/RtwT8Rqђ!-[>C{䰹c).B$&w(8l +l9bhj rFs)x9` EHɨvK CoRKі F\ti2a<:G)ZΖ_F!$cgik{rP85D;RL&nv_ qra/@Zo)&lM,y \:10K>Q頙IBhPcj7RnZAF>5ztsӘ[~!}ƆOS!+0Przo^8ziφ,kmMnsf ]ëFEYnpZ+ۂ5:4'9~WkݮktwEĔWjCGDHO͉;)kڀX&!{0۽ G^k6?T\SG˽ / / ' ;H+Z\ QS-rqh0 RC'aܕֻH^ v? K՟&,ڨ㳁*Q %/ȯtǙ՜"5ƪx]xy0bt>.ɶbI&* JҕHzb6'mٟw]JoxƦduOI oPiTo01b/׌x'm|QX s-n5\gan̾"S{QTۗ uމ 2s N{}ݰXwU*cĴnڡW{1>a:͈b.sEXDwA 0ve0:(],xֳ!z.B\2Dh̘Q[ Uם4m}8&Ӻ]p8q 5džRBA#9ڵ"N&q eT JC&@d5š1"Vcd~{gA/R_ nu?|"}pBq%jj?c5CzH|iv!t]PVMr[L`gm txBcX'!9kfNSQbS.Fsr!7"jvh>כ ꄚa\ۙH]C;r^E/&t~$0^vIGCÛadeI8 ZwH#@Yc.vpit>H2f$=Pf~~)Wux:2A?694-<Ԭ69]pz|[i$G`zt#Lt΍X7<&\Sd6$ܧdZ7'Hl!,FKڤًgN.VÑH?HG7O7H⺇4W) 8t 'R*Ykѯ{{#}7dw" sgT Ʃ(R 8[djd3w})-QDL6@%@3C6AE#s}1NJ6KL+G7o,^C)* JɇwVWkR<Lhટy'v"e}4hjDPBcN!"l7a@޸`{ty4ȃuo\ ϝ:$vf凤Q2*64ya(i$!>˩F-fb[\o)'V_Q!0ڲݰ %*](žogN\kX&~;d>@Z}C_ I:*+B>Eh:8l KT'/5N,(v/y88McЍļ.G+s9 :xTN0ˁ-d4 +DjdۣO`T Qܯ)$rx:eAvY|u1}kwI U>WJrZ\_[-?CEoMVK ;aDw9&j>q`VInΚJ#z70ZO,U]=Azޅիw-b2ثͳ?ruKZ}G*GZ8lVkI]. 8-^J`F-4ۤ`װ v7U^Z;(ڗ.6KBfsG{^_))'l OW2 40h5qQes+QyQ0pM N(=/Eim+ F5/>|'ClS3GT^!cF Bc5.3DSu㪎 X'&9L_`%E~^$Ed9K?W AX=!D>7+@=O@S96;wjh%  uh2jLO9,i3"8QsVDԍB-0&!1ž'AS6[au\tf||gO֒?YC"}tɥ>cP8Q)mX6XKT{$Zd8)HLr#ئzh-e!Ti?A$7{lOL:>} CmuʮggHк `(u=lG鹀]Cd c1Bo=$`Q8O`| 0"Wf:yhMB@[/rGKk*NGIIܔI(VyBI'~o T']S)b^<'0JEUnv ,<D)5+')qv$%qfit.dJ/T~[n=5lt ?DQ*y~> BsxQ 0D$go7A+wΪnM'TęQc "9dN{<'~tT(EƛR OHnorCSc,\%Tj,gف]3Bšc?cR4zuUq$߻kC6K&Y~s ϨG&l`` ltDtip  ]LɬKz`T6jd_`7Px N2&7-sV}"ʹinnTn}M^%aU8fzJy{a1jψ,5xxmCSEZV,8,͑sARBVXĈ]8Oga$H~w]\2 %1\nLrәl)]T~U(ѱ .he)tg|,ۭ.,@u2 oc{n3m::HRtC;5Mu罙&k£XѼOZ&+~9MƦEO"% &oi/Ea 3@]=(X'J gW6 *4pӊ,l_͝NPabĨdk"t. g -x('@ zyfFZ*W1P4} 8ԯnw~ϳ7 IeF,H0J˚J:d*b֚_&M }ɺ|9#nnwQ K[ {Oݨ02TeQ ŧ$Ǥ;?M/٪! G2j:Mf7/5W]ޫ}hEy`:}*)2χ:bN.q)eE|'I&/nByYsRX x 6יObo[Rg#%dZMeQDIǏNXMt7~{x'7 4:]IW}l^D&07N Cwu^Pe,\I7h-iM0#GԠbђ+ozh։[]H&_a`iFv<"XecUw| |vC%B R2пMEoEյ̺92\^B{U6lW0 ;6Tm"#OXD#+A7R6>~kt!+@pp-+X3.qñIMn.]A1Ddk=E3஦]_'&{YQ_׋mB́0m[])zj|DΰAnd Crӥejw';3U±GPsk򧵥 徃ܧG;i0&ub EqocPw{:W$b4:$ ZDr^Q1 F=X!PZ#aD&:Cfjy*%dH/n}_gEm@>p~XxvΒw~E#U&;Z0<Òj7S\DjiNZe?a(̳=yEA۰=p&40_H9P A Q!$rƻR'K.Z.}aȃXfʷ]@5"m 37VTEvjA=Bch:zݦ(>՗^]ᔌ&t#gm}:$M>TT[ /Q!n\/HfZ*קHSW7KշDWxRŁ=18Hmr|EP#054 {=𿢘Gx0qG̯mUZࡨYh{߽ g sR(b ]k̻^W< 6oq@14baLPp׷1wG/R6`g: B.,hJ|H]6n g:b_L{a0P&piR+oqz+@pbʘ?Mjs@{8,̧B%I_B9&.Rw<)_&5Kڟ@OV?avI>VE:IzYzrϜb`^KS9_u.NGGcvEgщh 6";*RҕuB{P`W.YReSejZ3~g~R]CZJtcmlԸ@AkRAhի\$TJqWIecbu`OⳚĒ۵O 4;z̯:R6kV ~CѢ{X^&HF^ņEC- 8 {Q0SX.EpWs_"*<|8??`ޅ6M=_Cb&{`xV,m~&\y~)|K7N;t7c0dfHTcc:ߞ_cٻU=v #΂KTE1V%~(g.j}/}$dd Ygk0g[Z0Cb--S=4/}:i,>]m}/4.9$HRC"&,8k!O`k2H}ҵO/ )k\yn9xZ%gmX0b?fqn'SPF:^_̵*3Ը1P?9O@)ժ(Q@I(]Ss 0&FH^Nj+!F~|7U>lU34"xBj]:CVGOi1=_]oKq7MU7pK Nf 9F~[^:!&p9FԚOa^p| y3[aeƳ͋RdxACh+_隧#}ku |}ޫ4z<!0DC?7H s4;RV@'æ,sΚ&ivq~1L`o*F:FB%Ct(sݕ;w>$ uq@ܰ2/}Dk2C#DǡOd"}o8뉤a[OyyE@{d'`+NB3jpa笢:(A!ṐdUxnF y 75Oy'|s!u*2vB%Xn:iVv>NX?(U3"$5!]6X(}S"RDH6l1pzYT?g&=Vy,/LqDWmu>ȑl>א9?OBۄĴ\mqK&LJmLh&F# Ϋ|;ci!Owc2NpHĨ]wAdP  O\]"8@6}÷($Ĩ־=V`*Tj~3RD嗙0ΘZ 8N0K˼=YatR6=ۭyns2f$Ё+v-#a#ޯYGx`_=N^!nyf.br~ieW&a蠃hy xѥ^~+|1?Ϊd,R ̒07JD3k=*FJ9gfƱR X8b@;h@S ~bjUF~\ϻke9#+Z9Rb(ٜE߸ J8b-MI`1Zx~_Y0yLZg)do_3-?'R6"X!-))n^\j}= 7=}9^;?б:βvr.,TjnW:9+^]?[.ϥ5MJ#5pmF6\T7%nۧi06zUԆkl9Q H}kI#^P-'C+RQ]oks_B_.7SE3lrbg0%կOlSvR/F^%%Q$_]z]AI jU]4*SYa.==y(e.2( 1]3|nWT41ih6=+\U9kg>tKv iUۍß,TS'62WtH،$7&럩rpc3e(w8u-hRApcj7^5WeW#My}-KuPa& wG~ࣶǑc/P Z5FQE|l'uQ'MLpFs4Eb{[I;YϦ-9-ɳ" !r ,P"r, an %>i9ϲOloBL׆tSCX? Y0 [iSв ySyE ѽiREjY5 DM4O3!A;5s)_ըRl}3zn@yPu87hY!wדe"u}YǘCR H(r6 HAXΈ҃zyXW[*("רeC4w)ڀ[%E;6 ڜ_^|,hjdj]X~KG4O@ O1)Vh1$wKmPcDs!ʺl諌Yৼj.x}|1\(PkHijQsu^f'efz}Ip SZ9q9Gd^x]K¶"J678پV%B`qCm"X]ɗe$J˂@["y%j3v =t-a'PIIJ E)"B >/(,܄ѝh= 9{2\krd O2ȓKKxZK=p]c˗0ܢ pt~yFV7ʩrA󁇁 oI|=3355[Ay8jňttGNcy@FjIr7i9n< Yaa+4j 'wC#W3DCw-K:bu<¬6R6oVy`.4rX{"K[D)p +v4 NղP,dgJ͐=&C }](Ջ֐ёR{Lhg3`4NZod6qcId]b ~o,RXqT(ZKwr< "o{ “|1Hx!aKʐ8eOw9T@=pM [!hS]a*9B\"ߵE}Ngqq%'-.k+jBВ1yE7md*Bj/փӃ,(|}b.PH\{֩<ؖ7@2"o8lYT& #7icN &D9o-%-lF 9Ӂs5ٿߴmMZ(?TEcZol[4c6<5cZ@mWenwO[V^?> I QNŹm+0-{u/I xBSREq-|9S'-HRb4B.8Q2xd4Zڴ@ ֯_ݿNMbmY_d4pFvy7]%Ks\| |dX)@:vtr?xw᱾ D>Yèd}Jy9㼗t~W2lU͒{|U'pRL  *`NOKٸTd)/t HjP9}4%Z=9rb,)N593Њ]G{"&m^SL9Š%Ƣ̹l8\ .~o- 1TSXaR\ӯ?fC`'F(YvunEjqK^]NlU@gK"iJݷ. [?7jf3ۉGĚϭԕ )(S:ss,MK/UD-MhkctÂuS҆:2ٝu5&xG^L}> _]}^\ z@&9.qj7 7%MUիv[Q\-la ך3ǽ?0 ."߅2X[-Y(r< ;kP Dd^MÊ9tI>Dr~Y?;Osh&% ZP_1OhhyJޮ[:8bgZ(Tq&Z]3uj uf̷N1Kqȸe !m(wk9<e="38)zͷ ybzA̤G̿aA7N'Nr 2W?qIg^Ո~F0*Z} 1*e NuXCLM6M(J.Lˋnf&b60^Irpv9*/o, 'mlt: @vP ɖQ6R$"b̓ۙW}(T8E9@󤕳5=plOkO-qipG[-͵2ٍUH]΢F^¶@W h&75Hx@C|syw'kᩖ7ж4`.,۰V!̌'50O)2?楤h>j "9BR^tq{0ѱ١lgiu,jr·0]!Q("r=nxȄe6.ˎOD2ޝ/E]ğyڧ Сwƴs k$p!* GSu+aږSl&hW.(TQ21TRQ_T&߯~B g dЙsَQ~KrC ~ۥU2|"i}wV lh}#?c>pu2$Pl[^- !Xd0~?9?2wJՕpm (< $Sh2󬚌;.r<:r׳9:uâ847vwblK Ki03 PK/u{TȉZۄZUqzlmfTI;Ɔ5 6=dSsMǀ檔5Pp>PjٟO[Q̂P V-:~}x+'id1FSfP@—Bg{ƒx0n;X{*%!A60|6Ltu8}sDg$6g&`^q[qyF ,p%NeLM<2n-]ͨ' Ʀ#"4jGK`ŦaOD2yl>uJ#SOwfGSp@WyjaJ7-ܲn̯-\еsMIs忱GV`ިH~n^\.̺c@$ )I'M%D2SZY R (~40MMz,oh>~YvNvs(PM?i)r%5UxQP/M k4DX PQ *Gv#S">/ iA΁E 4IAA|ҀNW!hR40- s2=_1`@VZ&r (4gULBߧ2 &2$pP-n 䪑 } ҡYZtO{(`Wr1x-k'ov1ViEj_t[_f9;8tsm^B5ӆ-\_oIfs4Ac7]x &CZ}gRǔ-= ox_oyZN/˯j<,}%`%28(>^G^+AXI4A5JK <|`H#4Jr #XLuH~}1yd guͧșv ߍ),`un5~m8O;Ī m%7𧇫_ݻ|f)/4BjK"9mbS?jzQQgq"i-8d4lx*C D/FݟN-&mBslӌ.JH%V p \n DocY{p)Aw =/[yS,> br m|r,Y$>=Z}8`^b}iu^/I~z(0) $*昦an|L`A"u -jv  M߱i=V5ֿ\ɭ(}\P%[+RFJ=\!Oz&-JRKaT=EmH094}6?nnYA#'bza:_*OC`ͮҩxRW-i%in2]1V"dvi)^ w]`8M ~+\\3wч6:]0'򟮫 Ù7 8!2:7-.T^**7\Z4xw2hO*.µdz*4}X -bÖZxTVNcyN\N!(%DfN 'BـdJ=4 h,$Qn㼿 |BjP*^uFFM(&;o&}!مy_<•ݴ+iHX~lvuV򭿈0:sUM1QY5ڲLlW ﭼ#VhLZ9uiBnXsv*1F(sL%:)Zam!XL>)|޿ nn4zɍb6rB] q(M]=IIoOQꅁz) }9j+[:#~x,AŘ31M"T(ٕtsR]`6>D\fr0OysպɌPjp =N0]zPo,ٍ⫆I]' kbXb,vj_~Adˋ?V~MZ`F{A퇜Om9j!Q( 7;$N=W$JAp328f y˧w$ɓE66#f@sS 4.ȶwHCA`2)`GIu߹Oψbq Ji61;1dn ( "S?3v80eUF#D4bskֻ}хFط"OMkG- }1^r DE޷탈_?%uwHScb -wcUkIiZ ķK=X)oWBR:S !43a8Z?%xcVlOG&fVO<%qߏ ~+ȷh!:ArU©#=.JZ e) #>(Һ9J)Ḭ;r;]wS0ºQ ;*l4}%ƀUIqaOE '1ƀrT~yvZstDL3֏DRʴcot&CA|kSfj~oMYۢ0#1;qGAH7 @jTVKb*+giI<)= vH fJUMI3J%Jnʑ}ORTOJ#КO \)yA,yI FL܏jͮ^o)o?(s!`Dsş4 {K_K)V*y@w;RhPly+m#5-iz=zCc3qR.1|le!Oإu9&lY3UR/x{[(d*+/.@8Q{)[R0` g̡8B~ۛ!IGĜC';l2g/"jAɉf]aD;mUטzh.G"J#U9%3d:w~kY.V6/`=:p n8?v&~#kL*?SMcQ?3oz(ko1 r"۱gzXM䋸T]BCͯpZpC\\'^.ƮTe]IS%&kroO8ИQU)LMKz̯jbphv5} dώu_yJP%ǛJf|o98iK.Dmryg8F 0l2jma`G} ն[wz.I0ejԊ_ɪn EeeUD%HGȑ'@ߥ2H9y o0eq?\A⨓7mο,\p_?r*uPdSꂞ^eK&3n-D 갹\Y!.gTSCp)hYx]0GG M=F2~¡P}+ vt#p:-xBuW w , bJټ/h+l{ңmuZPRzCUu%2ׅHUm416 }9|H|eDEwɼQ{p5ѣ$*.ojx^Lin&Oœq61鍡j1)\d0Jad?Hm3n7#Dw G81nĦ![聵u񂍕Olnq  '  HqA[N ڿ> /&4`Fnyz20Qqk'CcsZ&6{=]ȀoA]mh>{p֙-(rl1vi Y8KdIG0ZpD坝L +x-;D@h8$%3Sm K0:LJQML%//&ha,ڏz-sug`d k>1SMab, $3gE"~kPn<Z(L꣱w- Hkd~||CID&f]v*(:~ 'P8ڮ,iY=9FϴtN$o{^M&LeLm=f{J6 :e8|/+JرIi)<u{Ҫ} xcKYѳjpwU,0AD56"00V%n$?x:B:I,vW[?gnGgT>{j&HCӂ@e>Mپ n+{B߹xJ*'KmoX;s^C/UBh_ ??Űud!I61 ;6$>㫊&rLEGniڏFnt G*U˨DdWW; [;{*jnWՋ-[#XㅞPa6)̛]5t<=@&;z0qݱR1( 7|~p^4wi)k B3q U:GSkkrհAP"GQOY ZA掏u4?T pA\j`{HӏN#YKTl~~]$!\ۍ3׸ ]NlvΕ ʱY w IӪ_>W i5tR  >D܇@T+0+sVڥf?L `lz\AH& &':'"5idaUPzŨ 8 `e0Pox|L9y3Nf7GlEF/FO+#z+9t^N](W}vTEi(S<$<-jM衼{"sx#503oE!E2EC+*Ʉov\8 35LK݆nS(\F?}JQNJ$>7i9,/%5TT_'<Bq-LoӲ̨B\g B;ഗl4'{ Z rMT|>۩518!b_莶]TNitI4Q V4ԝJ-mܞvh#=?rȨ*9W5$PtL DNr2]1ܒ!Ip-JQklEt/\jq39D;ky"ar2РaZEȟD L`bôK79e,2T[,鰇Yɲ&?n.Ըyu֮i%*Yd ƭBbWMVQuLy% ՜t+߇d{_E^̄\=&(9 ҁW9 :raA;](WMb Ex\{px+ -,1VSΨ꺙qm].^0~As!x],0-2҇ȁN͛Vp p'X(\0v ,g|[]"Puؗ 8Ѷ iVɌ-0mxbsѾS~'7ij+7%,6ܲX',V'KF3?Kju̗vNF紨.Z'Hݕ"֬f2ο*=0ktp(h6jd7,G Km o"Wz>*7id r|*%h;~~9Y^ KN}C?o^ژTzIcdX`x<<;n|z 5zy9'a`@hG gB@:ӫvIhMR|J71AA=|)#=vx@V~)EEX#V_ۆ:|Puqd.* VtZ"VfgѠҕJ/5K玮]iyh=)yUIZYDsG(D2N))b6Ad84!ҡ#KVg3Lp5&utj/^k- ND5#S"<)TнgKDҵ//M }@L=[>UI?@])Dn Ibw obтOPPAXx أ H0%ձTw{)GiKQ(Ѭ始Cp)S{LrJ$^MX=QBi sr\^5c ω62Og&:T)x"F@e2}0`gBW^A'IvԒn߁̧Bn-`gi{<8bU JM(2m{0k1־6r20 B/z# ٖ T,S%)=5 0آ[Mh3z>m*tK 7-^ywY!lܴ0M@ى@E1Kp /YƠpmcg\n+R SMaoFf%Xr2[hgrn VUkD +9KBqmͦ9rcuG<"/t 䇢 Ej뿿gkuPi%foEO4z}:"yT ˈBpHx2#pVȒdRDdm(|2[-W :C?xC/|B ӗzJh:ǘSS/m1.BͶTNR]gm! Ma:btސAȰ0,|rRrgO;F@ Cf?+չ.6/rR4噵"|~)eM9iatd&>x%a}Ϡg´ @@>SLyDT$ϚKS"Ps+"yBc8sJBX*yN+ lYCx̊W(&kq⣸}?M2*M# HjYZcRc~pɮFy1L ׎mqu1~CU6 ]*қ"xnɐZ38jDNL9Ӫ,^ӴY񌁔>Ix;4~= ~pDʼnsZ_hs_s Xږx6bsD%*y |5L)<Ε]<v:!6 cviGe(iY#tX~+UN6Z+5Y)UbbTEad ѤYJk>~[W:ykpʚ f:Y)j{IYy(O96`djB)TNlFA5~2*)S+(qM{*ýlӓU3]JY7t" p9A7bN4[s"񷶂f*  R'IBGrޝ^ -]K Q˪M+!B%h^uFo5W-G TƈDO 97 V]g$=_ KM: 1c**n? 57ceGnٓJWa{Bt9Ba:53a ?Ʉ1 9|\&9qKNUf$Q|G:!O0"Q)v:W'zi;޷BMg]s]WK*vo"dYBtPALJ/UXVKlY\JmN+" Ql%VU2Kf "!CtS$,^2siz)ijM䎛&/ sX!%`f+%R"!P˩Bk蒄zoce㖰8~M5Kd:5~7nb#Y[lW1_q'G` 2NUw]aƆbmӐpl٦i)9pENjz99gf js!cpp|$ Ļr=(}јә6dm}T9vVq=R;Qu`:PiQs\ToR^h8/AݢA>@HĘE4'K}(cL87`>gi-)%90(Q??8C@p,uSEϠ/&DO2t$̆wMM-=@,KL8\}۴* __tH #K b5hs_=!0HZ`pZZ|$( 7ҏY]Eb{ʱ T{GЧH\6L[Np }9" ǁ#-K~>lId=9B U"d2'P­x4qKFewcSy]ol #-zwʽeᆙK)x%M,t FYӖ(i~Bc1op{V o_; U:F aO Ғa?_0 @x jX6ae5lٽlij)X`5ºzkpMf̾%[H43(XWgXdt15y;ݱ\=ntĹh #\b2$ܹoj\JA ׯ*LE&tKekEioq\@ q z[7iu2:qS?i|/ ĔR9MdRP󛬶x\QgިjƕZV!uOw/,$I)5T~F7 t6dV+~k6ɕ׬"\W5b!/m@P-ڔUB ן?;G !7HcIcj?'LA G4 yld9I_1E)n^ xUŞy, \>;ԩOq&"`1]WGShcś~ii|  |KbL# U63z|W5&'8+N] _;FHZ؊#PU++fuzPmQqA m}ܻP,\g$WtB>IP#͆ݷI ~*_jaz0ޣKL0rJ>Q'xxZ%bb5"#Jz1S yqVݟjm.c[m_Le!$;ұl=zhQ|I mt d}#)Z9!tNǡGh{o 髤KYzB&7?$a@^䖦wՙ߿=LG…f?$RDD,;esC' Cw*;~S1;|5+x2Ұ$C ]/\ (Y~DkQݍKܯRz4oc/NVẫk>:ntuNùaN T]xyf_vq7OEm! ҅@S\}7y( Y~+[0Ge:36`?AsUbd ^6.rc:84wbXUʡHkCET-j'9Џ8gE8*笹"ffg$%0l'm JEnR(|$·o neX-}<2a#cWyBwSl\zӯRQ2qNJ26(qD}im!% 8!R&l]-C ‡q&y`q}QmFx~Ɩ0t6`ǘ&{ʔb#r P0\JU8O,)+=ureRrFɾyY A@]^~wK><, \Na\X5]6N@Ѓ?>~b%QU=p ]":+Df*!-vo*78M|P0`$T ~#uuR:y&["7Eo6պ)#/FO-q v_[vWkfrjy]l!^^+\ RLC|{,0z8ahB/Lk+R ;ȲNJnj c]JXX< 9-Y:6KTyʡ,K 9tvPKDvLRf!^:l-PQU oE5$L4!v_GyQ6S puTf)hG w`G/=`\:Ao*^zK;dee]l8 "^;k6t87\ի|Ujwz_s-Ā% \&3<Mz k3 "@%-tC}}oO N; Ƶ5au!йđ}-O,^1@ m6dzStW#A}lP!c$܄Q>RY9qfSy[(Aq#iS U a!ӡ\`!KuŻì3F!CV mE}3W<ڕ'zIoDX[W·^[)%Ar:Jv[s(pvV%J+nDk Y7qMQwSN{L25>dV")f^gSn`?c:9t2qPJ!^#?_[ Tq.SՁH\PQl7! ڵ? f1ӚGDojP*+2?<⋑MzJCb IH xƓ + 9/C71DyUaj19T9X433֜ab؈zMT-^^Lr7ko4p8\,^gj42[+W ?b D0nb|LLaK uy C f8nl"H Iig/ç-38otD/ 5S*K u+GR31K)沆:г3V:VCϷqwqj*>sڝĻɫXUw;0^BL@s,+xAu|3HLO!KMY `n1MK dM/唹a4t$Rmʹ舻+߭ -{@3+?f"FD-Vzb\ZBB @9"<;KgFb*7 B~dIoDz (-)u*!}Xš`sp_! dAغq|C7fZ@{eѾUyw1w0MoV1+*?^pwb?Aˈ/yxͬ_Spt$aRPCr!orzex![N‚͹C;yPeɁĤ(=Y-mKYޮC< C̳;t>4N>AaH6P=5-A &; f*pFtI? Ӝ%[R?7qVX0GQth:` F#_&dGlQ' Vv킳U/+/i}U<7UC-փ @kqхD ̠q6CAA9%)X>OA&vaAYY3,GR(EwKv1S#0r#=p_5ˈbSaa 8"A{`5e9H#5) C)5T Qej dDŽGa&&4m,*;ݩ+De^؉]"+EGhM|n]b>" *,5 u$%t>Z.k֢kP YAM¯f~4 <#}.@A (yKQ'g*^V2VZ`ttkՓW-+263?q+-•><ۯ'/#6TI$ A M9,ϳW!cG 6q #CaD~mS4vBDy%k,:(ʹ~CȌC?{Qi,ϩ&"|ɂ'ܠOJuPWrEђJ2ʛ({!E=f87'A-  ->|@4nb}8r6Y4V. B. ^waB1.} .@Ii֜G-GׯnA7% _D~͢P&ĎgXS"EEJX-S.xҿlSpjȵd:^*+EƆzkM8$]Fx(kDBAH-bE%&SC>i_ U,w w4R^ ,)?*EaB"WK[v̜}fCaSO3LdG# 'uASp?FFF"cKsN[XM>ub#Q%ⅆd1mipNg~{ß`V=돟3o *hj5#~zJOS3\0z:}))X= fHl}=梞:WjM~ۣ<GNB~i5 |Fks|;-vХp3a{]]s \vd}Z'ɲM2C>Z$aCRJ/A17cJVNC#]tK䏼dS iB& @LezGThNEMn@2owZYNvϞظ_Zx$Uu!z3eoj<v} ո\zͦ *oݮ䕞_&u糬8 ]S"Xqy\?!EO]\./m5sZO(#M+wӧB" QkZ"M2{|+Y|e'>k#K۳X0k&o] ǛZ_χSI;^r]`@"eSC]1a|x6_x>`7}+Ό2u<ȓ ~^Kšxb;I6"_Cqj}VݻKlp0 @1}UB%>v / BJ~6 Lـ&dD♿om`6M;A]Z-زI7Z4@CGgEd ,ƁbӷYOޭxf [^ 4CK_M891B^i;i:5 QPMH/r %ތ+OUkDz@J,xD!JLz均 RZ@}@A+Sow^ χK k^kun-*z<$&ԁ 7hCSbmO.Ql?Bvh#ZU$Ԟ[ {rVG4#4L^=p4MRnf&W-ϡ&G~jW٭zGN4y *}lm  0d/ȎEqF nWʗX3VREJjx[սϢSazʕQ kGƾ d7?R,Q-XTϹ,mJH;j,ш? W">M!?H9oY. h5^Dn6Fuq8a W;\-vb͊|Vd!$֘o,{a )Cu@nۅ޳WzX8kK듔O.Neu BoL7lnX_¥>qG-+.2¯Dw "pWpɎٵ { %CM9H*/:%A, nI 3]7DVOOۛ9(H-}OUDp'}eP3iY5 ^qc =ÃL)#YQ4?7#œ| ƽ[GzrT槨+씰EW\φE1Gcdut]ft6y"p9S'Ր#{& ;Ԝoe+iE$S7۝[91։|k1졛 \Rc]Uحd{<{.c-βgVLڙs ' ,Ă$FeE.bC& ,.~Ȕc9 yGIHrn2@c-FW%(V0 |bחC WB. @lw5$qyЯBśeqɺG%β 4ۃl<-%8v.%^l 3h9fao*8 Ԟh< ; ȑ^vWIc2SVә ~hݳX< 8t5.pe@a[̠ڱChբ`]Pg}2u*|`P^sԮS,'WPCyy9NSroܝNoD %L)}sR;z'x.ଈj~\U+,-8_5*vaxgٳ.Mry+wy|EU}Sr,|\ {_O9p1LB%=pDӒ_X9}Q9uvht^ %3xN ?T[&k&NTZOT]SDaځ^# 6j"^`0]/eK)<2/ʢ:Iw}D&\LŽ^n8gu! aƸq}ޤ]D*Ș ۝j؃Q{uۤKp"B|``tRk]3 j"'4k937f3U, k\ ȥBpG90O`Dr[N)d`4"G">9ҪWSD?;ʍCĽ-+~ӒU>ko|^'F/?cL{nլ#4{V^W7QzV(,:\K!ЭC6xקJ 'Ϭժiur'qP)+}dO(+`x ` &!\"[՘!A?4;fx^]U{s"շďf7X=O?q TtLZ'y). 1 PAG71RV':,qSwK1Ao=?Wg<%k7$`  +:4.,(\`,EsT\o߈kմ682})Xåq4bOX֎}|HÔWi򘽟w7fkB}mWVC4\H55wumԬsb# #{'M`" p( v^r/9mʔ/cW+C\TTἍxr5vŴ]%^`x63OP~^hly8L{E"P!yo&Xm1\<!! ͭ?g:Bϙ$ބ[rDe33f҆~O+>ǨL=ScQw@ucJ9d9'+S >-Gpcopb5 iyRHD/a~IUMvy\E\QNJI3jJ:Wqy(ū7 &"r!.d飙Fb2\svPŊ%,ۅ P9V X릯M$ď Y/ ]?`ËmF1ʲѬ"ۖ< PZ 89N/M9;NAMǒ(Gq KlWY r\(]q>z>?Iq##Y.PY8d'Z7:Ő:ƦwpݖIPāRXRѮ4{K6$Up}맜cToJZ&7, 뗅/nEUNJIF<ꧧ"͸'[.E|8"}7*hǻ@fxhKW}}qjgB{iޔӐ7x~:~QOc^yï ȿ[p<\CwUN`"Rzr3a_1EJT!0f˒xf9}t@X;G}躰uY s*{׷O I$<ͪWN1Yȿ7p4!0?֫f".`%%+H*IƟF7&=0ѕE}t8-$RHA0~ IB MBK[lR7)Neh59Tw()d ڸÄz$(ʂ#i=s!T*u0?,Z:¾RY6Wٱ}N4(/[f\I*B^Xܯ48N6$]rr_![olBZz*|TaKS :.˙kVh\D +W@a(G&U7,x)'@( b2\p&)h&̹1<40Z?H;;\`xLt5pq.xd@{ Žل&YF yjbkM6aQw( 2Y5OeW 0ODl /Ia"۽+Ts#*j$hgzV3NiѮ }#92 eTi782SUx 2Is]\[hZ%` {GIBfZ0b#4\Xtf6-DuԾ0^zȜ&5W? 9GAvd:B%nlyV KgOL͜I&,2RzG9j1=BO=hC#WP`RN坌"(В@׺YJ&gRLW岌N)y h"R<`6?\]?"T~ Ir5ʾR,iWܞC|E0˄bɟ GLXrw 8Z; !-ZK Ɠyq8G d2 P]V4w$D'>LWlBRv3ٱCNٻ ?$-;̢H\ H(ILԦzHAtѼVZ2/"ts3P'vI=xDo L+[5hյrU[ZWǺ|%,$FBݬx~iIecl6-YKrA ڲfz%/Io^p!Rl^t=-nj VhB%== r"Tt}JQUy;3AC^F*֡U fr~VEMW%φc.M$x񡈫c1DYR t^qN){ "BTńp"ti;0^f<7Oʼnx sJ &KT-(/\0RO۟ʡ롤|K͈2gC^ϰVbr7Se|o1rTQC6sSmH̨T?k@gpcph= [/l&SU{kd2n[IP[  A:Sg3'.KA7(0EsjֆW6s6ug˩l#'Zҩ'c3a"}ԦNv& (@£>둾cE6щ=7F1R;Gj[gi,xbiO|;qsr~Ub rfղWUY_-x)YĄUޚ(32O$:ػ{?ADk^A(V~sF-tة ֊7:l`(K$Pk҈oyzZ% KPЪ4@pf{⹯=*tfRs@nQ<r":[u** >/Hw2cU@SR=Ygm ۣKfkVrHUqCF|j3:.=6vipz  B:O1"8Q"DL)8KKgH2\oO(1IO׫c&K(!(J_>᝝}QuPoBUdEf B^:>P䉡}5];Ŏ©@ V4:ӖCjʓ43^8Ǜ(ᑕOV#9;DҊyM*jHGVlGpNpd ܥtIbc*SdԬ^Y(^ONn4 >T̼px<&-8c_DU5&`=XRRgҢ"Zmgl}Z?zP^4Lɯ` s{Nωyu_mpXz$ S/:7)YʵJIdL4n`k h̀DV2Ҵ@=+?76Sf,tcx=u۸g;[P N][ }U{P"}՟k2eM-*sXeX]\+M=W޼Τ%)ĒN|o*.c<@rxU|E=^/k_3}9eL[M{u׸N횄Eܻ'׿xW{Ű2;ǹB uf6a/'VobŔ܆>L N.OTu]U~U-`޲p{v/vu3ﱇw4Ž&)ÿBl/P<0ɐn{ĺ 4:/؂eV