| 12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598 |
- <?php
- /**
- * @file
- * Mollom client class.
- *
- * @license MIT|GNU GPL v2
- * See LICENSE-MIT.txt or LICENSE-GPL.txt shipped with this library.
- */
- /**
- * The base class for Mollom client implementations.
- */
- abstract class Mollom {
- /**
- * The Mollom API version, used in HTTP requests.
- */
- const API_VERSION = 'v1';
- /**
- * Network communication failure code: Server could not be reached.
- *
- * @see MollomNetworkException
- */
- const NETWORK_ERROR = 900;
- /**
- * Server communication failure code: Unexpected server response.
- *
- * Using the 5xx HTTP status code range, but not re-using an existing HTTP
- * code to prevent bogus bug reports. 511 is the closest comparable code
- * 501 (Not Implemented) plus 10.
- *
- * @see MollomResponseException
- */
- const RESPONSE_ERROR = 511;
- /**
- * Client communication failure code: Bad request.
- *
- * Used in case of a too large system time offset from UTC.
- *
- * @see MollomBadRequestException
- * @see Mollom::TIME_OFFSET_MAX
- */
- const REQUEST_ERROR = 400;
- /**
- * Client communication failure code: Authentication error.
- *
- * @see MollomAuthenticationException
- */
- const AUTH_ERROR = 401;
- /**
- * Maximum allowed time offset from UTC in seconds allowed for the client's local time.
- *
- * @see Mollom::handleRequest()
- * @see MollomBadRequestException
- *
- * @var integer
- */
- const TIME_OFFSET_MAX = 300;
- /**
- * List of ISO 639-1 language codes supported by Mollom.
- *
- * If your application has a predefined list of ISO 639-1 languages already,
- * intersect your list with this via strtok($langcode, '-').
- *
- * Should be a constant, but PHP does not support arrays for constants.
- */
- public static $LANGUAGES_SUPPORTED = array(
- 'af',
- 'ar',
- 'bg',
- 'bn',
- 'cs',
- 'da',
- 'de',
- 'el',
- 'en',
- 'es',
- 'et',
- 'fa',
- 'fi',
- 'fr',
- 'gu',
- 'he',
- 'hi',
- 'hr',
- 'hu',
- 'id',
- 'it',
- 'ja',
- 'kn',
- 'ko',
- 'lt',
- 'lv',
- 'mk',
- 'ml',
- 'mr',
- 'ne',
- 'nl',
- 'no',
- 'pa',
- 'pl',
- 'pt',
- 'ro',
- 'ru',
- 'sk',
- 'sl',
- 'so',
- 'sq',
- 'sv',
- 'sw',
- 'ta',
- 'te',
- 'th',
- 'tl',
- 'tr',
- 'uk',
- 'ur',
- 'vi',
- 'zh-cn',
- 'zh-tw',
- );
- /**
- * The public Mollom API key to use for request authentication.
- *
- * @var string
- */
- public $publicKey = '';
- /**
- * The private Mollom API key to use for request authentication.
- *
- * @var string
- */
- public $privateKey = '';
- /**
- * OAuth protocol parameter strategy to use.
- *
- * Either 'header' for HTTP headers (preferred), or 'query' for GET/POST
- * request parameters.
- *
- * @see Mollom::addAuthentication()
- *
- * @var string
- */
- public $oAuthStrategy = 'header';
- /**
- * The Mollom server to communicate with, without protocol.
- *
- * @var string
- */
- public $server = 'rest.mollom.com';
- /**
- * Maximum number of attempts for a request to a Mollom server.
- *
- * @see Mollom::query()
- * @see Mollom::$requestTimeout
- *
- * @var integer
- */
- public $requestMaxAttempts = 2;
- /**
- * Seconds in which a request to a Mollom server times out.
- *
- * Mollom servers usually respond within a few milliseconds. However, if a
- * server is under very high load, or in case of networking issues, a response
- * might take longer. A maximum response time of 3 seconds ensures that the
- * client waits long enough in edge-cases, but also not too long in case a
- * particular server is unreachable.
- *
- * The timeout applies per request. Mollom::query() will retry a request until
- * it reaches Mollom::$requestMaxAttempts. With the default values, a Mollom
- * API call has a total timeout of 6 seconds in case of a server failure.
- *
- * @see Mollom::request()
- * @see Mollom::$requestMaxAttempts
- *
- * @var float
- */
- public $requestTimeout = 3.0;
- /**
- * The status code of the last server response, or TRUE if the request succeeded.
- *
- * If not TRUE, then the value is either one of
- * - Mollom::NETWORK_ERROR
- * - Mollom::RESPONSE_ERROR
- * - Mollom::REQUEST_ERROR
- * - Mollom::AUTH_ERROR
- * or the actual HTTP status code returned by the server.
- *
- * @var int|bool|null
- */
- public $lastResponseCode = NULL;
- /**
- * The amount of items contained in a list response.
- *
- * @var int
- */
- public $listCount = NULL;
- /**
- * The current offset of a list response.
- *
- * @var int
- */
- public $listOffset = 0;
- /**
- * The total amount of items contained in a list response.
- *
- * @var int
- */
- public $listTotal = NULL;
- /**
- * Flag indicating whether to invoke Mollom::writeLog() in Mollom::query().
- *
- * @var bool
- */
- public $writeLog = TRUE;
- /**
- * A list of logged requests.
- *
- * @var array
- */
- public $log = array();
- function __construct() {
- $this->publicKey = $this->loadConfiguration('publicKey');
- $this->privateKey = $this->loadConfiguration('privateKey');
- }
- /**
- * Loads a configuration value from client-side storage.
- *
- * @param string $name
- * The configuration setting name to load, one of:
- * - publicKey: The public API key for Mollom authentication.
- * - privateKey: The private API key for Mollom authentication.
- * - expectedLanguages: List of expected language codes for site content.
- *
- * @return mixed
- * The stored configuration value or NULL if there is none.
- *
- * @see Mollom::saveConfiguration()
- * @see Mollom::deleteConfiguration()
- */
- abstract protected function loadConfiguration($name);
- /**
- * Saves a configuration value to client-side storage.
- *
- * @param string $name
- * The configuration setting name to save.
- * @param mixed $value
- * The value to save.
- *
- * @see Mollom::loadConfiguration()
- * @see Mollom::deleteConfiguration()
- */
- abstract protected function saveConfiguration($name, $value);
- /**
- * Deletes a configuration value from client-side storage.
- *
- * @param string $name
- * The configuration setting name to delete.
- *
- * @see Mollom::loadConfiguration()
- * @see Mollom::saveConfiguration()
- */
- abstract protected function deleteConfiguration($name);
- /**
- * Returns platform and version information about the Mollom client.
- *
- * Retrieves platform and Mollom client version information to send along to
- * Mollom when verifying keys.
- *
- * This information is used to speed up support requests and technical
- * inquiries. The data may also be aggregated to help the Mollom staff to make
- * decisions on new features or the necessity of back-porting improved
- * functionality to older versions.
- *
- * @return array
- * An associative array containing:
- * - platformName: The name of the platform/distribution; e.g., "Drupal".
- * - platformVersion: The version of platform/distribution; e.g., "7.0".
- * - clientName: The official Mollom client name; e.g., "Mollom".
- * - clientVersion: The version of the Mollom client; e.g., "7.x-1.0".
- */
- abstract public function getClientInformation();
- /**
- * Writes log messages to a permanent location/storage.
- *
- * Not abstract, since clients are not required to write log messages.
- * However, all clients should permanently store the log messages, as it
- * dramatically improves resolution of support requests filed by users.
- * The log may be written and appended to a file (via file_put_contents()),
- * syslog (on *nix-based systems), or a database.
- *
- * @see Mollom::log
- */
- public function writeLog() {
- // After writing log messages, empty the log.
- $this->purgeLog();
- }
- /**
- * Purges captured log messages.
- *
- * @see Mollom::writeLog()
- */
- final public function purgeLog() {
- $this->log = array();
- }
- /**
- * Send or retrieve data from/to Mollom.
- *
- * @param string $method
- * The HTTP method to use; i.e., 'GET', 'POST', or 'PUT'.
- * @param string $path
- * The REST path/resource to request; e.g., 'site/1a2b3c'.
- * @param array $data
- * An associative array of query parameters to send with the request.
- * @param array $expected
- * (optional) An element that is expected in the response, denoted as a list
- * of parent element keys to the element and the element key itself; e.g., a
- * value of array('content', 'id') expects $response['content']['id'] to
- * exist in the response.
- *
- * @return mixed
- * On success, the parsed response body. On failure, the last response code,
- * in case it is a known one; otherwise Mollom::NETWORK_ERROR.
- *
- * @see Mollom::handleRequest()
- * @see Mollom::request()
- */
- public function query($method, $path, array $data = array(), array $expected = array()) {
- // Reset list response properties.
- $this->listCount = NULL;
- $this->listOffset = 0;
- $this->listTotal = NULL;
- // Send the request to the server.
- $server = 'http://' . $this->server;
- $max_attempts = $this->requestMaxAttempts;
- while ($max_attempts-- > 0) {
- try {
- $result = $this->handleRequest($method, $server, $path, $data, $expected);
- }
- catch (MollomBadRequestException $e) {
- // Irrecoverable error, so don't try further.
- break;
- }
- catch (MollomAuthenticationException $e) {
- // Irrecoverable error, so don't try further.
- break;
- }
- catch (MollomException $e) {
- // If the requested resource does not exist, or the request was
- // malformed, there is no point in trying further.
- if ($e->getCode() >= 400 && $e->getCode() < 500) {
- break;
- }
- }
- // Unless we have a positive result, try again.
- if ($this->lastResponseCode === TRUE) {
- break;
- }
- }
- // Write all captured log messages.
- if ($this->writeLog) {
- $this->writeLog();
- }
- // If there is a result and the last request succeeded, return the result to
- // the caller.
- if (isset($result) && $this->lastResponseCode === TRUE) {
- // Generically handle the special case of 'list' responses.
- if (isset($result['list']) && is_array($result)) {
- // Assign list meta properties to corresponding class properties.
- $this->listCount = (int) $result['listCount'];
- $this->listOffset = (int) $result['listOffset'];
- $this->listTotal = (int) $result['listTotal'];
- // If there is only one item, parseXML() is not able to detect it as a
- // list and will return a named key for the value. Ensure an indexed
- // array is returned.
- if (is_array($result['list'])) {
- $result['list'] = array_values($result['list']);
- }
- // In XML, the 'list' element is parsed as a string when the list is
- // empty.
- else {
- $result['list'] = array();
- }
- }
- return $result;
- }
- // If the last request succeeded but there was a unexpected response, return
- // the error code.
- if ($this->lastResponseCode === self::RESPONSE_ERROR) {
- return $this->lastResponseCode;
- }
- // Return an authentication error, which may require special client-side
- // processing.
- if ($this->lastResponseCode === self::AUTH_ERROR) {
- return $this->lastResponseCode;
- }
- // Return a request error, which always requires client-side measures to
- // resolve the problem.
- if ($this->lastResponseCode >= self::REQUEST_ERROR && $this->lastResponseCode < 500) {
- return $this->lastResponseCode;
- }
- // In case of any kind of HTTP error (0 [invalid-address],
- // -1002 [bad URI], etc), return a generic NETWORK_ERROR.
- return self::NETWORK_ERROR;
- }
- /**
- * Prepares a HTTP request to a Mollom server and processes the response.
- *
- * @param string $method
- * The HTTP method to use; i.e., 'GET' or 'POST'.
- * @param string $server
- * The base URL of the server to perform the request against; e.g.,
- * 'http://foo.mollom.com'.
- * @param string $path
- * The REST path/resource to request; e.g., 'site/1a2b3c'.
- * @param array $data
- * An associative array of query parameters to send with the request.
- * @param array $expected
- * (optional) An element that is expected in the response, denoted as a list
- * of parent element keys to the element and the element key itself; e.g., a
- * value of array('content', 'id') expects $response['content']['id'] to
- * exist in the response. If the expected element does not exist, a
- * MollomResponseException is thrown.
- *
- * @return array
- * An associative array representing the parsed response body on success. On
- * any failure, a MollomException is thrown. Additionally,
- * Mollom::lastResponseCode is set to TRUE on success, or to the Mollom or
- * HTTP status code on failure.
- *
- * @throws MollomNetworkException
- * @throws MollomAuthenticationException
- * @throws MollomResponseException
- * @throws MollomException
- *
- * @see Mollom::lastResponseCode
- * @see Mollom::query()
- * @see Mollom::httpBuildQuery()
- * @see Mollom::parseXML()
- * @see json_decode()
- */
- protected function handleRequest($method, $server, $path, $data, $expected = array()) {
- $time_start = microtime(TRUE);
- if (!empty($this->publicKey) && !empty($this->privateKey)) {
- // @todo Move into class property.
- $headers['Accept'] = 'application/xml, application/json;q=0.8, */*;q=0.5';
- if ($method == 'POST') {
- $headers['Content-Type'] = 'application/x-www-form-urlencoded';
- }
- // Append API version to REST endpoint.
- $server .= '/' . self::API_VERSION;
- // Add OAuth request parameters.
- $query = $this->addAuthentication($method, $server, $path, $data, $headers);
- $response = $this->request($method, $server, $path, $query, $headers);
- // Determine basic error condition based on HTTP status code.
- $response->isError = ($response->code < 200 || $response->code >= 300);
- }
- else {
- $headers = array();
- $response = new stdClass;
- $response->code = self::AUTH_ERROR;
- $response->message = 'Missing API keys.';
- $response->isError = TRUE;
- $response->body = NULL;
- }
- $time_stop = microtime(TRUE);
- // Parse the response body string into an array.
- $response->data = array();
- if (isset($response->body) && isset($response->headers['content-type'])) {
- if (strstr($response->headers['content-type'], 'application/json')) {
- $response->data = json_decode($response->body, TRUE);
- }
- elseif (strstr($response->headers['content-type'], 'application/xml')) {
- try {
- $response->elements = new SimpleXmlIterator($response->body);
- $response->data = $this->parseXML($response->elements);
- }
- catch (Exception $e) {
- // Invalid XML so just set the data back to blank.
- $response->data = array();
- }
- }
- }
- // A 'code' in the response has precedence, regardless of a possibly
- // positive HTTP status code.
- if (isset($response->data['code']) && $response->data['code'] != 200) {
- $response->isError = TRUE;
- // Replace HTTP status code with 'code' from response.
- $response->code = (int) $response->data['code'];
- // If there is no HTTP status message, take over 'message' from response,
- // if any.
- if (!isset($response->message) && isset($response->data['message'])) {
- $response->message = $response->data['message'];
- }
- }
- // Verify that the expected (parent) element exists in the response.
- // There is no notion of DTD/schema in JSON. A dedicated class for each
- // response would be a large overhead. Therefore, response validation is
- // limited to checking for one expected element (in a nested array of
- // variable depth).
- if (!$response->isError && !empty($response->data) && !empty($expected)) {
- $ref = &$response->data;
- $parent = reset($expected);
- while ($parent !== FALSE && is_array($ref) && array_key_exists($parent, $ref)) {
- $ref = &$ref[$parent];
- $parent = next($expected);
- }
- // Only if $parent is FALSE we have reached the last expected key.
- if ($parent !== FALSE) {
- $response->isError = TRUE;
- $response->code = self::RESPONSE_ERROR;
- $response->message = 'Unexpected server response.';
- }
- }
- $request_info = array(
- 'request' => $method . ' ' . $server . '/' . $path,
- 'headers' => $headers,
- 'data' => $data,
- 'response_code' => $response->code,
- 'response_message' => $response->message,
- 'response' => !empty($response->data) ? $response->data : $response->body,
- 'response_time' => $time_stop - $time_start,
- );
- if ($response->isError) {
- if ($response->code <= 0) {
- throw new MollomNetworkException('Network error.', self::NETWORK_ERROR, NULL, $this, $request_info);
- }
- if ($response->code == self::AUTH_ERROR) {
- // Check whether authentication failed due to a too large time offset.
- if (isset($response->headers['date'])) {
- // Parse the 'Date' HTTP header in the response into a UNIX timestamp;
- // strtotime() normally performs poorly on date operations, but in
- // this case, there is a standardized date format and timezone
- // conversion, so it is safe to use.
- $offset = abs(time() - strtotime($response->headers['date']));
- // The abs() above turns a negative offset into an absolute integer
- // value, so the difference is always positive.
- if ($offset > self::TIME_OFFSET_MAX) {
- throw new MollomBadRequestException(sprintf('Invalid client system time: Too large offset from UTC: %s seconds.', $offset), self::REQUEST_ERROR, NULL, $this, $request_info);
- }
- }
- throw new MollomAuthenticationException('Invalid authentication.', self::AUTH_ERROR, NULL, $this, $request_info);
- }
- if ($response->code == self::RESPONSE_ERROR || $response->code >= 500) {
- throw new MollomResponseException($response->message, $response->code, NULL, $this, $request_info);
- }
- throw new MollomException($response->message, $response->code, NULL, $this, $request_info);
- }
- else {
- $this->lastResponseCode = TRUE;
- // No message is logged in case of success.
- $this->log[] = array(
- 'severity' => 'debug',
- ) + $request_info;
- return $response->data;
- }
- }
- /**
- * Returns GET/POST request parameters after adding 2-legged OAuth authentication parameters.
- *
- * All available OAuth libraries for PHP are needlessly over-engineered,
- * poorly written and maintained, contain code for server implementations, and
- * would be mostly overhead for the simple purpose of 2-legged authentication.
- * Therefore, this simple function implements OAuth request signing based on
- * latest RFC 5849, using the public API key as client/consumer key and the
- * private API key as client secret.
- *
- * Make sure that your server time is synchronized with the world clocks, and
- * that you do not share your private key with anyone else.
- *
- * @param string $method
- * The HTTP method to use; i.e., 'GET' or 'POST'.
- * @param string $server
- * The base URL of the server to perform the request against; e.g.,
- * 'http://foo.mollom.com'.
- * @param string $path
- * The REST path/resource to request; e.g., 'site/1a2b3c'.
- * @param array $data
- * An associative array of query parameters to send with the request. Passed
- * by reference.
- * @param array $headers
- * An associative array of HTTP request headers to send along with the
- * request. Passed by reference.
- *
- * @return string
- * A string containing encoded request parameters derived of $data to use as
- * GET query string or POST body data.
- *
- * @see http://tools.ietf.org/html/rfc5849
- * @see Mollom::oAuthStrategy
- */
- public function addAuthentication($method, $server, $path, &$data, &$headers) {
- $oauth['oauth_consumer_key'] = $this->publicKey;
- $oauth['oauth_version'] = '1.0';
- // Random string; must be unique across all requests with the same
- // timestamp, client credentials, and token combinations. (3.3)
- $oauth['oauth_nonce'] = md5(microtime() . mt_rand());
- // Number of seconds since January 1, 1970 00:00:00 GMT. (3.3)
- $oauth['oauth_timestamp'] = time();
- $oauth['oauth_signature_method'] = 'HMAC-SHA1';
- // Prepare the request query parameters to return (and pass on to request())
- // and the query parameters to include in the OAuth signature base string.
- // Note that Mollom::httpBuildQuery() sorts parameters already.
- if ($this->oAuthStrategy == 'header') {
- $query = $this->httpBuildQuery($data);
- $oauth_query = $this->httpBuildQuery($oauth + $data);
- }
- elseif ($this->oAuthStrategy == 'query') {
- $data += $oauth;
- $oauth_query = $query = $this->httpBuildQuery($data);
- }
- // Skip authentication entirely; required to create testing site record when
- // running tests.
- else {
- $query = $this->httpBuildQuery($data);
- $oauth_query = '';
- }
- // Generate the signature. (3.4.1)
- // Base string to sign is compound of
- // - uppercase HTTP method
- // - rawurlencoded lowercase server URI (without default ports 80/443),
- // including path in natural case
- // - encoded, sorted, and lastly (double-)rawurlencoded request parameters,
- // having GET query parameters first, and POST body data parameters last
- // (if any)
- // delimited by "&". (3.4.1.1)
- $base_string = implode('&', array(
- $method,
- self::rawurlencode($server . '/' . $path),
- self::rawurlencode($oauth_query),
- ));
- // Key is unconditionally compound of client secret and token secret, even
- // if empty. (3.4.2)
- $key = self::rawurlencode($this->privateKey) . '&' . '';
- $oauth['oauth_signature'] = base64_encode(hash_hmac('sha1', $base_string, $key, TRUE));
- // Add the OAuth protocol parameters as HTTP header, or append the signature
- // to query parameters.
- if ($this->oAuthStrategy == 'header') {
- foreach ($oauth as $key => $value) {
- $oauth[$key] = $key . '="' . self::rawurlencode($value) . '"';
- }
- // Header parameters are joined and delimited by comma. (3.5.1)
- $headers['Authorization'] = 'OAuth ' . implode(', ', $oauth);
- }
- elseif ($this->oAuthStrategy == 'query') {
- $oauth['oauth_signature'] = rawurlencode($oauth['oauth_signature']);
- $data['oauth_signature'] = $oauth['oauth_signature'];
- $query .= '&oauth_signature=' . $oauth['oauth_signature'];
- }
- return $query;
- }
- /**
- * Encodes a URL compliant with OAuth RFC 5849.
- *
- * @param string $value
- * The URL string to be encoded.
- *
- * @return string
- * The rawurlencode()d URL, containing string literals for "~" (tildes) and
- * " " (spaces). (RFC 5849 3.4.1.3.1 and 3.6)
- */
- public static function rawurlencode($value) {
- return strtr(rawurlencode($value), array('%7E' => '~', '+' => ' '));
- }
- /**
- * Performs a HTTP request to a Mollom server.
- *
- * @param string $method
- * The HTTP method to use; i.e., 'GET', 'POST', or 'PUT'.
- * @param string $server
- * The base URL of the server to perform the request against; e.g.,
- * 'http://foo.mollom.com'.
- * @param string $path
- * The REST path/resource to request; e.g., 'site/1a2b3c'.
- * @param string $query
- * (optional) A prepared string of HTTP query parameters to append to $path
- * for $method GET, or to use as request body for $method POST.
- * @param array $headers
- * (optional) An associative array of HTTP request headers to send along
- * with the request.
- *
- * @return object
- * An object containing response properties:
- * - code: The HTTP status code as integer returned by the Mollom server.
- * - message: The HTTP status message string returned by the Mollom server,
- * or NULL if there is no message.
- * - headers: An associative array containing the HTTP response headers
- * returned by the Mollom server. Header name keys are expected to be
- * lower-case; i.e., "content-type" instead of "Content-Type".
- * - body: The HTTP response body string returned by the Mollom server, or
- * NULL if there is none.
- *
- * @see Mollom::handleRequest()
- */
- abstract protected function request($method, $server, $path, $query = NULL, array $headers = array());
- /**
- * Converts a SimpleXMLIterator structure into an associative array.
- *
- * Used to parse an XML response from Mollom servers into a PHP array. For
- * example:
- * @code
- * $elements = new SimpleXmlIterator($response_body);
- * $parsed_response = $this->parseXML($elements);
- * @endcode
- *
- * @param SimpleXMLIterator $sxi
- * A SimpleXMLIterator structure of the server response body.
- *
- * @return array
- * An associative, possibly multidimensional array.
- */
- public static function parseXML(SimpleXMLIterator $sxi) {
- $a = array();
- $remove = array();
- for ($sxi->rewind(); $sxi->valid(); $sxi->next()) {
- $key = $sxi->key();
- // Recurse into non-scalar values.
- if ($sxi->hasChildren()) {
- $value = self::parseXML($sxi->current());
- }
- // Use a simple key/value pair for scalar values.
- else {
- $value = strval($sxi->current());
- }
- if (!isset($a[$key])) {
- $a[$key] = $value;
- }
- // Convert already existing keys into indexed keys, retaining other
- // existing keys in the array; i.e., two or more XML elements of the
- // same name and on the same level.
- // Note that this XML to PHP array conversion does not support multiple
- // different elements that each appear multiple times.
- else {
- // First time we reach here, convert the existing keyed item. Do not
- // remove $key, so we enter this path again.
- if (!isset($remove[$key])) {
- $a[] = $a[$key];
- // Mark $key for removal.
- $remove[$key] = $key;
- }
- // Add the new item.
- $a[] = $value;
- }
- }
- // Lastly, remove named keys that have been converted to indexed keys.
- foreach ($remove as $key) {
- unset($a[$key]);
- }
- return $a;
- }
- /**
- * Builds an RFC-compliant, rawurlencoded query string.
- *
- * PHP did a design decision to only support HTTP query parameters in the form
- * of foo[]=1&foo[]=2, primarily for its built-in and automated conversion to
- * PHP arrays. Other platforms (including the Mollom backend) do not support
- * this syntax and expect multiple parameters to be in the form of
- * foo=1&foo=2.
- *
- * @see http_build_query()
- * @see http://en.wikipedia.org/wiki/Query_string
- * @see http://tools.ietf.org/html/rfc3986#section-3.4
- *
- * @param array $query
- * The query parameter array to be processed, e.g. $_GET.
- * @param string $parent
- * Internal use only. Used to build the $query array key for nested items.
- *
- * @return string
- * A rawurlencoded string which can be used as or appended to the URL query
- * string.
- *
- * @see Mollom::httpParseQuery()
- */
- public static function httpBuildQuery(array $query, $parent = '') {
- $params = array();
- foreach ($query as $key => $value) {
- // For indexed (unnamed) child array keys, use the same parameter name,
- // leading to param=foo¶m=bar instead of param[]=foo¶m[]=bar.
- if ($parent && is_int($key)) {
- $key = rawurlencode($parent);
- }
- else {
- $key = ($parent ? $parent . '[' . rawurlencode($key) . ']' : rawurlencode($key));
- }
- // Recurse into children.
- if (is_array($value)) {
- if (!empty($value)) {
- $params[] = self::httpBuildQuery($value, $key);
- }
- // An empty array means that the parameter accepts a list of values, but
- // the list is empty and no value should be assigned, so pass NULL.
- else {
- $params[] = $key . '=';
- }
- }
- // If a query parameter value is NULL, only append its key, followed by
- // "=" (3.4.1.3.2).
- elseif (!isset($value)) {
- $params[] = $key . '=';
- }
- else {
- $params[] = $key . '=' . rawurlencode($value);
- }
- }
- // Parameters are sorted by name, using ascending byte value ordering. If
- // two or more parameters share the same name, they are sorted by their
- // value. (3.4.1.3.2)
- sort($params, SORT_STRING);
- $result = implode('&', $params);
- // Prior to PHP 5.3.0, rawurlencode encoded tildes (~) as per RFC 1738.
- // Percent-encoded octets corresponding to unreserved characters can be
- // decoded at any time. For example, the octet corresponding to the tilde
- // ("~") character is often encoded as "%7E" by older URI processing
- // implementations; the "%7E" can be replaced by "~" without changing its
- // interpretation.
- // @see http://php.net/manual/en/function.rawurlencode.php
- // @see http://tools.ietf.org/html/rfc3986#section-2.3
- $result = str_replace('%7E', '~', $result);
- return $result;
- }
- /**
- * Parses an RFC-compliant, rawurlencoded query string.
- *
- * Mollom clients normally do not need this function, as they do not need to
- * process requests from a server - unless a client attempts to implement
- * client-side unit testing.
- *
- * @param string $query
- * The query parameter string to process, e.g. $_SERVER['QUERY_STRING']
- * (GET) or php://input (POST/PUT).
- *
- * @return array
- * A query parameter array parsed from $query.
- *
- * @see Mollom::httpBuildQuery()
- * @see parse_str()
- */
- public static function httpParseQuery($query) {
- if ($query === '') {
- return array();
- }
- // Explode parameters into arrays to check for duplicate names.
- $params = array();
- $seen = array();
- $duplicate = array();
- foreach (explode('&', $query) as $chunk) {
- $param = explode('=', $chunk, 2);
- if (isset($seen[$param[0]])) {
- $duplicate[$param[0]] = TRUE;
- }
- $seen[$param[0]] = TRUE;
- $params[] = $param;
- }
- // Implode back into a string.
- $query = '';
- foreach ($params as $param) {
- $query .= $param[0];
- if (isset($duplicate[$param[0]])) {
- $query .= '[]';
- }
- if (isset($param[1])) {
- $query .= '=' . $param[1];
- }
- $query .= '&';
- }
- // Parse query string as usual.
- parse_str($query, $result);
- return $result;
- }
- /**
- * Retrieves GET/HEAD or POST/PUT parameters of an inbound HTTP request.
- *
- * @return array
- * An array containing either GET/HEAD query string parameters or POST/PUT
- * post body parameters. Parameter parsing accounts for multiple request
- * parameters in non-PHP format; e.g., 'foo=one&foo=bar'.
- */
- public static function getServerParameters() {
- if ($_SERVER['REQUEST_METHOD'] == 'GET' || $_SERVER['REQUEST_METHOD'] == 'HEAD') {
- $data = self::httpParseQuery($_SERVER['QUERY_STRING']);
- }
- elseif ($_SERVER['REQUEST_METHOD'] == 'POST' || $_SERVER['REQUEST_METHOD'] == 'PUT') {
- $data = self::httpParseQuery(file_get_contents('php://input'));
- }
- return $data;
- }
- /**
- * Retrieves the OAuth Authorization header of an inbound HTTP request.
- *
- * @return array
- * An array containing all key/value pairs extracted out of the
- * 'Authorization' HTTP header, if any.
- */
- public static function getServerAuthentication() {
- $header = array();
- // PHP as Apache module provides a SAPI function.
- // PHP 5.4+ enables getallheaders() also for FastCGI.
- if (function_exists('getallheaders')) {
- $headers = getallheaders();
- if (isset($headers['Authorization'])) {
- $input = $headers['Authorization'];
- }
- }
- // PHP as CGI with server/.htaccess configuration (e.g., via mod_rewrite)
- // may transfer/forward HTTP request data into server variables.
- elseif (isset($_SERVER['HTTP_AUTHORIZATION'])) {
- $input = $_SERVER['HTTP_AUTHORIZATION'];
- }
- // PHP as CGI may provide HTTP request data as environment variables.
- elseif (isset($_ENV['HTTP_AUTHORIZATION'])) {
- $input = $_ENV['HTTP_AUTHORIZATION'];
- }
- if (isset($input)) {
- preg_match_all('@([^, =]+)="([^"]*)"@', $input, $header);
- if (!empty($header[1]) && !empty($header[2])) {
- $header = array_combine($header[1], $header[2]);
- }
- }
- return $header;
- }
- /**
- * Retrieves a list of sites accessible to this client.
- *
- * Used by Mollom resellers only.
- *
- * @return array
- * An array containing site resources, as returned by Mollom::getsite().
- */
- public function getSites() {
- $result = $this->query('GET', 'site', array(), array('list'));
- return isset($result['list']) ? $result['list'] : $result;
- }
- /**
- * Retrieves information about a site.
- *
- * @param string $publicKey
- * (optional) The public Mollom API key of the site to retrieve. Defaults to
- * the public key of the client.
- *
- * @return mixed
- * On success, an associative array containing:
- * - publicKey: The public Mollom API key of the site.
- * - privateKey: The private Mollom API key of the site.
- * - url: The URL of the site.
- * - email: The e-mail address of the primary contact of the site.
- * - platformName: (optional) The name of the platform running the site
- * (e.g., "Drupal").
- * - platformVersion: (optional) The version of the platform running the
- * site (e.g., "6.20").
- * - clientName: (optional) The name of the Mollom client plugin used
- * (e.g., "Mollom").
- * - clientVersion: (optional) The version of the Mollom client plugin used
- * (e.g., "6.15").
- * - expectedLanguages: (optional) An array of language ISO codes, content
- * is expected to be submitted in on the site.
- * On failure, the error response code returned by the server.
- */
- public function getSite($publicKey = NULL) {
- if (!isset($publicKey)) {
- $publicKey = $this->publicKey;
- }
- $publicKey = rawurlencode($publicKey);
- $result = $this->query('GET', 'site/' . $publicKey, array(), array('site', 'publicKey'));
- return isset($result['site']) ? $result['site'] : $result;
- }
- /**
- * Creates a new site.
- *
- * @param array $data
- * An associative array of properties for the new site. At least 'url' and
- * 'email' are required. See Mollom::getSite() for details.
- *
- * @return mixed
- * On success, the full site information of the created site; see
- * Mollom::getSite() for details. On failure, the error response code
- * returned by the server. Or FALSE if 'url' or 'email' was not specified.
- */
- public function createSite(array $data = array()) {
- if (empty($data['url']) || empty($data['email'])) {
- return FALSE;
- }
- $result = $this->query('POST', 'site', $data, array('site', 'publicKey'));
- return isset($result['site']) ? $result['site'] : $result;
- }
- /**
- * Updates a site.
- *
- * Note that most Mollom clients want to use Mollom::verifyKeys() only. This
- * method is primarily used by Mollom resellers, who are provisioning sites
- * and may need to set other site properties.
- *
- * @param array $data
- * (optional) An associative array of properties to set for the site. See
- * Mollom::getSite() for details.
- * @param string $publicKey
- * (optional) The public Mollom API key of the site to update. Defaults to
- * the public key of the client.
- *
- * @return mixed
- * On success, the full site information of the created site; see
- * Mollom::getSite() for details. On failure, the error response code
- * returned by the server.
- */
- public function updateSite(array $data = array(), $publicKey = NULL) {
- if (!isset($publicKey)) {
- $publicKey = $this->publicKey;
- }
- $publicKey = rawurlencode($publicKey);
- $result = $this->query('POST', 'site/' . $publicKey, $data, array('site', 'publicKey'));
- return isset($result['site']) ? $result['site'] : $result;
- }
- /**
- * Updates a site to verify API keys and send client information.
- *
- * Mollom API keys are validated in all API calls already. This method should
- * be used when the API keys of a Mollom client are configured for a site. It
- * should be invoked at least once for a site, to send client and version
- * information to Mollom in order to aid with Mollom support requests.
- *
- * @return mixed
- * TRUE on success. On failure, the error response code returned by the
- * server; either Mollom::REQUEST_ERROR, Mollom::AUTH_ERROR or
- * Mollom::NETWORK_ERROR.
- */
- public function verifyKeys() {
- $data = $this->getClientInformation();
- $result = $this->updateSite($data);
- // lastResponseCode will either be TRUE, REQUEST_ERROR, AUTH_ERROR, or
- // NETWORK_ERROR.
- return $this->lastResponseCode === TRUE ? TRUE : $this->lastResponseCode;
- }
- /**
- * Deletes a site.
- *
- * @param string $publicKey
- * The public Mollom API key of the site to delete.
- *
- * @return bool
- * TRUE on success, FALSE otherwise.
- */
- public function deleteSite($publicKey) {
- $publicKey = rawurlencode($publicKey);
- $result = $this->query('POST', 'site/' . $publicKey . '/delete');
- return $this->lastResponseCode === TRUE;
- }
- /**
- * Checks user-submitted content with Mollom.
- *
- * @param array $data
- * An associative array containing any of the keys:
- * - id: The existing content ID of the content, if it or a variant or
- * revision of it has been checked before.
- * - postTitle: The title of the content.
- * - postBody: The body of the content. If the content consists of multiple
- * fields, concatenate them into one postBody string, separated by " \n"
- * (space and line-feed).
- * - authorName: The (real) name of the content author.
- * - authorUrl: The homepage/website URL of the content author.
- * - authorMail: The e-mail address of the content author.
- * - authorIp: The IP address of the content author.
- * - authorId: The local user ID on the client site of the content author.
- * - authorOpenid: An indexed array of Open IDs of the content author.
- * - checks: An indexed array of strings denoting the checks to perform, one
- * or more of: 'spam', 'quality', 'profanity', 'language', 'sentiment'.
- * Defaults to 'spam'.
- * - type: An optional string identifier to request a special content
- * classification behavior. Possible values are:
- * - 'user': Enables classification of 'author*' request parameters as
- * primary content. postTitle and postBody may be left empty without
- * negative impact on the classification result. Use this for checking
- * user registration forms. Optionally pass additional user profile text
- * fields as postBody.
- * - unsure: Integer denoting whether a "unsure" response should be allowed
- * (1) for the 'spam' check (which should lead to CAPTCHA) or not (0).
- * Defaults to 1.
- * - strictness: A string denoting the strictness of Mollom checks to
- * perform; one of 'strict', 'normal', or 'relaxed'. Defaults to 'normal'.
- * - rateLimit: Seconds that must have passed by for the same author to post
- * again. Defaults to 15.
- * - honeypot: The value of a client-side honeypot form element, if
- * non-empty.
- * - stored: Integer denoting whether the content has been stored (1) on the
- * client-side or not (0). Use 0 during form validation, 1 after
- * successful submission. Defaults to 0.
- * - url: The absolute URL to the stored content.
- * - contextUrl: An absolute URL to parent/context content of the stored
- * content; e.g., the URL of the article or forum thread a comment is
- * posted on (not the parent comment that was replied to).
- * - contextTitle: The title of the parent/context content of the stored
- * content; e.g., the title of the article or forum thread a comment is
- * posted on (not the parent comment that was replied to).
- * - contextCreated: The creation date of the parent/context content of the
- * stored content as a unix timestamp in seconds; e.g., the creation date
- * of the article or forum thread a comment is posted on (not the parent
- * comment that was replied to).
- * - trackingImageId: An optional string identifier used to request an
- * image beacon. This is used for form behavior analysis.
- *
- * @return mixed
- * On success, an associative array representing the full content record,
- * containing the additional keys:
- * - spamScore: A floating point value with a precision of 2, ranging
- * between 0.00 and 1.00; whereas 0.00 denotes 100% spam, 0.50 denotes
- * "unsure", and 1.00 denotes ham. Only returned if 'spam' was passed for
- * 'checks'.
- * - spamClassification: The final spam classification; one of 'spam',
- * 'unsure', or 'ham'. Only returned if 'spam' was passed for 'checks'.
- * - profanityScore: A floating point value with a precision of 2, ranging
- * between 0.00 and 1.00; whereas 0.00 denotes 0% profanity and 1.00
- * denotes 100% profanity. Only returned if 'profanity' was passed for
- * 'checks'.
- * - qualityScore: A floating point value with a precision of 2, ranging
- * between 0.00 and 1.00; whereas 0.00 denotes poor quality and 1.00
- * high quality. Only returned if 'quality' was passed for 'checks'.
- * - sentimentScore: A floating point value with a precision of 2, ranging
- * between 0.00 and 1.00; whereas 0.00 denotes bad sentiment and 1.00
- * good sentiment. Only returned if 'sentiment' was passed for 'checks'.
- * - reason: A string denoting the reason for Mollom's classification; e.g.,
- * - rateLimit: Author was seen on Mollom-protected sites within the given
- * 'rateLimit' time-frame.
- * On failure, the error response code returned by the server.
- */
- public function checkContent(array $data = array()) {
- $path = 'content';
- if (!empty($data['id'])) {
- // The ID originates from raw form input. Ensure we hit the right endpoint
- // in case a bogus bot fills in even hidden input fields with random
- // strings, by performing a basic syntax validation.
- if (preg_match('@^[a-z0-9-]+$@i', $data['id'])) {
- $path .= '/' . rawurlencode($data['id']);
- }
- unset($data['id']);
- }
- $result = $this->query('POST', $path, $data, array('content', 'id'));
- // parseXML() can only convert multiple sub-elements into an indexed array.
- if (isset($result['content']['languages']) && is_array($result['content']['languages'])) {
- $result['content']['languages'] = array_values($result['content']['languages']);
- }
- return isset($result['content']) ? $result['content'] : $result;
- }
- /**
- * Retrieves a CAPTCHA resource from Mollom.
- *
- * @param array $data
- * An associative array containing:
- * - type: A string denoting the type of CAPTCHA to create; one of 'image'
- * or 'audio'.
- * and any of the keys:
- * - contentId: The ID of a content resource to link the CAPTCHA to. Allows
- * Mollom to learn when it was unsure.
- * - ssl: An integer denoting whether to create a CAPTCHA URL using HTTPS
- * (1) or not (0). Only available for paid subscriptions.
- *
- * @return mixed
- * On success, an associative array representing the full CAPTCHA record,
- * containing:
- * - id: The ID of the CAPTCHA.
- * - url: The URL of the CAPTCHA.
- * On failure, the error response code returned by the server.
- * Or FALSE if a unknown 'type' was specified.
- */
- public function createCaptcha(array $data = array()) {
- if (!isset($data['type']) || !in_array($data['type'], array('image', 'audio'))) {
- return FALSE;
- }
- $path = 'captcha';
- $result = $this->query('POST', $path, $data, array('captcha', 'id'));
- return isset($result['captcha']) ? $result['captcha'] : $result;
- }
- /**
- * Checks whether a user-submitted solution for a CAPTCHA is correct.
- *
- * @param array $data
- * An associative array containing:
- * - id: The ID of the CAPTCHA to check.
- * - solution: The answer provided by the author.
- * and any of the keys:
- * - authorName: The (real) name of the content author.
- * - authorUrl: The homepage/website URL of the content author.
- * - authorMail: The e-mail address of the content author.
- * - authorIp: The IP address of the content author.
- * - authorId: The local user ID on the client site of the content author.
- * - authorOpenid: An indexed array of Open IDs of the content author.
- * - rateLimit: Seconds that must have passed by for the same author to post
- * again. Defaults to 15.
- * - honeypot: The value of a client-side honeypot form element, if
- * non-empty.
- *
- * @return mixed
- * On success, an associative array representing the full CAPTCHA record,
- * additionally containing:
- * - solved: Whether the provided solution was correct (1) or not (0).
- * - reason: A string denoting the reason for Mollom's classification; e.g.,
- * - rateLimit: Author was seen on Mollom-protected sites within the given
- * 'rateLimit' time-frame.
- * On failure, the error response code returned by the server.
- * Or FALSE if no 'id' was specified.
- */
- public function checkCaptcha(array $data = array()) {
- if (empty($data['id'])) {
- return FALSE;
- }
- // The ID originates from raw form input. Ensure we hit the right endpoint
- // in case a bogus bot fills in even hidden input fields with random
- // strings, by performing a basic syntax validation.
- if (!preg_match('@^[a-z0-9-]+$@i', $data['id'])) {
- return FALSE;
- }
- $path = 'captcha/' . rawurlencode($data['id']);
- unset($data['id']);
- $result = $this->query('POST', $path, $data, array('captcha', 'id'));
- return isset($result['captcha']) ? $result['captcha'] : $result;
- }
- /**
- * Sends feedback to Mollom.
- *
- * @param array $data
- * An associative array containing:
- * - reason: A string denoting the reason for why the content associated
- * with either contentId or captchaId is being reported; one of:
- * - spam: The content is spam, unsolicited advertising.
- * - profanity: The content contains obscene, violent, profane language.
- * - quality: The content is of low quality.
- * - unwanted: The content is unwanted, taunting, off-topic.
- * - type (optional): A string denoting the type of feedback submitted.
- * - moderate: Feedback from the admin moderation process (default).
- * - flag: feedback from end users flagging content as inappropriate
- * - source (optional): A string denoting the user-interface source of the feedback.
- * and at least one of:
- * - contentId: A Mollom content ID associated with the content.
- * - captchaId: A Mollom CAPTCHA ID associated with the content.
- *
- * @return bool
- * TRUE if the feedback was sent successfully, FALSE otherwise.
- */
- public function sendFeedback(array $data) {
- if (empty($data['contentId']) && empty($data['captchaId'])) {
- return FALSE;
- }
- if (empty($data['reason'])) {
- return FALSE;
- }
- $this->query('POST', 'feedback', $data);
- return $this->lastResponseCode === TRUE ? TRUE : FALSE;
- }
- /**
- * Retrieves the blacklist for a site.
- *
- * @param string $publicKey
- * (optional) The public Mollom API key of the site to retrieve the
- * blacklist for. Defaults to the public key of the client.
- *
- * @return mixed
- * An array containing blacklist entries; see Mollom::getBlacklistEntry()
- * for details. On failure, the error response code returned by the server.
- *
- * @todo List parameters.
- */
- public function getBlacklist($publicKey = NULL) {
- if (!isset($publicKey)) {
- $publicKey = $this->publicKey;
- }
- $publicKey = rawurlencode($publicKey);
- $result = $this->query('GET', 'blacklist/' . $publicKey, array(), array('list'));
- return isset($result['list']) ? $result['list'] : $result;
- }
- /**
- * Retrieves a blacklist entry stored for a site.
- *
- * @param string $entryId
- * The ID of the blacklist entry to retrieve.
- * @param string $publicKey
- * (optional) The public Mollom API key of the site to retrieve the
- * blacklist entry for. Defaults to the public key of the client.
- *
- * @return mixed
- * On success, an associative array containing:
- * - id: The ID the of blacklist entry.
- * - created: A timestamp in seconds since the UNIX epoch of when the entry
- * was created.
- * - value: The blacklisted string/value.
- * - reason: A string denoting the reason for why the value is blacklisted;
- * one of 'spam', 'profanity', 'quality', or 'unwanted'. Defaults to
- * 'unwanted'.
- * - context: A string denoting where the entry's value may match; one of
- * 'allFields', 'links', 'authorName', 'authorMail', 'authorIp',
- * 'authorIp', or 'postTitle'. Defaults to 'allFields'.
- * - match: A string denoting how precise the entry's value may match; one
- * of 'exact' or 'contains'. Defaults to 'contains'.
- * - status: An integer denoting whether the entry is enabled (1) or not
- * (0).
- * - note: A custom string explaining the entry. Useful in a multi-moderator
- * scenario.
- * On failure, the error response code returned by the server.
- */
- public function getBlacklistEntry($entryId, $publicKey = NULL) {
- if (!isset($publicKey)) {
- $publicKey = $this->publicKey;
- }
- $path = 'blacklist/' . rawurlencode($publicKey) . '/' . rawurlencode($entryId);
- $result = $this->query('GET', $path, array(), array('entry', 'id'));
- return isset($result['entry']) ? $result['entry'] : $result;
- }
- /**
- * Creates or updates a blacklist entry for a site.
- *
- * @param array $data
- * An associative array describing the blacklist entry to create or update.
- * See return value of Mollom::getBlacklistEntry() for details. To update
- * an existing entry, its ID must be specified in 'id'.
- * @param string $publicKey
- * (optional) The public Mollom API key of the site to save the blacklist
- * entry for. Defaults to the public key of the client.
- *
- * @return mixed
- * On success, the full blacklist entry record of the saved entry; see
- * Mollom::getBlacklistEntry() for details. On failure, the error response
- * code returned by the server.
- */
- public function saveBlacklistEntry(array $data = array(), $publicKey = NULL) {
- if (!isset($publicKey)) {
- $publicKey = $this->publicKey;
- }
- $path = 'blacklist/' . rawurlencode($publicKey);
- if (!empty($data['id'])) {
- $path .= '/' . rawurlencode($data['id']);
- unset($data['id']);
- }
- $result = $this->query('POST', $path, $data, array('entry', 'id'));
- return isset($result['entry']) ? $result['entry'] : $result;
- }
- /**
- * Deletes a blacklist entry from a site.
- *
- * @param string $entryId
- * The ID of the blacklist entry to delete.
- * @param string $publicKey
- * (optional) The public Mollom API key of the site to create the blacklist
- * entry for. Defaults to the public key of the client.
- *
- * @return bool
- * TRUE on success, FALSE otherwise.
- */
- public function deleteBlacklistEntry($entryId, $publicKey = NULL) {
- if (!isset($publicKey)) {
- $publicKey = $this->publicKey;
- }
- $path = 'blacklist/' . rawurlencode($publicKey) . '/' . rawurlencode($entryId) . '/delete';
- $result = $this->query('POST', $path);
- return $this->lastResponseCode === TRUE;
- }
- /**
- * Generates a URL to a tracking image.
- *
- * This image can be used by form behavior analysis to analyze the
- * humanity of the content author.
- *
- * @return array
- * An associative array of tracking information generated.
- * - tracking_url: the url to the tracking image (without any http protocol)
- * - tracking_id: the tracking id generated
- */
- public function getTrackingImage() {
- $public_key = $this->publicKey;
- $ts = time();
- $tracking_id = hash_hmac('sha1', $public_key . $ts, $this->privateKey);
- $result = array(
- 'tracking_url' => '//' . $this->server . '/v1/tracking/' . urlencode($tracking_id) . '.png?public_key=' . $public_key . "×tamp=" . $ts,
- 'tracking_id' => $tracking_id,
- );
- return $result;
- }
- }
- /**
- * A catchable Mollom exception.
- *
- * The Mollom class internally uses exceptions to handle HTTP request errors
- * within the Mollom::handleRequest() method. All exceptions thrown in the
- * Mollom class and derived classes should be instances of the MollomException
- * class if they pertain to errors that can be catched/handled within the class.
- * Other errors should not use the MollomException class and handled
- * differently.
- *
- * No MollomException is supposed to pile up as a user-facing fatal error. All
- * functions that invoke Mollom::handleRequest() have to catch Mollom
- * exceptions.
- *
- * @see Mollom::query()
- * @see Mollom::handleRequest()
- *
- * @param $message
- * The Exception message to throw.
- * @param $code
- * The Exception code.
- * @param $previous
- * (optional) The previous Exception, if any.
- * @param $instance
- * The Mollom class instance the Exception is thrown in.
- * @param $arguments
- * (optional) A associative array containing information about a performed
- * HTTP request that failed:
- * - request: (string) The HTTP method and URI of the performed request; e.g.,
- * "GET http://server.mollom.com/v1/foo/bar". In case of GET requests, do
- * not add query parameters to the URI; pass them in 'data' instead.
- * - data: (array) An associative array containing HTTP GET/POST/PUT request
- * query parameters that were sent to the server.
- * - response: (mixed) The server response, either as string, or the already
- * parsed response; i.e., an array.
- */
- class MollomException extends Exception {
- /**
- * @var Mollom
- */
- protected $mollom;
- /**
- * The severity of this exception.
- *
- * By default, all exceptions should be logged and appear as errors (unless
- * overridden by a later log entry).
- *
- * @var string
- */
- protected $severity = 'error';
- /**
- * Overrides Exception::__construct().
- */
- function __construct($message = '', $code = 0, Exception $previous = NULL, Mollom $mollom, array $request_info = array()) {
- // Fatal error on PHP <5.3 when passing more arguments to Exception.
- if (version_compare(phpversion(), '5.3') >= 0) {
- parent::__construct($message, $code, $previous);
- }
- else {
- parent::__construct($message, $code);
- }
- $this->mollom = $mollom;
- // Set the error code on the Mollom class.
- $mollom->lastResponseCode = $code;
- // Log the exception.
- // To aid Mollom technical support, include the IP address of the server we
- // tried to reach in case a request fails.
- // PHP's native gethostbyname() is available on all platforms, but its DNS
- // lookup and caching behavior is undocumented and unclear. User comments on
- // php.net mention that it does not have an own cache and also does not use
- // the OS/platform's native DNS name resolver. Due to that, we only use it
- // under error conditions.
- $message = array(
- 'severity' => $this->severity,
- 'message' => 'Error @code: %message (@server-ip)',
- 'arguments' => array(
- '@code' => $code,
- '%message' => $message,
- '@server-ip' => gethostbyname($mollom->server),
- ),
- );
- // Add HTTP request information, if available.
- if (!empty($request_info)) {
- $message += $request_info;
- }
- $mollom->log[] = $message;
- }
- }
- /**
- * Mollom network error exception.
- *
- * Thrown in case a HTTP request results in code <= 0, denoting a low-level
- * communication error.
- */
- class MollomNetworkException extends MollomException {
- /**
- * Overrides MollomException::$severity.
- *
- * The client may be able to recover from this error, so use a warning level.
- */
- protected $severity = 'warning';
- }
- /**
- * Mollom authentication error exception.
- *
- * Thrown in case API keys or other authentication parameters are invalid.
- */
- class MollomAuthenticationException extends MollomException {
- }
- /**
- * Mollom error due to bad client request exception.
- *
- * Thrown in case the local time diverges too much from UTC.
- *
- * @see Mollom::TIME_OFFSET_MAX
- * @see Mollom::REQUEST_ERROR
- * @see Mollom::handleRequest()
- */
- class MollomBadRequestException extends MollomException {
- }
- /**
- * Mollom server response exception.
- *
- * Thrown when a request to a Mollom server succeeds, but the response does not
- * contain an expected element; e.g., a backend configuration or execution
- * error that possibly exists on one server only.
- *
- * @see Mollom::handleRequest()
- */
- class MollomResponseException extends MollomException {
- /**
- * Overrides MollomException::$severity.
- *
- * Might be a client-side error, but more likely a server-side error. The
- * client may be able to recover from this error.
- */
- protected $severity = 'debug';
- }
|