diff --git a/src/controllers/components/SocialComponent.php b/src/controllers/components/SocialComponent.php
index 6fd461b49..5f885f4cd 100644
--- a/src/controllers/components/SocialComponent.php
+++ b/src/controllers/components/SocialComponent.php
@@ -1215,11 +1215,12 @@ class SocialComponent extends Component implements CrawlConstants
foreach ($bot_followers as $bot_follower) {
$bots[] = $bot_follower['USER_NAME'];
}
- if (@preg_match_all('/(?<!\w)@(\w+) (.*)/', $description,
+ if (preg_match_all('/(?<!\w)@(\w+) (.*)/si', $description,
$matches)) {
foreach ($matches[1] as $match) {
- $index = array_search($match,$bots);
- if ($index) {
+ $match = mb_strtolower($match);
+ $index = array_search($match, $bots);
+ if ($index !== false) {
$bots_called[] = $bot_followers[$index];
}
}
@@ -1269,10 +1270,14 @@ class SocialComponent extends Component implements CrawlConstants
$num_bots = count($bots_called);
$sites = [];
$post_data = [];
+ $time = time();
for ($i = 0; $i < $num_bots; $i++) {
$sites[$i][CrawlConstants::URL] =
$bots_called[$i]['CALLBACK_URL'];
- $post_data[$i] = "post=$post_followed";
+ $post_data[$i] = "post=$post_followed&bot_token=" .
+ hash("sha256", $bots_called[$i]['BOT_TOKEN'] .
+ $time . $post_followed) . "*" . $time .
+ "&bot_name=". $bots_called[$i]['USER_NAME'];
}
$outputs = [];
if (count($sites) > 0) {
diff --git a/src/examples/bot_examples/weather/WeatherBot.php b/src/examples/bot_examples/weather/WeatherBot.php
index 4b0886478..e5337a390 100644
--- a/src/examples/bot_examples/weather/WeatherBot.php
+++ b/src/examples/bot_examples/weather/WeatherBot.php
@@ -33,58 +33,117 @@
namespace seekquarry\yioop\examples\weatherbot;
/**
- * Bot work instruction:
- * The current folder has to be moved to root path
- * Create a bot with CALLBACK_URL as http://HOSTNAME/weather/WeatherBot.php
- * Add the bot to the group you wanted to converse
- * Talk to your bot in yioop Groups by calling bot as @bot_name in comment
- */
-/**
- * This file contains an example bot called weather bot
- * to get weather updates whenever you ask
+ * This class demonstrates a simple Weather Chat Bot using the Yioop
+ * ChatBot APIs for Yioop Discussion Groups.
+ * To use this bot:
+ * (1) Move this file to some folder of a web server you have access to.
+ * Denote by some_url the url of this folder. If you point your
+ * browser at this folder you should see a message that begins with:
+ * There was a configuration issue with your query.
+ * (2) Create a new Yioop User.
+ * (3) Under Manage Accounts, click on the lock symbol next to Account Details
+ * (4) Check the Bot User check bot, click save.
+ * (5) Two form variables should appear: Bot Unique Token and Bot Callback URL.
+ * Fill in a value for Bot Unique Token that matches the value set
+ * for ACCESS_TOKEN in the code within the WeatherBot class.
+ * Fill in some_url (as defined in step (1)) for the value of Bot Callback
+ * URL
+ * (6) Add the the user you created in Yioop to the group that you would like
+ * the bot to service. Let the name of this user be user_name.
+ * (7) Talk to your bot in yioop in this groups by commenting on an
+ * already existing thread with a message beginning with @user_name.
*/
class WeatherBot
{
/**
- * @param string $access_token
+ * Url of site that this bot gets weather information from
+ */
+ const WEATHER_URL = "http://query.yahooapis.com/v1/public/yql";
+ /**
+ * Token given when setting up the bot in Yioop for callback requests
+ * This bots checks that a request from a Yioop Intance sends
+ * a timestamp as well as the hash of this timestamp with the bot_token
+ * and post data and that these match the expected values
+ */
+ const ACCESS_TOKEN = "bot_token";
+ /**
+ * Number of seconds that the passed timestamp can differ from the current
+ * time on the WeatherBot machine.
+ */
+ const TIME_WINDOW = 60;
+ /**
+ * This is the method called to get the WeatherBot to handle an incoming
+ * HTTP request, and echo a weather realted message
*/
- function setKey($access_token)
+ function processRequest()
{
- $this->access_token = $access_token;
+ $result = "There was a configuration issue with your query.";
+ if ($this->checkBotToken() && !empty($_REQUEST['post']) &&
+ !empty($_REQUEST['bot_name'])) {
+ $location = filter_var($_REQUEST['post'], \FILTER_SANITIZE_STRING);
+ $location = trim(mb_strtolower($location));
+ $result = $this->getWeather($location);
+ if (empty($result)) {
+ $result = "I failed to find the weather for that location.\n".
+ "I respond to queries in the format:\n" .
+ " @{$_REQUEST['bot_name']} some_location";
+ }
+ }
+ echo $result;
}
/**
- * Get weather information
+ * This method is used to check a request that it comes from a site
+ * that knows the bot_token in use by this WeatherBot.
+ */
+ function checkBotToken()
+ {
+ if (!empty($_REQUEST['bot_token'])) {
+ $token_parts = explode("*", $_REQUEST['bot_token']);
+ $post = empty($_REQUEST["post"]) ? "" : $_REQUEST["post"];
+ $hash = hash("sha256", self::ACCESS_TOKEN . $token_parts[1].
+ $post);
+ if (isset($token_parts[1]) &&
+ abs(time() - $token_parts[1]) < self::TIME_WINDOW) {
+ // second check avoids timing attacks, works for > php 5.6
+ if ((!function_exists('hash_equals') &&
+ $hash == $token_parts[0]) ||
+ hash_equals($hash, $token_parts[0])) {
+ return true;
+ }
+ }
+ }
+ return false;
+ }
+ /**
+ * Get weather information about a location
*
* @param string $location the location to get weather updates for
* @return string weather information
*/
function getWeather($location)
{
- $WEATHER_URL = "http://query.yahooapis.com/v1/public/yql";
$yql_query = "select * from weather.forecast where woeid in
- (select woeid from geo.places(1) where text='".$location
+ (select woeid from geo.places(1) where text='" . $location
."')";
- $url = $WEATHER_URL . "?q=" . urlencode($yql_query) . "&format=json";
+ $url = self::WEATHER_URL . "?q=" .
+ urlencode($yql_query) . "&format=json";
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
$data = curl_exec($ch);
curl_close($ch);
- $result = json_decode($data);
- $temp = $result->query->results->channel->item->condition->temp;
- $text = $result->query->results->channel->item->condition->text;
- return "The weather is $temp and $text in $location";
+ $result = @json_decode($data);
+ $temp = empty($result->query->results->channel->item->condition->temp) ?
+ "" : $result->query->results->channel->item->condition->temp;
+ $text = empty($result->query->results->channel->item->condition->text) ?
+ "" : mb_strtolower(
+ $result->query->results->channel->item->condition->text);
+ if (empty($temp) || empty($text)) {
+ return "";
+ }
+ return "The weather is $temp and $text in $location.";
}
}
-$access_token ="bot_token";
$bot = new WeatherBot();
-$bot->setKey($access_token);
-$location = "";
-$result = "";
-if (!empty($_REQUEST['post'])){
- $location = explode(" ", $_REQUEST['post']);
-}
-if (!empty($location[4])) {
- $result = $bot->getWeather($location[4]);
-}
-echo $result;
+$bot->processRequest();
+
diff --git a/src/library/Utility.php b/src/library/Utility.php
index e4c428106..fa10b1df4 100755
--- a/src/library/Utility.php
+++ b/src/library/Utility.php
@@ -1552,7 +1552,6 @@ function crawlCrypt($string, $salt = null)
}
return crypt($string, $salt);
}
-
/**
* Used by a controller to take a table and return those rows in the
* table that a given queue_server would be responsible for handling
@@ -1578,7 +1577,6 @@ function partitionByHash($table, $field, $num_partition, $instance,
foreach ($table as $row) {
$cell = ($field === null) ? $row : $row[$field];
$hash_int = calculatePartition($cell, $num_partition, $callback);
-
if ($hash_int == $instance) {
$out_table[] = $row;
}
@@ -2461,4 +2459,4 @@ function bchexdec($hex)
bcpow('16', strval($len - $i))));
}
return $dec;
-}
\ No newline at end of file
+}
diff --git a/src/models/ProfileModel.php b/src/models/ProfileModel.php
index 30e8bda11..c07b385b4 100755
--- a/src/models/ProfileModel.php
+++ b/src/models/ProfileModel.php
@@ -382,8 +382,6 @@ class ProfileModel extends Model
$n = [];
$n[] = <<<EOT
<?php
-namespace seekquarry\yioop\configs;
-
/**
* SeekQuarry/Yioop --
* Open Source Pure PHP Search Engine, Crawler, and Indexer
@@ -407,15 +405,18 @@ namespace seekquarry\yioop\configs;
*
* END LICENSE
*
- * Computer generated file giving the key defines of directory locations
- * as well as database settings used to run the SeekQuarry/Yioop search engine
- *
* @author Chris Pollett chris@pollett.org
* @license http://www.gnu.org/licenses/ GPL3
* @link http://www.seekquarry.com/
* @copyright 2009-2012
* @filesource
*/
+namespace seekquarry\yioop\configs;
+
+/**
+ * Computer generated file giving the key defines of directory locations
+ * as well as database settings used to run the SeekQuarry/Yioop search engine
+ */
EOT;
$base_url = C\NAME_SERVER;
if (C\nsdefined("BASE_URL")) {
@@ -767,4 +768,4 @@ EOT;
}
return $match;
}
-}
\ No newline at end of file
+}
diff --git a/src/models/SigninModel.php b/src/models/SigninModel.php
index 1c89e13b7..38122cee3 100755
--- a/src/models/SigninModel.php
+++ b/src/models/SigninModel.php
@@ -56,7 +56,12 @@ class SigninModel extends Model
return false;
}
$row = $db->fetchArray($result);
- return L\crawlCrypt($password, $row['PASSWORD']) == $row['PASSWORD'] ;
+ // avoid timeing attacks if possible
+ if (function_exists('hash_equals')) {
+ return hash_equals(L\crawlCrypt($password, $row['PASSWORD']),
+ $row['PASSWORD']);
+ }
+ return L\crawlCrypt($password, $row['PASSWORD']) == $row['PASSWORD'];
}
/**
* Get user details from database