<?php
/*
 * Gallery - a web based photo album viewer and editor
 * Copyright (C) 2000-2008 Bharat Mediratta
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or (at
 * your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street - Fifth Floor, Boston, MA  02110-1301, USA.
 */

/**
 * Utility functions for communicating with the Akismet service.
 *
 * @package Comment
 * @subpackage Classes
 * @author Bharat Mediratta <bharat@menalto.com>
 * @author Jay Rossiter <cryptographite@users.sourceforge.net>
 * @version $Revision: 17989 $
 */
class AkismetApi {
    /**
     * Check a comment to see if it's spam.
     * @param GalleryComment $comment a reference to a GalleryComment instance
     * @return array GalleryStatus a status code
     *               true if the comment is spam.
     */
    function checkComment(&$comment) {
	list ($ret, $body, $status, $headers) = $this->_request(
	    'comment-check', $comment,
	    (bool)$comment->testPersistentFlag(STORAGE_FLAG_NEWLY_CREATED));
	if ($ret) {
	    return array($ret, null);
	}

        /* Comment is not spam - just return */
        if (!preg_match('{^HTTP/1.\d 200 OK$}', $status) || $body == 'false') {
            return array(null, false);
        }

        return array(null, true);
    }

    /**
     * Submit this comment back to Akismet as spam.
     * @param GalleryComment $comment a reference to a GalleryComment instance
     * @return GalleryStatus a status code
     */
    function submitSpam(&$comment) {
	return $this->_submit('submit-spam', $comment);
    }

    /**
     * Submit this comment back to Akismet as ham (not spam).
     * @param GalleryComment $comment a reference to a GalleryComment instance
     * @return GalleryStatus a status code
     */
    function submitHam(&$comment) {
	return $this->_submit('submit-ham', $comment);
    }

    /**
     * Verify an Akismet API key.
     * @param string $apiKey the api key
     * @return array GalleryStatus a status code
     *               true if the api key is valid
     */
    function verifyApiKey($apiKey) {
	global $gallery;
	$urlGenerator =& $gallery->getUrlGenerator();
	$baseUrl = $urlGenerator->generateUrl(array(), array('forceFullUrl' => 1));

        $url = 'http://rest.akismet.com/1.1/verify-key';

	list ($ret, $userAgent) = $this->_getUserAgent();
	if ($ret) {
	    return array($ret, null);
	}

        list ($body, $status, $headers) = GalleryCoreApi::postToWebPage(
	    $url,
	    array('key' => $apiKey, 'blog' => $baseUrl),
	    array('User-Agent' => $userAgent));

        if (!preg_match('{^HTTP/1.\d 200 OK$}', $status) || $body != 'valid') {
            return array(null, false);
        }

        return array(null, true);
    }

    /**
     * Internal helper method used to submit comments back to Akismet to change their status.
     * @param string $action the Akismet action to take (legal values are
     *                       'submit-spam' and 'submit-ham'
     * @param GalleryComment $comment a reference to a GalleryComment instance
     * @return array GalleryStatus a status code
     * @access private
     */
    function _submit($action, &$comment) {
	list ($ret, $body, $status, $headers) = $this->_request($action, $comment, false);
	if ($ret) {
	    return $ret;
	}

        if (!preg_match('{^HTTP/1.\d 200 OK$}', $status)) {
	    return GalleryCoreApi::error(ERROR_UNKNOWN);
	}

	return null;
    }

    /**
     * Internal helper method used to make requests to Akismet and return the
     * body, status and header values.
     * @param string $type the type of Akismet request (legal values are 'comment-check',
     *                     'submit-ham', 'submit-spam')
     * @param GalleryComment $comment a reference to a GalleryComment instance
     * @param bool $includeBrowserSpecificDetails set to true to pass along specific details
     *             from the request that triggered this action (such as the USER_AGENT, etc).
     *             Only set this to true if the user initiatiating the call to _request is the
     *             one who originated the comment.
     *
     * @return array GalleryStatus a status code
     *               string the HTTP response body
     *               string the HTTP response status
     *               string the HTTP response headers
     * @access private
     */
    function _request($type, &$comment, $includeBrowserSpecificDetails) {
        global $gallery;
	$urlGenerator =& $gallery->getUrlGenerator();
	$baseUrl = $urlGenerator->generateUrl(array(), array('forceFullUrl' => true));

        list ($ret, $apiKey) =
	    GalleryCoreApi::getPluginParameter('module', 'comment', 'akismet.apiKey');
        if ($ret) {
            return array($ret, null, null, null);
        }

        /* We can't operate without an API key */
        if (empty($apiKey)) {
	    return array(GalleryCoreApi::error(ERROR_BAD_PARAMETER), null, null, 'Missing API key');
        }

        /* Generate the target URL based on the API Key and the request-type */
        $url = sprintf('http://%s.rest.akismet.com/1.1/%s', $apiKey, $type);

        $postDataArray['blog'] = $baseUrl;
        $postDataArray['user_ip'] = $comment->getHost();
	if ($includeBrowserSpecificDetails) {
	    $postDataArray['user_agent'] = GalleryUtilities::getServerVar('HTTP_USER_AGENT');
	}
        $postDataArray['comment_type'] = $comment->getEntityType();
        $postDataArray['comment_author'] = $comment->getAuthor();
        $postDataArray['comment_author_email'] = '';
        $postDataArray['comment_author_url'] = '';
        $postDataArray['comment_content'] = $comment->getSubject() . ' ' . $comment->getComment();

	if ($includeBrowserSpecificDetails) {
	    foreach (array('HTTP_ACCEPT', 'HTTP_ACCEPT_CHARSET', 'HTTP_ACCEPT_ENCODING',
			   'HTTP_ACCEPT_LANGUAGE', 'HTTP_CONNECTION', 'HTTP_HOST',
			   'HTTP_KEEP_ALIVE', 'HTTP_REFERER', 'HTTP_USER_AGENT', 'QUERY_STRING',
			   'REMOTE_ADDR', 'REMOTE_HOST', 'REMOTE_PORT') as $key) {
		$postDataArray[$key] = GalleryUtilities::getServerVar($key);
	    }
	}

        /* Generate a permalink to the comment using the full URL, without HTML encoding */
        $urlGenerator =& $gallery->getUrlGenerator();
        $postDataArray['permalink'] = $urlGenerator->generateUrl(
	    array('view' => 'core.ShowItem',
		  'itemId' => $comment->getParentId()),
	    array('htmlEntities' => false,
		  'forceFullUrl' => true));

	list ($ret, $userAgent) = $this->_getUserAgent();
	if ($ret) {
	    return array($ret, null, null, null);
	}

        /* Post the request to the Akismet server */
        list ($body, $status, $headers) = GalleryCoreApi::postToWebPage(
	    $url, $postDataArray, array('User-Agent' => $userAgent));

	return array(null, $body, $status, $headers);
    }

    /**
     * Internal helper method to get the user agent in the form that Akismet prefers.
     * @return string the user agent string (eg: "Gallery 2.3 | Comment 1.1.3")
     * @access private
     */
    function _getUserAgent() {
        list ($ret, $core) = GalleryCoreApi::loadPlugin('module', 'core', true);
        if ($ret) {
            return array($ret, null);
        }
        $galleryVersion = $core->getGalleryVersion();

        list ($ret, $comment) = GalleryCoreApi::loadPlugin('module', 'comment', true);
        if ($ret) {
            return array($ret, null);
        }

        $userAgent = sprintf('Gallery %s | Comment %s',
			     $galleryVersion,
			     $comment->getVersion());
	return array(null, $userAgent);
    }
}
?>
