diff --git a/.htaccess b/.htaccess index 7f3935117..5f6414882 100644 --- a/.htaccess +++ b/.htaccess @@ -28,3 +28,4 @@ AddType audio/ogg .oga RewriteCond %{REQUEST_FILENAME} !-d RewriteRule ^(.*)$ index.php?q=$1 [E=REMOTE_USER:%{HTTP:Authorization},L,QSA] + diff --git a/Zotlabs/Identity/BasicId.php b/Zotlabs/Identity/BasicId.php new file mode 100644 index 000000000..3c149808f --- /dev/null +++ b/Zotlabs/Identity/BasicId.php @@ -0,0 +1,18 @@ +test = ((array_key_exists('test',$req)) ? intval($req['test']) : 0); + $this->test_results = array('success' => false); + $this->debug_msg = ''; + + $this->success = false; + $this->address = $req['auth']; + $this->desturl = $req['dest']; + $this->sec = $req['sec']; + $this->version = $req['version']; + $this->delegate = $req['delegate']; + + $c = get_sys_channel(); + if(! $c) { + logger('unable to obtain response (sys) channel'); + $this->Debug('no local channels found.'); + $this->Finalise(); + } + + $x = $this->GetHublocs($this->address); + + if($x) { + foreach($x as $xx) { + if($this->Verify($c,$xx)) + break; + } + } + + /** + * @FIXME we really want to save the return_url in the session before we + * visit rmagic. This does however prevent a recursion if you visit + * rmagic directly, as it would otherwise send you back here again. + * But z_root() probably isn't where you really want to go. + */ + + if(strstr($this->desturl,z_root() . '/rmagic')) + goaway(z_root()); + + $this->Finalise(); + + } + + function GetHublocs($address) { + + // Try and find a hubloc for the person attempting to auth. + // Since we're matching by address, we have to return all entries + // some of which may be from re-installed hubs; and we'll need to + // try each sequentially to see if one can pass the test + + $x = q("select * from hubloc left join xchan on xchan_hash = hubloc_hash + where hubloc_addr = '%s' order by hubloc_id desc", + dbesc($address) + ); + + if(! $x) { + // finger them if they can't be found. + $ret = zot_finger($address, null); + if ($ret['success']) { + $j = json_decode($ret['body'], true); + if($j) + import_xchan($j); + $x = q("select * from hubloc left join xchan on xchan_hash = hubloc_hash + where hubloc_addr = '%s' order by hubloc_id desc", + dbesc($address) + ); + } + } + if(! $x) { + logger('mod_zot: auth: unable to finger ' . $address); + $this->Debug('no hubloc found for ' . $address . ' and probing failed.'); + $this->Finalise(); + } + + return $x; + } + + + function Verify($channel,$hubloc) { + + logger('auth request received from ' . $hubloc['hubloc_addr'] ); + + $this->remote = remote_channel(); + $this->remote_service_class = ''; + $this->remote_level = 0; + $this->remote_hub = $hubloc['hubloc_url']; + $this->dnt = 0; + + // check credentials and access + + // If they are already authenticated and haven't changed credentials, + // we can save an expensive network round trip and improve performance. + + // Also check that they are coming from the same site as they authenticated with originally. + + $already_authed = (((remote_channel()) && ($hubloc['hubloc_hash'] == remote_channel()) + && ($hubloc['hubloc_url'] === $_SESSION['remote_hub'])) ? true : false); + + if($this->delegate && $this->delegate !== $_SESSION['delegate_channel']) + $already_authed = false; + + if($already_authed) + return true; + + if(local_channel()) { + + // tell them to logout if they're logged in locally as anything but the target remote account + // in which case just shut up because they don't need to be doing this at all. + + if (get_app()->channel['channel_hash'] == $hubloc['xchan_hash']) { + return true; + } + else { + logger('already authenticated locally as somebody else.'); + notice( t('Remote authentication blocked. You are logged into this site locally. Please logout and retry.') . EOL); + if($this->test) { + $this->Debug('already logged in locally with a conflicting identity.'); + return false; + } + } + return false; + } + + // Auth packets MUST use ultra top-secret hush-hush mode - e.g. the entire packet is encrypted using the + // site private key + // The actual channel sending the packet ($c[0]) is not important, but this provides a + // generic zot packet with a sender which can be verified + + $p = zot_build_packet($channel,$type = 'auth_check', + array(array('guid' => $hubloc['hubloc_guid'],'guid_sig' => $hubloc['hubloc_guid_sig'])), + $hubloc['hubloc_sitekey'], $this->sec); + + $this->Debug('auth check packet created using sitekey ' . $hubloc['hubloc_sitekey']); + $this->Debug('packet contents: ' . $p); + + $result = zot_zot($hubloc['hubloc_callback'],$p); + if(! $result['success']) { + logger('auth_check callback failed.'); + if($this->test) + $this->Debug('auth check request to your site returned .' . print_r($result, true)); + return false; + } + + $j = json_decode($result['body'], true); + if(! $j) { + logger('auth_check json data malformed.'); + if($this->test) + $this->Debug('json malformed: ' . $result['body']); + return false; + } + + $this->Debug('auth check request returned .' . print_r($j, true)); + + if(! $j['success']) + return false; + + // legit response, but we do need to check that this wasn't answered by a man-in-middle + + if (! rsa_verify($this->sec . $hubloc['xchan_hash'],base64url_decode($j['confirm']),$hubloc['xchan_pubkey'])) { + logger('final confirmation failed.'); + if($this->test) + $this->Debug('final confirmation failed. ' . $sec . print_r($j,true) . print_r($hubloc,true)); + return false; + } + + if (array_key_exists('service_class',$j)) + $this->remote_service_class = $j['service_class']; + if (array_key_exists('level',$j)) + $this->remote_level = $j['level']; + if (array_key_exists('DNT',$j)) + $this->dnt = $j['DNT']; + + + // log them in + + if ($this->test) { + // testing only - return the success result + $this->test_results['success'] = true; + $this->Debug('Authentication Success!'); + $this->Finalise(); + } + + $_SESSION['authenticated'] = 1; + + // check for delegation and if all is well, log them in locally with delegation restrictions + + $this->delegate_success = false; + + if($this->delegate) { + $r = q("select * from channel left join xchan on channel_hash = xchan_hash where xchan_addr = '%s' limit 1", + dbesc($this->delegate) + ); + if ($r && intval($r[0]['channel_id'])) { + $allowed = perm_is_allowed($r[0]['channel_id'],$hubloc['xchan_hash'],'delegate'); + if($allowed) { + $_SESSION['delegate_channel'] = $r[0]['channel_id']; + $_SESSION['delegate'] = $hubloc['xchan_hash']; + $_SESSION['account_id'] = intval($r[0]['channel_account_id']); + require_once('include/security.php'); + // this will set the local_channel authentication in the session + change_channel($r[0]['channel_id']); + $this->delegate_success = true; + } + } + } + + if (! $this->delegate_success) { + // normal visitor (remote_channel) login session credentials + $_SESSION['visitor_id'] = $hubloc['xchan_hash']; + $_SESSION['my_url'] = $hubloc['xchan_url']; + $_SESSION['my_address'] = $this->address; + $_SESSION['remote_service_class'] = $this->remote_service_class; + $_SESSION['remote_level'] = $this->remote_level; + $_SESSION['remote_hub'] = $this->remote_hub; + $_SESSION['DNT'] = $this->dnt; + } + + $arr = array('xchan' => $hubloc, 'url' => $this->desturl, 'session' => $_SESSION); + call_hooks('magic_auth_success',$arr); + get_app()->set_observer($hubloc); + require_once('include/security.php'); + get_app()->set_groups(init_groups_visitor($_SESSION['visitor_id'])); + info(sprintf( t('Welcome %s. Remote authentication successful.'),$hubloc['xchan_name'])); + logger('mod_zot: auth success from ' . $hubloc['xchan_addr']); + $this->success = true; + return true; + } + + function Debug($msg) { + $this->debug_msg .= $msg . EOL; + } + + + function Finalise() { + + if($this->test) { + $this->test_results['message'] = $this->debug_msg; + json_return_and_die($this->test_results); + } + + goaway($this->desturl); + } + +} + + +/** + * + * Magic Auth + * ========== + * + * So-called "magic auth" takes place by a special exchange. On the site where the "channel to be authenticated" lives (e.g. $mysite), + * a redirection is made via $mysite/magic to the zot endpoint of the remote site ($remotesite) with special GET parameters. + * + * The endpoint is typically https://$remotesite/post - or whatever was specified as the callback url in prior communications + * (we will bootstrap an address and fetch a zot info packet if possible where no prior communications exist) + * + * Five GET parameters are supplied: + * * auth => the urlencoded webbie (channel@host.domain) of the channel requesting access + * * dest => the desired destination URL (urlencoded) + * * sec => a random string which is also stored on $mysite for use during the verification phase. + * * version => the zot revision + * * delegate => optional urlencoded webbie of a local channel to invoke delegation rights for + * + * * test => (optional 1 or 0 - debugs the authentication exchange and returns a json response instead of redirecting the browser session) + * + * When this packet is received, an "auth-check" zot message is sent to $mysite. + * (e.g. if $_GET['auth'] is foobar@podunk.edu, a zot packet is sent to the podunk.edu zot endpoint, which is typically /post) + * If no information has been recorded about the requesting identity a zot information packet will be retrieved before + * continuing. + * + * The sender of this packet is an arbitrary/random site channel. The recipients will be a single recipient corresponding + * to the guid and guid_sig we have associated with the requesting auth identity + * + * \code{.json} + * { + * "type":"auth_check", + * "sender":{ + * "guid":"kgVFf_...", + * "guid_sig":"PT9-TApz...", + * "url":"http:\/\/podunk.edu", + * "url_sig":"T8Bp7j...", + * "sitekey":"aMtgKTiirXrICP..." + * }, + * "recipients":{ + * { + * "guid":"ZHSqb...", + * "guid_sig":"JsAAXi..." + * } + * } + * "callback":"\/post", + * "version":1, + * "secret":"1eaa661", + * "secret_sig":"eKV968b1..." + * } + * \endcode + * + * auth_check messages MUST use encapsulated encryption. This message is sent to the origination site, which checks the 'secret' to see + * if it is the same as the 'sec' which it passed originally. It also checks the secret_sig which is the secret signed by the + * destination channel's private key and base64url encoded. If everything checks out, a json packet is returned: + * + * \code{.json} + * { + * "success":1, + * "confirm":"q0Ysovd1u...", + * "service_class":(optional) + * "level":(optional) + * "DNT": (optional do-not-track - 1 or 0) + * } + * \endcode + * + * 'confirm' in this case is the base64url encoded RSA signature of the concatenation of 'secret' with the + * base64url encoded whirlpool hash of the requestor's guid and guid_sig; signed with the source channel private key. + * This prevents a man-in-the-middle from inserting a rogue success packet. Upon receipt and successful + * verification of this packet, the destination site will redirect to the original destination URL and indicate a successful remote login. + * Service_class can be used by cooperating sites to provide different access rights based on account rights and subscription plans. It is + * a string whose contents are not defined by protocol. Example: "basic" or "gold". + * + * @param[in,out] App &$a + */ diff --git a/Zotlabs/Zot/IHandler.php b/Zotlabs/Zot/IHandler.php new file mode 100644 index 000000000..eeca1555c --- /dev/null +++ b/Zotlabs/Zot/IHandler.php @@ -0,0 +1,22 @@ +error = false; + $this->validated = false; + $this->messagetype = ''; + $this->response = array('success' => false); + + $this->handler = $handler; + + if(! is_array($data)) + $data = json_decode($data,true); + + if($data && is_array($data)) { + $this->encrypted = ((array_key_exists('iv',$data)) ? true : false); + + if($this->encrypted) { + $this->data = @json_decode(@crypto_unencapsulate($data,$prvkey),true); + } + if(! $this->data) + $this->data = $data; + + if($this->data && is_array($this->data) && array_key_exists('type',$this->data)) + $this->messagetype = $this->data['type']; + } + if(! $this->messagetype) + $this->error = true; + + $this->sender = ((array_key_exists('sender',$this->data)) ? $this->data['sender'] : null); + $this->recipients = ((array_key_exists('recipients',$this->data)) ? $this->data['recipients'] : null); + + + if($this->sender) + $this->ValidateSender(); + + $this->Dispatch(); + } + + function ValidateSender() { + $hubs = zot_gethub($this->sender,true); + if (! $hubs) { + + /* Have never seen this guid or this guid coming from this location. Check it and register it. */ + /* (!!) this will validate the sender. */ + + $result = zot_register_hub($this->sender); + + if ((! $result['success']) || (! ($hubs = zot_gethub($this->sender,true)))) { + $this->response['message'] = 'Hub not available.'; + json_return_and_die($this->response); + } + } + foreach($hubs as $hub) { + update_hub_connected($hub,((array_key_exists('sitekey',$this->sender)) ? $this->sender['sitekey'] : '')); + } + $this->validated = true; + } + + + function Dispatch() { + + /* Handle tasks which don't require sender validation */ + + switch($this->messagetype) { + case 'ping': + /* no validation needed */ + $this->handler->Ping(); + break; + case 'pickup': + /* perform site validation, as opposed to sender validation */ + $this->handler->Pickup($this->data); + break; + + default: + if(! $this->validated) { + $this->response['message'] = 'Sender not valid'; + json_return_and_die($this->response); + } + break; + } + + /* Now handle tasks which require sender validation */ + + switch($this->messagetype) { + + case 'auth_check': + $this->handler->AuthCheck($this->data,$this->encrypted); + break; + + case 'request': + $this->handler->Request($this->data); + break; + + case 'purge': + $this->handler->Purge($this->sender,$this->recipients); + break; + + case 'refresh': + case 'force_refresh': + $this->handler->Refresh($this->sender,$this->recipients); + break; + + case 'notify': + $this->handler->Notify($this->data); + break; + + default: + $this->response['message'] = 'Not implemented'; + json_return_and_die($this->response); + break; + } + + } +} + + + +/** + * @brief zot communications and messaging. + * + * Sender HTTP posts to this endpoint ($site/post typically) with 'data' parameter set to json zot message packet. + * This packet is optionally encrypted, which we will discover if the json has an 'iv' element. + * $contents => array( 'alg' => 'aes256cbc', 'iv' => initialisation vector, 'key' => decryption key, 'data' => encrypted data); + * $contents->iv and $contents->key are random strings encrypted with this site's RSA public key and then base64url encoded. + * Currently only 'aes256cbc' is used, but this is extensible should that algorithm prove inadequate. + * + * Once decrypted, one will find the normal json_encoded zot message packet. + * + * Defined packet types are: notify, purge, refresh, force_refresh, auth_check, ping, and pickup + * + * Standard packet: (used by notify, purge, refresh, force_refresh, and auth_check) + * \code{.json} + * { + * "type": "notify", + * "sender":{ + * "guid":"kgVFf_1...", + * "guid_sig":"PT9-TApzp...", + * "url":"http:\/\/podunk.edu", + * "url_sig":"T8Bp7j5...", + * }, + * "recipients": { optional recipient array }, + * "callback":"\/post", + * "version":1, + * "secret":"1eaa...", + * "secret_sig": "df89025470fac8..." + * } + * \endcode + * + * Signature fields are all signed with the sender channel private key and base64url encoded. + * Recipients are arrays of guid and guid_sig, which were previously signed with the recipients private + * key and base64url encoded and later obtained via channel discovery. Absence of recipients indicates + * a public message or visible to all potential listeners on this site. + * + * "pickup" packet: + * The pickup packet is sent in response to a notify packet from another site + * \code{.json} + * { + * "type":"pickup", + * "url":"http:\/\/example.com", + * "callback":"http:\/\/example.com\/post", + * "callback_sig":"teE1_fLI...", + * "secret":"1eaa...", + * "secret_sig":"O7nB4_..." + * } + * \endcode + * + * In the pickup packet, the sig fields correspond to the respective data + * element signed with this site's system private key and then base64url encoded. + * The "secret" is the same as the original secret from the notify packet. + * + * If verification is successful, a json structure is returned containing a + * success indicator and an array of type 'pickup'. + * Each pickup element contains the original notify request and a message field + * whose contents are dependent on the message type. + * + * This JSON array is AES encapsulated using the site public key of the site + * that sent the initial zot pickup packet. + * Using the above example, this would be example.com. + * + * \code{.json} + * { + * "success":1, + * "pickup":{ + * "notify":{ + * "type":"notify", + * "sender":{ + * "guid":"kgVFf_...", + * "guid_sig":"PT9-TApz...", + * "url":"http:\/\/z.podunk.edu", + * "url_sig":"T8Bp7j5D..." + * }, + * "callback":"\/post", + * "version":1, + * "secret":"1eaa661..." + * }, + * "message":{ + * "type":"activity", + * "message_id":"10b049ce384cbb2da9467319bc98169ab36290b8bbb403aa0c0accd9cb072e76@podunk.edu", + * "message_top":"10b049ce384cbb2da9467319bc98169ab36290b8bbb403aa0c0accd9cb072e76@podunk.edu", + * "message_parent":"10b049ce384cbb2da9467319bc98169ab36290b8bbb403aa0c0accd9cb072e76@podunk.edu", + * "created":"2012-11-20 04:04:16", + * "edited":"2012-11-20 04:04:16", + * "title":"", + * "body":"Hi Nickordo", + * "app":"", + * "verb":"post", + * "object_type":"", + * "target_type":"", + * "permalink":"", + * "location":"", + * "longlat":"", + * "owner":{ + * "name":"Indigo", + * "address":"indigo@podunk.edu", + * "url":"http:\/\/podunk.edu", + * "photo":{ + * "mimetype":"image\/jpeg", + * "src":"http:\/\/podunk.edu\/photo\/profile\/m\/5" + * }, + * "guid":"kgVFf_...", + * "guid_sig":"PT9-TAp...", + * }, + * "author":{ + * "name":"Indigo", + * "address":"indigo@podunk.edu", + * "url":"http:\/\/podunk.edu", + * "photo":{ + * "mimetype":"image\/jpeg", + * "src":"http:\/\/podunk.edu\/photo\/profile\/m\/5" + * }, + * "guid":"kgVFf_...", + * "guid_sig":"PT9-TAp..." + * } + * } + * } + * } + * \endcode + * + * Currently defined message types are 'activity', 'mail', 'profile', 'location' + * and 'channel_sync', which each have different content schemas. + * + * Ping packet: + * A ping packet does not require any parameters except the type. It may or may + * not be encrypted. + * + * \code{.json} + * { + * "type": "ping" + * } + * \endcode + * + * On receipt of a ping packet a ping response will be returned: + * + * \code{.json} + * { + * "success" : 1, + * "site" { + * "url": "http:\/\/podunk.edu", + * "url_sig": "T8Bp7j5...", + * "sitekey": "-----BEGIN PUBLIC KEY----- + * MIICIjANBgkqhkiG9w0BAQE..." + * } + * } + * \endcode + * + * The ping packet can be used to verify that a site has not been re-installed, and to + * initiate corrective action if it has. The url_sig is signed with the site private key + * and base64url encoded - and this should verify with the enclosed sitekey. Failure to + * verify indicates the site is corrupt or otherwise unable to communicate using zot. + * This return packet is not otherwise verified, so should be compared with other + * results obtained from this site which were verified prior to taking action. For instance + * if you have one verified result with this signature and key, and other records for this + * url which have different signatures and keys, it indicates that the site was re-installed + * and corrective action may commence (remove or mark invalid any entries with different + * signatures). + * If you have no records which match this url_sig and key - no corrective action should + * be taken as this packet may have been returned by an imposter. + * + * @param[in,out] App &$a + */ + diff --git a/Zotlabs/Zot/ZotHandler.php b/Zotlabs/Zot/ZotHandler.php new file mode 100644 index 000000000..f9bb05410 --- /dev/null +++ b/Zotlabs/Zot/ZotHandler.php @@ -0,0 +1,38 @@ +page['htmlhead'] .= replace_macros(get_markup_template("login_head.tpl"), array( -// '$baseurl' => $a->get_baseurl(true) -// )); - $tpl = get_markup_template("login.tpl"); if(strlen($a->query_string)) $_SESSION['login_return_url'] = $a->query_string; diff --git a/doc/Hubzilla_on_OpenShift.bb b/doc/Hubzilla_on_OpenShift.bb index 9b2c539dc..9ccd66284 100644 --- a/doc/Hubzilla_on_OpenShift.bb +++ b/doc/Hubzilla_on_OpenShift.bb @@ -8,7 +8,7 @@ Create an account on OpenShift, then use the registration e-mail and password to [code]rhc app-create your_app_name php-5.4 mysql-5.5 cron phpmyadmin --namespace your_domain --from-code https://github.com/redmatrix/hubzilla.git -l your@email.address -p your_account_password [/code] -Make a note of the database username and password OpenShift creates for your instance, and use these at [url=https://your_app_name-your_domain.rhcloud.com/]https://your_app_name-your_domain.rhcloud.com/[/url] to complete the setup. +Make a note of the database username and password OpenShift creates for your instance, and use these at [url=https://your_app_name-your_domain.rhcloud.com/]https://your_app_name-your_domain.rhcloud.com/[/url] to complete the setup. You MUST change server address from 127.0.0.1 to localhost. NOTE: PostgreSQL is NOT supported by the deploy script yet, see [zrl=https://zot-mor.rhcloud.com/display/3c7035f2a6febf87057d84ea0ae511223e9b38dc27913177bc0df053edecac7c@zot-mor.rhcloud.com?zid=haakon%40zot-mor.rhcloud.com]this thread[/zrl]. diff --git a/doc/database/db_abook.bb b/doc/database/db_abook.bb index 2e4b9c4a7..364429884 100644 --- a/doc/database/db_abook.bb +++ b/doc/database/db_abook.bb @@ -39,8 +39,9 @@ [tr][td]abook_unconnected[/td][td]currently unused. Projected usage is to indicate "one-way" connections which were insitgated on this end but are still pending on the remote end. [/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td] [tr][td]abook_self[/td][td]is a special case where the owner is the target. Every channel has one abook entry with abook_self and with a target abook_xchan set to channel.channel_hash . When this flag is present, abook_my_perms is the default permissions granted to all new connections and several other fields are unused.[/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td] [tr][td]abook_feed[/td][td]indicates this connection is an RSS/Atom feed and may trigger special handling.[/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td] -[tr][td]abook_incl[/td][td]connection filter allow rules separated by LF[/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td] -[tr][td]abook_excl[/td][td]connection filter deny rules separated by LF[/td][td]int(11)[/td][td]NO[/td][td]MUL[/td][td]0[/td][td] +[tr][td]abook_incl[/td][td]connection filter allow rules separated by LF[/td][td]text[/td][td]NO[/td][td]MUL[/td][td]0[/td][td] +[tr][td]abook_excl[/td][td]connection filter deny rules separated by LF[/td][td]text[/td][td]NO[/td][td]MUL[/td][td]0[/td][td] +[tr][td]abook_instance[/td][td]comma separated list of site urls of all channel clones that this connection is connected with (used only for singleton networks which don't support cloning)[/td][td]text[/td][td]NO[/td][td]MUL[/td][td]0[/td][td] [/table] diff --git a/doc/hidden_configs.bb b/doc/hidden_configs.bb index 1323730aa..8a02a23f2 100644 --- a/doc/hidden_configs.bb +++ b/doc/hidden_configs.bb @@ -65,7 +65,7 @@ This document assumes you're an administrator. this website. Can be overwritten by user settings. [b]system > projecthome[/b] Set the project homepage as the homepage of your hub. - [b]system > workflowchannelnext[/b] + [b]system > workflow_channel_next[/b] The page to direct users to immediately after creating a channel. [b]system > max_daily_registrations[/b] Set the maximum number of new registrations allowed on any day. @@ -154,6 +154,8 @@ This document assumes you're an administrator. Needed in some Windows installations to locate the openssl configuration file on the system. [b]system > hide_help[/b] Don't display help documentation link in nav bar + [b]system > expire_delivery_reports[/b] + Expiration in days for delivery reports - default 30 [b]Directory config[/b] [b]Directory search defaults[/b] diff --git a/doc/hook/check_channelallowed.bb b/doc/hook/check_channelallowed.bb new file mode 100644 index 000000000..e7559c92f --- /dev/null +++ b/doc/hook/check_channelallowed.bb @@ -0,0 +1,11 @@ +[h2]check_channelallowed[/h2] + +Called when checking the channel (xchan) black and white lists to see if a channel is blocked. + +Hook data + + array('hash' => xchan_hash of xchan to check); + + create and set array element 'allowed' to true or false to override the system checks + + diff --git a/doc/hook/check_siteallowed.bb b/doc/hook/check_siteallowed.bb new file mode 100644 index 000000000..28134cbd2 --- /dev/null +++ b/doc/hook/check_siteallowed.bb @@ -0,0 +1,10 @@ +[h2]check_siteallowed[/h2] + +Called when checking the site black and white lists to see if a site is blocked. + +Hook data + + array('url' => URL of site to check); + + create and set array element 'allowed' to true or false to override the system checks + diff --git a/doc/hooklist.bb b/doc/hooklist.bb index 45a4861d9..9172628a0 100644 --- a/doc/hooklist.bb +++ b/doc/hooklist.bb @@ -82,6 +82,12 @@ Hooks allow plugins/addons to "hook into" the code at many points and alter the [zrl=[baseurl]/help/hook/check_account_password]check_account_password[/zrl] Used to provide policy control over account passwords (minimum length, character set inclusion, etc.) +[zrl=[baseurl]/help/hook/check_channelallowed]check_channelallowed[/zrl] + Used to over-ride or bypass the channel black/white block lists + +[zrl=[baseurl]/help/hook/check_siteallowed]check_siteallowed[/zrl] + Used to over-ride or bypass the site black/white block lists + [zrl=[baseurl]/help/hook/connect_premium]connect_premium[/zrl] Called when connecting to a premium channel diff --git a/include/RedDAV/RedBrowser.php b/include/RedDAV/RedBrowser.php index efea5d92f..1aa5f435e 100644 --- a/include/RedDAV/RedBrowser.php +++ b/include/RedDAV/RedBrowser.php @@ -188,7 +188,7 @@ class RedBrowser extends DAV\Browser\Plugin { $parentHash = ''; $owner = $this->auth->owner_id; - $splitPath = split('/', $fullPath); + $splitPath = explode('/', $fullPath); if (count($splitPath) > 3) { for ($i = 3; $i < count($splitPath); $i++) { $attachName = urldecode($splitPath[$i]); diff --git a/include/account.php b/include/account.php index b3a520fd4..e448bdcc6 100644 --- a/include/account.php +++ b/include/account.php @@ -67,7 +67,7 @@ function check_account_invite($invite_code) { $result['message'] .= t('An invitation is required.') . EOL; } $r = q("select * from register where `hash` = '%s' limit 1", dbesc($invite_code)); - if(! results($r)) { + if(! $r) { $result['message'] .= t('Invitation could not be verified.') . EOL; } } @@ -718,4 +718,4 @@ function upgrade_message($bbcode = false) { function upgrade_bool_message($bbcode = false) { $x = upgrade_link($bbcode); return t('This action is not available under your subscription plan.') . (($x) ? ' ' . $x : '') ; -} \ No newline at end of file +} diff --git a/include/api.php b/include/api.php index f279b2aa3..4ad915c03 100644 --- a/include/api.php +++ b/include/api.php @@ -1,10 +1,10 @@ $usercache[$nick], + 'id' => $nick, 'name' => $name, 'screen_name' => $nick, 'location' => '', //$uinfo[0]['default-location'], @@ -2299,12 +2298,11 @@ require_once('include/api_auth.php'); api_register_func('api/direct_messages','api_direct_messages_inbox',true); - function api_oauth_request_token(&$a, $type){ try{ - $oauth = new FKOAuth1(); + $oauth = new ZotOAuth1(); $req = OAuthRequest::from_request(); -logger('Req: ' . var_export($req,true)); + logger('Req: ' . var_export($req,true),LOGGER_DATA); $r = $oauth->fetch_request_token($req); }catch(Exception $e){ logger('oauth_exception: ' . print_r($e->getMessage(),true)); @@ -2314,9 +2312,10 @@ logger('Req: ' . var_export($req,true)); echo $r; killme(); } + function api_oauth_access_token(&$a, $type){ try{ - $oauth = new FKOAuth1(); + $oauth = new ZotOAuth1(); $req = OAuthRequest::from_request(); $r = $oauth->fetch_access_token($req); }catch(Exception $e){ diff --git a/include/api_auth.php b/include/api_auth.php index ee9db3f55..c9978c99d 100644 --- a/include/api_auth.php +++ b/include/api_auth.php @@ -1,16 +1,18 @@ verify_request($req); @@ -23,16 +25,14 @@ function api_login(&$a){ call_hooks('logged_in', $a->user); return; } - echo __file__.__line__.__function__."
"; 
-//			var_dump($consumer, $token); 
-		die();
+		killme();
 	}
 	catch(Exception $e) {
 		logger(__file__.__line__.__function__."\n".$e);
 	}
-
 		
-	// workaround for HTTP-auth in CGI mode
+	// workarounds for HTTP-auth in CGI mode
+
 	if(x($_SERVER,'REDIRECT_REMOTE_USER')) {
 		$userpass = base64_decode(substr($_SERVER["REDIRECT_REMOTE_USER"],6)) ;
 		if(strlen($userpass)) {
@@ -51,45 +51,49 @@ function api_login(&$a){
 		}
 	}
 
-
-	if (!isset($_SERVER['PHP_AUTH_USER'])) {
-		logger('API_login: ' . print_r($_SERVER,true), LOGGER_DEBUG);
-		header('WWW-Authenticate: Basic realm="Red"');
-		header('HTTP/1.0 401 Unauthorized');
-		die('This api requires login');
-	}
-		
-	// process normal login request
 	require_once('include/auth.php');
-	$channel_login = 0;
-	$record = account_verify_password($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']);
-	if(! $record) {
-	        $r = q("select * from channel where channel_address = '%s' limit 1",
+	require_once('include/security.php');
+
+	// process normal login request
+
+	if(isset($_SERVER['PHP_AUTH_USER'])) {
+		$channel_login = 0;
+		$record = account_verify_password($_SERVER['PHP_AUTH_USER'],$_SERVER['PHP_AUTH_PW']);
+		if(! $record) {
+	        $r = q("select * from channel left join account on account.account_id = channel.channel_account_id 
+				where channel.channel_address = '%s' limit 1",
 		       dbesc($_SERVER['PHP_AUTH_USER'])
 			);
         	if ($r) {
-			$x = q("select * from account where account_id = %d limit 1",
-			       intval($r[0]['channel_account_id'])
-				);
-			if ($x) {
-				$record = account_verify_password($x[0]['account_email'],$_SERVER['PHP_AUTH_PW']);
+				$record = account_verify_password($r[0]['account_email'],$_SERVER['PHP_AUTH_PW']);
 				if($record)
 					$channel_login = $r[0]['channel_id'];
 			}
 		}
-		if(! $record) {	
-			logger('API_login failure: ' . print_r($_SERVER,true), LOGGER_DEBUG);
-			header('WWW-Authenticate: Basic realm="Red"');
-			header('HTTP/1.0 401 Unauthorized');
-			die('This api requires login');
-		}
 	}
 
-	require_once('include/security.php');
-	authenticate_success($record);
+	if($record) {
+		authenticate_success($record);
 
-	if($channel_login)
-		change_channel($channel_login);
+		if($channel_login)
+			change_channel($channel_login);
+
+		$_SESSION['allow_api'] = true;
+		return true;
+	}
+	else {
+		$_SERVER['PHP_AUTH_PW'] = '*****';
+		logger('API_login failure: ' . print_r($_SERVER,true), LOGGER_DEBUG);
+		log_failed_login('API login failure');
+		retry_basic_auth();
+	}
 
-	$_SESSION['allow_api'] = true;
 }
+
+
+function retry_basic_auth() {
+	header('WWW-Authenticate: Basic realm="Hubzilla"');
+	header('HTTP/1.0 401 Unauthorized');
+	echo('This api requires login');
+	killme();
+}
\ No newline at end of file
diff --git a/include/comanche.php b/include/comanche.php
index 9585a6578..ef71886f2 100644
--- a/include/comanche.php
+++ b/include/comanche.php
@@ -282,15 +282,16 @@ function comanche_widget($name, $text) {
 		}
 	}
 
-	if(file_exists('widget/' . trim($name) . '.php'))
+	$func = 'widget_' . trim($name);
+
+	if((! function_exists($func)) && file_exists('widget/' . trim($name) . '.php'))
 		require_once('widget/' . trim($name) . '.php');
 	else {
-		$theme_widget = 'widget_' . trim($name) . '.php';
-		if(theme_include($theme_widget))
+		$theme_widget = $func . '.php';
+		if((! function_exists($func)) && theme_include($theme_widget))
 			require_once(theme_include($theme_widget));
 	}
 
-	$func = 'widget_' . trim($name);
 	if (function_exists($func))
 		return $func($vars);
 }
diff --git a/include/externals.php b/include/externals.php
index 18c034bb2..3a3a32420 100644
--- a/include/externals.php
+++ b/include/externals.php
@@ -28,7 +28,10 @@ function externals_run($argv, $argc){
 		} 
 		else {
 			$randfunc = db_getfunc('RAND');
-			$r = q("select site_url, site_pull from site where site_url != '%s' and site_flags != %d and site_type = %d order by $randfunc limit 1",
+
+			// fixme this query does not deal with directory realms. 
+
+			$r = q("select site_url, site_pull from site where site_url != '%s' and site_flags != %d and site_type = %d and site_dead = 0 order by $randfunc limit 1",
 				dbesc(z_root()),
 				intval(DIRECTORY_MODE_STANDALONE),
 				intval(SITE_TYPE_ZOT)
@@ -37,19 +40,11 @@ function externals_run($argv, $argc){
 				$url = $r[0]['site_url'];
 		}
 
-		// Note: blacklisted sites must be stored in the config as an array. 
-		// No simple way to turn this into a personal config because we have no identity here.
-		// For that we probably need a variant of superblock.
-
 		$blacklisted = false;
-		$bl1 = get_config('system','blacklisted_sites');
-		if(is_array($bl1) && $bl1) {
-			foreach($bl1 as $bl) {
-				if($bl && strpos($url,$bl) !== false) {
-					$blacklisted = true;
-					break;
-				}
-			}
+
+		if(! check_siteallowed($url)) {
+			logger('blacklisted site: ' . $url);
+			$blacklisted = true;
 		}
 
 		$attempts ++;
diff --git a/include/follow.php b/include/follow.php
index 40ad2c299..97be82da7 100644
--- a/include/follow.php
+++ b/include/follow.php
@@ -161,6 +161,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
 			}
 		}
 		if($r) {
+			$xchan = $r[0];
 			$xchan_hash = $r[0]['xchan_hash'];
 			$their_perms = 0;
 		}
@@ -172,7 +173,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
 		return $result;
 	}
 
-	$x = array('channel_id' => $uid, 'follow_address' => $url, 'xchan' => $r[0], 'allowed' => 1);
+	$x = array('channel_id' => $uid, 'follow_address' => $url, 'xchan' => $r[0], 'allowed' => 1, 'singleton' => 0);
 
 	call_hooks('follow_allow',$x);
 
@@ -180,7 +181,7 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
 		$result['message'] = t('Protocol disabled.');
 		return $result;
 	}
-
+	$singleton = intval($x['singleton']);
 
 	if((local_channel()) && $uid == local_channel()) {
 		$aid = get_account_id();
@@ -221,13 +222,22 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
 		return $result;
 	}
 
-	$r = q("select abook_xchan from abook where abook_xchan = '%s' and abook_channel = %d limit 1",
+	$r = q("select abook_xchan, abook_instance from abook where abook_xchan = '%s' and abook_channel = %d limit 1",
 		dbesc($xchan_hash),
 		intval($uid)
 	);
 	if($r) {
-		$x = q("update abook set abook_their_perms = %d where abook_id = %d",
+		$abook_instance = $r[0]['abook_instance'];
+
+		if(($singleton) && strpos($abook_instance,z_root()) === false) {
+			if($abook_instance)
+				$abook_instance .= ',';
+			$abook_instance .= z_root();
+		}
+
+		$x = q("update abook set abook_their_perms = %d, abook_instance = '%s' where abook_id = %d",
 			intval($their_perms),
+			dbesc($abook_instance),
 			intval($r[0]['abook_id'])
 		);		
 	}
@@ -237,8 +247,8 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
 		if($closeness === false)
 			$closeness = 80;
 
-		$r = q("insert into abook ( abook_account, abook_channel, abook_closeness, abook_xchan, abook_feed, abook_their_perms, abook_my_perms, abook_created, abook_updated )
-			values( %d, %d, %d, '%s', %d, %d, %d, '%s', '%s' ) ",
+		$r = q("insert into abook ( abook_account, abook_channel, abook_closeness, abook_xchan, abook_feed, abook_their_perms, abook_my_perms, abook_created, abook_updated, abook_instance )
+			values( %d, %d, %d, '%s', %d, %d, %d, '%s', '%s', '%s' ) ",
 			intval($aid),
 			intval($uid),
 			intval($closeness),
@@ -247,7 +257,8 @@ function new_contact($uid,$url,$channel,$interactive = false, $confirm = false)
 			intval(($is_http) ? $their_perms|PERMS_R_STREAM|PERMS_A_REPUBLISH : $their_perms),
 			intval($my_perms),
 			dbesc(datetime_convert()),
-			dbesc(datetime_convert())
+			dbesc(datetime_convert()),
+			dbesc(($singleton) ? z_root() : '')
 		);
 	}
 
diff --git a/include/identity.php b/include/identity.php
index 95ade3b28..98ba26bd8 100644
--- a/include/identity.php
+++ b/include/identity.php
@@ -896,12 +896,6 @@ function profile_load(&$a, $nickname, $profile = '') {
 
 	$_SESSION['theme'] = $p[0]['channel_theme'];
 
-//	$a->set_template_engine(); // reset the template engine to the default in case the user's theme doesn't specify one
-
-//	$theme_info_file = "view/theme/".current_theme()."/php/theme.php";
-//	if (file_exists($theme_info_file)){
-//		require_once($theme_info_file);
-//	}
 }
 
 /**
diff --git a/include/items.php b/include/items.php
index 7d349c631..ef1867c14 100755
--- a/include/items.php
+++ b/include/items.php
@@ -550,6 +550,7 @@ function get_public_feed($channel, $params) {
 	$params['direction'] = ((x($params,'direction')) ? $params['direction']     : 'desc');
 	$params['pages']     = ((x($params,'pages'))     ? intval($params['pages']) : 0);
 	$params['top']       = ((x($params,'top'))       ? intval($params['top'])   : 0);
+	$params['cat']       = ((x($params,'cat'))       ? $params['cat']          : '');
 
 	switch($params['type']) {
 		case 'json':
@@ -593,7 +594,8 @@ function get_feed_for($channel, $observer_hash, $params) {
 		'direction' => $params['direction'],  // FIXME
 		'pages' => $params['pages'],
 		'order' => 'post',
-		'top'   => $params['top']
+		'top'   => $params['top'],
+		'cat'   => $params['cat']
 		), $channel, $observer_hash, CLIENT_MODE_NORMAL, get_app()->module);
 	
 
@@ -3472,7 +3474,7 @@ function post_is_importable($item,$abook) {
 	unobscure($item);
 
 	$text = prepare_text($item['body'],$item['mimetype']);
-	$text = html2plain($text);
+	$text = html2plain(($item['title']) ? $item['title'] . ' ' . $text : $text);
 
 
 	$lang = null;
@@ -4817,6 +4819,9 @@ function items_fetch($arr,$channel = null,$observer_hash = null,$client_mode = C
 	if($arr['since_id'])
 		$sql_extra .= " and item.id > " . $since_id . " ";
 
+	if($arr['cat'])
+		$sql_extra .= protect_sprintf(term_query('item', $arr['cat'], TERM_CATEGORY));
+
 	if($arr['gid'] && $uid) {
 		$r = q("SELECT * FROM `groups` WHERE id = %d AND uid = %d LIMIT 1",
 			intval($arr['group']),
diff --git a/include/message.php b/include/message.php
index 820d814b6..940fcc275 100644
--- a/include/message.php
+++ b/include/message.php
@@ -13,6 +13,7 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto='
 	$ret = array('success' => false);
 
 	$a = get_app();
+	$observer_hash = get_observer_hash();
 
 	if(! $recipient) {
 		$ret['message'] = t('No recipient provided.');
@@ -148,8 +149,8 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto='
 
 	$match = null;
 	$images = null;
-	if(preg_match_all("/\[zmg\](.*?)\[\/zmg\]/",((strpos($body,'[/crypt]')) ? $_POST['media_str'] : $body),$match))
-		$images = $match[1];
+	if(preg_match_all("/\[zmg\=([0-9]*)x([0-9]*)\](.*?)\[\/zmg\]/",((strpos($body,'[/crypt]')) ? $_POST['media_str'] : $body),$match))
+		$images = $match[3];
 
 	$match = false;
 
@@ -173,7 +174,7 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto='
 					'revision' => $r['data']['revision']
 				);
 			}
-			$body = str_replace($match[1],'',$body);
+			$body = trim(str_replace($match[1],'',$body));
 		}
 	}
 
@@ -230,7 +231,7 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto='
 				dbesc($image_uri),
 				intval($channel['channel_id']),
 				dbesc('<' . $channel['channel_hash'] . '>')
-			); 
+			);
 			$r = q("UPDATE attach SET allow_cid = '%s' WHERE hash = '%s' AND is_photo = 1 and uid = %d and allow_cid = '%s'",
 				dbesc('<' . $recipient . '>'),
 				dbesc($image_uri),
@@ -239,7 +240,7 @@ function send_message($uid = 0, $recipient='', $body='', $subject='', $replyto='
 			); 
 		}
 	}
-	
+
 	if($attaches) {
 		foreach($attaches as $attach) {
 			$hash = substr($attach,0,strpos($attach,','));
@@ -505,3 +506,4 @@ function private_messages_fetch_conversation($channel_id, $messageitem_id, $upda
 	return $messages;
 
 }
+
diff --git a/include/network.php b/include/network.php
index 026f5ee0a..68452c3d1 100644
--- a/include/network.php
+++ b/include/network.php
@@ -1709,3 +1709,190 @@ function do_delivery($deliveries) {
 	
 
 }
+
+
+function get_site_info() {
+
+	global $db;
+	global $a;
+
+	$register_policy = Array('REGISTER_CLOSED', 'REGISTER_APPROVE', 'REGISTER_OPEN');
+	$directory_mode = Array('DIRECTORY_MODE_NORMAL', 'DIRECTORY_MODE_SECONDARY','DIRECTORY_MODE_PRIMARY', 256 => 'DIRECTORY_MODE_STANDALONE');
+		
+	$sql_extra = '';
+
+	$r = q("select * from channel left join account on account_id = channel_account_id where ( account_roles & 4096 )>0 and account_default_channel = channel_id");
+
+
+	if($r) {
+		$admin = array();
+		foreach($r as $rr) {
+			if($rr['channel_pageflags'] & PAGE_HUBADMIN)
+				$admin[] = array( 'name' => $rr['channel_name'], 'address' => $rr['channel_address'] . '@' . get_app()->get_hostname(), 'channel' => z_root() . '/channel/' . $rr['channel_address']);
+		}
+		if(! $admin) {
+			foreach($r as $rr) {
+				$admin[] = array( 'name' => $rr['channel_name'], 'address' => $rr['channel_address'] . '@' . get_app()->get_hostname(), 'channel' => z_root() . '/channel/' . $rr['channel_address']);
+			}
+		}
+	}
+	else {
+		$admin = false;
+	}
+
+	$def_service_class = get_config('system','default_service_class');
+	if($def_service_class)
+		$service_class = get_config('service_class',$def_service_class);
+	else
+		$service_class = false;
+
+	$visible_plugins = array();
+	if(is_array($a->plugins) && count($a->plugins)) {
+		$r = q("select * from addon where hidden = 0");
+		if(count($r))
+			foreach($r as $rr)
+				$visible_plugins[] = $rr['name'];
+	}
+	sort($visible_plugins);
+
+	if(@is_dir('.git') && function_exists('shell_exec'))
+		$commit = trim(@shell_exec('git log -1 --format="%h"'));
+	if(! isset($commit) || strlen($commit) > 16)
+		$commit = '';
+
+	$site_info = get_config('system','info');
+	$site_name = get_config('system','sitename');
+	if(! get_config('system','hidden_version_siteinfo')) {
+		$version = RED_VERSION;
+		$tag = get_std_version();
+
+		if(@is_dir('.git') && function_exists('shell_exec')) {
+			$commit = trim( @shell_exec('git log -1 --format="%h"'));
+//			if(! get_config('system','hidden_tag_siteinfo'))
+//				$tag = trim( @shell_exec('git describe --tags --abbrev=0'));
+//			else 
+//				$tag = '';
+		}
+		if(! isset($commit) || strlen($commit) > 16)
+			$commit = '';
+	}
+	else {
+			$version = $commit = '';
+	}
+		
+	//Statistics
+	$channels_total_stat = intval(get_config('system','channels_total_stat'));
+	$channels_active_halfyear_stat = intval(get_config('system','channels_active_halfyear_stat'));
+	$channels_active_monthly_stat = intval(get_config('system','channels_active_monthly_stat'));
+	$local_posts_stat = intval(get_config('system','local_posts_stat'));
+	$hide_in_statistics = intval(get_config('system','hide_in_statistics'));
+	$site_expire = intval(get_config('system', 'default_expire_days'));
+
+		
+	$data = Array(
+		'version' => $version,
+		'version_tag' => $tag,
+		'commit' => $commit,
+		'url' => z_root(),
+		'plugins' => $visible_plugins,
+		'register_policy' =>  $register_policy[get_config('system','register_policy')],
+		'directory_mode' =>  $directory_mode[get_config('system','directory_mode')],
+		'language' => get_config('system','language'),
+		'rss_connections' => get_config('system','feed_contacts'),
+		'expiration' => $site_expire,
+		'default_service_restrictions' => $service_class,
+		'admin' => $admin,
+		'site_name' => (($site_name) ? $site_name : ''),
+		'platform' => PLATFORM_NAME,
+		'dbdriver' => $db->getdriver(),
+		'lastpoll' => get_config('system','lastpoll'),
+		'info' => (($site_info) ? $site_info : ''),
+		'channels_total' => $channels_total_stat,
+		'channels_active_halfyear' => $channels_active_halfyear_stat,
+		'channels_active_monthly' => $channels_active_monthly_stat,
+		'local_posts' => $local_posts_stat,
+		'hide_in_statistics' => $hide_in_statistics
+	);
+	return $data;
+}
+
+
+
+function check_siteallowed($url) {
+
+	$retvalue = true;
+
+
+	$arr = array('url' => $url);
+	call_hooks('check_siteallowed',$arr);
+
+	if(array_key_exists('allowed',$arr))
+		return $arr['allowed'];
+
+	$bl1 = get_config('system','whitelisted_sites');
+	if(is_array($bl1) && $bl1) {
+		foreach($bl1 as $bl) {
+			if($bl1 === '*')
+				$retvalue = true;
+			if($bl && strpos($url,$bl) !== false)
+				return true;
+		}
+	}
+	$bl1 = get_config('system','blacklisted_sites');
+	if(is_array($bl1) && $bl1) {
+		foreach($bl1 as $bl) {
+			if($bl1 === '*')
+				$retvalue = false;
+			if($bl && strpos($url,$bl) !== false) {
+				return false;
+			}
+		}
+	}
+	return $retvalue;
+}
+
+function check_channelallowed($hash) {
+
+	$retvalue = true;
+
+	$arr = array('hash' => $hash);
+	call_hooks('check_channelallowed',$arr);
+
+	if(array_key_exists('allowed',$arr))
+		return $arr['allowed'];
+
+	$bl1 = get_config('system','whitelisted_channels');
+	if(is_array($bl1) && $bl1) {
+		foreach($bl1 as $bl) {
+			if($bl1 === '*')
+				$retvalue = true;
+			if($bl && strpos($hash,$bl) !== false)
+				return true;
+		}
+	}
+	$bl1 = get_config('system','blacklisted_channels');
+	if(is_array($bl1) && $bl1) {
+		foreach($bl1 as $bl) {
+			if($bl1 === '*')
+				$retvalue = false;
+			if($bl && strpos($hash,$bl) !== false) {
+				return false;
+			}
+		}
+	}
+	return $retvalue;
+}
+
+function deliverable_singleton($xchan) {
+	$r = q("select abook_instance from abook where abook_xchan = '%s' limit 1",
+		dbesc($xchan['xchan_hash'])
+	);
+	if($r) {
+		if(! $r[0]['abook_instance'])
+			return true;
+		if(strpos($r[0]['abook_instance'],z_root()) !== false)
+			return true;
+	}
+	return false;
+}
+
diff --git a/include/notifier.php b/include/notifier.php
index b7830285a..66b6160e4 100644
--- a/include/notifier.php
+++ b/include/notifier.php
@@ -57,6 +57,8 @@ require_once('include/html2plain.php');
  *       purge_all              channel_id
  *       expire                 channel_id
  *       relay					item_id (item was relayed to owner, we will deliver it as owner)
+ *       single_activity        item_id (deliver to a singleton network from the appropriate clone)
+ *       single_mail            mail_id (deliver to a singleton network from the appropriate clone)
  *       location               channel_id
  *       request                channel_id            xchan_hash             message_id
  *       rating                 xlink_id
diff --git a/include/oauth.php b/include/oauth.php
index 80336f906..4e5e4ecf8 100644
--- a/include/oauth.php
+++ b/include/oauth.php
@@ -1,4 +1,5 @@
 
@@ -9,16 +10,17 @@ define('REQUEST_TOKEN_DURATION', 300);
 define('ACCESS_TOKEN_DURATION', 31536000);
 
 require_once("library/OAuth1.php");
-require_once("library/oauth2-php/lib/OAuth2.inc");
 
-class FKOAuthDataStore extends OAuthDataStore {
-  function gen_token(){
+//require_once("library/oauth2-php/lib/OAuth2.inc");
+
+class ZotOAuthDataStore extends OAuthDataStore {
+
+	function gen_token(){
 		return md5(base64_encode(pack('N6', mt_rand(), mt_rand(), mt_rand(), mt_rand(), mt_rand(), uniqid())));
-  }
+	}
 	
-  function lookup_consumer($consumer_key) {
-		logger(__function__.":".$consumer_key);
-//      echo "
"; var_dump($consumer_key); killme();
+	function lookup_consumer($consumer_key) {
+		logger('consumer_key: ' . $consumer_key, LOGGER_DEBUG);
 
 		$r = q("SELECT client_id, pw, redirect_uri FROM clients WHERE client_id = '%s'",
 			dbesc($consumer_key)
@@ -29,10 +31,11 @@ class FKOAuthDataStore extends OAuthDataStore {
 			return new OAuthConsumer($r[0]['client_id'],$r[0]['pw'],$r[0]['redirect_uri']);
 		}
 		return null;
-  }
+	}
 
-  function lookup_token($consumer, $token_type, $token) {
-		logger(__function__.":".$consumer.", ". $token_type.", ".$token);
+	function lookup_token($consumer, $token_type, $token) {
+
+		logger(__function__.":".$consumer.", ". $token_type.", ".$token, LOGGER_DEBUG);
 
 		$r = q("SELECT id, secret, scope, expires, uid  FROM tokens WHERE client_id = '%s' AND scope = '%s' AND id = '%s'",
 			dbesc($consumer->key),
@@ -48,10 +51,9 @@ class FKOAuthDataStore extends OAuthDataStore {
 			return $ot;
 		}
 		return null;
-  }
+	}
 
-  function lookup_nonce($consumer, $token, $nonce, $timestamp) {
-//		echo __file__.":".__line__."
"; var_dump($consumer,$key); killme();
+	function lookup_nonce($consumer, $token, $nonce, $timestamp) {
 
 		$r = q("SELECT id, secret FROM tokens WHERE client_id = '%s' AND id = '%s' AND expires = %d",
 			dbesc($consumer->key),
@@ -62,10 +64,12 @@ class FKOAuthDataStore extends OAuthDataStore {
 		if (count($r))
 			return new OAuthToken($r[0]['id'],$r[0]['secret']);
 		return null;
-  }
+	}
+
+	function new_request_token($consumer, $callback = null) {
+
+		logger(__function__.":".$consumer.", ". $callback, LOGGER_DEBUG);
 
-  function new_request_token($consumer, $callback = null) {
-		logger(__function__.":".$consumer.", ". $callback);
 		$key = $this->gen_token();
 		$sec = $this->gen_token();
 		
@@ -82,29 +86,31 @@ class FKOAuthDataStore extends OAuthDataStore {
 				'request',
 				time()+intval(REQUEST_TOKEN_DURATION));
 
-		if (!$r) return null;
+		if(! $r)
+			return null;
 		return new OAuthToken($key,$sec);
-  }
+	}
 
-  function new_access_token($token, $consumer, $verifier = null) {
-    logger(__function__.":".$token.", ". $consumer.", ". $verifier);
+	function new_access_token($token, $consumer, $verifier = null) {
+
+		logger(__function__.":".$token.", ". $consumer.", ". $verifier, LOGGER_DEBUG);
     
-    // return a new access token attached to this consumer
-    // for the user associated with this token if the request token
-    // is authorized
-    // should also invalidate the request token
-    
-    $ret=Null;
-    
-    // get user for this verifier
-    $uverifier = get_config("oauth", $verifier);
-    logger(__function__.":".$verifier.",".$uverifier);
-    if (is_null($verifier) || ($uverifier!==false)){
+		// return a new access token attached to this consumer
+		// for the user associated with this token if the request token
+		// is authorized
+		// should also invalidate the request token
+	
+		$ret=Null;
+	
+		// get user for this verifier
+		$uverifier = get_config("oauth", $verifier);
+		logger(__function__.":".$verifier.",".$uverifier, LOGGER_DEBUG);
+		if (is_null($verifier) || ($uverifier!==false)) {
 		
-		$key = $this->gen_token();
-		$sec = $this->gen_token();
+			$key = $this->gen_token();
+			$sec = $this->gen_token();
 
-		$r = q("INSERT INTO tokens (id, secret, client_id, scope, expires, uid) VALUES ('%s','%s','%s','%s', %d, %d)",
+			$r = q("INSERT INTO tokens (id, secret, client_id, scope, expires, uid) VALUES ('%s','%s','%s','%s', %d, %d)",
 				dbesc($key),
 				dbesc($sec),
 				dbesc($consumer->key),
@@ -112,81 +118,70 @@ class FKOAuthDataStore extends OAuthDataStore {
 				time()+intval(ACCESS_TOKEN_DURATION),
 				intval($uverifier));
 
-		if ($r)
-			$ret = new OAuthToken($key,$sec);		
-	}
+			if ($r)
+				$ret = new OAuthToken($key,$sec);		
+		}
 		
 		
-	q("DELETE FROM tokens WHERE id='%s'", $token->key);
+		q("DELETE FROM tokens WHERE id='%s'", $token->key);
 	
 	
-	if (!is_null($ret) && $uverifier!==false){
-		del_config("oauth", $verifier);
-	/*	$apps = get_pconfig($uverifier, "oauth", "apps");
-		if ($apps===false) $apps=array();
-		$apps[] = $consumer->key;
-		set_pconfig($uverifier, "oauth", "apps", $apps);*/
+		if (!is_null($ret) && $uverifier!==false) {
+			del_config("oauth", $verifier);
+	
+			//	$apps = get_pconfig($uverifier, "oauth", "apps");
+			//	if ($apps===false) $apps=array();
+			//  $apps[] = $consumer->key;
+			// set_pconfig($uverifier, "oauth", "apps", $apps);
+		}
+		return $ret;
 	}
-		
-    return $ret;
-    
-  }
 }
 
-class FKOAuth1 extends OAuthServer {
+class ZotOAuth1 extends OAuthServer {
 
 	function __construct() {
-		parent::__construct(new FKOAuthDataStore());
+		parent::__construct(new ZotOAuthDataStore());
 		$this->add_signature_method(new OAuthSignatureMethod_PLAINTEXT());
 		$this->add_signature_method(new OAuthSignatureMethod_HMAC_SHA1());
 	}
 	
 	function loginUser($uid){
-		logger("RedOAuth1::loginUser $uid");
-		$a = get_app();
+
+		logger("ZotOAuth1::loginUser $uid");
+
 		$r = q("SELECT * FROM channel WHERE channel_id = %d LIMIT 1",
 			intval($uid)
 		);
 		if(count($r)){
 			$record = $r[0];
 		} else {
-		   logger('FKOAuth1::loginUser failure: ' . print_r($_SERVER,true), LOGGER_DEBUG);
-		    header('HTTP/1.0 401 Unauthorized');
-		    die('This api requires login');
+			logger('ZotOAuth1::loginUser failure: ' . print_r($_SERVER,true), LOGGER_DEBUG);
+			header('HTTP/1.0 401 Unauthorized');
+			echo('This api requires login');
+			killme();
 		}
 
 		$_SESSION['uid'] = $record['channel_id'];
-		$_SESSION['theme'] = $record['channel_theme'];
-		$_SESSION['account_id'] = $record['channel_account_id'];
-		$_SESSION['mobile_theme'] = get_pconfig($record['channel_id'], 'system', 'mobile_theme');
-		$_SESSION['authenticated'] = 1;
-		$_SESSION['my_url'] = $a->get_baseurl() . '/channel/' . $record['channel_address'];
 		$_SESSION['addr'] = $_SERVER['REMOTE_ADDR'];
-		$_SESSION['allow_api'] = true;
+
 		$x = q("select * from account where account_id = %d limit 1",
 			intval($record['channel_account_id'])
 		);
-		if($x)
-			$a->account = $x[0];
-
-		change_channel($record['channel_id']);
-
-		$a->channel = $record;
-
-		if(strlen($a->channel['channel_timezone'])) {
-			date_default_timezone_set($a->channel['channel_timezone']);
+		if($x) {
+			require_once('include/security.php');
+			authenticate_success($x[0],true,false,true,true);
+			$_SESSION['allow_api'] = true;
 		}
-
-//		q("UPDATE `user` SET `login_date` = '%s' WHERE `uid` = %d LIMIT 1",
-//			dbesc(datetime_convert()),
-//			intval($_SESSION['uid'])
-//		);
-//
-//		call_hooks('logged_in', $a->user);		
 	}
 	
 }
+
 /*
+ *
+
+ not yet used
+
 class FKOAuth2 extends OAuth2 {
 
 	private function db_secret($client_secret){
diff --git a/include/plugin.php b/include/plugin.php
index 8749f3fbf..1f4d60736 100755
--- a/include/plugin.php
+++ b/include/plugin.php
@@ -495,6 +495,15 @@ function format_css_if_exists($source) {
 		return '' . "\r\n";
 }
 
+/*
+ * This basically calculates the baseurl. We have other functions to do that, but
+ * there was an issue with script paths and mixed-content whose details are arcane 
+ * and perhaps lost in the message archives. The short answer is that we're ignoring 
+ * the URL which we are "supposed" to use, and generating script paths relative to 
+ * the URL which we are currently using; in order to ensure they are found and aren't
+ * blocked due to mixed content issues. 
+ */
+
 function script_path() {
 	if(x($_SERVER,'HTTPS') && $_SERVER['HTTPS'])
 		$scheme = 'https';
@@ -616,3 +625,14 @@ function get_markup_template($s, $root = '') {
 	$template = $t->get_markup_template($s, $root);
 	return $template;
 }
+
+// return the standardised version. Since we can't easily compare
+// before the STD_VERSION definition was applied, we have to treat 
+// all prior release versions the same. You can dig through them
+// with other means (such as RED_VERSION) if necessary. 
+
+function get_std_version() {
+	if(defined('STD_VERSION'))
+		return STD_VERSION;
+	return '0.0.0';
+}
diff --git a/include/security.php b/include/security.php
index 9a25d9e0e..d4ebe0024 100644
--- a/include/security.php
+++ b/include/security.php
@@ -93,6 +93,7 @@ function change_channel($change_channel) {
 	$ret = false;
 
 	if($change_channel) {
+
 		$r = q("select channel.*, xchan.* from channel left join xchan on channel.channel_hash = xchan.xchan_hash where channel_id = %d and channel_account_id = %d and channel_removed = 0 limit 1",
 			intval($change_channel),
 			intval(get_account_id())
@@ -136,14 +137,14 @@ function change_channel($change_channel) {
 }
 
 /**
- * @brief Creates an addiontal SQL where statement to check permissions.
+ * @brief Creates an additional SQL where statement to check permissions.
  *
  * @param int $owner_id
- * @param bool $remote_verified default false, not used at all
- * @param string $groups this param is not used at all
+ * @param bool $remote_observer - if unset use current observer
  *
  * @return string additional SQL where statement
  */
+
 function permissions_sql($owner_id, $remote_observer = null) {
 
 	$local_channel = local_channel();
@@ -208,8 +209,7 @@ function permissions_sql($owner_id, $remote_observer = null) {
  * @brief Creates an addiontal SQL where statement to check permissions for an item.
  *
  * @param int $owner_id
- * @param bool $remote_verified default false, not used at all
- * @param string $groups this param is not used at all
+ * @param bool $remote_observer, use current observer if unset
  *
  * @return string additional SQL where statement
  */
@@ -400,11 +400,9 @@ function check_form_security_token_ForbiddenOnErr($typename = '', $formname = 'f
 }
 
 
-// Returns an array of group id's this contact is a member of.
-// This array will only contain group id's related to the uid of this
-// DFRN contact. They are *not* neccessarily unique across the entire site. 
+// Returns an array of group hash id's on this entire site (across all channels) that this connection is a member of.
+// var $contact_id = xchan_hash of connection
 
-if(! function_exists('init_groups_visitor')) {
 function init_groups_visitor($contact_id) {
 	$groups = array();
 	$r = q("SELECT hash FROM `groups` left join group_member on groups.id = group_member.gid WHERE xchan = '%s' ",
@@ -415,7 +413,7 @@ function init_groups_visitor($contact_id) {
 			$groups[] = $rr['hash'];
 	}
 	return $groups;
-}}
+}
 
 
 
diff --git a/include/widgets.php b/include/widgets.php
index a3f7444ec..4b14d6c94 100644
--- a/include/widgets.php
+++ b/include/widgets.php
@@ -666,7 +666,7 @@ function widget_eventsmenu($arr) {
 	if (! local_channel())
 		return;
 
-	return replace_macros(get_markup_template('events_side.tpl'), array(
+	return replace_macros(get_markup_template('events_menu_side.tpl'), array(
 		'$title' => t('Events Menu'),
 		'$day' => t('Day View'),
 		'$week' => t('Week View'),
@@ -677,6 +677,18 @@ function widget_eventsmenu($arr) {
 	));
 }
 
+function widget_eventstools($arr) {
+	if (! local_channel())
+		return;
+
+	return replace_macros(get_markup_template('events_tools_side.tpl'), array(
+		'$title' => t('Events Tools'),
+		'$export' => t('Export Calendar'),
+		'$import' => t('Import Calendar'),
+		'$submit' => t('Submit')
+	));
+}
+
 function widget_design_tools($arr) {
 	$a = get_app();
 
@@ -1136,7 +1148,7 @@ function widget_forums($arr) {
 		foreach($r1 as $rr) {
 			if($unseen && (! intval($rr['unseen'])))
 				continue;
-			$o .= '
  • ' . ((intval($rr['unseen'])) ? intval($rr['unseen']) : '') . ' ' . $rr['xchan_name'] . '
  • '; + $o .= '
  • ' . ((intval($rr['unseen'])) ? intval($rr['unseen']) : '') . ' ' . $rr['xchan_name'] . '
  • '; } $o .= ''; } @@ -1147,6 +1159,8 @@ function widget_forums($arr) { function widget_tasklist($arr) { + if (! local_channel()) + return; require_once('include/event.php'); $o .= ''; @@ -1285,7 +1299,6 @@ function widget_album($args) { //edit album name $album_edit = null; - $photos = array(); if($r) { $twist = 'rotright'; @@ -1324,6 +1337,7 @@ function widget_album($args) { $o .= replace_macros($tpl, array( '$photos' => $photos, '$album' => (($title) ? $title : $album), + '$album_id' => rand(), '$album_edit' => array(t('Edit Album'), $album_edit), '$can_post' => false, '$upload' => array(t('Upload'), z_root() . '/photos/' . get_app()->profile['channel_address'] . '/upload/' . bin2hex($album)), diff --git a/include/zot.php b/include/zot.php index c0d537eb9..390407e4e 100644 --- a/include/zot.php +++ b/include/zot.php @@ -554,18 +554,8 @@ function zot_gethub($arr,$multiple = false) { if($arr['guid'] && $arr['guid_sig'] && $arr['url'] && $arr['url_sig']) { - $blacklisted = false; - $bl1 = get_config('system','blacklisted_sites'); - if(is_array($bl1) && $bl1) { - foreach($bl1 as $bl) { - if($bl && strpos($arr['url'],$bl) !== false) { - $blacklisted = true; - break; - } - } - } - if($blacklisted) { - logger('zot_gethub: blacklisted site: ' . $arr['url']); + if(! check_siteallowed($arr['url'])) { + logger('blacklisted site: ' . $arr['url']); return null; } @@ -1246,6 +1236,10 @@ function zot_import($arr, $sender_url) { $no_dups = array(); if($deliveries) { foreach($deliveries as $d) { + if(! is_array($d)) { + logger('Delivery hash array is not an array: ' . print_r($d,true)); + continue; + } if(! in_array($d['hash'],$no_dups)) $no_dups[] = $d['hash']; } @@ -1617,6 +1611,14 @@ function process_delivery($sender, $arr, $deliveries, $relay, $public = false, $ $channel = $r[0]; $DR->addto_recipient($channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>'); + /* blacklisted channels get a permission denied, no special message to tip them off */ + + if(! check_channelallowed($sender['hash'])) { + $DR->update('permission denied'); + $result[] = $DR->get(); + continue; + } + /** * @FIXME: Somehow we need to block normal message delivery from our clones, as the delivered * message doesn't have ACL information in it as the cloned copy does. That copy @@ -2088,6 +2090,14 @@ function process_mail_delivery($sender, $arr, $deliveries) { $channel = $r[0]; $DR->addto_recipient($channel['channel_name'] . ' <' . $channel['channel_address'] . '@' . get_app()->get_hostname() . '>'); + /* blacklisted channels get a permission denied, no special message to tip them off */ + + if(! check_channelallowed($sender['hash'])) { + $DR->update('permission denied'); + $result[] = $DR->get(); + continue; + } + if(! perm_is_allowed($channel['channel_id'],$sender['hash'],'post_mail')) { logger("permission denied for mail delivery {$channel['channel_id']}"); $DR->update('permission denied'); @@ -3484,13 +3494,13 @@ function import_author_zot($x) { * @param array $data * @return array */ -function zot_process_message_request($data) { +function zot_reply_message_request($data) { $ret = array('success' => false); if (! $data['message_id']) { $ret['message'] = 'no message_id'; logger('no message_id'); - return $ret; + json_return_and_die($ret); } $sender = $data['sender']; @@ -3508,7 +3518,7 @@ function zot_process_message_request($data) { if (! $c) { logger('recipient channel not found.'); $ret['message'] .= 'recipient not found.' . EOL; - return $ret; + json_return_and_die($ret); } /* @@ -3526,7 +3536,7 @@ function zot_process_message_request($data) { ); if (! $r) { logger('no hubs'); - return $ret; + json_return_and_die($ret); } $hubs = $r; @@ -3567,8 +3577,7 @@ function zot_process_message_request($data) { } } $ret['success'] = true; - - return $ret; + json_return_and_die($ret); } @@ -3789,6 +3798,7 @@ function zotinfo($arr) { $ret['site'] = array(); $ret['site']['url'] = z_root(); $ret['site']['url_sig'] = base64url_encode(rsa_sign(z_root(),$e['channel_prvkey'])); + $ret['site']['zot_auth'] = z_root() . '/magic'; $dirmode = get_config('system','directory_mode'); if(($dirmode === false) || ($dirmode == DIRECTORY_MODE_NORMAL)) @@ -3979,3 +3989,421 @@ function delivery_report_is_storable($dr) { } + +function update_hub_connected($hub,$sitekey = '') { + + if($sitekey) { + + /* + * This hub has now been proven to be valid. + * Any hub with the same URL and a different sitekey cannot be valid. + * Get rid of them (mark them deleted). There's a good chance they were re-installs. + */ + + q("update hubloc set hubloc_deleted = 1, hubloc_error = 1 where hubloc_url = '%s' and hubloc_sitekey != '%s' ", + dbesc($hub['hubloc_url']), + dbesc($sitekey) + ); + + } + else { + $sitekey = $hub['sitekey']; + } + + // $sender['sitekey'] is a new addition to the protcol to distinguish + // hublocs coming from re-installed sites. Older sites will not provide + // this field and we have to still mark them valid, since we can't tell + // if this hubloc has the same sitekey as the packet we received. + + + // Update our DB to show when we last communicated successfully with this hub + // This will allow us to prune dead hubs from using up resources + + $r = q("update hubloc set hubloc_connected = '%s' where hubloc_id = %d and hubloc_sitekey = '%s' ", + dbesc(datetime_convert()), + intval($hub['hubloc_id']), + dbesc($sitekey) + ); + + // a dead hub came back to life - reset any tombstones we might have + + if(intval($hub['hubloc_error'])) { + q("update hubloc set hubloc_error = 0 where hubloc_id = %d and hubloc_sitekey = '%s' ", + intval($hub['hubloc_id']), + dbesc($sitekey) + ); + if(intval($r[0]['hubloc_orphancheck'])) { + q("update hubloc set hubloc_orhpancheck = 0 where hubloc_id = %d and hubloc_sitekey = '%s' ", + intval($hub['hubloc_id']), + dbesc($sitekey) + ); + } + q("update xchan set xchan_orphan = 0 where xchan_orphan = 1 and xchan_hash = '%s'", + dbesc($hub['hubloc_hash']) + ); + } + + return $hub['hubloc_url']; +} + + +function zot_reply_ping() { + + $ret = array('success'=> false); + + // Useful to get a health check on a remote site. + // This will let us know if any important communication details + // that we may have stored are no longer valid, regardless of xchan details. + logger('POST: got ping send pong now back: ' . z_root() , LOGGER_DEBUG ); + + $ret['success'] = true; + $ret['site'] = array(); + $ret['site']['url'] = z_root(); + $ret['site']['url_sig'] = base64url_encode(rsa_sign(z_root(),get_config('system','prvkey'))); + $ret['site']['sitekey'] = get_config('system','pubkey'); + json_return_and_die($ret); +} + +function zot_reply_pickup($data) { + + $ret = array('success'=> false); + + /* + * The 'pickup' message arrives with a tracking ID which is associated with a particular outq_hash + * First verify that that the returned signatures verify, then check that we have an outbound queue item + * with the correct hash. + * If everything verifies, find any/all outbound messages in the queue for this hubloc and send them back + */ + + if((! $data['secret']) || (! $data['secret_sig'])) { + $ret['message'] = 'no verification signature'; + logger('mod_zot: pickup: ' . $ret['message'], LOGGER_DEBUG); + json_return_and_die($ret); + } + + $r = q("select distinct hubloc_sitekey from hubloc where hubloc_url = '%s' and hubloc_callback = '%s' and hubloc_sitekey != '' group by hubloc_sitekey ", + dbesc($data['url']), + dbesc($data['callback']) + ); + if(! $r) { + $ret['message'] = 'site not found'; + logger('mod_zot: pickup: ' . $ret['message']); + json_return_and_die($ret); + } + + foreach ($r as $hubsite) { + + // verify the url_sig + // If the server was re-installed at some point, there could be multiple hubs with the same url and callback. + // Only one will have a valid key. + + $forgery = true; + $secret_fail = true; + + $sitekey = $hubsite['hubloc_sitekey']; + + logger('mod_zot: Checking sitekey: ' . $sitekey, LOGGER_DATA); + + if(rsa_verify($data['callback'],base64url_decode($data['callback_sig']),$sitekey)) { + $forgery = false; + } + if(rsa_verify($data['secret'],base64url_decode($data['secret_sig']),$sitekey)) { + $secret_fail = false; + } + if((! $forgery) && (! $secret_fail)) + break; + } + + if($forgery) { + $ret['message'] = 'possible site forgery'; + logger('mod_zot: pickup: ' . $ret['message']); + json_return_and_die($ret); + } + + if($secret_fail) { + $ret['message'] = 'secret validation failed'; + logger('mod_zot: pickup: ' . $ret['message']); + json_return_and_die($ret); + } + + /* + * If we made it to here, the signatures verify, but we still don't know if the tracking ID is valid. + * It wouldn't be an error if the tracking ID isn't found, because we may have sent this particular + * queue item with another pickup (after the tracking ID for the other pickup was verified). + */ + + $r = q("select outq_posturl from outq where outq_hash = '%s' and outq_posturl = '%s' limit 1", + dbesc($data['secret']), + dbesc($data['callback']) + ); + if(! $r) { + $ret['message'] = 'nothing to pick up'; + logger('mod_zot: pickup: ' . $ret['message']); + json_return_and_die($ret); + } + + /* + * Everything is good if we made it here, so find all messages that are going to this location + * and send them all. + */ + + $r = q("select * from outq where outq_posturl = '%s'", + dbesc($data['callback']) + ); + if($r) { + logger('mod_zot: successful pickup message received from ' . $data['callback'] . ' ' . count($r) . ' message(s) picked up', LOGGER_DEBUG); + + $ret['success'] = true; + $ret['pickup'] = array(); + foreach($r as $rr) { + if($rr['outq_msg']) { + $x = json_decode($rr['outq_msg'],true); + + if(! $x) + continue; + + if(array_key_exists('message_list',$x)) { + foreach($x['message_list'] as $xx) { + $ret['pickup'][] = array('notify' => json_decode($rr['outq_notify'],true),'message' => $xx); + } + } + else + $ret['pickup'][] = array('notify' => json_decode($rr['outq_notify'],true),'message' => $x); + $x = q("delete from outq where outq_hash = '%s'", + dbesc($rr['outq_hash']) + ); + } + } + } + + $encrypted = crypto_encapsulate(json_encode($ret),$sitekey); + json_return_and_die($encrypted); + + /* pickup: end */ +} + + + +function zot_reply_auth_check($data,$encrypted_packet) { + + $ret = array('success' => false); + + /* + * Requestor visits /magic/?dest=somewhere on their own site with a browser + * magic redirects them to $destsite/post [with auth args....] + * $destsite sends an auth_check packet to originator site + * The auth_check packet is handled here by the originator's site + * - the browser session is still waiting + * inside $destsite/post for everything to verify + * If everything checks out we'll return a token to $destsite + * and then $destsite will verify the token, authenticate the browser + * session and then redirect to the original destination. + * If authentication fails, the redirection to the original destination + * will still take place but without authentication. + */ + logger('mod_zot: auth_check', LOGGER_DEBUG); + + if (! $encrypted_packet) { + logger('mod_zot: auth_check packet was not encrypted.'); + $ret['message'] .= 'no packet encryption' . EOL; + json_return_and_die($ret); + } + + $arr = $data['sender']; + $sender_hash = make_xchan_hash($arr['guid'],$arr['guid_sig']); + + // garbage collect any old unused notifications + + // This was and should be 10 minutes but my hosting provider has time lag between the DB and + // the web server. We should probably convert this to webserver time rather than DB time so + // that the different clocks won't affect it and allow us to keep the time short. + + q("delete from verify where type = 'auth' and created < %s - INTERVAL %s", + db_utcnow(), db_quoteinterval('30 MINUTE') + ); + + $y = q("select xchan_pubkey from xchan where xchan_hash = '%s' limit 1", + dbesc($sender_hash) + ); + + // We created a unique hash in mod/magic.php when we invoked remote auth, and stored it in + // the verify table. It is now coming back to us as 'secret' and is signed by a channel at the other end. + // First verify their signature. We will have obtained a zot-info packet from them as part of the sender + // verification. + + if ((! $y) || (! rsa_verify($data['secret'], base64url_decode($data['secret_sig']),$y[0]['xchan_pubkey']))) { + logger('mod_zot: auth_check: sender not found or secret_sig invalid.'); + $ret['message'] .= 'sender not found or sig invalid ' . print_r($y,true) . EOL; + json_return_and_die($ret); + } + + // There should be exactly one recipient, the original auth requestor + + $ret['message'] .= 'recipients ' . print_r($recipients,true) . EOL; + + if ($data['recipients']) { + + $arr = $data['recipients'][0]; + $recip_hash = make_xchan_hash($arr['guid'], $arr['guid_sig']); + $c = q("select channel_id, channel_account_id, channel_prvkey from channel where channel_hash = '%s' limit 1", + dbesc($recip_hash) + ); + if (! $c) { + logger('mod_zot: auth_check: recipient channel not found.'); + $ret['message'] .= 'recipient not found.' . EOL; + json_return_and_die($ret); + } + + $confirm = base64url_encode(rsa_sign($data['secret'] . $recip_hash,$c[0]['channel_prvkey'])); + + // This additionally checks for forged sites since we already stored the expected result in meta + // and we've already verified that this is them via zot_gethub() and that their key signed our token + + $z = q("select id from verify where channel = %d and type = 'auth' and token = '%s' and meta = '%s' limit 1", + intval($c[0]['channel_id']), + dbesc($data['secret']), + dbesc($data['sender']['url']) + ); + if (! $z) { + logger('mod_zot: auth_check: verification key not found.'); + $ret['message'] .= 'verification key not found' . EOL; + json_return_and_die($ret); + } + $r = q("delete from verify where id = %d", + intval($z[0]['id']) + ); + + $u = q("select account_service_class from account where account_id = %d limit 1", + intval($c[0]['channel_account_id']) + ); + + logger('mod_zot: auth_check: success', LOGGER_DEBUG); + $ret['success'] = true; + $ret['confirm'] = $confirm; + if ($u && $u[0]['account_service_class']) + $ret['service_class'] = $u[0]['account_service_class']; + + // Set "do not track" flag if this site or this channel's profile is restricted + // in some way + + if (intval(get_config('system','block_public'))) + $ret['DNT'] = true; + if (! perm_is_allowed($c[0]['channel_id'],'','view_profile')) + $ret['DNT'] = true; + if (get_pconfig($c[0]['channel_id'],'system','do_not_track')) + $ret['DNT'] = true; + if (get_pconfig($c[0]['channel_id'],'system','hide_online_status')) + $ret['DNT'] = true; + + json_return_and_die($ret); + } + json_return_and_die($ret); +} + + +function zot_reply_purge($sender,$recipients) { + + $ret = array('success' => false); + + if ($recipients) { + // basically this means "unfriend" + foreach ($recipients as $recip) { + $r = q("select channel.*,xchan.* from channel + left join xchan on channel_hash = xchan_hash + where channel_guid = '%s' and channel_guid_sig = '%s' limit 1", + dbesc($recip['guid']), + dbesc($recip['guid_sig']) + ); + if ($r) { + $r = q("select abook_id from abook where uid = %d and abook_xchan = '%s' limit 1", + intval($r[0]['channel_id']), + dbesc(make_xchan_hash($sender['guid'],$sender['guid_sig'])) + ); + if ($r) { + contact_remove($r[0]['channel_id'],$r[0]['abook_id']); + } + } + } + $ret['success'] = true; + } + else { + // Unfriend everybody - basically this means the channel has committed suicide + $arr = $sender; + $sender_hash = make_xchan_hash($arr['guid'],$arr['guid_sig']); + + require_once('include/Contact.php'); + remove_all_xchan_resources($sender_hash); + + $ret['success'] = true; + } + + json_return_and_die($ret); +} + + +function zot_reply_refresh($sender,$recipients) { + + $ret = array('success' => false); + + // remote channel info (such as permissions or photo or something) + // has been updated. Grab a fresh copy and sync it. + // The difference between refresh and force_refresh is that + // force_refresh unconditionally creates a directory update record, + // even if no changes were detected upon processing. + + if($recipients) { + + // This would be a permissions update, typically for one connection + + foreach ($recipients as $recip) { + $r = q("select channel.*,xchan.* from channel + left join xchan on channel_hash = xchan_hash + where channel_guid = '%s' and channel_guid_sig = '%s' limit 1", + dbesc($recip['guid']), + dbesc($recip['guid_sig']) + ); + + $x = zot_refresh(array( + 'xchan_guid' => $sender['guid'], + 'xchan_guid_sig' => $sender['guid_sig'], + 'hubloc_url' => $sender['url'] + ), $r[0], (($msgtype === 'force_refresh') ? true : false)); + } + } + else { + // system wide refresh + + $x = zot_refresh(array( + 'xchan_guid' => $sender['guid'], + 'xchan_guid_sig' => $sender['guid_sig'], + 'hubloc_url' => $sender['url'] + ), null, (($msgtype === 'force_refresh') ? true : false)); + } + + $ret['success'] = true; + json_return_and_die($ret); + +} + + +function zot_reply_notify($data) { + + $ret = array('success' => false); + + logger('notify received from ' . $data['sender']['url']); + + $async = get_config('system','queued_fetch'); + + if($async) { + // add to receive queue + // qreceive_add($data); + } + else { + $x = zot_fetch($data); + $ret['delivery_report'] = $x; + } + + $ret['success'] = true; + json_return_and_die($ret); + +} \ No newline at end of file diff --git a/install/schema_mysql.sql b/install/schema_mysql.sql index 3dab6c822..9c24cbd94 100644 --- a/install/schema_mysql.sql +++ b/install/schema_mysql.sql @@ -23,6 +23,7 @@ CREATE TABLE IF NOT EXISTS `abook` ( `abook_profile` char(64) NOT NULL DEFAULT '', `abook_incl` TEXT NOT NULL DEFAULT '', `abook_excl` TEXT NOT NULL DEFAULT '', + `abook_instance` TEXT NOT NULL DEFAULT '', PRIMARY KEY (`abook_id`), KEY `abook_account` (`abook_account`), KEY `abook_channel` (`abook_channel`), diff --git a/install/schema_postgres.sql b/install/schema_postgres.sql index 95ed9acb7..0214c0c2a 100644 --- a/install/schema_postgres.sql +++ b/install/schema_postgres.sql @@ -22,6 +22,7 @@ CREATE TABLE "abook" ( "abook_profile" char(64) NOT NULL DEFAULT '', "abook_incl" TEXT NOT NULL DEFAULT '', "abook_excl" TEXT NOT NULL DEFAULT '', + "abook_instance" TEXT NOT NULL DEFAULT '', PRIMARY KEY ("abook_id") ); create index "abook_account" on abook ("abook_account"); diff --git a/install/update.php b/install/update.php index 10ae6725e..24f4f21d5 100644 --- a/install/update.php +++ b/install/update.php @@ -1,6 +1,6 @@ registerFilter(). It can be used to unregister a filter by name. + - $smarty->registerFilter('output', $callback, 'name'); + $smarty->unregister('output', 'name'); + + Closures + $smarty->registerFilter() does now accept closures. + - $smarty->registerFilter('pre', function($source) {return $source;}); + If no optional filter name was specified it gets the default name 'closure'. + If you register multiple closures register each with a unique filter name. + - $smarty->registerFilter('pre', function($source) {return $source;}, 'clousre_1'); + - $smarty->registerFilter('pre', function($source) {return $source;}, 'clousre_2'); + + +Smarty 3.1.22 + + Namespace support within templates + ================================== + Within templates you can now use namespace specifications on: + - Constants like foo\bar\FOO + - Class names like foo\bar\Baz::FOO, foo\bar\Baz::$foo, foo\bar\Baz::foo() + - PHP function names like foo\bar\baz() + + Security + ======== + - disable special $smarty variable - + The Smarty_Security class has the new property $disabled_special_smarty_vars. + It's an array which can be loaded with the $smarty special variable names like + 'template_object', 'template', 'current_dir' and others which will be disabled. + Note: That this security check is performed at compile time. + + - limit template nesting - + Property $max_template_nesting of Smarty_Security does set the maximum template nesting level. + The main template is level 1. The nesting level is checked at run time. When the maximum will be exceeded + an Exception will be thrown. The default setting is 0 which does disable this check. + + - trusted static methods - + The Smarty_Security class has the new property $trusted_static_methods to restrict access to static methods. + It's an nested array of trusted class and method names. + Format: + array ( + 'class_1' => array('method_1', 'method_2'), // allowed methods + 'class_2' => array(), // all methods of class allowed + ) + To disable access for all methods of all classes set $trusted_static_methods = null; + The default value is an empty array() which does enables all methods of all classes, but for backward compatibility + the setting of $static_classes will be checked. + Note: That this security check is performed at compile time. + + - trusted static properties - + The Smarty_Security class has the new property $trusted_static_properties to restrict access to static properties. + It's an nested array of trusted class and property names. + Format: + array ( + 'class_1' => array('prop_1', 'prop_2'), // allowed properties listed + 'class_2' => array(), // all properties of class allowed + } + To disable access for all properties of all classes set $trusted_static_properties = null; + The default value is an empty array() which does enables all properties of all classes, but for backward compatibility + the setting of $static_classes will be checked. + Note: That this security check is performed at compile time. + + - trusted constants . + The Smarty_Security class has the new property $trusted_constants to restrict access to constants. + It's an array of trusted constant names. + Format: + array ( + 'SMARTY_DIR' , // allowed constant + } + If the array is empty (default) the usage of constants can be controlled with the + Smarty_Security::$allow_constants property (default true) + + + + Compiled Templates + ================== + Smarty does now automatically detects a change of the $merge_compiled_includes and $escape_html + property and creates different compiled templates files depending on the setting. + + Same applies to config files and the $config_overwrite, $config_booleanize and + $config_read_hidden properties. + + Debugging + ========= + The layout of the debug window has been changed for better readability + + New class constants + Smarty::DEBUG_OFF + Smarty::DEBUG_ON + Smarty::DEBUG_INDIVIDUAL + have been introduced for setting the $debugging property. + + Smarty::DEBUG_INDIVIDUAL will create for each display() and fetch() call an individual debug window. + + . + \ No newline at end of file diff --git a/library/Smarty/README b/library/Smarty/README index 6367f030e..08b397c3f 100644 --- a/library/Smarty/README +++ b/library/Smarty/README @@ -1,4 +1,4 @@ -Smarty 3.1.21 +Smarty 3.x Author: Monte Ohrt Author: Uwe Tews @@ -460,12 +460,13 @@ included template. PLUGINS ======= -Smarty3 are following the same coding rules as in Smarty2. -The only difference is that the template object is passed as additional third parameter. +Smarty 3 plugins follow the same coding rules as in Smarty 2. +The main difference is that the template object is now passed in place of the smarty object. +The smarty object can be still be accessed through $template->smarty. -smarty_plugintype_name (array $params, object $smarty, object $template) +smarty_plugintype_name (array $params, Smarty_Internal_Template $template) -The Smarty 2 plugins are still compatible as long as they do not make use of specific Smarty2 internals. +The Smarty 2 plugins are still compatible as long as they do not make use of specific Smarty 2 internals. TEMPLATE INHERITANCE: diff --git a/library/Smarty/SMARTY_2_BC_NOTES.txt b/library/Smarty/SMARTY_2_BC_NOTES.txt deleted file mode 100644 index 79a2cb1b6..000000000 --- a/library/Smarty/SMARTY_2_BC_NOTES.txt +++ /dev/null @@ -1,109 +0,0 @@ -= Known incompatibilities with Smarty 2 = - -== Syntax == - -Smarty 3 API has a new syntax. Much of the Smarty 2 syntax is supported -by a wrapper but deprecated. See the README that comes with Smarty 3 for more -information. - -The {$array|@mod} syntax has always been a bit confusing, where an "@" is required -to apply a modifier to an array instead of the individual elements. Normally you -always want the modifier to apply to the variable regardless of its type. In Smarty 3, -{$array|mod} and {$array|@mod} behave identical. It is safe to drop the "@" and the -modifier will still apply to the array. If you really want the modifier to apply to -each array element, you must loop the array in-template, or use a custom modifier that -supports array iteration. Most smarty functions already escape values where necessary -such as {html_options} - -== PHP Version == -Smarty 3 is PHP 5 only. It will not work with PHP 4. - -== {php} Tag == -The {php} tag is disabled by default. The use of {php} tags is -deprecated. It can be enabled with $smarty->allow_php_tag=true. - -But if you scatter PHP code which belongs together into several -{php} tags it may not work any longer. - -== Delimiters and whitespace == -Delimiters surrounded by whitespace are no longer treated as Smarty tags. -Therefore, { foo } will not compile as a tag, you must use {foo}. This change -Makes Javascript/CSS easier to work with, eliminating the need for {literal}. -This can be disabled by setting $smarty->auto_literal = false; - -== Unquoted Strings == -Smarty 2 was a bit more forgiving (and ambiguous) when it comes to unquoted strings -in parameters. Smarty3 is more restrictive. You can still pass strings without quotes -so long as they contain no special characters. (anything outside of A-Za-z0-9_) - -For example filename strings must be quoted - -{include file='path/foo.tpl'} - - -== Extending the Smarty class == -Smarty 3 makes use of the __construct method for initialization. If you are extending -the Smarty class, its constructor is not called implicitly if the your child class defines -its own constructor. In order to run Smarty's constructor, a call to parent::__construct() -within your child constructor is required. - - -class MySmarty extends Smarty { - function __construct() { - parent::__construct(); - - // your initialization code goes here - - } -} - - -== Autoloader == -Smarty 3 does register its own autoloader with spl_autoload_register. If your code has -an existing __autoload function then this function must be explicitly registered on -the __autoload stack. See http://us3.php.net/manual/en/function.spl-autoload-register.php -for further details. - -== Plugin Filenames == -Smarty 3 optionally supports the PHP spl_autoloader. The autoloader requires filenames -to be lower case. Because of this, Smarty plugin file names must also be lowercase. -In Smarty 2, mixed case file names did work. - -== Scope of Special Smarty Variables == -In Smarty 2 the special Smarty variables $smarty.section... and $smarty.foreach... -had global scope. If you had loops with the same name in subtemplates you could accidentally -overwrite values of parent template. - -In Smarty 3 these special Smarty variable have only local scope in the template which -is defining the loop. If you need their value in a subtemplate you have to pass them -as parameter. - -{include file='path/foo.tpl' index=$smarty.section.foo.index} - - -== SMARTY_RESOURCE_CHAR_SET == -Smarty 3 sets the constant SMARTY_RESOURCE_CHAR_SET to utf-8 as default template charset. -This is now used also on modifiers like escape as default charset. If your templates use -other charsets make sure that you define the constant accordingly. Otherwise you may not -get any output. - -== newline at {if} tags == -A \n was added to the compiled code of the {if},{else},{elseif},{/if} tags to get output of newlines as expected by the template source. -If one of the {if} tags is at the line end you will now get a newline in the HTML output. - -== trigger_error() == -The API function trigger_error() has been removed because it did just map to PHP trigger_error. -However it's still included in the Smarty2 API wrapper. - -== Smarty constants == -The constants -SMARTY_PHP_PASSTHRU -SMARTY_PHP_QUOTE -SMARTY_PHP_REMOVE -SMARTY_PHP_ALLOW -have been replaced with class constants -Smarty::PHP_PASSTHRU -Smarty::PHP_QUOTE -Smarty::PHP_REMOVE -Smarty::PHP_ALLOW - diff --git a/library/Smarty/SMARTY_3.0_BC_NOTES.txt b/library/Smarty/SMARTY_3.0_BC_NOTES.txt deleted file mode 100644 index fd8b540c2..000000000 --- a/library/Smarty/SMARTY_3.0_BC_NOTES.txt +++ /dev/null @@ -1,24 +0,0 @@ -== Smarty2 backward compatibility == -All Smarty2 specific API functions and deprecated functionallity has been moved -to the SmartyBC class. - -== {php} Tag == -The {php} tag is no longer available in the standard Smarty calls. -The use of {php} tags is deprecated and only available in the SmartyBC class. - -== {include_php} Tag == -The {include_php} tag is no longer available in the standard Smarty calls. -The use of {include_php} tags is deprecated and only available in the SmartyBC class. - -== php template resource == -The support of the php template resource is removed. - -== $cache_dir, $compile_dir, $config_dir, $template_dir access == -The mentioned properties can't be accessed directly any longer. You must use -corresponding getter/setters like addConfigDir(), setConfigDir(), getConfigDir() - -== obsolete Smarty class properties == -The following no longer used properties are removed: -$allow_php_tag -$allow_php_template -$deprecation_notices \ No newline at end of file diff --git a/library/Smarty/SMARTY_3.1_NOTES.txt b/library/Smarty/SMARTY_3.1_NOTES.txt deleted file mode 100644 index 57709f0d7..000000000 --- a/library/Smarty/SMARTY_3.1_NOTES.txt +++ /dev/null @@ -1,306 +0,0 @@ -Smarty 3.1 Notes -================ - -Smarty 3.1 is a departure from 2.0 compatibility. Most notably, all -backward compatibility has been moved to a separate class file named -SmartyBC.class.php. If you require compatibility with 2.0, you will -need to use this class. - -Some differences from 3.0 are also present. 3.1 begins the journey of -requiring setters/getters for property access. So far this is only -implemented on the five directory properties: template_dir, -plugins_dir, configs_dir, compile_dir and cache_dir. These properties -are now protected, it is required to use the setters/getters instead. -That said, direct property access will still work, however slightly -slower since they will now fall through __set() and __get() and in -turn passed through the setter/getter methods. 3.2 will exhibit a full -list of setter/getter methods for all (currently) public properties, -so code-completion in your IDE will work as expected. - -There is absolutely no PHP allowed in templates any more. All -deprecated features of Smarty 2.0 are gone. Again, use the SmartyBC -class if you need any backward compatibility. - -Internal Changes - - Full UTF-8 Compatibility - -The plugins shipped with Smarty 3.1 have been rewritten to fully -support UTF-8 strings if Multibyte String is available. Without -MBString UTF-8 cannot be handled properly. For those rare cases where -templates themselves have to juggle encodings, the new modifiers -to_charset and from_charset may come in handy. - - Plugin API and Performance - -All Plugins (modifiers, functions, blocks, resources, -default_template_handlers, etc) are now receiving the -Smarty_Internal_Template instance, where they were supplied with the -Smarty instance in Smarty 3.0. *. As The Smarty_Internal_Template -mimics the behavior of Smarty, this API simplification should not -require any changes to custom plugins. - -The plugins shipped with Smarty 3.1 have been rewritten for better -performance. Most notably {html_select_date} and {html_select_time} -have been improved vastly. Performance aside, plugins have also been -reviewed and generalized in their API. {html_select_date} and -{html_select_time} now share almost all available options. - -The escape modifier now knows the $double_encode option, which will -prevent entities from being encoded again. - -The capitalize modifier now know the $lc_rest option, which makes sure -all letters following a captial letter are lower-cased. - -The count_sentences modifier now accepts (.?!) as -legitimate endings of a sentence - previously only (.) was -accepted - -The new unescape modifier is there to reverse the effects of the -escape modifier. This applies to the escape formats html, htmlall and -entity. - - default_template_handler_func - -The invocation of $smarty->$default_template_handler_func had to be -altered. Instead of a Smarty_Internal_Template, the fifth argument is -now provided with the Smarty instance. New footprint: - - -/** - * Default Template Handler - * - * called when Smarty's file: resource is unable to load a requested file - * - * @param string $type resource type (e.g. "file", "string", "eval", "resource") - * @param string $name resource name (e.g. "foo/bar.tpl") - * @param string &$content template's content - * @param integer &$modified template's modification time - * @param Smarty $smarty Smarty instance - * @return string|boolean path to file or boolean true if $content and $modified - * have been filled, boolean false if no default template - * could be loaded - */ -function default_template_handler_func($type, $name, &$content, &$modified, Smarty $smarty) { - if (false) { - // return corrected filepath - return "/tmp/some/foobar.tpl"; - } elseif (false) { - // return a template directly - $content = "the template source"; - $modified = time(); - return true; - } else { - // tell smarty that we failed - return false; - } -} - - Stuff done to the compiler - -Many performance improvements have happened internally. One notable -improvement is that all compiled templates are now handled as PHP -functions. This speeds up repeated templates tremendously, as each one -calls an (in-memory) PHP function instead of performing another file -include/scan. - -New Features - - Template syntax - - {block}..{/block} - -The {block} tag has a new hide option flag. It does suppress the block -content if no corresponding child block exists. -EXAMPLE: -parent.tpl -{block name=body hide} child content "{$smarty.block.child}" was -inserted {block} -In the above example the whole block will be suppressed if no child -block "body" is existing. - - {setfilter}..{/setfilter} - -The new {setfilter} block tag allows the definition of filters which -run on variable output. -SYNTAX: -{setfilter filter1|filter2|filter3....} -Smarty3 will lookup up matching filters in the following search order: -1. varibale filter plugin in plugins_dir. -2. a valid modifier. A modifier specification will also accept -additional parameter like filter2:'foo' -3. a PHP function -{/setfilter} will turn previous filter setting off again. -{setfilter} tags can be nested. -EXAMPLE: -{setfilter filter1} - {$foo} - {setfilter filter2} - {$bar} - {/setfilter} - {$buh} -{/setfilter} -{$blar} -In the above example filter1 will run on the output of $foo, filter2 -on $bar, filter1 again on $buh and no filter on $blar. -NOTES: -- {$foo nofilter} will suppress the filters -- These filters will run in addition to filters defined by -registerFilter('variable',...), autoLoadFilter('variable',...) and -defined default modifier. -- {setfilter} will effect only the current template, not included -subtemplates. - - Resource API - -Smarty 3.1 features a new approach to resource management. The -Smarty_Resource API allows simple, yet powerful integration of custom -resources for templates and configuration files. It offers simple -functions for loading data from a custom resource (e.g. database) as -well as define new template types adhering to the special -non-compiling (e,g, plain php) and non-compile-caching (e.g. eval: -resource type) resources. - -See demo/plugins/resource.mysql.php for an example custom database -resource. - -Note that old-fashioned registration of callbacks for resource -management has been deprecated but is still possible with SmartyBC. - - CacheResource API - -In line with the Resource API, the CacheResource API offers a more -comfortable handling of output-cache data. With the -Smarty_CacheResource_Custom accessing databases is made simple. With -the introduction of Smarty_CacheResource_KeyValueStore the -implementation of resources like memcache or APC became a no-brainer; -simple hash-based storage systems are now supporting hierarchical -output-caches. - -See demo/plugins/cacheresource.mysql.php for an example custom -database CacheResource. -See demo/plugins/cacheresource.memcache.php for an example custom -memcache CacheResource using the KeyValueStore helper. - -Note that old-fashioned registration of $cache_handler is not possible -anymore. As the functionality had not been ported to Smarty 3.0.x -properly, it has been dropped from 3.1 completely. - -Locking facilities have been implemented to avoid concurrent cache -generation. Enable cache locking by setting -$smarty->cache_locking = true; - - Relative Paths in Templates (File-Resource) - -As of Smarty 3.1 {include file="../foo.tpl"} and {include -file="./foo.tpl"} will resolve relative to the template they're in. -Relative paths are available with {include file="..."} and -{extends file="..."}. As $smarty->fetch('../foo.tpl') and -$smarty->fetch('./foo.tpl') cannot be relative to a template, an -exception is thrown. - - Addressing a specific $template_dir - -Smarty 3.1 introduces the $template_dir index notation. -$smarty->fetch('[foo]bar.tpl') and {include file="[foo]bar.tpl"} -require the template bar.tpl to be loaded from $template_dir['foo']; -Smarty::setTemplateDir() and Smarty::addTemplateDir() offer ways to -define indexes along with the actual directories. - - Mixing Resources in extends-Resource - -Taking the php extends: template resource one step further, it is now -possible to mix resources within an extends: call like -$smarty->fetch("extends:file:foo.tpl|db:bar.tpl"); - -To make eval: and string: resources available to the inheritance -chain, eval:base64:TPL_STRING and eval:urlencode:TPL_STRING have been -introduced. Supplying the base64 or urlencode flags will trigger -decoding the TPL_STRING in with either base64_decode() or urldecode(). - - extends-Resource in template inheritance - -Template based inheritance may now inherit from php's extends: -resource like {extends file="extends:foo.tpl|db:bar.tpl"}. - - New Smarty property escape_html - -$smarty->escape_html = true will autoescape all template variable -output by calling htmlspecialchars({$output}, ENT_QUOTES, -SMARTY_RESOURCE_CHAR_SET). -NOTE: -This is a compile time option. If you change the setting you must make -sure that the templates get recompiled. - - New option at Smarty property compile_check - -The automatic recompilation of modified templates can now be -controlled by the following settings: -$smarty->compile_check = COMPILECHECK_OFF (false) - template files -will not be checked -$smarty->compile_check = COMPILECHECK_ON (true) - template files will -always be checked -$smarty->compile_check = COMPILECHECK_CACHEMISS - template files will -be checked if caching is enabled and there is no existing cache file -or it has expired - - Automatic recompilation on Smarty version change - -Templates will now be automatically recompiled on Smarty version -changes to avoide incompatibillities in the compiled code. Compiled -template checked against the current setting of the SMARTY_VERSION -constant. - - default_config_handler_func() - -Analogous to the default_template_handler_func() -default_config_handler_func() has been introduced. - - default_plugin_handler_func() - -An optional default_plugin_handler_func() can be defined which gets called -by the compiler on tags which can't be resolved internally or by plugins. -The default_plugin_handler() can map tags to plugins on the fly. - -New getters/setters - -The following setters/getters will be part of the official -documentation, and will be strongly recommended. Direct property -access will still work for the foreseeable future... it will be -transparently routed through the setters/getters, and consequently a -bit slower. - -array|string getTemplateDir( [string $index] ) -replaces $smarty->template_dir; and $smarty->template_dir[$index]; -Smarty setTemplateDir( array|string $path ) -replaces $smarty->template_dir = "foo"; and $smarty->template_dir = -array("foo", "bar"); -Smarty addTemplateDir( array|string $path, [string $index]) -replaces $smarty->template_dir[] = "bar"; and -$smarty->template_dir[$index] = "bar"; - -array|string getConfigDir( [string $index] ) -replaces $smarty->config_dir; and $smarty->config_dir[$index]; -Smarty setConfigDir( array|string $path ) -replaces $smarty->config_dir = "foo"; and $smarty->config_dir = -array("foo", "bar"); -Smarty addConfigDir( array|string $path, [string $index]) -replaces $smarty->config_dir[] = "bar"; and -$smarty->config_dir[$index] = "bar"; - -array getPluginsDir() -replaces $smarty->plugins_dir; -Smarty setPluginsDir( array|string $path ) -replaces $smarty->plugins_dir = "foo"; -Smarty addPluginsDir( array|string $path ) -replaces $smarty->plugins_dir[] = "bar"; - -string getCompileDir() -replaces $smarty->compile_dir; -Smarty setCompileDir( string $path ) -replaces $smarty->compile_dir = "foo"; - -string getCacheDir() -replaces $smarty->cache_dir; -Smarty setCacheDir( string $path ) -replaces $smarty->cache_dir; diff --git a/library/Smarty/change_log.txt b/library/Smarty/change_log.txt index a0161659d..2bcdfd024 100644 --- a/library/Smarty/change_log.txt +++ b/library/Smarty/change_log.txt @@ -1,8 +1,407 @@ - ===== 3.1.22-dev ===== (xx.xx.2014) + ===== 3.1.28-dev===== (xx.xx.2015) + 05.12.2015 + -bugfix {strip} should insert a single space https://github.com/smarty-php/smarty/issues/111 + + 25.11.2015 + -bugfix a left delimter like '[%' did fail on [%$var_[%$variable%]%] (forum topic 25798) + + 02.11.2015 + - bugfix {include} with variable file name like {include file="foo_`$bar`.tpl"} did fail in 3.1.28-dev https://github.com/smarty-php/smarty/issues/102 + + 01.11.2015 + - update config file processing + + 31.10.2015 + - bugfix add missing $trusted_dir property to SmartyBC class (forum topic 25751) + + 29.10.2015 + - improve template scope handling + + 24.10.2015 + - more optimizations of template processing + - bugfix Error when using {include} within {capture} https://github.com/smarty-php/smarty/issues/100 + + 21.10.2015 + - move some code into runtime extensions + + 18.10.2015 + - optimize filepath normalization + - rework of template inheritance + - speed and size optimizations + - bugfix under HHVM temporary cache file must only be created when caches template was updated + - fix compiled code for new {block} assign attribute + - update code generated by template function call handler + + 18.09.2015 + - bugfix {if $foo instanceof $bar} failed to compile if 2nd value is a variable https://github.com/smarty-php/smarty/issues/92 + + 17.09.2015 + - bugfix {foreach} first attribute was not correctly reset since commit 05a8fa2 of 02.08.2015 https://github.com/smarty-php/smarty/issues/90 + + 16.09.2015 + - update compiler by moving no longer needed properties, code optimizations and other + + 14.09.2015 + - optimize autoloader + - optimize subtemplate handling + - update template inheritance processing + - move code of {call} processing back into Smarty_Internal_Template class + - improvement invalidate OPCACHE for cleared compiled and cached template files (forum topic 25557) + - bugfix unintended multiple debug windows (forum topic 25699) + + 30.08.2015 + - size optimization move some runtime functions into extension + - optimize inline template processing + - optimization merge inheritance child and parent templates into one compiled template file + + 29.08.2015 + - improvement convert template inheritance into runtime processing + - bugfix {$smarty.block.parent} did always reference the root parent block https://github.com/smarty-php/smarty/issues/68 + + 23.08.2015 + - introduce Smarty::$resource_cache_mode and cache template object of {include} inside loop + - load seldom used Smarty API methods dynamically to reduce memory footprint + - cache template object of {include} if same template is included several times + - convert debug console processing to object + - use output buffers for better performance and less memory usage + - optimize nocache hash processing + - remove not really needed properties + - optimize rendering + - move caching to Smarty::_cache + - remove properties with redundant content + - optimize Smarty::templateExists() + - optimize use_include_path processing + - relocate properties for size optimization + - remove redundant code + - bugfix compiling super globals like {$smarty.get.foo} did fail in the master branch https://github.com/smarty-php/smarty/issues/77 + + 06.08.2015 + - avoid possible circular object references caused by parser/lexer objects + - rewrite compileAll... utility methods + - commit several internal improvements + - bugfix Smarty failed when compile_id did contain "|" + + 03.08.2015 + - rework clear cache methods + - bugfix compileAllConfig() was broken since 3.1.22 because of the changes in config file processing + - improve getIncludePath() to return directory if no file was given + + 02.08.2015 + - optimization and code cleanup of {foreach} and {section} compiler + - rework {capture} compiler + + 01.08.2015 + - update DateTime object can be instance of DateTimeImmutable since PHP5.5 https://github.com/smarty-php/smarty/pull/75 + - improvement show resource type and start of template source instead of uid on eval: and string: resource (forum topic 25630) + + 31.07.2015 + - optimize {foreach} and {section} compiler + + 29.07.2015 + - optimize {section} compiler for speed and size of compiled code + + 28.07.2015 + - update for PHP 7 compatibility + + 26.07.2015 + - improvement impement workaround for HHVM PHP incompatibillity https://github.com/facebook/hhvm/issues/4797 + + 25.07.2015 + - bugfix parser did hang on text starting fetch('foo.tpl') https://github.com/smarty-php/smarty/issues/70 + - improvement Added $limit parameter to regex_replace modifier #71 + - new feature multiple indices on file: resource + + 06.07.2015 + - optimize {block} compilation + - optimization get rid of __get and __set in source object + + 01.07.2015 + - optimize compile check handling + - update {foreach} compiler + - bugfix debugging console did not display string values containing \n, \r or \t correctly https://github.com/smarty-php/smarty/issues/66 + - optimize source resources + + 28.06.2015 + - move $smarty->enableSecurity() into Smarty_Security class + - optimize security isTrustedResourceDir() + - move auto load filter methods into extension + - move $smarty->getTemplateVars() into extension + - move getStreamVariable() into extension + - move $smarty->append() and $smarty->appendByRef() into extension + - optimize autoloader + - optimize file path normalization + - bugfix PATH_SEPARATOR was replaced by mistake in autoloader + - remove redundant code + + 27.06.2015 + - bugfix resolve naming conflict between custom Smarty delimiter '<%' and PHP ASP tags https://github.com/smarty-php/smarty/issues/64 + - update $smarty->_realpath for relative path not starting with './' + - update Smarty security with new realpath handling + - update {include_php} with new realpath handling + - move $smarty->loadPlugin() into extension + - minor compiler optimizations + - bugfix allow function plugins with name ending with 'close' https://github.com/smarty-php/smarty/issues/52 + - rework of $smarty->clearCompiledTemplate() and move it to its own extension + + 19.06.2015 + - improvement allow closures as callback at $smarty->registerFilter() https://github.com/smarty-php/smarty/issues/59 + + ===== 3.1.27===== (18.06.2015) + 18.06.2015 + - bugfix another update on file path normalization failed on path containing something like "/.foo/" https://github.com/smarty-php/smarty/issues/56 + + ===== 3.1.26===== (18.06.2015) + 18.06.2015 + - bugfix file path normalization failed on path containing something like "/.foo/" https://github.com/smarty-php/smarty/issues/56 + + 17.06.2015 + - bugfix calling a plugin with nocache option but no other attributes like {foo nocache} caused call to undefined function https://github.com/smarty-php/smarty/issues/55 + + ===== 3.1.25===== (15.06.2015) + 15.06.2015 + - optimization of smarty_cachereource_keyvaluestore.php code + + 14.06.2015 + - bugfix a relative sub template path could fail if template_dir path did contain /../ https://github.com/smarty-php/smarty/issues/50 + - optimization rework of path normalization + - bugfix an output tag with variable, modifier followed by an operator like {$foo|modifier+1} did fail https://github.com/smarty-php/smarty/issues/53 + + 13.06.2015 + - bugfix a custom cache resource using smarty_cachereource_keyvaluestore.php did fail if php.ini mbstring.func_overload = 2 (forum topic 25568) + + 11.06.2015 + - bugfix the lexer could hang on very large quoted strings (forum topic 25570) + + 08.06.2015 + - bugfix using {$foo} as array index like $bar.{$foo} or in double quoted string like "some {$foo} thing" failed https://github.com/smarty-php/smarty/issues/49 + + 04.06.2015 + - bugfix possible error message on unset() while compiling {block} tags https://github.com/smarty-php/smarty/issues/46 + + 01.06.2015 + - bugfix including template variables broken since 3.1.22 https://github.com/smarty-php/smarty/issues/47 + + 27.05.2015 + - bugfix {include} with variable file name must not create by default individual cache file (since 3.1.22) https://github.com/smarty-php/smarty/issues/43 + + 24.05.2015 + - bugfix if condition string 'neq' broken due to a typo https://github.com/smarty-php/smarty/issues/42 + + ===== 3.1.24===== (23.05.2015) + 23.05.2015 + - improvement on php_handling to allow very large PHP sections, better error handling + - improvement allow extreme large comment sections (forum 25538) + + 21.05.2015 + - bugfix broken PHP 5.2 compatibility when compiling 1 did compile into wrong code https://github.com/smarty-php/smarty/issues/41 + + 19.05.2015 + - bugfix compiler did overwrite existing variable value when setting the nocache attribute https://github.com/smarty-php/smarty/issues/39 + - bugfix output filter trimwhitespace could run into the pcre.backtrack_limit on large output (code.google issue 220) + - bugfix compiler could run into the pcre.backtrack_limit on larger comment or {php} tag sections (forum 25538) + + 18.05.2015 + - improvement introduce shortcuts in lexer/parser rules for most frequent terms for higher + compilation speed + + 16.05.2015 + - bugfix {php}{/php} did work just for single lines https://github.com/smarty-php/smarty/issues/33 + - improvement remove not needed ?> handling from parser to new compiler module + + 05.05.2015 + - bugfix code could be messed up when {tags} are used in multiple attributes https://github.com/smarty-php/smarty/issues/23 + + 04.05.2015 + - bugfix Smarty_Resource::parseResourceName incompatible with Google AppEngine (https://github.com/smarty-php/smarty/issues/22) + - improvement use is_file() checks to avoid errors suppressed by @ which could still cause problems (https://github.com/smarty-php/smarty/issues/24) + + 28.04.2015 + - bugfix plugins of merged subtemplates not loaded in 3.1.22-dev (forum topic 25508) 2nd fix + + 28.04.2015 + - bugfix plugins of merged subtemplates not loaded in 3.1.22-dev (forum topic 25508) + + 23.04.2015 + - bugfix a nocache template variable used as parameter at {insert} was by mistake cached + + 20.04.2015 + - bugfix at a template function containing nocache code a parmeter could overwrite a template variable of same name + + 27.03.2015 + - bugfix Smarty_Security->allow_constants=false; did also disable true, false and null (change of 16.03.2015) + - improvement added a whitelist for trusted constants to security Smarty_Security::$trusted_constants (forum topic 25471) + + 20.03.2015 + - bugfix make sure that function properties get saved only in compiled files containing the fuction definition {forum topic 25452} + - bugfix correct update of global variable values on exit of template functions. (reported under Smarty Developers) + + 16.03.2015 + - bugfix problems with {function}{/function} and {call} tags in different subtemplate cache files {forum topic 25452} + - bugfix Smarty_Security->allow_constants=false; did not disallow direct usage of defined constants like {SMARTY_DIR} {forum topic 25457} + - bugfix {block}{/block} tags did not work inside double quoted strings https://github.com/smarty-php/smarty/issues/18 + + + 15.03.2015 + - bugfix $smarty->compile_check must be restored before rendering of a just updated cache file {forum 25452} + + 14.03.2015 + - bugfix {nocache} {/nocache} tags corrupted code when used within a nocache section caused by a nocache template variable. + + - bugfix template functions defined with {function} in an included subtemplate could not be called in nocache + mode with {call... nocache} if the subtemplate had it's own cache file {forum 25452} + + 10.03.2015 + - bugfix {include ... nocache} whith variable file or compile_id attribute was not executed in nocache mode. + + 12.02.2015 + - bugfix multiple Smarty::fetch() of same template when $smarty->merge_compiled_includes = true; could cause function already defined error + + 11.02.2015 + - bugfix recursive {includes} did create E_NOTICE message when $smarty->merge_compiled_includes = true; (github issue #16) + + 22.01.2015 + - new feature security can now control access to static methods and properties + see also NEW_FEATURES.txt + + 21.01.2015 + - bugfix clearCompiledTemplates(), clearAll() and clear() could try to delete whole drive at wrong path permissions because realpath() fail (forum 25397) + - bugfix 'self::' and 'parent::' was interpreted in template syntax as static class + + 04.01.2015 + - push last weeks changes to github + + - different optimizations + - improvement automatically create different versions of compiled templates and config files depending + on property settings. + - optimization restructure template processing by moving code into classes it better belongs to + - optimization restructure config file processing + + 31.12.2014 + - bugfix use function_exists('mb_get_info') for setting Smarty::$_MBSTRING. + Function mb_split could be overloaded depending on php.ini mbstring.func_overload + + + 29.12.2014 + - new feature security can now limit the template nesting level by property $max_template_nesting + see also NEW_FEATURES.txt (forum 25370) + + 29.12.2014 + - new feature security can now disable special $smarty variables listed in property $disabled_special_smarty_vars + see also NEW_FEATURES.txt (forum 25370) + + 27.12.2014 + - bugfix clear internal _is_file_cache when plugins_dir was modified + + 13.12.2014 + - improvement optimization of lexer and parser resulting in a up to 30% higher compiling speed + + 11.12.2014 + - bugfix resolve parser ambiguity between constant print tag {CONST} and other smarty tags after change of 09.12.2014 + + 09.12.2014 + - bugfix variables $null, $true and $false did not work after the change of 12.11.2014 (forum 25342) + - bugfix call of template function by a variable name did not work after latest changes (forum 25342) + + 23.11.2014 + - bugfix a plugin with attached modifier could fail if the tag was immediately followed by another Smarty tag (since 3.1.21) (forum 25326) + + 13.11.2014 + - improvement move autoload code into Autoloader.php. Use Composer autoloader when possible + + 12.11.2014 + - new feature added support of namespaces to template code + + 08.11.2014 - 10.11.2014 + - bugfix subtemplate called in nocache mode could be called with wrong compile_id when it did change on one of the calling templates + - improvement add code of template functions called in nocache mode dynamically to cache file (related to bugfix of 01.11.2014) + - bugfix Debug Console did not include all data from merged compiled subtemplates + + 04.11.2014 + - new feature $smarty->debugging = true; => overwrite existing Debug Console window (old behaviour) + $smarty->debugging = 2; => individual Debug Console window by template name + + 03.11.2014 + - bugfix Debug Console did not show included subtemplates since 3.1.17 (forum 25301) + - bugfix Modifier debug_print_var did not limit recursion or prevent recursive object display at Debug Console + (ATTENTION: parameter order has changed to be able to specify maximum recursion) + - bugfix Debug consol did not include subtemplate information with $smarty->merge_compiled_includes = true + - improvement The template variables are no longer displayed as objects on the Debug Console + - improvement $smarty->createData($parent = null, $name = null) new optional name parameter for display at Debug Console + - addition of some hooks for future extension of Debug Console + + 01.11.2014 + - bugfix and enhancement on subtemplate {include} and template {function} tags. + * Calling a template which has a nocache section could fail if it was called from a cached and a not cached subtemplate. + * Calling the same subtemplate cached and not cached with the $smarty->merge_compiled_includes enabled could cause problems + * Many smaller related changes + + 30.10.2014 + - bugfix access to class constant by object like {$object::CONST} or variable class name {$class::CONST} did not work (forum 25301) + + 26.10.2014 + - bugfix E_NOTICE message was created during compilation when ASP tags '<%' or '%>' are in template source text + - bugfix merge_compiled_includes option failed when caching enables and same subtemplate was included cached and not cached + ===== 3.1.21 ===== (18.10.2014) 18.10.2014 - - composer moved to github - - add COMPOSER_RELEASE_NOTES + - composer moved to github 17.10.2014 - bugfix on $php_handling security and optimization of smarty_internal_parsetree (Thue Kristensen) @@ -43,7 +442,7 @@ 04.07.2014 - bugfix the bufix of 02.06.2014 broke correct handling of child templates with same name but different template folders in extends resource (issue 194 and topic 25099) - ===== 3.1.19 ===== (06.30.2014) + ===== 3.1.19 ===== (30.06.2014) 20.06.2014 - bugfix template variables could not be passed as parameter in {include} when the include was in a {nocache} section (topic 25131) @@ -732,7 +1131,7 @@ 15/07/2011 - bugfix individual cache_lifetime of {include} did not work correctly inside {block} tags -- added caches for Smarty_Template_Source and Smarty_Template_Compiled to reduce I/O for multiple cache_id rendering +- added caches for Smarty_Internal_TemplateSource and Smarty_Internal_TemplateCompiled to reduce I/O for multiple cache_id rendering 14/07/2011 - made Smarty::loadPlugin() respect the include_path if required diff --git a/library/Smarty/demo/configs/test.conf b/library/Smarty/demo/configs/test.conf deleted file mode 100644 index 5eac748ec..000000000 --- a/library/Smarty/demo/configs/test.conf +++ /dev/null @@ -1,5 +0,0 @@ -title = Welcome to Smarty! -cutoff_size = 40 - -[setup] -bold = true diff --git a/library/Smarty/demo/index.php b/library/Smarty/demo/index.php deleted file mode 100644 index 33f3035c5..000000000 --- a/library/Smarty/demo/index.php +++ /dev/null @@ -1,30 +0,0 @@ -force_compile = true; -$smarty->debugging = true; -$smarty->caching = true; -$smarty->cache_lifetime = 120; - -$smarty->assign("Name", "Fred Irving Johnathan Bradley Peppergill", true); -$smarty->assign("FirstName", array("John", "Mary", "James", "Henry")); -$smarty->assign("LastName", array("Doe", "Smith", "Johnson", "Case")); -$smarty->assign("Class", array(array("A", "B", "C", "D"), array("E", "F", "G", "H"), - array("I", "J", "K", "L"), array("M", "N", "O", "P"))); - -$smarty->assign("contacts", array(array("phone" => "1", "fax" => "2", "cell" => "3"), - array("phone" => "555-4444", "fax" => "555-3333", "cell" => "760-1234"))); - -$smarty->assign("option_values", array("NY", "NE", "KS", "IA", "OK", "TX")); -$smarty->assign("option_output", array("New York", "Nebraska", "Kansas", "Iowa", "Oklahoma", "Texas")); -$smarty->assign("option_selected", "NE"); - -$smarty->display('index.tpl'); diff --git a/library/Smarty/demo/plugins/cacheresource.apc.php b/library/Smarty/demo/plugins/cacheresource.apc.php deleted file mode 100644 index d7336f2bf..000000000 --- a/library/Smarty/demo/plugins/cacheresource.apc.php +++ /dev/null @@ -1,83 +0,0 @@ - $v) { - $_res[$k] = $v; - } - - return $_res; - } - - /** - * Save values for a set of keys to cache - * - * @param array $keys list of values to save - * @param int $expire expiration time - * - * @return boolean true on success, false on failure - */ - protected function write(array $keys, $expire = null) - { - foreach ($keys as $k => $v) { - apc_store($k, $v, $expire); - } - - return true; - } - - /** - * Remove values from cache - * - * @param array $keys list of keys to delete - * - * @return boolean true on success, false on failure - */ - protected function delete(array $keys) - { - foreach ($keys as $k) { - apc_delete($k); - } - - return true; - } - - /** - * Remove *all* values from cache - * - * @return boolean true on success, false on failure - */ - protected function purge() - { - return apc_clear_cache('user'); - } -} diff --git a/library/Smarty/demo/plugins/cacheresource.memcache.php b/library/Smarty/demo/plugins/cacheresource.memcache.php deleted file mode 100644 index e265365fb..000000000 --- a/library/Smarty/demo/plugins/cacheresource.memcache.php +++ /dev/null @@ -1,97 +0,0 @@ -memcache = new Memcache(); - $this->memcache->addServer('127.0.0.1', 11211); - } - - /** - * Read values for a set of keys from cache - * - * @param array $keys list of keys to fetch - * - * @return array list of values with the given keys used as indexes - * @return boolean true on success, false on failure - */ - protected function read(array $keys) - { - $_keys = $lookup = array(); - foreach ($keys as $k) { - $_k = sha1($k); - $_keys[] = $_k; - $lookup[$_k] = $k; - } - $_res = array(); - $res = $this->memcache->get($_keys); - foreach ($res as $k => $v) { - $_res[$lookup[$k]] = $v; - } - - return $_res; - } - - /** - * Save values for a set of keys to cache - * - * @param array $keys list of values to save - * @param int $expire expiration time - * - * @return boolean true on success, false on failure - */ - protected function write(array $keys, $expire = null) - { - foreach ($keys as $k => $v) { - $k = sha1($k); - $this->memcache->set($k, $v, 0, $expire); - } - - return true; - } - - /** - * Remove values from cache - * - * @param array $keys list of keys to delete - * - * @return boolean true on success, false on failure - */ - protected function delete(array $keys) - { - foreach ($keys as $k) { - $k = sha1($k); - $this->memcache->delete($k); - } - - return true; - } - - /** - * Remove *all* values from cache - * - * @return boolean true on success, false on failure - */ - protected function purge() - { - $this->memcache->flush(); - } -} diff --git a/library/Smarty/demo/plugins/cacheresource.mysql.php b/library/Smarty/demo/plugins/cacheresource.mysql.php deleted file mode 100644 index d8d00ab26..000000000 --- a/library/Smarty/demo/plugins/cacheresource.mysql.php +++ /dev/null @@ -1,162 +0,0 @@ -CREATE TABLE IF NOT EXISTS `output_cache` ( - * `id` CHAR(40) NOT NULL COMMENT 'sha1 hash', - * `name` VARCHAR(250) NOT NULL, - * `cache_id` VARCHAR(250) NULL DEFAULT NULL, - * `compile_id` VARCHAR(250) NULL DEFAULT NULL, - * `modified` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP, - * `content` LONGTEXT NOT NULL, - * PRIMARY KEY (`id`), - * INDEX(`name`), - * INDEX(`cache_id`), - * INDEX(`compile_id`), - * INDEX(`modified`) - * ) ENGINE = InnoDB;
    - * - * @package CacheResource-examples - * @author Rodney Rehm - */ -class Smarty_CacheResource_Mysql extends Smarty_CacheResource_Custom -{ - // PDO instance - protected $db; - protected $fetch; - protected $fetchTimestamp; - protected $save; - - public function __construct() - { - try { - $this->db = new PDO("mysql:dbname=test;host=127.0.0.1", "smarty"); - } - catch (PDOException $e) { - throw new SmartyException('Mysql Resource failed: ' . $e->getMessage()); - } - $this->fetch = $this->db->prepare('SELECT modified, content FROM output_cache WHERE id = :id'); - $this->fetchTimestamp = $this->db->prepare('SELECT modified FROM output_cache WHERE id = :id'); - $this->save = $this->db->prepare('REPLACE INTO output_cache (id, name, cache_id, compile_id, content) - VALUES (:id, :name, :cache_id, :compile_id, :content)'); - } - - /** - * fetch cached content and its modification time from data source - * - * @param string $id unique cache content identifier - * @param string $name template name - * @param string $cache_id cache id - * @param string $compile_id compile id - * @param string $content cached content - * @param integer $mtime cache modification timestamp (epoch) - * - * @return void - */ - protected function fetch($id, $name, $cache_id, $compile_id, &$content, &$mtime) - { - $this->fetch->execute(array('id' => $id)); - $row = $this->fetch->fetch(); - $this->fetch->closeCursor(); - if ($row) { - $content = $row['content']; - $mtime = strtotime($row['modified']); - } else { - $content = null; - $mtime = null; - } - } - - /** - * Fetch cached content's modification timestamp from data source - * - * @note implementing this method is optional. Only implement it if modification times can be accessed faster than loading the complete cached content. - * - * @param string $id unique cache content identifier - * @param string $name template name - * @param string $cache_id cache id - * @param string $compile_id compile id - * - * @return integer|boolean timestamp (epoch) the template was modified, or false if not found - */ - protected function fetchTimestamp($id, $name, $cache_id, $compile_id) - { - $this->fetchTimestamp->execute(array('id' => $id)); - $mtime = strtotime($this->fetchTimestamp->fetchColumn()); - $this->fetchTimestamp->closeCursor(); - - return $mtime; - } - - /** - * Save content to cache - * - * @param string $id unique cache content identifier - * @param string $name template name - * @param string $cache_id cache id - * @param string $compile_id compile id - * @param integer|null $exp_time seconds till expiration time in seconds or null - * @param string $content content to cache - * - * @return boolean success - */ - protected function save($id, $name, $cache_id, $compile_id, $exp_time, $content) - { - $this->save->execute(array( - 'id' => $id, - 'name' => $name, - 'cache_id' => $cache_id, - 'compile_id' => $compile_id, - 'content' => $content, - )); - - return !!$this->save->rowCount(); - } - - /** - * Delete content from cache - * - * @param string $name template name - * @param string $cache_id cache id - * @param string $compile_id compile id - * @param integer|null $exp_time seconds till expiration or null - * - * @return integer number of deleted caches - */ - protected function delete($name, $cache_id, $compile_id, $exp_time) - { - // delete the whole cache - if ($name === null && $cache_id === null && $compile_id === null && $exp_time === null) { - // returning the number of deleted caches would require a second query to count them - $query = $this->db->query('TRUNCATE TABLE output_cache'); - - return - 1; - } - // build the filter - $where = array(); - // equal test name - if ($name !== null) { - $where[] = 'name = ' . $this->db->quote($name); - } - // equal test compile_id - if ($compile_id !== null) { - $where[] = 'compile_id = ' . $this->db->quote($compile_id); - } - // range test expiration time - if ($exp_time !== null) { - $where[] = 'modified < DATE_SUB(NOW(), INTERVAL ' . intval($exp_time) . ' SECOND)'; - } - // equal test cache_id and match sub-groups - if ($cache_id !== null) { - $where[] = '(cache_id = ' . $this->db->quote($cache_id) - . ' OR cache_id LIKE ' . $this->db->quote($cache_id . '|%') . ')'; - } - // run delete query - $query = $this->db->query('DELETE FROM output_cache WHERE ' . join(' AND ', $where)); - - return $query->rowCount(); - } -} diff --git a/library/Smarty/demo/plugins/resource.extendsall.php b/library/Smarty/demo/plugins/resource.extendsall.php deleted file mode 100644 index 500b3c862..000000000 --- a/library/Smarty/demo/plugins/resource.extendsall.php +++ /dev/null @@ -1,60 +0,0 @@ -smarty->getTemplateDir() as $key => $directory) { - try { - $s = Smarty_Resource::source(null, $source->smarty, '[' . $key . ']' . $source->name); - if (!$s->exists) { - continue; - } - $sources[$s->uid] = $s; - $uid .= $s->filepath; - } - catch (SmartyException $e) { - } - } - - if (!$sources) { - $source->exists = false; - $source->template = $_template; - - return; - } - - $sources = array_reverse($sources, true); - reset($sources); - $s = current($sources); - - $source->components = $sources; - $source->filepath = $s->filepath; - $source->uid = sha1($uid); - $source->exists = $exists; - if ($_template && $_template->smarty->compile_check) { - $source->timestamp = $s->timestamp; - } - // need the template at getContent() - $source->template = $_template; - } -} diff --git a/library/Smarty/demo/plugins/resource.mysql.php b/library/Smarty/demo/plugins/resource.mysql.php deleted file mode 100644 index dfc9606b4..000000000 --- a/library/Smarty/demo/plugins/resource.mysql.php +++ /dev/null @@ -1,81 +0,0 @@ -CREATE TABLE IF NOT EXISTS `templates` ( - * `name` varchar(100) NOT NULL, - * `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - * `source` text, - * PRIMARY KEY (`name`) - * ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    - * Demo data: - *
    INSERT INTO `templates` (`name`, `modified`, `source`) VALUES ('test.tpl', "2010-12-25 22:00:00", '{$x="hello world"}{$x}');
    - * - * @package Resource-examples - * @author Rodney Rehm - */ -class Smarty_Resource_Mysql extends Smarty_Resource_Custom -{ - // PDO instance - protected $db; - // prepared fetch() statement - protected $fetch; - // prepared fetchTimestamp() statement - protected $mtime; - - public function __construct() - { - try { - $this->db = new PDO("mysql:dbname=test;host=127.0.0.1", "smarty"); - } - catch (PDOException $e) { - throw new SmartyException('Mysql Resource failed: ' . $e->getMessage()); - } - $this->fetch = $this->db->prepare('SELECT modified, source FROM templates WHERE name = :name'); - $this->mtime = $this->db->prepare('SELECT modified FROM templates WHERE name = :name'); - } - - /** - * Fetch a template and its modification time from database - * - * @param string $name template name - * @param string $source template source - * @param integer $mtime template modification timestamp (epoch) - * - * @return void - */ - protected function fetch($name, &$source, &$mtime) - { - $this->fetch->execute(array('name' => $name)); - $row = $this->fetch->fetch(); - $this->fetch->closeCursor(); - if ($row) { - $source = $row['source']; - $mtime = strtotime($row['modified']); - } else { - $source = null; - $mtime = null; - } - } - - /** - * Fetch a template's modification time from database - * - * @note implementing this method is optional. Only implement it if modification times can be accessed faster than loading the comple template source. - * - * @param string $name template name - * - * @return integer timestamp (epoch) the template was modified - */ - protected function fetchTimestamp($name) - { - $this->mtime->execute(array('name' => $name)); - $mtime = $this->mtime->fetchColumn(); - $this->mtime->closeCursor(); - - return strtotime($mtime); - } -} diff --git a/library/Smarty/demo/plugins/resource.mysqls.php b/library/Smarty/demo/plugins/resource.mysqls.php deleted file mode 100644 index f694ddf11..000000000 --- a/library/Smarty/demo/plugins/resource.mysqls.php +++ /dev/null @@ -1,62 +0,0 @@ -CREATE TABLE IF NOT EXISTS `templates` ( - * `name` varchar(100) NOT NULL, - * `modified` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, - * `source` text, - * PRIMARY KEY (`name`) - * ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    - * Demo data: - *
    INSERT INTO `templates` (`name`, `modified`, `source`) VALUES ('test.tpl', "2010-12-25 22:00:00", '{$x="hello world"}{$x}');
    - * - * @package Resource-examples - * @author Rodney Rehm - */ -class Smarty_Resource_Mysqls extends Smarty_Resource_Custom -{ - // PDO instance - protected $db; - // prepared fetch() statement - protected $fetch; - - public function __construct() - { - try { - $this->db = new PDO("mysql:dbname=test;host=127.0.0.1", "smarty"); - } - catch (PDOException $e) { - throw new SmartyException('Mysql Resource failed: ' . $e->getMessage()); - } - $this->fetch = $this->db->prepare('SELECT modified, source FROM templates WHERE name = :name'); - } - - /** - * Fetch a template and its modification time from database - * - * @param string $name template name - * @param string $source template source - * @param integer $mtime template modification timestamp (epoch) - * - * @return void - */ - protected function fetch($name, &$source, &$mtime) - { - $this->fetch->execute(array('name' => $name)); - $row = $this->fetch->fetch(); - $this->fetch->closeCursor(); - if ($row) { - $source = $row['source']; - $mtime = strtotime($row['modified']); - } else { - $source = null; - $mtime = null; - } - } -} diff --git a/library/Smarty/demo/templates/footer.tpl b/library/Smarty/demo/templates/footer.tpl deleted file mode 100644 index e04310fdd..000000000 --- a/library/Smarty/demo/templates/footer.tpl +++ /dev/null @@ -1,2 +0,0 @@ - - diff --git a/library/Smarty/demo/templates/header.tpl b/library/Smarty/demo/templates/header.tpl deleted file mode 100644 index 13fa6cb5a..000000000 --- a/library/Smarty/demo/templates/header.tpl +++ /dev/null @@ -1,5 +0,0 @@ - - - {$title} - {$Name} - - diff --git a/library/Smarty/demo/templates/index.tpl b/library/Smarty/demo/templates/index.tpl deleted file mode 100644 index 1fbb6d379..000000000 --- a/library/Smarty/demo/templates/index.tpl +++ /dev/null @@ -1,87 +0,0 @@ -{config_load file="test.conf" section="setup"} -{include file="header.tpl" title=foo} - -
    -
    -{* bold and title are read from the config file *}
    -    {if #bold#}{/if}
    -        {* capitalize the first letters of each word of the title *}
    -        Title: {#title#|capitalize}
    -        {if #bold#}{/if}
    -
    -    The current date and time is {$smarty.now|date_format:"%Y-%m-%d %H:%M:%S"}
    -
    -    The value of global assigned variable $SCRIPT_NAME is {$SCRIPT_NAME}
    -
    -    Example of accessing server environment variable SERVER_NAME: {$smarty.server.SERVER_NAME}
    -
    -    The value of {ldelim}$Name{rdelim} is {$Name}
    -
    -variable modifier example of {ldelim}$Name|upper{rdelim}
    -
    -{$Name|upper}
    -
    -
    -An example of a section loop:
    -
    -    {section name=outer
    -    loop=$FirstName}
    -        {if $smarty.section.outer.index is odd by 2}
    -            {$smarty.section.outer.rownum} . {$FirstName[outer]} {$LastName[outer]}
    -        {else}
    -            {$smarty.section.outer.rownum} * {$FirstName[outer]} {$LastName[outer]}
    -        {/if}
    -        {sectionelse}
    -        none
    -    {/section}
    -
    -    An example of section looped key values:
    -
    -    {section name=sec1 loop=$contacts}
    -        phone: {$contacts[sec1].phone}
    -        
    - - fax: {$contacts[sec1].fax} -
    - - cell: {$contacts[sec1].cell} -
    - {/section} -

    - - testing strip tags - {strip} - - - - -
    - - This is a test - -
    - {/strip} - -

    - -This is an example of the html_select_date function: - -
    - {html_select_date start_year=1998 end_year=2010} -
    - -This is an example of the html_select_time function: - -
    - {html_select_time use_24_hours=false} -
    - -This is an example of the html_options function: - -
    - -
    - -{include file="footer.tpl"} diff --git a/library/Smarty/libs/Autoloader.php b/library/Smarty/libs/Autoloader.php new file mode 100644 index 000000000..7d0c388a6 --- /dev/null +++ b/library/Smarty/libs/Autoloader.php @@ -0,0 +1,124 @@ + 'Smarty.class.php', 'smartybc' => 'SmartyBC.class.php',); + + /** + * Registers Smarty_Autoloader backward compatible to older installations. + * + * @param bool $prepend Whether to prepend the autoloader or not. + */ + public static function registerBC($prepend = false) + { + /** + * register the class autoloader + */ + if (!defined('SMARTY_SPL_AUTOLOAD')) { + define('SMARTY_SPL_AUTOLOAD', 0); + } + if (SMARTY_SPL_AUTOLOAD && + set_include_path(get_include_path() . PATH_SEPARATOR . SMARTY_SYSPLUGINS_DIR) !== false + ) { + $registeredAutoLoadFunctions = spl_autoload_functions(); + if (!isset($registeredAutoLoadFunctions['spl_autoload'])) { + spl_autoload_register(); + } + } else { + self::register($prepend); + } + } + + /** + * Registers Smarty_Autoloader as an SPL autoloader. + * + * @param bool $prepend Whether to prepend the autoloader or not. + */ + public static function register($prepend = false) + { + self::$SMARTY_DIR = defined('SMARTY_DIR') ? SMARTY_DIR : dirname(__FILE__) . DIRECTORY_SEPARATOR; + self::$SMARTY_SYSPLUGINS_DIR = defined('SMARTY_SYSPLUGINS_DIR') ? SMARTY_SYSPLUGINS_DIR : + self::$SMARTY_DIR . 'sysplugins' . DIRECTORY_SEPARATOR; + if (version_compare(phpversion(), '5.3.0', '>=')) { + spl_autoload_register(array(__CLASS__, 'autoload'), true, $prepend); + } else { + spl_autoload_register(array(__CLASS__, 'autoload')); + } + } + + /** + * Handles auto loading of classes. + * + * @param string $class A class name. + */ + public static function autoload($class) + { + $_class = strtolower($class); + $file = self::$SMARTY_SYSPLUGINS_DIR . $_class . '.php'; + if (strpos($_class, 'smarty_internal_') === 0) { + if (strpos($_class, 'smarty_internal_compile_') === 0) { + if (is_file($file)) { + require $file; + } + return; + } + @include $file; + return; + } + if (preg_match('/^(smarty_(((template_(source|config|cache|compiled|resource_base))|((cached|compiled)?resource)|(variable|security)))|(smarty(bc)?)$)/', + $_class, $match)) { + if (!empty($match[3])) { + @include $file; + return; + } elseif (!empty($match[9]) && isset(self::$rootClasses[$_class])) { + $file = self::$rootClasses[$_class]; + require $file; + return; + } + } + if (0 !== strpos($_class, 'smarty')) { + return; + } + if (is_file($file)) { + require $file; + return; + } + return; + } +} diff --git a/library/Smarty/libs/Smarty.class.php b/library/Smarty/libs/Smarty.class.php index 832b0d309..ac09d0a43 100644 --- a/library/Smarty/libs/Smarty.class.php +++ b/library/Smarty/libs/Smarty.class.php @@ -2,15 +2,17 @@ /** * Project: Smarty: the PHP compiling template engine * File: Smarty.class.php - * SVN: $Id: Smarty.class.php 4897 2014-10-14 22:29:58Z Uwe.Tews@googlemail.com $ + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. + * * This library 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 * Lesser General Public License for more details. + * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA @@ -19,12 +21,13 @@ * smarty-discussion-subscribe@googlegroups.com * * @link http://www.smarty.net/ - * @copyright 2008 New Digital Group, Inc. + * @copyright 2015 New Digital Group, Inc. + * @copyright 2015 Uwe Tews * @author Monte Ohrt * @author Uwe Tews * @author Rodney Rehm * @package Smarty - * @version 3.1.21 + * @version 3.1.28-dev */ /** @@ -53,7 +56,7 @@ if (!defined('SMARTY_PLUGINS_DIR')) { define('SMARTY_PLUGINS_DIR', SMARTY_DIR . 'plugins' . DS); } if (!defined('SMARTY_MBSTRING')) { - define('SMARTY_MBSTRING', function_exists('mb_split')); + define('SMARTY_MBSTRING', function_exists('mb_get_info')); } if (!defined('SMARTY_RESOURCE_CHAR_SET')) { // UTF-8 can only be done properly when mbstring is available! @@ -70,36 +73,41 @@ if (!defined('SMARTY_RESOURCE_DATE_FORMAT')) { } /** - * register the class autoloader + * Try loading the Smarty_Internal_Data class + * If we fail we must load Smarty's autoloader. + * Otherwise we may have a global autoloader like Composer */ -if (!defined('SMARTY_SPL_AUTOLOAD')) { - define('SMARTY_SPL_AUTOLOAD', 0); -} - -if (SMARTY_SPL_AUTOLOAD && set_include_path(get_include_path() . PATH_SEPARATOR . SMARTY_SYSPLUGINS_DIR) !== false) { - $registeredAutoLoadFunctions = spl_autoload_functions(); - if (!isset($registeredAutoLoadFunctions['spl_autoload'])) { - spl_autoload_register(); +if (!class_exists('Smarty_Autoloader', false)) { + if (!class_exists('Smarty_Internal_Data', true)) { + require_once dirname(__FILE__) . '/Autoloader.php'; + Smarty_Autoloader::registerBC(); } -} else { - spl_autoload_register('smartyAutoload'); } /** * Load always needed external class files */ -include_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_data.php'; -include_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_templatebase.php'; -include_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_template.php'; -include_once SMARTY_SYSPLUGINS_DIR . 'smarty_resource.php'; -include_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_resource_file.php'; -include_once SMARTY_SYSPLUGINS_DIR . 'smarty_cacheresource.php'; -include_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_cacheresource_file.php'; +if (!class_exists('Smarty_Internal_Data', false)) { + require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_data.php'; +} +require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_extension_handler.php'; +require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_templatebase.php'; +require_once SMARTY_SYSPLUGINS_DIR . 'smarty_internal_template.php'; +require_once SMARTY_SYSPLUGINS_DIR . 'smarty_resource.php'; +require_once SMARTY_SYSPLUGINS_DIR . 'smarty_variable.php'; +require_once SMARTY_SYSPLUGINS_DIR . 'smarty_template_source.php'; +require_once SMARTY_SYSPLUGINS_DIR . 'smarty_template_resource_base.php'; /** * This is the main Smarty class * * @package Smarty + * + * @method int clearAllCache(int $exp_time = null, string $type = null) + * @method int clearCache(string $template_name, string $cache_id = null, string $compile_id = null, int $exp_time = null, string $type = null) + * @method int compileAllTemplates(Smarty $smarty, string $extension = '.tpl', bool $force_compile = false, int $time_limit = 0, int $max_errors = null) + * @method int compileAllConfig(Smarty $smarty, string $extension = '.conf', bool $force_compile = false, int $time_limit = 0, int $max_errors = null) + * */ class Smarty extends Smarty_Internal_TemplateBase { @@ -110,23 +118,36 @@ class Smarty extends Smarty_Internal_TemplateBase /** * smarty version */ - const SMARTY_VERSION = 'Smarty-3.1.21-dev'; + const SMARTY_VERSION = '3.1.28-dev/77'; /** * define variable scopes */ const SCOPE_LOCAL = 0; - const SCOPE_PARENT = 1; - const SCOPE_ROOT = 2; - const SCOPE_GLOBAL = 3; + + const SCOPE_PARENT = 2; + + const SCOPE_TPL_ROOT = 4; + + const SCOPE_ROOT = 8; + + const SCOPE_SMARTY = 16; + + const SCOPE_GLOBAL = 32; + + const SCOPE_BUBBLE_UP = 64; + /** * define caching modes */ const CACHING_OFF = 0; + const CACHING_LIFETIME_CURRENT = 1; + const CACHING_LIFETIME_SAVED = 2; + /** - * define constant for clearing cache files be saved expiration datees + * define constant for clearing cache files be saved expiration dates */ const CLEAR_EXPIRED = - 1; @@ -134,31 +155,66 @@ class Smarty extends Smarty_Internal_TemplateBase * define compile check modes */ const COMPILECHECK_OFF = 0; + const COMPILECHECK_ON = 1; + const COMPILECHECK_CACHEMISS = 2; + + /** + * define debug modes + */ + const DEBUG_OFF = 0; + + const DEBUG_ON = 1; + + const DEBUG_INDIVIDUAL = 2; + /** * modes for handling of "" tags in templates. */ const PHP_PASSTHRU = 0; //-> print tags as plain text + const PHP_QUOTE = 1; //-> escape tags as entities + const PHP_REMOVE = 2; //-> escape tags as entities + const PHP_ALLOW = 3; //-> escape tags as entities + /** * filter types */ const FILTER_POST = 'post'; + const FILTER_PRE = 'pre'; + const FILTER_OUTPUT = 'output'; + const FILTER_VARIABLE = 'variable'; + /** * plugin types */ const PLUGIN_FUNCTION = 'function'; + const PLUGIN_BLOCK = 'block'; + const PLUGIN_COMPILER = 'compiler'; + const PLUGIN_MODIFIER = 'modifier'; + const PLUGIN_MODIFIERCOMPILER = 'modifiercompiler'; + /** + * Resource caching modes + */ + const RESOURCE_CACHE_OFF = 0; + + const RESOURCE_CACHE_AUTOMATIC = 1; // cache template objects by rules + + const RESOURCE_CACHE_TEMPLATE = 2; // cache all template objects + + const RESOURCE_CACHE_ON = 4; // cache source and compiled resources + /**#@-*/ /** @@ -167,26 +223,31 @@ class Smarty extends Smarty_Internal_TemplateBase public static $global_tpl_vars = array(); /** - * error handler returned by set_error_hanlder() in Smarty::muteExpectedErrors() + * error handler returned by set_error_handler() in Smarty::muteExpectedErrors() */ public static $_previous_error_handler = null; + /** * contains directories outside of SMARTY_DIR that are to be muted by muteExpectedErrors() */ public static $_muted_directories = array(); + /** * Flag denoting if Multibyte String functions are available */ public static $_MBSTRING = SMARTY_MBSTRING; + /** * The character set to adhere to (e.g. "UTF-8") */ public static $_CHARSET = SMARTY_RESOURCE_CHAR_SET; + /** * The date format to be used internally * (accepts date() and strftime()) */ public static $_DATE_FORMAT = SMARTY_RESOURCE_DATE_FORMAT; + /** * Flag denoting if PCRE should run in UTF-8 mode */ @@ -202,163 +263,152 @@ class Smarty extends Smarty_Internal_TemplateBase */ /** - * auto literal on delimiters with whitspace + * auto literal on delimiters with whitespace * * @var boolean */ public $auto_literal = true; + /** * display error on not assigned variables * * @var boolean */ public $error_unassigned = false; + /** - * look up relative filepaths in include_path + * look up relative file path in include_path * * @var boolean */ public $use_include_path = false; + /** * template directory * * @var array */ - private $template_dir = array(); + private $template_dir = array('./templates/'); + /** * joined template directory string used in cache keys * * @var string */ - public $joined_template_dir = null; + public $_joined_template_dir = null; + /** * joined config directory string used in cache keys * * @var string */ - public $joined_config_dir = null; + public $_joined_config_dir = null; + /** * default template handler * * @var callable */ public $default_template_handler_func = null; + /** * default config handler * * @var callable */ public $default_config_handler_func = null; + /** * default plugin handler * * @var callable */ public $default_plugin_handler_func = null; + /** * compile directory * * @var string */ - private $compile_dir = null; + private $compile_dir = './templates_c/'; + /** * plugins directory * * @var array */ - private $plugins_dir = array(); + private $plugins_dir = null; + /** * cache directory * * @var string */ - private $cache_dir = null; + private $cache_dir = './cache/'; + /** * config directory * * @var array */ - private $config_dir = array(); + private $config_dir = array('./configs/'); + /** * force template compiling? * * @var boolean */ public $force_compile = false; + /** * check template for modifications? * * @var boolean */ public $compile_check = true; + /** * use sub dirs for compiled/cached files? * * @var boolean */ public $use_sub_dirs = false; + /** * allow ambiguous resources (that are made unique by the resource handler) * * @var boolean */ public $allow_ambiguous_resources = false; - /** - * caching enabled - * - * @var boolean - */ - public $caching = false; + /** * merge compiled includes * * @var boolean */ public $merge_compiled_includes = false; - /** - * template inheritance merge compiled includes - * - * @var boolean - */ - public $inheritance_merge_compiled_includes = true; - /** - * cache lifetime in seconds - * - * @var integer - */ - public $cache_lifetime = 3600; + /** * force cache file creation * * @var boolean */ public $force_cache = false; - /** - * Set this if you want different sets of cache files for the same - * templates. - * - * @var string - */ - public $cache_id = null; - /** - * Set this if you want different sets of compiled files for the same - * templates. - * - * @var string - */ - public $compile_id = null; + /** * template left-delimiter * * @var string */ public $left_delimiter = "{"; + /** * template right-delimiter * * @var string */ public $right_delimiter = "}"; + /**#@+ * security */ @@ -370,33 +420,28 @@ class Smarty extends Smarty_Internal_TemplateBase * @see Smarty_Security */ public $security_class = 'Smarty_Security'; + /** * implementation of security class * * @var Smarty_Security */ public $security_policy = null; + /** * controls handling of PHP-blocks * * @var integer */ public $php_handling = self::PHP_PASSTHRU; + /** * controls if the php template file resource is allowed * * @var bool */ public $allow_php_templates = false; - /** - * Should compiled-templates be prevented from being called directly? - * {@internal - * Currently used by Smarty_Internal_Template only. - * }} - * - * @var boolean - */ - public $direct_access_security = true; + /**#@-*/ /** * debug mode @@ -405,6 +450,7 @@ class Smarty extends Smarty_Internal_TemplateBase * @var boolean */ public $debugging = false; + /** * This determines if debugging is enable-able from the browser. *