public_distributor.php 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283
  1. <?php
  2. /**
  3. *
  4. * Script for exposing a distributor object to public web access.
  5. * This is intended for use only on the servers of people who want to run a distributor for Sloodle objects.
  6. *
  7. * ( However, functionality that accesses it to send the avatar of their choice suitable objects
  8. * for their Sloodle install, once written, is intended to go into the regular Sloodle distribution. )
  9. *
  10. * It should be installed under mod/sloodle, ie. in the same directory as init.php
  11. *
  12. * Parts of this script require manual configuration. (See below)
  13. *
  14. * Distributor functionality is based on that used in view/distributor.php
  15. *
  16. * WARNING:
  17. * As of 2009-12-18, this script does not respect course permissions or distributor public / group settings.
  18. * Ideally, it should probably ignore course permissions but only run if the distributor is public.
  19. * Since it is anticipated that only a few people will want to run it on their servers, it should probably either:
  20. * a) Not be included in the standard Sloodle distribution, or
  21. * b) Included, but with an "exit" right at the start so that it only runs if manually altered.
  22. * For now, we'll check it into tools/distribution/, and install it manually where we need it.
  23. *
  24. * Usage examples:
  25. *
  26. * Send the object "thing" in the distributor with ID 14 to the user with the key 746ad236-d28d-4aab-93de-1e09a076c5f3
  27. * http://dev1.socialminds.jp/mod/sloodle/public_distributor.php?id=14&object=thing&user=746ad236-d28d-4aab-93de-1e09a076c5f3
  28. *
  29. * Send the object "thing" in the distributor with ID 14 to the user with the name Edmund Earp
  30. * http://dev1.socialminds.jp/mod/sloodle/public_distributor.php?id=14&object=thing&av_name=Edmund%20Earp
  31. *
  32. * @package sloodle
  33. * @copyright Copyright (c) 2008 to 2010 Sloodle (various contributors)
  34. * @license http://www.gnu.org/licenses/gpl-3.0.html GNU GPL v3
  35. *
  36. * @contributor Edmund Edgar, KK Social Minds
  37. * @contributor Peter R. Bloomfield
  38. */
  39. //
  40. // Manual configuration begins
  41. //
  42. // List the name2key services you want to use here.
  43. $name2keyServices = array(
  44. 'http://vision-tech.org/name2key/search.php?name=' => 'LookupBasedName2KeyService',
  45. 'http://kdc.ethernia.net/sys/name2key.php' => 'GenericName2KeyService'
  46. );
  47. /*
  48. If you want to receive alerts when this service is about to break, put your address and the failure threshold here.
  49. Probably best to keep at least one provider in reserve, eg. if you have 3 providers, have it warn you when the first 2 go down.
  50. */
  51. $warningMailAddressesAndThresholds = array(
  52. 'you@example.com' => 2
  53. );
  54. //
  55. // Manual configuration ends
  56. //
  57. /** SLOODLE and Moodle configuration */
  58. require_once('init.php');
  59. /** General SLOODLE library functionality */
  60. require_once(SLOODLE_LIBROOT.'/general.php');
  61. // Get the distributor course module ID from a URL parameter.
  62. // NB If it turns out that it's hard to keep these stable over time,
  63. // ...it may be better to hard-code the distributor ID here
  64. // ...or to remove it and wrap this script in another one,
  65. // ...eg. distributor_stable_1_0.php would set $id for the 1.0 stable distributor, then include this file
  66. $id = required_param('id', PARAM_INT);
  67. if (!$cm = get_coursemodule_from_id('sloodle', $id)) error('Course module ID was incorrect.');
  68. // Ignore the course data. If there's a public distributor, we'll let anyone use it.
  69. // The following was in the original view/distributor.php
  70. /*
  71. // Fetch the course data
  72. if (!$this->course = get_record('course', 'id', $this->cm->course)) error('Failed to retrieve course.');
  73. $this->sloodle_course = new SloodleCourse();
  74. if (!$this->sloodle_course->load($this->course)) error(get_string('failedcourseload', 'sloodle'));
  75. */
  76. // Fetch the SLOODLE instance itself
  77. if (!$sloodle = sloodle_get_record('sloodle', 'id', $cm->instance)) error('Failed to find SLOODLE module instance');
  78. if (!$distributor = sloodle_get_record('sloodle_distributor', 'sloodleid', $sloodle->id)) error('Failed to get SLOODLE Distributor data.');
  79. $entries = sloodle_get_records('sloodle_distributor_entry', 'distributorid', $distributor->id, 'name');
  80. // If the query failed, then assume there were simply no items available
  81. if (!is_array($entries)) $entries = array();
  82. $numitems = count($entries);
  83. if (isset($_REQUEST['user'])) $send_user = $_REQUEST['user'];
  84. if (isset($_REQUEST['object'])) $send_object = $_REQUEST['object'];
  85. if (isset($_REQUEST['av_name'])) $av_name = $_REQUEST['av_name'];
  86. // If we don't have the avatar key but we do have their name, try to look up the key based on the name
  87. if ( empty($send_user) && ( !empty($av_name) ) ) {
  88. $atLeastOneServiceSucceeded = false;
  89. // Try to do a lookup on a name2key service
  90. $failedServices = array();
  91. foreach( $name2keyServices as $url => $cls ) {
  92. $service = new $cls();
  93. $service->setServiceBaseURL( $url );
  94. $service->setAvatarName( $av_name );
  95. if ( $service->lookup() ) {
  96. $atLeastOneServiceSucceeded = true;
  97. if ( $service->foundKey() ) {
  98. $send_user = $service->uuid();
  99. break;
  100. } else if ( $service->isExhaustive() ) {
  101. // The service confirmed that they key doesn't exist, so we won't trouble the other services
  102. break;
  103. }
  104. // If we couldn't find the key but the service may just not know about it, stay in the loop and try the next one
  105. } else {
  106. // The service failed to respond or its response didn't make sense, so keep going and try the next one
  107. // We'll make a note of the fact that it failed so that we can tell the administrator if too many services are broken.
  108. $failedServices[] = $service->queryURL();
  109. }
  110. }
  111. if ( ( count($failedServices) > 0 ) && ( count($warningMailAddressesAndThresholds) > 0 ) ) {
  112. foreach( $warningMailAddressesAndThresholds as $emailaddress => $threshold ) {
  113. if ( count($failedServices) >= $threshold ) {
  114. mail( $emailaddress, 'Sloodle name2key lookups failing', "The following lookups failed: \n".join("\n",$failedServices)."\n\nThis is above your warning threshold of $threshold.\n" );
  115. }
  116. }
  117. }
  118. if ( !$atLeastOneServiceSucceeded ) {
  119. print 'sloodleobjectdistributor:allkey2nameservicesfailed'."\n";
  120. exit;
  121. }
  122. if ( empty( $send_user ) ) {
  123. print 'sloodleobjectdistributor:keyfornamenotfound'."\n";
  124. exit;
  125. }
  126. }
  127. if (empty($distributor->channel)) {
  128. print 'sloodleobjectdistributor:nochannel'."\n";
  129. exit;
  130. }
  131. // If the user and object parameters are set, then try to send an object
  132. if (!empty($send_user) && !empty($send_object)) {
  133. // Convert the HTML entities back again
  134. $send_object = htmlentities(stripslashes($send_object));
  135. // Construct and send the request
  136. $request = "1|OK\\nSENDOBJECT|$send_user|$send_object";
  137. $ok = sloodle_send_xmlrpc_message($distributor->channel, 0, $request);
  138. // What was the result?
  139. if ($ok) {
  140. print 'sloodleobjectdistributor:successful'."\n";
  141. } else {
  142. print 'sloodleobjectdistributor:failed'."\n";
  143. }
  144. print 'Object'.': '.$send_object."\n";
  145. print 'uuid'.': '.$send_user."\n";
  146. exit;
  147. }
  148. if ($numitems < 1) {
  149. print 'sloodleobjectdistributor:noobjects'."\n";
  150. exit;
  151. }
  152. foreach ($entries as $e) {
  153. print $e->name."\n";
  154. }
  155. /*
  156. The following is based on how http://name2key.alpha-fox.com works.
  157. You just append /?name= to the URL, followed by the avatar name...
  158. ... and it returns either the name or the null key 00000000-0000-0000-0000-000000000000
  159. This seems pretty standard - hopefully any other service we will need to use will work this way too.
  160. If they don't, make a new class inheriting from GenericName2KeyService and override whatever they do differently.
  161. */
  162. class GenericName2KeyService {
  163. var $_av_name = null;
  164. var $_base_url = null;
  165. var $_result = '';
  166. function setServiceBaseURL( $url ) {
  167. // eg http://name2key.alpha-fox.com
  168. $this->_base_url = $url;
  169. }
  170. function setAvatarName( $av_name) {
  171. $this->_av_name = $av_name;
  172. }
  173. function queryURL() {
  174. return $this->_base_url.'/?name='.urlencode($this->_av_name);
  175. }
  176. // Do a lookup, and return true if the lookup goes as expected.
  177. // NB This will return true even if we couldn't find the key...
  178. // ... which we'll consider a successful lookup that successfully told us that the service didn't know an avatar of that name.
  179. // Use foundKey() to find out whether the key was actually there or not.
  180. function lookup() {
  181. $ch = curl_init();
  182. curl_setopt( $ch, CURLOPT_URL, $this->queryURL() );
  183. curl_setopt( $ch, CURLOPT_RETURNTRANSFER, 1 );
  184. curl_setopt( $ch, CURLOPT_TIMEOUT,$this->timeout() );
  185. $result = curl_exec($ch);
  186. if ( $result ) {
  187. $this->_result = $this->cleanResult( $result );
  188. return $this->isResultExpectedFormat( $result );
  189. }
  190. return false;
  191. }
  192. // clean the result after we get it back
  193. // if there's a new line or something, strip it.
  194. function cleanResult( $result ) {
  195. $result = preg_replace('/\n/', '', $result);
  196. $result = preg_replace('/\r/', '', $result);
  197. return $result;
  198. }
  199. // We should get either a key or a null key.
  200. // Anything else - say an error message - will be considered a failure.
  201. function isResultExpectedFormat( $result ) {
  202. return ( preg_match('/^........-....-....-....-............$/', $result ) );
  203. }
  204. // Return true if we found a key
  205. function foundKey() {
  206. return ( ( $this->_result != '' ) && ( $this->_result != '00000000-0000-0000-0000-000000000000' ) );
  207. }
  208. function uuid() {
  209. if ( $this->foundKey() ) {
  210. return $this->_result;
  211. } else {
  212. return null;
  213. }
  214. }
  215. // Return true if the service claims to be able to reach all the keys on the grid.
  216. // Lookup-based services should be able to do this, while database-based services should not.
  217. function isExhaustive() {
  218. return false;
  219. }
  220. // timeout in seconds after which we should give up waiting for a service.
  221. // bear in mind that we may try several services, while the user's waiting, so we shouldn't make this too long.
  222. function timeout() {
  223. return 10;
  224. }
  225. }
  226. class LookupBasedName2KeyService extends GenericName2KeyService {
  227. function isExhaustive() {
  228. return true;
  229. }
  230. }
  231. ?>