From 45bbf1cd3fc25ecbcfe68bea74d704c355406d40 Mon Sep 17 00:00:00 2001 From: Friendika Date: Sat, 29 Jan 2011 01:25:46 -0800 Subject: [PATCH 01/23] document poormancron as alternative in INSTALL.txt --- INSTALL.txt | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/INSTALL.txt b/INSTALL.txt index 70e4f24e..12dca9c5 100644 --- a/INSTALL.txt +++ b/INSTALL.txt @@ -29,7 +29,7 @@ php.ini file - Mysql 5.x - ability to schedule jobs with cron (Linux/Mac) or Scheduled Tasks -(Windows) +(Windows) [Note: other options are presented in Section 7 of this document] - Installation into a top-level domain or sub-domain (without a directory/path component in the URL) is preferred. Directory paths will @@ -89,3 +89,18 @@ You can generally find the location of PHP by executing "which php". If you have troubles with this section please contact your hosting provider for assistance. Friendika will not work correctly if you cannot perform this step. +Alternative: You may be able to use the 'poormancron' plugin to perform this +step if you are using a recent Friendika release. To do this, edit the file +".htconfig.php" and look for a line describing your plugins. On a fresh +installation, it will look like + +$a->config['system']['addon'] = 'js_upload'; + +This indicates the "js_upload" addon module is enabled. You may add additional +addons/plugins using this same line in the configuration file. Change it to +read + +$a->config['system']['addon'] = 'js_upload,poormancron'; + +and save your changes. + From f970d9ab4d608d36aca405c11746082156e1e661 Mon Sep 17 00:00:00 2001 From: Friendika Date: Sat, 29 Jan 2011 22:35:11 -0800 Subject: [PATCH 02/23] implement SSL redirection policy (none, full, selfsign) --- boot.php | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/boot.php b/boot.php index 223c9416..83ceaac2 100644 --- a/boot.php +++ b/boot.php @@ -10,6 +10,16 @@ define ( 'EOL', "
\r\n" ); define ( 'ATOM_TIME', 'Y-m-d\TH:i:s\Z' ); define ( 'DOWN_ARROW', '⇩' ); + +/** + * SSL redirection policies + */ + +define ( 'SSL_POLICY_NONE', 0 ); +define ( 'SSL_POLICY_FULL', 1 ); +define ( 'SSL_POLICY_SELFSIGN' 2 ); + + /** * log levels */ @@ -273,7 +283,14 @@ class App { if(strlen($this->baseurl)) return $this->baseurl; - $this->baseurl = (($ssl) ? 'https' : $this->scheme) . "://" . $this->hostname . ((isset($this->path) && strlen($this->path)) ? '/' . $this->path : '' ); + $scheme = $this->scheme; + + if(($ssl) || ($a->config['ssl_policy'] == SSL_POLICY_FULL)) + $scheme = 'https'; + if(($a->config['ssl_policy'] == SSL_POLICY_SELFSIGN) && (local_user() || x($_POST,'auth-params'))) + $scheme = 'https'; + + $this->baseurl = ( $scheme . "://" . $this->hostname . ((isset($this->path) && strlen($this->path)) ? '/' . $this->path : '' ); return $this->baseurl; } From a362dc46e3ea75a9223e753d854631f2b80c6762 Mon Sep 17 00:00:00 2001 From: Friendika Date: Sat, 29 Jan 2011 22:38:58 -0800 Subject: [PATCH 03/23] cleanup ssl_policy implementation --- boot.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/boot.php b/boot.php index 83ceaac2..c4afa07b 100644 --- a/boot.php +++ b/boot.php @@ -17,7 +17,7 @@ define ( 'DOWN_ARROW', '⇩' ); define ( 'SSL_POLICY_NONE', 0 ); define ( 'SSL_POLICY_FULL', 1 ); -define ( 'SSL_POLICY_SELFSIGN' 2 ); +define ( 'SSL_POLICY_SELFSIGN', 2 ); /** @@ -280,8 +280,8 @@ class App { } function get_baseurl($ssl = false) { - if(strlen($this->baseurl)) - return $this->baseurl; +// if(strlen($this->baseurl)) +// return $this->baseurl; $scheme = $this->scheme; @@ -290,7 +290,7 @@ class App { if(($a->config['ssl_policy'] == SSL_POLICY_SELFSIGN) && (local_user() || x($_POST,'auth-params'))) $scheme = 'https'; - $this->baseurl = ( $scheme . "://" . $this->hostname . ((isset($this->path) && strlen($this->path)) ? '/' . $this->path : '' ); + $this->baseurl = $scheme . "://" . $this->hostname . ((isset($this->path) && strlen($this->path)) ? '/' . $this->path : '' ); return $this->baseurl; } From 8673b08c1c1daffcd166ad96b2b3729a893ab5c8 Mon Sep 17 00:00:00 2001 From: Friendika Date: Sat, 29 Jan 2011 22:41:01 -0800 Subject: [PATCH 04/23] even more cleanup prior to push --- boot.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/boot.php b/boot.php index c4afa07b..bb7819fd 100644 --- a/boot.php +++ b/boot.php @@ -17,7 +17,7 @@ define ( 'DOWN_ARROW', '⇩' ); define ( 'SSL_POLICY_NONE', 0 ); define ( 'SSL_POLICY_FULL', 1 ); -define ( 'SSL_POLICY_SELFSIGN', 2 ); +define ( 'SSL_POLICY_SELFSIGN', 2 ); /** @@ -280,8 +280,6 @@ class App { } function get_baseurl($ssl = false) { -// if(strlen($this->baseurl)) -// return $this->baseurl; $scheme = $this->scheme; From c0128432825416929b00d0fa9563f2d1f92e9ba3 Mon Sep 17 00:00:00 2001 From: Friendika Date: Sat, 29 Jan 2011 23:47:40 -0800 Subject: [PATCH 05/23] identify email-only contacts --- mod/follow.php | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/mod/follow.php b/mod/follow.php index a90ae3f5..62ba2585 100644 --- a/mod/follow.php +++ b/mod/follow.php @@ -10,7 +10,9 @@ function follow_post(&$a) { // NOTREACHED } - $url = notags(trim($_POST['url'])); + $url = $orig_url = notags(trim($_POST['url'])); + + $email_conversant = false; if($url) { $links = lrdd($url); @@ -29,6 +31,11 @@ function follow_post(&$a) { } } + else { + if((strpos($orig_url,'@')) && validate_email($orig_url)) { + $email_conversant = true; + } + } } // If we find a DFRN site, send our subscriber to the other person's From e8d52d879baf6a35de335cb2ef6a2fb0a5eb8b3e Mon Sep 17 00:00:00 2001 From: Friendika Date: Sun, 30 Jan 2011 15:28:50 -0800 Subject: [PATCH 06/23] more debugging to see if the google push hub is even trying to publish our content. --- mod/dfrn_poll.php | 1 + mod/item.php | 1 + 2 files changed, 2 insertions(+) diff --git a/mod/dfrn_poll.php b/mod/dfrn_poll.php index 1061cdb8..85e7fc0a 100644 --- a/mod/dfrn_poll.php +++ b/mod/dfrn_poll.php @@ -26,6 +26,7 @@ function dfrn_poll_init(&$a) { } if(($dfrn_id === '') && (! x($_POST,'dfrn_id')) && ($a->argc > 1)) { + logger('dfrn_poll: public feed request from ' . $_SERVER['REMOTE_ADDR'] ); header("Content-type: application/atom+xml"); $o = get_feed_for($a, '*', $a->argv[1],$last_update); echo $o; diff --git a/mod/item.php b/mod/item.php index 11e79a7a..cc1886c1 100644 --- a/mod/item.php +++ b/mod/item.php @@ -71,6 +71,7 @@ function item_post(&$a) { $location = notags(trim($_POST['location'])); $coord = notags(trim($_POST['coord'])); $verb = notags(trim($_POST['verb'])); + $emailcc = notags(trim($_POST['emailcc'])); if(! strlen($body)) { notice( t('Empty post discarded.') . EOL ); From a598f17e6d95d8a82df2994d61bfab74271dfe58 Mon Sep 17 00:00:00 2001 From: Friendika Date: Sun, 30 Jan 2011 18:25:41 -0800 Subject: [PATCH 07/23] wrong ref in ssl policy --- boot.php | 4 ++-- include/notifier.php | 2 +- include/poller.php | 2 +- index.php | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/boot.php b/boot.php index bb7819fd..6b4fa080 100644 --- a/boot.php +++ b/boot.php @@ -283,9 +283,9 @@ class App { $scheme = $this->scheme; - if(($ssl) || ($a->config['ssl_policy'] == SSL_POLICY_FULL)) + if(($ssl) || ($this->config['ssl_policy'] == SSL_POLICY_FULL)) $scheme = 'https'; - if(($a->config['ssl_policy'] == SSL_POLICY_SELFSIGN) && (local_user() || x($_POST,'auth-params'))) + if(($this->config['ssl_policy'] == SSL_POLICY_SELFSIGN) && (local_user() || x($_POST,'auth-params'))) $scheme = 'https'; $this->baseurl = $scheme . "://" . $this->hostname . ((isset($this->path) && strlen($this->path)) ? '/' . $this->path : '' ); diff --git a/include/notifier.php b/include/notifier.php index dd5d55ed..59e29d7d 100644 --- a/include/notifier.php +++ b/include/notifier.php @@ -363,7 +363,7 @@ function notifier_run($argv, $argc){ continue; $params = 'hub.mode=publish&hub.url=' . urlencode($a->get_baseurl() . '/dfrn_poll/' . $owner['nickname'] ); post_url($h,$params); - logger('pubsub: publish: ' . $h . ' returned ' . $a->get_curl_code()); + logger('pubsub: publish: ' . $h . ' ' . $params . ' returned ' . $a->get_curl_code()); if(count($hubs) > 1) sleep(7); // try and avoid multiple hubs responding at precisely the same time } diff --git a/include/poller.php b/include/poller.php index 1003b2f0..0dbc6f58 100644 --- a/include/poller.php +++ b/include/poller.php @@ -76,7 +76,7 @@ function poller_run($argv, $argc){ $contact['priority'] = (($interval !== false) ? intval($interval) : 3); $hub_update = false; - if(datetime_convert('UTC','UTC', 'now') > datetime_convert('UTC','UTC', $t . " + 1 day")) + if((datetime_convert('UTC','UTC', 'now') > datetime_convert('UTC','UTC', $t . " + 1 day")) || $force) $hub_update = true; } diff --git a/index.php b/index.php index ae6a578c..685a76a1 100644 --- a/index.php +++ b/index.php @@ -140,7 +140,7 @@ if(strlen($a->module)) { } else { if((x($_SERVER,'QUERY_STRING')) && ($_SERVER['QUERY_STRING'] === 'q=internal_error.html') && isset($dreamhost_error_hack)) { - logger('index.php: dreamhost_error_hack invoked'); + logger('index.php: dreamhost_error_hack invoked. Original URI =' . $_SERVER['REQUEST_URI']); goaway($a->get_baseurl() . $_SERVER['REQUEST_URI']); } From 3eefe8b50003c858d4930c03cc06d2679a14347c Mon Sep 17 00:00:00 2001 From: Friendika Date: Sun, 30 Jan 2011 19:38:03 -0800 Subject: [PATCH 08/23] more ssl_policy cleanup, allow manual feed update per contact when hub is whacked, log feed parse errors --- boot.php | 10 ++++++---- include/items.php | 4 ++++ include/poller.php | 3 +++ mod/contacts.php | 10 ++++++++++ view/de/contact_edit.tpl | 5 +++-- view/en/contact_edit.tpl | 5 +++-- view/fr/contact_edit.tpl | 5 +++-- view/it/contact_edit.tpl | 5 +++-- view/theme/default/style.css | 3 +++ view/theme/duepuntozero/style.css | 4 ++++ 10 files changed, 42 insertions(+), 12 deletions(-) diff --git a/boot.php b/boot.php index 6b4fa080..6cbb4f07 100644 --- a/boot.php +++ b/boot.php @@ -283,10 +283,12 @@ class App { $scheme = $this->scheme; - if(($ssl) || ($this->config['ssl_policy'] == SSL_POLICY_FULL)) - $scheme = 'https'; - if(($this->config['ssl_policy'] == SSL_POLICY_SELFSIGN) && (local_user() || x($_POST,'auth-params'))) - $scheme = 'https'; + if(x($this->config,'ssl_policy')) { + if(($ssl) || ($this->config['ssl_policy'] == SSL_POLICY_FULL)) + $scheme = 'https'; + if(($this->config['ssl_policy'] == SSL_POLICY_SELFSIGN) && (local_user() || x($_POST,'auth-params'))) + $scheme = 'https'; + } $this->baseurl = $scheme . "://" . $this->hostname . ((isset($this->path) && strlen($this->path)) ? '/' . $this->path : '' ); return $this->baseurl; diff --git a/include/items.php b/include/items.php index e238280f..a5991d66 100644 --- a/include/items.php +++ b/include/items.php @@ -903,6 +903,10 @@ function consume_feed($xml,$importer,&$contact, &$hub, $datedir = 0) { $feed->enable_order_by_date(false); $feed->init(); + if($feed->error()) + logger('consume_feed: Error parsing XML: ' . $feed->error()); + + // Check at the feed level for updated contact name and/or photo $name_updated = ''; diff --git a/include/poller.php b/include/poller.php index 0dbc6f58..4567a5cf 100644 --- a/include/poller.php +++ b/include/poller.php @@ -58,6 +58,9 @@ function poller_run($argv, $argc){ foreach($contacts as $contact) { + if($manual_id) + $contact['last-update'] = '0000-00-00 00:00:00'; + if($contact['priority'] || $contact['subhub']) { $hub_update = true; diff --git a/mod/contacts.php b/mod/contacts.php index 177ca997..4c627c88 100644 --- a/mod/contacts.php +++ b/mod/contacts.php @@ -121,6 +121,15 @@ function contacts_content(&$a) { return; // NOTREACHED } + if($cmd === 'update') { + + // pull feed and consume it, which should subscribe to the hub. + + $php_path = ((x($a->config,'php_path') && strlen($a->config['php_path'])) ? $a->config['php_path'] : 'php'); + proc_run($php_path,"include/poller.php","$contact_id"); + goaway($a->get_baseurl() . '/contacts/' . $contact_id); + // NOTREACHED + } if($cmd === 'block') { $blocked = (($orig_record[0]['blocked']) ? 0 : 1); @@ -248,6 +257,7 @@ function contacts_content(&$a) { '$last_update' => (($r[0]['last-update'] == '0000-00-00 00:00:00') ? t('Never') : datetime_convert('UTC',date_default_timezone_get(),$r[0]['last-update'],'D, j M Y, g:i A')), + '$udnow' => t('Update now'), '$profile_select' => contact_profile_assign($r[0]['profile-id'],(($r[0]['network'] !== 'dfrn') ? true : false)), '$contact_id' => $r[0]['id'], '$block_text' => (($r[0]['blocked']) ? t('Unblock this contact') : t('Block this contact') ), diff --git a/view/de/contact_edit.tpl b/view/de/contact_edit.tpl index fcd3f2d6..0b32bdd6 100644 --- a/view/de/contact_edit.tpl +++ b/view/de/contact_edit.tpl @@ -3,6 +3,8 @@
$name
+
+
@@ -24,13 +26,12 @@
- -
$lastupdtext$last_update
$updpub
$poll_interval +
diff --git a/view/en/contact_edit.tpl b/view/en/contact_edit.tpl index ea546b78..9aca6018 100644 --- a/view/en/contact_edit.tpl +++ b/view/en/contact_edit.tpl @@ -3,6 +3,8 @@
$name
+ +
@@ -24,13 +26,12 @@
- -
$lastupdtext$last_update
$updpub
$poll_interval +
diff --git a/view/fr/contact_edit.tpl b/view/fr/contact_edit.tpl index c7d2d597..9f36ddcc 100644 --- a/view/fr/contact_edit.tpl +++ b/view/fr/contact_edit.tpl @@ -3,6 +3,8 @@
$name
+ +
@@ -24,14 +26,13 @@
- -
$lastupdtext$last_update
$updpub
$poll_interval +
diff --git a/view/it/contact_edit.tpl b/view/it/contact_edit.tpl index d4217ba8..ac0f13a5 100644 --- a/view/it/contact_edit.tpl +++ b/view/it/contact_edit.tpl @@ -3,6 +3,8 @@
$name
+ +
@@ -24,13 +26,12 @@
- -
$lastupdtext$last_update
$updpub
$poll_interval +
diff --git a/view/theme/default/style.css b/view/theme/default/style.css index 3ef63e26..ae45a424 100644 --- a/view/theme/default/style.css +++ b/view/theme/default/style.css @@ -1333,6 +1333,9 @@ input#dfrn-url { #contact-edit-poll-text { margin-bottom: 10px; } +#contact-edit-update-now { + margin-top: 15px; +} #contact-edit-photo-wrapper { margin-bottom: 20px; diff --git a/view/theme/duepuntozero/style.css b/view/theme/duepuntozero/style.css index 59a5bf88..d6a1744a 100644 --- a/view/theme/duepuntozero/style.css +++ b/view/theme/duepuntozero/style.css @@ -1209,6 +1209,10 @@ input#dfrn-url { margin-bottom: 10px; } +#contact-edit-update-now { + margin-top: 15px; +} + #contact-edit-photo-wrapper { margin-bottom: 20px; } From 6935d34e7683fa2cf43092b39346c3c43cb8d38d Mon Sep 17 00:00:00 2001 From: Friendika Date: Sun, 30 Jan 2011 19:49:26 -0800 Subject: [PATCH 09/23] disable "take me home" hotkey, different on FF (shift-home), IE ($). --- include/main.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/include/main.js b/include/main.js index fcd1d6fe..a24f0ae0 100644 --- a/include/main.js +++ b/include/main.js @@ -44,12 +44,13 @@ $('#pause').html(''); } } - if(event.keyCode == '36' && event.shiftKey == true) { - if(homebase !== undefined) { - event.preventDefault(); - document.location = homebase; - } - } +// this is shift-home on FF, but $ on IE, disabling until I figure out why the diff. +// if(event.keyCode == '36' && event.shiftKey == true) { +// if(homebase !== undefined) { +// event.preventDefault(); +// document.location = homebase; +// } +// } }); }); From d8877b88d6c26e29019312f02297411817692361 Mon Sep 17 00:00:00 2001 From: Friendika Date: Sun, 30 Jan 2011 22:45:02 -0800 Subject: [PATCH 10/23] limit privacy ACL's to networks which can handle privacy --- include/acl_selectors.php | 17 +++++++++-------- include/main.js | 1 + 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/include/acl_selectors.php b/include/acl_selectors.php index d0952421..554782a8 100644 --- a/include/acl_selectors.php +++ b/include/acl_selectors.php @@ -30,7 +30,7 @@ function group_select($selname,$selclass,$preselected = false,$size = 4) { -function contact_select($selname, $selclass, $preselected = false, $size = 4, $privmail = false, $celeb = false) { +function contact_select($selname, $selclass, $preselected = false, $size = 4, $privmail = false, $celeb = false, $privatenet = false) { $o = ''; @@ -43,6 +43,10 @@ function contact_select($selname, $selclass, $preselected = false, $size = 4, $p $sql_extra .= sprintf(" AND `rel` = %d ", intval(REL_BUD)); } + if($privmail || $privatenet) { + $sql_extra .= " AND `network` IN ( 'dfrn' ) "; + } + if($privmail) $o .= " +
+ diff --git a/view/en/jot.tpl b/view/en/jot.tpl index de88fb84..d2a1014e 100644 --- a/view/en/jot.tpl +++ b/view/en/jot.tpl @@ -40,8 +40,12 @@
-
Permission Settings$bang
+
Permission Settings$bang
+ diff --git a/view/fr/jot.tpl b/view/fr/jot.tpl index 9a5c5815..7c49d771 100644 --- a/view/fr/jot.tpl +++ b/view/fr/jot.tpl @@ -41,8 +41,13 @@
-
Permission Settings$bang
+
Permission Settings$bang
+ + diff --git a/view/it/jot.tpl b/view/it/jot.tpl index b0c90ddf..7153c341 100644 --- a/view/it/jot.tpl +++ b/view/it/jot.tpl @@ -40,8 +40,13 @@
-
Impostazione permessi$bang
+
Impostazione permessi$bang
+ + From e2544abd503ddcbd98a31c75b528aac0c980df35 Mon Sep 17 00:00:00 2001 From: Friendika Date: Mon, 31 Jan 2011 19:49:56 -0800 Subject: [PATCH 21/23] truncate names in acl selector --- include/acl_selectors.php | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/include/acl_selectors.php b/include/acl_selectors.php index 554782a8..63756ce3 100644 --- a/include/acl_selectors.php +++ b/include/acl_selectors.php @@ -17,8 +17,9 @@ function group_select($selname,$selclass,$preselected = false,$size = 4) { $selected = " selected=\"selected\" "; else $selected = ''; + $trimmed = substr($rr['name'],0,12); - $o .= "\r\n"; + $o .= "\r\n"; } } @@ -66,7 +67,9 @@ function contact_select($selname, $selclass, $preselected = false, $size = 4, $p else $selected = ''; - $o .= "\r\n"; + $trimmed = substr($rr['name'],0,24); + + $o .= "\r\n"; } } From 2cd1da54627bc298baab0ad5119a946713bf07a5 Mon Sep 17 00:00:00 2001 From: Friendika Date: Mon, 31 Jan 2011 19:53:40 -0800 Subject: [PATCH 22/23] readjust name length limits - acl selector --- include/acl_selectors.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/acl_selectors.php b/include/acl_selectors.php index 63756ce3..269dc3e3 100644 --- a/include/acl_selectors.php +++ b/include/acl_selectors.php @@ -67,7 +67,7 @@ function contact_select($selname, $selclass, $preselected = false, $size = 4, $p else $selected = ''; - $trimmed = substr($rr['name'],0,24); + $trimmed = substr($rr['name'],0,22); $o .= "\r\n"; } From 7dbdf8e6faf082e962772b76511f0fd24b6227cc Mon Sep 17 00:00:00 2001 From: Friendika Date: Mon, 31 Jan 2011 20:52:23 -0800 Subject: [PATCH 23/23] let there be purple! ;-) --- view/theme/purplezero/border.jpg | Bin 0 -> 364 bytes view/theme/purplezero/head.jpg | Bin 0 -> 1109 bytes view/theme/purplezero/shiny.png | Bin 0 -> 320 bytes view/theme/purplezero/style.css | 27 +++++++++++++++++++++++++++ 4 files changed, 27 insertions(+) create mode 100644 view/theme/purplezero/border.jpg create mode 100644 view/theme/purplezero/head.jpg create mode 100644 view/theme/purplezero/shiny.png create mode 100644 view/theme/purplezero/style.css diff --git a/view/theme/purplezero/border.jpg b/view/theme/purplezero/border.jpg new file mode 100644 index 0000000000000000000000000000000000000000..66c7a6fcc7493b233e78a81f9a97f26fd4133335 GIT binary patch literal 364 zcmex=LJ%Z3brsR%R9! z7G_o;!OF_Y#?HgR4g~z%+?+gu{6a#4{DOkQVlv{wB2uD)f)a`nQnIr0^76vsN-9cn zDl&5Nav(z(fm+$w*!eg(_~b+cMdU~Z{|_(-axfGyFfubLF)#@-G7B>PKf)jmaz7&j zGGJk52TDi@FfuTsN-+v03I{F(X#(j%(ZtNe3NlSh5KWwcK~U)bEe0NDMxd3WnyY(ZeeNV?BeR??&0Yb91<+v*#~fzWVs-^OvvRzW@073*;|G24;x2;66k1mmttzOe`$SEbJhE zF*20{F|!~GtD+&BkYgZwVxh2-Q6qGeQ$1KQT&+v`^t>=G+lZ(2vcTckIo98kA|IPpZ7=)dRQWHy3 zQxwWGOEMJPJ$(bfNsyJ91?X3xPgz)5fsxAtN{sCM+}xZzg8V{4g8YJl!eTPw!Xi?l zf`Sr?5>m3V^78V+;z}w?aw;-%@^auL2-M2X#?HsV!6zpoC?ZEPfF!{h23C|LC6DM5)s*(ihho@hVIgHE< zOf0NGDQQ8l9I_;%Ad`?tAoG9Jz>Ih}i$kn0dnwEFyv8X>MJrnVw7s%gXrJF`$#z9Q z