Uploading and preparing the repository to the dev version.

Original unminified source code (dev folder - js, css, less) (fixes #6)
Grunt build system
Multiple identities correction (fixes #9)
Compose html editor (fixes #12)
New general settings - Loading Description
New warning about default admin password
Split general and login screen settings
This commit is contained in:
RainLoop Team 2013-11-16 02:21:12 +04:00
parent afad45137e
commit 4cc2207513
846 changed files with 99453 additions and 2123 deletions

View file

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View file

@ -0,0 +1,211 @@
# tmhOAuth
An OAuth 1.0A library written in PHP by @themattharris, specifically for use
with the Twitter API.
**Disclaimer**: This project is a work in progress. Please use the issue tracker
to report any enhancements or issues you encounter.
## Goals
- Support OAuth 1.0A
- Use Authorisation headers instead of query string or POST parameters
- Allow uploading of images
- Provide enough information to assist with debugging
## Dependencies
The library has been tested with PHP 5.3+ and relies on CURL and hash_hmac. The
vast majority of hosting providers include these libraries and run with PHP 5.1+.
The code makes use of hash_hmac, which was introduced in PHP 5.1.2. If your version
of PHP is lower than this you should ask your hosting provider for an update.
## A note about security and SSL
Version 0.60 hardened the security of the library and defaulted `curl_ssl_verifypeer` to `true`.
As some hosting providers do not provide the most current certificate root file
it is now included in this repository. If the version is out of date OR you prefer
to download the certificate roots yourself, you can get them
from: http://curl.haxx.se/ca/cacert.pem
Before upgrading the version of tmhOAuth that you use, be sure to verify the SSL
handling works on your server by running the `examples/verify_ssl.php` script.
## Usage
This will be built out later but for the moment review the examples repository
<https://github.com/themattharris/tmhOAuth-examples> for ways the library can be
used. Each example contains instructions on how to use it.
## Notes for users of previous versions
If you previously used version 0.4 be aware the utility functions
have now been broken into their own file. Before you use version 0.5+ in your app
test locally to ensure your code doesn't need tmhUtilities included.
If you used custom HTTP request headers when they were defined as `'key: value'` strings
you should now define them as `'key' => 'value'` pairs.
Versions prior to 0.7.3 collapsed headers with the same value into one
$tmhOAuth->response['headers'] key. Since 0.7.3 headers with the same key will use an array
to store their values.
## Change History
### 0.7.5 - 20 Februrary 2013
- tidying up of composer.json. (Issue #112) Props: ceeram
### 0.7.4 - 19 Februrary 2013
- corrections to composer.json to support packagists requirements. (Issue #110)
### 0.7.3 - 18 Februrary 2013
- add support for making requests with the host header being different to the request host.
- ensure headers with the same key do not overwrite each other in $tmhOAuth->response['headers'].
- removed examples submodule in favor of examples including tmhOAuth, rather than tmhOAuth including examples
- made it so that if param values are sent to $tmhOAuth->request as an array (key -> array()) then $tmhOAuth->prepare_params will now implode them using ','
- fixed composer. (Issue #99). Props: rasa
- fixed PHPDoc. (Issue #47). Props: trante
- instead of void, $tmhOAuth->curlit now returns 0 if 'prevent_request' is set
### 0.7.2 - 01 November 2012
- use DIRECTORY_SEPARATOR for multi-environment support. (Issue #80) Props: whallz
- tidied up the curlHeader function to use explode instead of substr and store the keys in the format they are returned from the API
- removed content-length hack as it isn't needed if CURLOPT_POSTFIELDS is initialized on all POSTs
- removed the expects header hack as Twitter no longer requires it to be there
- introduce composer.json. (Issues #39, #77, #85) Props: akandels, conradkleinespel, dguyon, kud, philsturgeon, willdurand
- added support for specifying custom headers when using $tmhOAuth->request. (Issue #98)
### 0.7.1 - 27 October 2012
- set content-length to 0 explictly to avoid a bug between libcurl and Twitter. (Issue #94)
- allow initialization without a configuration array (default config to array())
- prevent ->url allowing double slashes in paths
### 0.7.0 - 04 September 2012
- changed version numbers to x.y.z format
- stronger method scoping (public and private)
- Typo fix in depenencies. (Issue #42) Props: tantek
- Only lowercase the host and scheme, and not path, in prepare_url. (Issue #56) Props: uzyn
- Fixed a number of PHP warnings by changing some tmhUtilty methods to static. (Issue #52) Props: DrayChou
- Raw headers and response body are now available as `$tmhOAuth->response['raw']`
- Moved the examples to their own repository <https://github.com/themattharris/tmhOAuth-examples>
- Removed the `noexamples` branch as master does not contain examples anymore
- Introduced `$tmhOAuth->config['timezone']` and set `date_default_timezone_set`. (Issue #70) Props: iamctodd
### 0.621 - 12 March 2012
- Ensure `$_SERVER['HTTPS']` isset before checking it's value. Props: kud
### 0.62 - 01 March 2012
- Fix array merging bug. Props: julien-c
- use is_callable instead of function_exists: Props: samwierema
- Allow options to be specified for the entify function. Props: davidcroda
- protocol was not inferred correctly for https when ['HTTPS'] == 'on'. Props: ospector
- Switched to https for twitter.com display URLs
- Improved the search results example
### 0.61 - 16 January 2012
- Removed trailing ?> from tmhOAuth.php and tmhUtilities.php to meet the Zend Framework's coding practices. Props: reedy
- Fixed bug where CURLOPT_SSL_VERIFYHOST was defaulted to true when it should have been defaulted to 2. Props: kevinsmcarthur
### 0.60 - 29 December 2011
- Changed any use of implode to the preferred format of implode($glue, $pieces). Props: reedy
- Moved oauth_verifier to the authorization header as shown in example of RFC 5849. Props: spacenick
- added curl error and error number values to the $tmhOAuth->response object
- added an example script for testing the SSL connection to twitter.com with the new SSL configuration of tmhOAuth
- added a function to generate the useragent depending on whether SSL is on or not
- defaulted CURLOPT_SSL_VERIFYPEER to true
- added CURLOPT_SSL_VERIFYHOST and defaulted it to true
- added the most current cacert.pem file from http://curl.haxx.se/ca/cacert.pem and configured curl to use it
### 0.58 - 29 December 2011
- Rearranged some configuration variables around to make commenting easier
- Standarised on lowercase booleans
### 0.57 - 11 December 2011
- Fixed prevent_request so OAuth Echo requests work again.
- Added a TwitPic OAuth Echo example
### 0.56 - 29 September 2011
- Fixed version reference in the UserAgent
- Updated tmhUtilities::entify with support for media
- Updated tmhUtilities::entify with support for multibyte characters. Props: andersonshatch
### 0.55 - 29 September 2011
- Added support for content encoding. Defaults to whatever localhost supports. Props: yusuke
### 0.54 - 29 September 2011
- User-Agent is now configurable and includes the current version number of the script
- Updated the Streaming examples to use SSL
### 0.53 - 15 July 2011
- Fixed issue where headers were being duplicated if the library was called more than once.
- Updated examples to fit the new location of access tokens and secrets on dev.twitter.com
- Added Photo Tweet example
### 0.52 - 06 July 2011
- Fixed issue where the preference for include_time in create_nonce was being ignored
### 0.51 - 06 July 2011
- Use isset instead of suppress errors. Props: funkatron
- Added example of using the Search API
- Added example of using friends/ids and users/lookup to get details of a users friends
- Added example of the authorize OAuth webflow
### 0.5 - 29 March 2011
- Moved utility functions out of the main class and into the tmhUtilities class.
- Added the ability to send OAuth parameters as part of the querystring or POST body.
- Section 3.4.1.2 says the url must be lowercase so prepare URL now does this.
- Added a convenience method for accessing the safe_encode/decode transforms.
- Updated the examples to use the new utilities library.
- Added examples for sitestreams and userstreams.
- Added a more advanced streaming API example.
### 0.4 - 03 March 2011
- Fixed handling of parameters when using DELETE. Thanks to yusuke for reporting
- Fixed php_self to handle port numbers other than 80/443. Props: yusuke
- Updated function pr to use pre only when not running in CLI mode
- Add support for proxy servers. Props juanchorossi
- Function request now returns the HTTP status code. Props: kronenthaler
- Documentation fixes for xAuth. Props: 140dev
- Some minor code formatting changes
### 0.3 - 28 September 2010
- Moved entities rendering into the library
### 0.2 - 17 September 2010
- Added support for the Streaming API
### 0.14 - 17 September 2010
- Fixed authorisation header for use with OAuth Echo
### 0.13 - 17 September 2010
- Added use_ssl configuration parameter
- Fixed config array typo
- Removed v from the config
- Remove protocol from the host (configured by use_ssl)
- Added include for easier debugging
### 0.12 - 17 September 2010
- Moved curl options to config
- Added the ability for curl to follow redirects, default false
### 0.11 - 17 September 2010
- Fixed a bug in the GET requests
### 0.1 - 26 August 2010
- Initial beta version
## Community
License: Apache 2 (see [included LICENSE file](https://github.com/themattharris/tmhOAuth/blob/master/LICENSE))
Follow [@tmhOAuth](https://twitter.com/intent/follow?screen_name=tmhOAuth) to receive updates on releases, or ask for support
Follow me on Twitter: [@themattharris](https://twitter.com/intent/follow?screen_name=themattharris)
Check out the Twitter Developer Resources: <https://dev.twitter.com>
## To Do
- Add good behavior logic to the Streaming API handler - i.e. on disconnect back off
- Async Curl support

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,724 @@
<?php
/**
* tmhOAuth
*
* An OAuth 1.0A library written in PHP.
* The library supports file uploading using multipart/form as well as general
* REST requests. OAuth authentication is sent using the an Authorization Header.
*
* @author themattharris
* @version 0.7.5
*
* 20 February 2013
*/
class tmhOAuth {
const VERSION = '0.7.5';
var $response = array();
/**
* Creates a new tmhOAuth object
*
* @param string $config, the configuration to use for this request
* @return void
*/
public function __construct($config=array()) {
$this->params = array();
$this->headers = array();
$this->auto_fixed_time = false;
$this->buffer = null;
// default configuration options
$this->config = array_merge(
array(
// leave 'user_agent' blank for default, otherwise set this to
// something that clearly identifies your app
'user_agent' => '',
// default timezone for requests
'timezone' => 'UTC',
'use_ssl' => true,
'host' => 'api.twitter.com',
'consumer_key' => '',
'consumer_secret' => '',
'user_token' => '',
'user_secret' => '',
'force_nonce' => false,
'nonce' => false, // used for checking signatures. leave as false for auto
'force_timestamp' => false,
'timestamp' => false, // used for checking signatures. leave as false for auto
// oauth signing variables that are not dynamic
'oauth_version' => '1.0',
'oauth_signature_method' => 'HMAC-SHA1',
// you probably don't want to change any of these curl values
'curl_connecttimeout' => 30,
'curl_timeout' => 10,
// for security this should always be set to 2.
'curl_ssl_verifyhost' => 2,
// for security this should always be set to true.
'curl_ssl_verifypeer' => true,
// you can get the latest cacert.pem from here http://curl.haxx.se/ca/cacert.pem
'curl_cainfo' => dirname(__FILE__) . DIRECTORY_SEPARATOR . 'cacert.pem',
'curl_capath' => dirname(__FILE__),
'curl_followlocation' => false, // whether to follow redirects or not
// support for proxy servers
'curl_proxy' => false, // really you don't want to use this if you are using streaming
'curl_proxyuserpwd' => false, // format username:password for proxy, if required
'curl_encoding' => '', // leave blank for all supported formats, else use gzip, deflate, identity
// streaming API
'is_streaming' => false,
'streaming_eol' => "\r\n",
'streaming_metrics_interval' => 60,
// header or querystring. You should always use header!
// this is just to help me debug other developers implementations
'as_header' => true,
'debug' => false,
),
$config
);
$this->set_user_agent();
date_default_timezone_set($this->config['timezone']);
}
/**
* Sets the useragent for PHP to use
* If '$this->config['user_agent']' already has a value it is used instead of one
* being generated.
*
* @return void value is stored to the config array class variable
*/
private function set_user_agent() {
if (!empty($this->config['user_agent']))
return;
if ($this->config['curl_ssl_verifyhost'] && $this->config['curl_ssl_verifypeer']) {
$ssl = '+SSL';
} else {
$ssl = '-SSL';
}
$ua = 'tmhOAuth ' . self::VERSION . $ssl . ' - //github.com/themattharris/tmhOAuth';
$this->config['user_agent'] = $ua;
}
/**
* Generates a random OAuth nonce.
* If 'force_nonce' is true a nonce is not generated and the value in the configuration will be retained.
*
* @param string $length how many characters the nonce should be before MD5 hashing. default 12
* @param string $include_time whether to include time at the beginning of the nonce. default true
* @return void value is stored to the config array class variable
*/
private function create_nonce($length=12, $include_time=true) {
if ($this->config['force_nonce'] == false) {
$sequence = array_merge(range(0,9), range('A','Z'), range('a','z'));
$length = $length > count($sequence) ? count($sequence) : $length;
shuffle($sequence);
$prefix = $include_time ? microtime() : '';
$this->config['nonce'] = md5(substr($prefix . implode('', $sequence), 0, $length));
}
}
/**
* Generates a timestamp.
* If 'force_timestamp' is true a nonce is not generated and the value in the configuration will be retained.
*
* @return void value is stored to the config array class variable
*/
private function create_timestamp() {
$this->config['timestamp'] = ($this->config['force_timestamp'] == false ? time() : $this->config['timestamp']);
}
/**
* Encodes the string or array passed in a way compatible with OAuth.
* If an array is passed each array value will will be encoded.
*
* @param mixed $data the scalar or array to encode
* @return $data encoded in a way compatible with OAuth
*/
private function safe_encode($data) {
if (is_array($data)) {
return array_map(array($this, 'safe_encode'), $data);
} else if (is_scalar($data)) {
return str_ireplace(
array('+', '%7E'),
array(' ', '~'),
rawurlencode($data)
);
} else {
return '';
}
}
/**
* Decodes the string or array from it's URL encoded form
* If an array is passed each array value will will be decoded.
*
* @param mixed $data the scalar or array to decode
* @return string $data decoded from the URL encoded form
*/
private function safe_decode($data) {
if (is_array($data)) {
return array_map(array($this, 'safe_decode'), $data);
} else if (is_scalar($data)) {
return rawurldecode($data);
} else {
return '';
}
}
/**
* Returns an array of the standard OAuth parameters.
*
* @return array all required OAuth parameters, safely encoded
*/
private function get_defaults() {
$defaults = array(
'oauth_version' => $this->config['oauth_version'],
'oauth_nonce' => $this->config['nonce'],
'oauth_timestamp' => $this->config['timestamp'],
'oauth_consumer_key' => $this->config['consumer_key'],
'oauth_signature_method' => $this->config['oauth_signature_method'],
);
// include the user token if it exists
if ( $this->config['user_token'] )
$defaults['oauth_token'] = $this->config['user_token'];
// safely encode
foreach ($defaults as $k => $v) {
$_defaults[$this->safe_encode($k)] = $this->safe_encode($v);
}
return $_defaults;
}
/**
* Extracts and decodes OAuth parameters from the passed string
*
* @param string $body the response body from an OAuth flow method
* @return array the response body safely decoded to an array of key => values
*/
public function extract_params($body) {
$kvs = explode('&', $body);
$decoded = array();
foreach ($kvs as $kv) {
$kv = explode('=', $kv, 2);
$kv[0] = $this->safe_decode($kv[0]);
$kv[1] = $this->safe_decode($kv[1]);
$decoded[$kv[0]] = $kv[1];
}
return $decoded;
}
/**
* Prepares the HTTP method for use in the base string by converting it to
* uppercase.
*
* @param string $method an HTTP method such as GET or POST
* @return void value is stored to the class variable 'method'
*/
private function prepare_method($method) {
$this->method = strtoupper($method);
}
/**
* Prepares the URL for use in the base string by ripping it apart and
* reconstructing it.
*
* Ref: 3.4.1.2
*
* @param string $url the request URL
* @return void value is stored to the class variable 'url'
*/
private function prepare_url($url) {
$parts = parse_url($url);
$port = isset($parts['port']) ? $parts['port'] : false;
$scheme = $parts['scheme'];
$host = $parts['host'];
$path = isset($parts['path']) ? $parts['path'] : false;
$port or $port = ($scheme == 'https') ? '443' : '80';
if (($scheme == 'https' && $port != '443')
|| ($scheme == 'http' && $port != '80')) {
$host = "$host:$port";
}
// the scheme and host MUST be lowercase
$this->url = strtolower("$scheme://$host");
// but not the path
$this->url .= $path;
}
/**
* Prepares all parameters for the base string and request.
* Multipart parameters are ignored as they are not defined in the specification,
* all other types of parameter are encoded for compatibility with OAuth.
*
* @param array $params the parameters for the request
* @return void prepared values are stored in the class variable 'signing_params'
*/
private function prepare_params($params) {
// do not encode multipart parameters, leave them alone
if ($this->config['multipart']) {
$this->request_params = $params;
$params = array();
}
// signing parameters are request parameters + OAuth default parameters
$this->signing_params = array_merge($this->get_defaults(), (array)$params);
// Remove oauth_signature if present
// Ref: Spec: 9.1.1 ("The oauth_signature parameter MUST be excluded.")
if (isset($this->signing_params['oauth_signature'])) {
unset($this->signing_params['oauth_signature']);
}
// Parameters are sorted by name, using lexicographical byte value ordering.
// Ref: Spec: 9.1.1 (1)
uksort($this->signing_params, 'strcmp');
// encode. Also sort the signed parameters from the POST parameters
foreach ($this->signing_params as $k => $v) {
$k = $this->safe_encode($k);
if (is_array($v))
$v = implode(',', $v);
$v = $this->safe_encode($v);
$_signing_params[$k] = $v;
$kv[] = "{$k}={$v}";
}
// auth params = the default oauth params which are present in our collection of signing params
$this->auth_params = array_intersect_key($this->get_defaults(), $_signing_params);
if (isset($_signing_params['oauth_callback'])) {
$this->auth_params['oauth_callback'] = $_signing_params['oauth_callback'];
unset($_signing_params['oauth_callback']);
}
if (isset($_signing_params['oauth_verifier'])) {
$this->auth_params['oauth_verifier'] = $_signing_params['oauth_verifier'];
unset($_signing_params['oauth_verifier']);
}
// request_params is already set if we're doing multipart, if not we need to set them now
if ( ! $this->config['multipart'])
$this->request_params = array_diff_key($_signing_params, $this->get_defaults());
// create the parameter part of the base string
$this->signing_params = implode('&', $kv);
}
/**
* Prepares the OAuth signing key
*
* @return void prepared signing key is stored in the class variable 'signing_key'
*/
private function prepare_signing_key() {
$this->signing_key = $this->safe_encode($this->config['consumer_secret']) . '&' . $this->safe_encode($this->config['user_secret']);
}
/**
* Prepare the base string.
* Ref: Spec: 9.1.3 ("Concatenate Request Elements")
*
* @return void prepared base string is stored in the class variable 'base_string'
*/
private function prepare_base_string() {
$url = $this->url;
# if the host header is set we need to rewrite the basestring to use
# that, instead of the request host. otherwise the signature won't match
# on the server side
if (!empty($this->custom_headers['Host'])) {
$url = str_ireplace(
$this->config['host'],
$this->custom_headers['Host'],
$url
);
}
$base = array(
$this->method,
$url,
$this->signing_params
);
$this->base_string = implode('&', $this->safe_encode($base));
}
/**
* Prepares the Authorization header
*
* @return void prepared authorization header is stored in the class variable headers['Authorization']
*/
private function prepare_auth_header() {
unset($this->headers['Authorization']);
uksort($this->auth_params, 'strcmp');
if (!$this->config['as_header']) :
$this->request_params = array_merge($this->request_params, $this->auth_params);
return;
endif;
foreach ($this->auth_params as $k => $v) {
$kv[] = "{$k}=\"{$v}\"";
}
$this->auth_header = 'OAuth ' . implode(', ', $kv);
$this->headers['Authorization'] = $this->auth_header;
}
/**
* Signs the request and adds the OAuth signature. This runs all the request
* parameter preparation methods.
*
* @param string $method the HTTP method being used. e.g. POST, GET, HEAD etc
* @param string $url the request URL without query string parameters
* @param array $params the request parameters as an array of key=value pairs
* @param string $useauth whether to use authentication when making the request.
* @return void
*/
private function sign($method, $url, $params, $useauth) {
$this->prepare_method($method);
$this->prepare_url($url);
$this->prepare_params($params);
// we don't sign anything is we're not using auth
if ($useauth) {
$this->prepare_base_string();
$this->prepare_signing_key();
$this->auth_params['oauth_signature'] = $this->safe_encode(
base64_encode(
hash_hmac(
'sha1', $this->base_string, $this->signing_key, true
)));
$this->prepare_auth_header();
}
}
/**
* Make an HTTP request using this library. This method doesn't return anything.
* Instead the response should be inspected directly.
*
* @param string $method the HTTP method being used. e.g. POST, GET, HEAD etc
* @param string $url the request URL without query string parameters
* @param array $params the request parameters as an array of key=value pairs. Default empty array
* @param string $useauth whether to use authentication when making the request. Default true
* @param string $multipart whether this request contains multipart data. Default false
* @param array $headers any custom headers to send with the request. Default empty array
* @return int the http response code for the request. 0 is returned if a connection could not be made
*/
public function request($method, $url, $params=array(), $useauth=true, $multipart=false, $headers=array()) {
// reset the request headers (we don't want to reuse them)
$this->headers = array();
$this->custom_headers = $headers;
$this->config['multipart'] = $multipart;
$this->create_nonce();
$this->create_timestamp();
$this->sign($method, $url, $params, $useauth);
if (!empty($this->custom_headers))
$this->headers = array_merge((array)$this->headers, (array)$this->custom_headers);
return $this->curlit();
}
/**
* Make a long poll HTTP request using this library. This method is
* different to the other request methods as it isn't supposed to disconnect
*
* Using this method expects a callback which will receive the streaming
* responses.
*
* @param string $method the HTTP method being used. e.g. POST, GET, HEAD etc
* @param string $url the request URL without query string parameters
* @param array $params the request parameters as an array of key=value pairs
* @param string $callback the callback function to stream the buffer to.
* @return void
*/
public function streaming_request($method, $url, $params=array(), $callback='') {
if ( ! empty($callback) ) {
if ( ! is_callable($callback) ) {
return false;
}
$this->config['streaming_callback'] = $callback;
}
$this->metrics['start'] = time();
$this->metrics['interval_start'] = $this->metrics['start'];
$this->metrics['tweets'] = 0;
$this->metrics['last_tweets'] = 0;
$this->metrics['bytes'] = 0;
$this->metrics['last_bytes'] = 0;
$this->config['is_streaming'] = true;
$this->request($method, $url, $params);
}
/**
* Handles the updating of the current Streaming API metrics.
*
* @return array the metrics for the streaming api connection
*/
private function update_metrics() {
$now = time();
if (($this->metrics['interval_start'] + $this->config['streaming_metrics_interval']) > $now)
return false;
$this->metrics['tps'] = round( ($this->metrics['tweets'] - $this->metrics['last_tweets']) / $this->config['streaming_metrics_interval'], 2);
$this->metrics['bps'] = round( ($this->metrics['bytes'] - $this->metrics['last_bytes']) / $this->config['streaming_metrics_interval'], 2);
$this->metrics['last_bytes'] = $this->metrics['bytes'];
$this->metrics['last_tweets'] = $this->metrics['tweets'];
$this->metrics['interval_start'] = $now;
return $this->metrics;
}
/**
* Utility function to create the request URL in the requested format
*
* @param string $request the API method without extension
* @param string $format the format of the response. Default json. Set to an empty string to exclude the format
* @return string the concatenation of the host, API version, API method and format
*/
public function url($request, $format='json') {
$format = strlen($format) > 0 ? ".$format" : '';
$proto = $this->config['use_ssl'] ? 'https:/' : 'http:/';
// backwards compatibility with v0.1
if (isset($this->config['v']))
$this->config['host'] = $this->config['host'] . '/' . $this->config['v'];
$request = ltrim($request, '/');
$pos = strlen($request) - strlen($format);
if (substr($request, $pos) === $format)
$request = substr_replace($request, '', $pos);
return implode('/', array(
$proto,
$this->config['host'],
$request . $format
));
}
/**
* Public access to the private safe decode/encode methods
*
* @param string $text the text to transform
* @param string $mode the transformation mode. either encode or decode
* @return string $text transformed by the given $mode
*/
public function transformText($text, $mode='encode') {
return $this->{"safe_$mode"}($text);
}
/**
* Utility function to parse the returned curl headers and store them in the
* class array variable.
*
* @param object $ch curl handle
* @param string $header the response headers
* @return string the length of the header
*/
private function curlHeader($ch, $header) {
$this->response['raw'] .= $header;
list($key, $value) = array_pad(explode(':', $header, 2), 2, null);
$key = trim($key);
$value = trim($value);
if ( ! isset($this->response['headers'][$key])) {
$this->response['headers'][$key] = $value;
} else {
if (!is_array($this->response['headers'][$key])) {
$this->response['headers'][$key] = array($this->response['headers'][$key]);
}
$this->response['headers'][$key][] = $value;
}
return strlen($header);
}
/**
* Utility function to parse the returned curl buffer and store them until
* an EOL is found. The buffer for curl is an undefined size so we need
* to collect the content until an EOL is found.
*
* This function calls the previously defined streaming callback method.
*
* @param object $ch curl handle
* @param string $data the current curl buffer
* @return int the length of the data string processed in this function
*/
private function curlWrite($ch, $data) {
$l = strlen($data);
if (strpos($data, $this->config['streaming_eol']) === false) {
$this->buffer .= $data;
return $l;
}
$buffered = explode($this->config['streaming_eol'], $data);
$content = $this->buffer . $buffered[0];
$this->metrics['tweets']++;
$this->metrics['bytes'] += strlen($content);
if ( ! is_callable($this->config['streaming_callback']))
return 0;
$metrics = $this->update_metrics();
$stop = call_user_func(
$this->config['streaming_callback'],
$content,
strlen($content),
$metrics
);
$this->buffer = $buffered[1];
if ($stop)
return 0;
return $l;
}
/**
* Makes a curl request. Takes no parameters as all should have been prepared
* by the request method
*
* the response data is stored in the class variable 'response'
*
* @return int the http response code for the request. 0 is returned if a connection could not be made
*/
private function curlit() {
$this->response['raw'] = '';
// method handling
switch ($this->method) {
case 'POST':
break;
default:
// GET, DELETE request so convert the parameters to a querystring
if ( ! empty($this->request_params)) {
foreach ($this->request_params as $k => $v) {
// Multipart params haven't been encoded yet.
// Not sure why you would do a multipart GET but anyway, here's the support for it
if ($this->config['multipart']) {
$params[] = $this->safe_encode($k) . '=' . $this->safe_encode($v);
} else {
$params[] = $k . '=' . $v;
}
}
$qs = implode('&', $params);
$this->url = strlen($qs) > 0 ? $this->url . '?' . $qs : $this->url;
$this->request_params = array();
}
break;
}
// configure curl
$c = curl_init();
curl_setopt_array($c, array(
CURLOPT_USERAGENT => $this->config['user_agent'],
CURLOPT_CONNECTTIMEOUT => $this->config['curl_connecttimeout'],
CURLOPT_TIMEOUT => $this->config['curl_timeout'],
CURLOPT_RETURNTRANSFER => true,
CURLOPT_SSL_VERIFYPEER => $this->config['curl_ssl_verifypeer'],
CURLOPT_SSL_VERIFYHOST => $this->config['curl_ssl_verifyhost'],
CURLOPT_FOLLOWLOCATION => $this->config['curl_followlocation'],
CURLOPT_PROXY => $this->config['curl_proxy'],
CURLOPT_ENCODING => $this->config['curl_encoding'],
CURLOPT_URL => $this->url,
// process the headers
CURLOPT_HEADERFUNCTION => array($this, 'curlHeader'),
CURLOPT_HEADER => false,
CURLINFO_HEADER_OUT => true,
));
if ($this->config['curl_cainfo'] !== false)
curl_setopt($c, CURLOPT_CAINFO, $this->config['curl_cainfo']);
if ($this->config['curl_capath'] !== false)
curl_setopt($c, CURLOPT_CAPATH, $this->config['curl_capath']);
if ($this->config['curl_proxyuserpwd'] !== false)
curl_setopt($c, CURLOPT_PROXYUSERPWD, $this->config['curl_proxyuserpwd']);
if ($this->config['is_streaming']) {
// process the body
$this->response['content-length'] = 0;
curl_setopt($c, CURLOPT_TIMEOUT, 0);
curl_setopt($c, CURLOPT_WRITEFUNCTION, array($this, 'curlWrite'));
}
switch ($this->method) {
case 'GET':
break;
case 'POST':
curl_setopt($c, CURLOPT_POST, true);
curl_setopt($c, CURLOPT_POSTFIELDS, $this->request_params);
break;
default:
curl_setopt($c, CURLOPT_CUSTOMREQUEST, $this->method);
}
if ( ! empty($this->request_params) ) {
// if not doing multipart we need to implode the parameters
if ( ! $this->config['multipart'] ) {
foreach ($this->request_params as $k => $v) {
$ps[] = "{$k}={$v}";
}
$this->request_params = implode('&', $ps);
}
curl_setopt($c, CURLOPT_POSTFIELDS, $this->request_params);
}
if ( ! empty($this->headers)) {
foreach ($this->headers as $k => $v) {
$headers[] = trim($k . ': ' . $v);
}
curl_setopt($c, CURLOPT_HTTPHEADER, $headers);
}
if (isset($this->config['prevent_request']) && (true == $this->config['prevent_request']))
return 0;
// do it!
$response = curl_exec($c);
$code = curl_getinfo($c, CURLINFO_HTTP_CODE);
$info = curl_getinfo($c);
$error = curl_error($c);
$errno = curl_errno($c);
curl_close($c);
// store the response
$this->response['code'] = $code;
$this->response['response'] = $response;
$this->response['info'] = $info;
$this->response['error'] = $error;
$this->response['errno'] = $errno;
if (!isset($this->response['raw'])) {
$this->response['raw'] = '';
}
$this->response['raw'] .= $response;
return $code;
}
}

View file

@ -0,0 +1,280 @@
<?php
/**
* tmhUtilities
*
* Helpful utility and Twitter formatting functions
*
* @author themattharris
* @version 0.5.0
*
* 04 September 2012
*/
class tmhUtilities {
const VERSION = '0.5.0';
/**
* Entifies the tweet using the given entities element.
* Deprecated.
* You should instead use entify_with_options.
*
* @param array $tweet the json converted to normalised array
* @param array $replacements if specified, the entities and their replacements will be stored to this variable
* @return the tweet text with entities replaced with hyperlinks
*/
public static function entify($tweet, &$replacements=array()) {
return tmhUtilities::entify_with_options($tweet, array(), $replacements);
}
/**
* Entifies the tweet using the given entities element, using the provided
* options.
*
* @param array $tweet the json converted to normalised array
* @param array $options settings to be used when rendering the entities
* @param array $replacements if specified, the entities and their replacements will be stored to this variable
* @return the tweet text with entities replaced with hyperlinks
*/
public static function entify_with_options($tweet, $options=array(), &$replacements=array()) {
$default_opts = array(
'encoding' => 'UTF-8',
'target' => '',
);
$opts = array_merge($default_opts, $options);
$encoding = mb_internal_encoding();
mb_internal_encoding($opts['encoding']);
$keys = array();
$is_retweet = false;
if (isset($tweet['retweeted_status'])) {
$tweet = $tweet['retweeted_status'];
$is_retweet = true;
}
if (!isset($tweet['entities'])) {
return $tweet['text'];
}
$target = (!empty($opts['target'])) ? ' target="'.$opts['target'].'"' : '';
// prepare the entities
foreach ($tweet['entities'] as $type => $things) {
foreach ($things as $entity => $value) {
$tweet_link = "<a href=\"https://twitter.com/{$tweet['user']['screen_name']}/statuses/{$tweet['id']}\"{$target}>{$tweet['created_at']}</a>";
switch ($type) {
case 'hashtags':
$href = "<a href=\"https://twitter.com/search?q=%23{$value['text']}\"{$target}>#{$value['text']}</a>";
break;
case 'user_mentions':
$href = "@<a href=\"https://twitter.com/{$value['screen_name']}\" title=\"{$value['name']}\"{$target}>{$value['screen_name']}</a>";
break;
case 'urls':
case 'media':
$url = empty($value['expanded_url']) ? $value['url'] : $value['expanded_url'];
$display = isset($value['display_url']) ? $value['display_url'] : str_replace('http://', '', $url);
// Not all pages are served in UTF-8 so you may need to do this ...
$display = urldecode(str_replace('%E2%80%A6', '&hellip;', urlencode($display)));
$href = "<a href=\"{$value['url']}\"{$target}>{$display}</a>";
break;
}
$keys[$value['indices']['0']] = mb_substr(
$tweet['text'],
$value['indices']['0'],
$value['indices']['1'] - $value['indices']['0']
);
$replacements[$value['indices']['0']] = $href;
}
}
ksort($replacements);
$replacements = array_reverse($replacements, true);
$entified_tweet = $tweet['text'];
foreach ($replacements as $k => $v) {
$entified_tweet = mb_substr($entified_tweet, 0, $k).$v.mb_substr($entified_tweet, $k + strlen($keys[$k]));
}
$replacements = array(
'replacements' => $replacements,
'keys' => $keys
);
mb_internal_encoding($encoding);
return $entified_tweet;
}
/**
* Returns the current URL. This is instead of PHP_SELF which is unsafe
*
* @param bool $dropqs whether to drop the querystring or not. Default true
* @return string the current URL
*/
public static function php_self($dropqs=true) {
$protocol = 'http';
if (isset($_SERVER['HTTPS']) && strtolower($_SERVER['HTTPS']) == 'on') {
$protocol = 'https';
} elseif (isset($_SERVER['SERVER_PORT']) && ($_SERVER['SERVER_PORT'] == '443')) {
$protocol = 'https';
}
$url = sprintf('%s://%s%s',
$protocol,
$_SERVER['SERVER_NAME'],
$_SERVER['REQUEST_URI']
);
$parts = parse_url($url);
$port = $_SERVER['SERVER_PORT'];
$scheme = $parts['scheme'];
$host = $parts['host'];
$path = @$parts['path'];
$qs = @$parts['query'];
$port or $port = ($scheme == 'https') ? '443' : '80';
if (($scheme == 'https' && $port != '443')
|| ($scheme == 'http' && $port != '80')) {
$host = "$host:$port";
}
$url = "$scheme://$host$path";
if ( ! $dropqs)
return "{$url}?{$qs}";
else
return $url;
}
public static function is_cli() {
return (PHP_SAPI == 'cli' && empty($_SERVER['REMOTE_ADDR']));
}
/**
* Debug function for printing the content of an object
*
* @param mixes $obj
*/
public static function pr($obj) {
if (!self::is_cli())
echo '<pre style="word-wrap: break-word">';
if ( is_object($obj) )
print_r($obj);
elseif ( is_array($obj) )
print_r($obj);
else
echo $obj;
if (!self::is_cli())
echo '</pre>';
}
/**
* Make an HTTP request using this library. This method is different to 'request'
* because on a 401 error it will retry the request.
*
* When a 401 error is returned it is possible the timestamp of the client is
* too different to that of the API server. In this situation it is recommended
* the request is retried with the OAuth timestamp set to the same as the API
* server. This method will automatically try that technique.
*
* This method doesn't return anything. Instead the response should be
* inspected directly.
*
* @param string $method the HTTP method being used. e.g. POST, GET, HEAD etc
* @param string $url the request URL without query string parameters
* @param array $params the request parameters as an array of key=value pairs
* @param string $useauth whether to use authentication when making the request. Default true.
* @param string $multipart whether this request contains multipart data. Default false
*/
public static function auto_fix_time_request($tmhOAuth, $method, $url, $params=array(), $useauth=true, $multipart=false) {
$tmhOAuth->request($method, $url, $params, $useauth, $multipart);
// if we're not doing auth the timestamp isn't important
if ( ! $useauth)
return;
// some error that isn't a 401
if ($tmhOAuth->response['code'] != 401)
return;
// some error that is a 401 but isn't because the OAuth token and signature are incorrect
// TODO: this check is horrid but helps avoid requesting twice when the username and password are wrong
if (stripos($tmhOAuth->response['response'], 'password') !== false)
return;
// force the timestamp to be the same as the Twitter servers, and re-request
$tmhOAuth->auto_fixed_time = true;
$tmhOAuth->config['force_timestamp'] = true;
$tmhOAuth->config['timestamp'] = strtotime($tmhOAuth->response['headers']['date']);
return $tmhOAuth->request($method, $url, $params, $useauth, $multipart);
}
/**
* Asks the user for input and returns the line they enter
*
* @param string $prompt the text to display to the user
* @return the text entered by the user
*/
public static function read_input($prompt) {
echo $prompt;
$handle = fopen("php://stdin","r");
$data = fgets($handle);
return trim($data);
}
/**
* Get a password from the shell.
*
* This function works on *nix systems only and requires shell_exec and stty.
*
* @param boolean $stars Wether or not to output stars for given characters
* @return string
* @url http://www.dasprids.de/blog/2008/08/22/getting-a-password-hidden-from-stdin-with-php-cli
*/
public static function read_password($prompt, $stars=false) {
echo $prompt;
$style = shell_exec('stty -g');
if ($stars === false) {
shell_exec('stty -echo');
$password = rtrim(fgets(STDIN), "\n");
} else {
shell_exec('stty -icanon -echo min 1 time 0');
$password = '';
while (true) :
$char = fgetc(STDIN);
if ($char === "\n") :
break;
elseif (ord($char) === 127) :
if (strlen($password) > 0) {
fwrite(STDOUT, "\x08 \x08");
$password = substr($password, 0, -1);
}
else
fwrite(STDOUT, "*");
$password .= $char;
endif;
endwhile;
}
// Reset
shell_exec('stty ' . $style);
echo PHP_EOL;
return $password;
}
/**
* Check if one string ends with another
*
* @param string $haystack the string to check inside of
* @param string $needle the string to check $haystack ends with
* @return true if $haystack ends with $needle, false otherwise
*/
public static function endswith($haystack, $needle) {
$haylen = strlen($haystack);
$needlelen = strlen($needle);
if ($needlelen > $haylen)
return false;
return substr_compare($haystack, $needle, -$needlelen) === 0;
}
}