general.php 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156
  1. <?php
  2. /**
  3. * Sloodle general library.
  4. *
  5. * Provides various utility functionality for general Sloodle purposes.
  6. *
  7. * @package sloodle
  8. * @copyright Copyright (c) 2007-8 Sloodle (various contributors)
  9. * @license http://www.gnu.org/licenses/gpl-3.0.html GNU GPL v3
  10. *
  11. * @contributor Edmund Edgar
  12. * @contributor Peter R. Bloomfield
  13. *
  14. */
  15. // This library expects that the Sloodle config file has already been included
  16. // (along with the Moodle libraries)
  17. /** Include our email functionality. */
  18. require_once(SLOODLE_LIBROOT.'/mail.php');
  19. require_once(SLOODLE_LIBROOT.'/object_configs.php');
  20. require_once(SLOODLE_LIBROOT.'/active_object.php');
  21. require_once(SLOODLE_LIBROOT.'/currency.php');
  22. /**
  23. * Force the user to login, but reject guest logins.
  24. * This function exists to workaround some Moodle 1.8 bugs.
  25. * @return void
  26. */
  27. function sloodle_require_login_no_guest()
  28. {
  29. global $CFG, $SESSION, $FULLME;
  30. // Attempt a direct login initially
  31. require_login(0, false);
  32. // Has the user been logged-in as a guest?
  33. if (isguestuser()) {
  34. // Make sure we can come back here after login
  35. $SESSION->wantsurl = $FULLME;
  36. // Redirect to the appropriate login page
  37. if (empty($CFG->loginhttps)) {
  38. redirect($CFG->wwwroot .'/login/index.php');
  39. } else {
  40. $wwwroot = str_replace('http:','https:', $CFG->wwwroot);
  41. redirect($wwwroot .'/login/index.php');
  42. }
  43. exit();
  44. }
  45. }
  46. /**
  47. * Sets a Sloodle configuration value.
  48. * This data will be stored in Moodle's "config" table, so it will persist even after Sloodle is uninstalled.
  49. * After being set, it will be available (read-only) as a named member of Moodle's $CFG variable.
  50. * <b>NOTE:</b> in Sloodle debug mode, this function will terminate the script with an error if the name is not prefixed with "sloodle_".
  51. * @param string $name The name of the value to be stored (should be prefixed with "sloodle_")
  52. * @param string $value The string representation of the value to be stored
  53. * @return bool True on success, or false on failure (may fail if database query encountered an error)
  54. * @see sloodle_get_config()
  55. */
  56. function sloodle_set_config($name, $value)
  57. {
  58. // If in debug mode, ensure the name is prefixed appropriately for Sloodle
  59. if (defined('SLOODLE_DEBUG') && SLOODLE_DEBUG) {
  60. if (substr_count($name, 'sloodle_') < 1) {
  61. exit ("ERROR: sloodle_set_config(..) called with invalid value name \"$name\". Expected \"sloodle_\" prefix.");
  62. }
  63. }
  64. // Use the standard Moodle config function, ignoring the 3rd parameter ("plugin", which defaults to NULL)
  65. return set_config(strtolower($name), $value);
  66. }
  67. /**
  68. * Gets a Sloodle configuration value from Moodle's "config" table.
  69. * This function does not necessarily need to be used.
  70. * All configuration data is available as named members of Moodle's $CFG global variable.
  71. * <b>NOTE:</b> in Sloodle debug mode, this function will terminate the script with an error if the name is not prefixed with "sloodle_".
  72. * @param string $name The name of the value to be stored (should be prefixed with "sloodle_")
  73. * @return mixed A string containing the configuration value, or false if the query failed (e.g. if the named value didn't exist)
  74. * @see sloodle_set_config()
  75. */
  76. function sloodle_get_config($name)
  77. {
  78. // If in debug mode, ensure the name is prefixed appropriately for Sloodle
  79. if (defined('SLOODLE_DEBUG') && SLOODLE_DEBUG) {
  80. if (substr_count($name, 'sloodle_') < 1) {
  81. exit ("ERROR: sloodle_get_config(..) called with invalid value name \"$name\". Expected \"sloodle_\" prefix.");
  82. }
  83. }
  84. // Use the Moodle config function, ignoring the plugin parameter
  85. $val = get_config(NULL, strtolower($name));
  86. // Older Moodle versions return a database record object instead of the value itself
  87. // Workaround:
  88. if (is_object($val)) return $val->value;
  89. return $val;
  90. }
  91. /**
  92. * Determines whether or not auto-registration is allowed for the site.
  93. * @return bool True if auto-reg is allowed on the site, or false otherwise.
  94. */
  95. function sloodle_autoreg_enabled_site()
  96. {
  97. return (bool)sloodle_get_config('sloodle_allow_autoreg');
  98. }
  99. /**
  100. * Determines whether or not auto-enrolment is allowed for the site.
  101. * @return bool True if auto-enrolment is allowed on the site, or false otherwise.
  102. */
  103. function sloodle_autoenrol_enabled_site()
  104. {
  105. return (bool)sloodle_get_config('sloodle_allow_autoenrol');
  106. }
  107. /**
  108. * Sends an XMLRPC message into Second Life.
  109. * @param string $channel A string containing a UUID identifying the XMLRPC channel in SL to be used
  110. * @param int $intval An integer value to be sent in the message
  111. * @param string $strval A string value to be sent in the message
  112. * @return bool True if successful, or false if an error occurs
  113. */
  114. function sloodle_send_xmlrpc_message($channel,$intval,$strval)
  115. {
  116. // Include our XMLRPC library
  117. require_once(SLOODLE_DIRROOT.'/lib/xmlrpc.inc');
  118. // Instantiate a new client object for communicating with Second Life
  119. $client = new xmlrpc_client("http://xmlrpc.secondlife.com/cgi-bin/xmlrpc.cgi");
  120. // Construct the content of the RPC
  121. $content = '<?xml version="1.0"?><methodCall><methodName>llRemoteData</methodName><params><param><value><struct><member><name>Channel</name><value><string>'.$channel.'</string></value></member><member><name>IntValue</name><value><int>'.$intval.'</int></value></member><member><name>StringValue</name><value><string>'.$strval.'</string></value></member></struct></value></param></params></methodCall>';
  122. // Attempt to send the data via http
  123. $response = $client->send(
  124. $content,
  125. 60,
  126. 'http'
  127. );
  128. //var_dump($response); // Debug output
  129. // Make sure we got a response value
  130. if (!isset($response->val) || empty($response->val) || is_null($response->val)) {
  131. // Report an error if we are in debug mode
  132. if (defined('SLOODLE_DEBUG') && SLOODLE_DEBUG) {
  133. print '<p align="left">Not getting the expected XMLRPC response. Is Second Life broken again?<br/>';
  134. if (isset($response->errstr)) print "XMLRPC Error - ".$response->errstr;
  135. print '</p>';
  136. }
  137. return FALSE;
  138. }
  139. // Check the contents of the response value
  140. //if (defined('SLOODLE_DEBUG') && SLOODLE_DEBUG) {
  141. // print_r($response->val);
  142. //}
  143. //TODO: Check the details of the response to see if this was successful or not...
  144. return TRUE;
  145. }
  146. /**
  147. * Old logging function
  148. * @todo <b>May require update?</b>
  149. */
  150. function sloodle_add_to_log($courseid = null, $module = null, $action = null, $url = null, $cmid = null, $info = null)
  151. {
  152. global $CFG;
  153. // TODO: Make sure we set this in the calling function, then remove this bit
  154. if ($courseid == null) {
  155. $courseid = optional_param('sloodle_courseid',0,PARAM_RAW);
  156. }
  157. // if no action is specified, use the object name
  158. if ($action == null) {
  159. $action = $_SERVER['X-SecondLife-Object-Name'];
  160. }
  161. $region = $_SERVER['X-SecondLife-Region'];
  162. if ($info == null) {
  163. $info = $region;
  164. }
  165. $slurl = '';
  166. if (preg_match('/^(.*)\(.*?\)$/',$region,$matches)) { // strip the coordinates, eg. Cicero (123,123)
  167. $region = $matches[1];
  168. }
  169. $xyz = $_SERVER['X-SecondLife-Local-Position'];
  170. if (preg_match('/^\((.*?),(.*?),(.*?)\)$/',$xyz,$matches)) {
  171. $xyz = $matches[1].'/'.$matches[2].'/'.$matches[3];
  172. }
  173. return add_to_log($courseid, null, $action, $CFG->wwwroot.'/mod/sloodle/toslurl.php?region='.urlencode($region).'&xyz='.$xyz, $userid, $info );
  174. //return add_to_log($courseid, null, "ok", "ok", $userid, "ok");
  175. }
  176. /**
  177. * Determines whether or not Sloodle is installed.
  178. * Queries Moodle's modules table for a Sloodle entry.
  179. * <b>NOTE:</b> does not check for the presence of the Sloodle files.
  180. * @return bool True if Sloodle is installed, or false otherwise.
  181. */
  182. function sloodle_is_installed()
  183. {
  184. // Is there a Sloodle entry in the modules table?
  185. return sloodle_record_exists('modules', 'name', 'sloodle');
  186. }
  187. /**
  188. * Generates a random login security token.
  189. * Uses mixed-case letters and numbers to generate a random 16-character string.
  190. * @return string
  191. * @see sloodle_random_web_password()
  192. */
  193. function sloodle_random_security_token()
  194. {
  195. // Define the characters we can use in our token, and get the length of it
  196. $str = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  197. $strlen = strlen($str) - 1;
  198. // Prepare the token variable
  199. $token = '';
  200. // Loop once for each output character
  201. for($length = 0; $length < 16; $length++) {
  202. // Shuffle the string, then pick and store a random character
  203. $str = str_shuffle($str);
  204. $char = mt_rand(0, $strlen);
  205. $token .= $str[$char];
  206. }
  207. return $token;
  208. }
  209. /**
  210. * Generates a random web password
  211. * Uses mixed-case letters and numbers to generate a random 8-character string.
  212. * @return string
  213. * @see sloodle_random_security_token()
  214. */
  215. function sloodle_random_web_password()
  216. {
  217. // Define the characters we can use in our token, and get the length of it
  218. $str = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
  219. $strlen = strlen($str) - 1;
  220. // Prepare the password string
  221. $pwd = '';
  222. // Loop once for each output character
  223. for($length = 0; $length < 8; $length++) {
  224. // Shuffle the string, then pick and store a random character
  225. $str = str_shuffle($str);
  226. $char = mt_rand(0, $strlen);
  227. $pwd .= $str[$char];
  228. }
  229. return $pwd;
  230. }
  231. /**
  232. * Generates a random prim password (7 to 9 digit number).
  233. * @return string The password as a string
  234. */
  235. function sloodle_random_prim_password()
  236. {
  237. return (string)mt_rand(1000000, 999999999);
  238. }
  239. /**
  240. * Converts a string vector to an array vector.
  241. * String vector should be of format "<x,y,z>".
  242. * Converts to associative array with members 'x', 'y', and 'z'.
  243. * Returns false if input parameter was not of correct format.
  244. * @param string $vector A string vector of format "<x,y,z>".
  245. * @return mixed
  246. * @see sloodle_array_to_vector()
  247. * @see sloodle_round_vector()
  248. */
  249. function sloodle_vector_to_array($vector)
  250. {
  251. if (preg_match('/<(.*?),(.*?),(.*?)>/',$vector,$vectorbits)) {
  252. $arr = array();
  253. $arr['x'] = $vectorbits[1];
  254. $arr['y'] = $vectorbits[2];
  255. $arr['z'] = $vectorbits[3];
  256. return $arr;
  257. }
  258. return false;
  259. }
  260. /**
  261. * Converts an array vector to a string vector.
  262. * Array vector should be associative, containing elements 'x', 'y', and 'z'.
  263. * Converts to a string vector of format "<x,y,z>".
  264. * @return string
  265. * @see sloodle_vector_to_array()
  266. * @see sloodle_round_vector()
  267. */
  268. function sloodle_array_to_vector($arr)
  269. {
  270. $ret = '<'.$arr['x'].','.$arr['y'].','.$arr['z'].'>';
  271. return $ret;
  272. }
  273. /**
  274. * Obtains the identified course module instance database record.
  275. * @param int $id The integer ID of a course module instance
  276. * @return mixed A database record if successful, or false if it could not be found
  277. */
  278. function sloodle_get_course_module_instance($id)
  279. {
  280. return sloodle_get_record('course_modules', 'id', $id);
  281. }
  282. /**
  283. * Determines whether or not the specified course module instance is visible.
  284. * Checks that the instance itself and the course section are both valid.
  285. * @param int $id The integer ID of a course module instance.
  286. * @return bool True if visible, or false if invisible or not found
  287. */
  288. function sloodle_is_course_module_instance_visible($id)
  289. {
  290. // Get the course module instance record, whether directly from the parameter, or from the database
  291. if (is_object($id)) {
  292. $course_module_instance = $id;
  293. } else if (is_int($id)) {
  294. if (!($course_module_instance = sloodle_get_record('course_modules', 'id', $id))) return FALSE;
  295. } else return FALSE;
  296. // Make sure the instance itself is visible
  297. if ((int)$course_module_instance->visible == 0) return FALSE;
  298. // Find out which section it is in, and if that section is valid
  299. if (!($section = sloodle_get_record('course_sections', 'id', $course_module_instance->section))) return FALSE;
  300. if ((int)$section->visible == 0) return FALSE;
  301. // Looks like the module is visible
  302. return TRUE;
  303. }
  304. /**
  305. * Determines if the specified course module instance is of the named type.
  306. * For example, this can check if a particular instance is a "forum" or a "chat".
  307. * @param int $id The integer ID of a course module instance
  308. * @param string $module_name Module type to check (must be the exact name of an installed module, e.g. 'sloodle' or 'quiz')
  309. * @return bool True if the module is of the specified type, or false otherwise
  310. */
  311. function sloodle_check_course_module_instance_type($id, $module_name)
  312. {
  313. // Get the record for the module type
  314. if (!($module_record = sloodle_get_record('modules', 'name', $module_name))) return FALSE;
  315. // Get the course module instance record, whether directly from the parameter, or from the database
  316. if (is_object($id)) {
  317. $course_module_instance = $id;
  318. } else if (is_int($id)) {
  319. if (!($course_module_instance = sloodle_get_record('course_modules', 'id', $id))) return FALSE;
  320. } else return FALSE;
  321. // Check the type of the instance
  322. return ($course_module_instance->module == $module_record->id);
  323. }
  324. /**
  325. * Obtains the ID number of the specified module (type not instance).
  326. * @param string $name The name of the module type to check, e.g. 'sloodle' or 'forum'
  327. * @return mixed Integer containing module ID, or false if it is not installed
  328. */
  329. function sloodle_get_module_id($name)
  330. {
  331. // Ensure the name is a non-empty string
  332. if (!is_string($name) || empty($name)) return FALSE;
  333. // Obtain the module record
  334. if (!($module_record = sloodle_get_record('modules', 'name', $module_name))) return FALSE;
  335. return $module_record->id;
  336. }
  337. /**
  338. * Checks if the specified position is in the current (site-wide) loginzone.
  339. * @param mixed $pos A string vector or an associated array vector
  340. * @return bool True if position is in LoginZone, or false if not
  341. * @see sloodle_login_zone_coordinates()
  342. * @todo Update or remove... no longer valid
  343. */
  344. function sloodle_position_is_in_login_zone($pos)
  345. {
  346. // Get a position array from the parameter
  347. $posarr = NULL;
  348. if (is_array($pos) && count($pos) == 3) {
  349. $posarr = $pos;
  350. } else if (is_string($pos)) {
  351. $posarr = sloodle_vector_to_array($pos);
  352. } else {
  353. return FALSE;
  354. }
  355. // Fetch the loginzone boundaries
  356. list($maxarr,$minarr) = sloodle_login_zone_coordinates();
  357. // Make sure the position is not past the maximum bounds
  358. if ( ($posarr['x'] > $maxarr['x']) || ($posarr['y'] > $maxarr['y']) || ($posarr['z'] > $maxarr['z']) ) {
  359. return FALSE;
  360. }
  361. // Make sure the position is not past the minimum bounds
  362. if ( ($posarr['x'] < $minarr['x']) || ($posarr['y'] < $minarr['y']) || ($posarr['z'] < $minarr['z']) ) {
  363. return FALSE;
  364. }
  365. return TRUE;
  366. }
  367. /**
  368. * Generates teleport coordinates for a user who has already finished the LoginZone process.
  369. * @param string $pos A string vector giving the position of the LoginZone
  370. * @param string $size A string vector giving the size of the LoginZone
  371. * @return array, bool An associative array vector containing a teleport location, or false if the operation fails.
  372. */
  373. function sloodle_finished_login_coordinates($pos, $size)
  374. {
  375. // Make sure the parameters are valid types
  376. if (!is_string($pos) || !is_string($size)) {
  377. return FALSE;
  378. }
  379. // Convert both to arrays
  380. $posarr = sloodle_vector_to_array($pos);
  381. $sizearr = sloodle_vector_to_array($size);
  382. // Calculate a position just below the loginzone
  383. $coord = array();
  384. $coord['x'] = round($posarr['x'],0);
  385. $coord['y'] = round($posarr['y'],0);
  386. $coord['z'] = round(($posarr['z']-(($sizearr['z'])/2)-2),0);
  387. return $coord;
  388. }
  389. /**
  390. * Generates a random position within a cuboid zone of the specified size.
  391. * (Note: leaves a 2 metre margin round the outside)
  392. * @param array $size Associative array giving the size of the zone
  393. * @return array An associative vector array
  394. */
  395. function sloodle_random_position_in_zone($size)
  396. {
  397. // Construct the half-size array
  398. $halfsize = array('x'=>($size['x'] / 2.0) - 2.0, 'y'=>($size['y'] / 2.0) - 2.0, 'z'=>($size['z'] / 2.0) - 2.0);
  399. $pos = array();
  400. $pos['x'] = mt_rand(0.0, $size['x'] - 4.0) - $halfsize['x'];
  401. $pos['y'] = mt_rand(0.0, $size['y'] - 4.0) - $halfsize['y'];
  402. $pos['z'] = mt_rand(0.0, $size['z'] - 4.0) - $halfsize['z'];
  403. return $pos;
  404. }
  405. // Round the specified 3d vector to integer values
  406. // $pos should be a vector string "<x,y,z>" or an associative array {x,y,z}
  407. // Return is the same as the type passed-in
  408. // If the input type is unrecognised, it simply returns it back out unchanged
  409. /**
  410. * Rounds the specified 3d vector integer values.
  411. * Can handle/return a string vector, or an array vector.
  412. * (Output type matches input type).
  413. * @param mixed $pos Either a string vector or an array vector
  414. * @return mixed
  415. */
  416. function sloodle_round_vector($pos)
  417. {
  418. // We will work with an array, but allow for conversion to/from string
  419. $arrayvec = $pos;
  420. $returnstring = FALSE;
  421. // Is it a string?
  422. if (is_string($pos)) {
  423. $arrayvec = sloodle_vector_to_array($pos);
  424. $returnstring = TRUE;
  425. } else if (!is_array($pos)) {
  426. return $pos;
  427. }
  428. // Construct an output array
  429. $output = array();
  430. foreach ($arrayvec as $key => $val) {
  431. $output[$key] = round($val, 0);
  432. }
  433. // If we need to convert it back to a string, then do so
  434. if ($returnstring) {
  435. return sloodle_array_to_vector($output);
  436. }
  437. return $output;
  438. }
  439. /**
  440. * Calculates the maximum and minimum bounds of the specified LoginZone
  441. * Returns the bounds as a numeric array of two associate array vectors: ($max, $min).
  442. * (Or returns false if no LoginZone position/size could be found in the Moodle configuration table).
  443. * @param string $pos A string vector giving the position of the LoginZone
  444. * @param string $size A string vector giving the size of the LoginZone
  445. * @return array
  446. */
  447. function sloodle_login_zone_bounds($pos, $size)
  448. {
  449. // Make sure the parameters are valid types
  450. if (($pos == FALSE) || ($size == FALSE)) {
  451. return FALSE;
  452. }
  453. // Convert both to arrays
  454. $posarr = sloodle_vector_to_array($pos);
  455. $sizearr = sloodle_vector_to_array($size);
  456. // Calculate the bounds
  457. $max = array();
  458. $max['x'] = $posarr['x']+(($sizearr['x'])/2)-2;
  459. $max['y'] = $posarr['y']+(($sizearr['y'])/2)-2;
  460. $max['z'] = $posarr['z']+(($sizearr['z'])/2)-2;
  461. $min = array();
  462. $min['x'] = $posarr['x']-(($sizearr['x'])/2)+2;
  463. $min['y'] = $posarr['y']-(($sizearr['y'])/2)+2;
  464. $min['z'] = $posarr['z']-(($sizearr['z'])/2)+2;
  465. return array($max,$min);
  466. }
  467. /**
  468. * Checks if the given prim password is valid.
  469. * @param string $password The password string to check
  470. * @return bool True if it is valid, or false otherwise.
  471. */
  472. function sloodle_validate_prim_password($password)
  473. {
  474. // Check that it's a string
  475. if (!is_string($password)) return false;
  476. // Check the length
  477. $len = strlen($password);
  478. if ($len < 5 || $len > 9) return false;
  479. // Check that it's all numbers
  480. if (!ctype_digit($password)) return false;
  481. // Check that it doesn't start with a 0
  482. if ($password[0] == '0') return false;
  483. // It all seems fine
  484. return true;
  485. }
  486. /**
  487. * Checks if the given prim password is valid, and provides feedback.
  488. * An array is written to by reference, each element containing error codes.
  489. * Each error code is a word. The full text of the error message may be obtained
  490. * from the string file by looking for "primpass:errorcode".
  491. *
  492. * @param string $password The password to validate
  493. * @param array &$errors An array (passed by reference) which will contain any error messages
  494. * @return bool True if the prim password is valid, or false otherwise
  495. */
  496. function sloodle_validate_prim_password_verbose($password, &$errors)
  497. {
  498. // Initialise variables
  499. $errors = array();
  500. $result = true;
  501. // Check that it's a string
  502. if (!is_string($password)) {
  503. $errors[] = 'invalidtype';
  504. $result = false;
  505. }
  506. // Check the length
  507. $len = strlen($password);
  508. if ($len < 5) {
  509. $errors[] = 'tooshort';
  510. $result = false;
  511. }
  512. if ($len > 9) {
  513. $errors[] = 'toolong';
  514. $result = false;
  515. }
  516. // Check that it's all numbers
  517. if (!ctype_digit($password)) {
  518. $errors[] = 'numonly';
  519. $result = false;
  520. }
  521. // Check that it doesn't start with a 0
  522. if ($password[0] == '0') {
  523. $errors[] = 'leadingzero';
  524. $result = false;
  525. }
  526. return $result;
  527. }
  528. /**
  529. * Stores a pending login notification for an auto-registered user.
  530. * A cron job will process the pending notification queue.
  531. * @param string $destination Identifies the destination of the notification (for SL, this will be the object UUID. The send function will construct the email address)
  532. * @param string $avatar Identifier for the avatar being notified
  533. * @param string $username The username to notify the user of
  534. * @param string $password The (plaintext) password to notify the user of
  535. * @return bool True if successful, or false otherwise
  536. */
  537. function sloodle_login_notification($destination, $avatar, $username, $password)
  538. {
  539. // If another pending notification already exists for the same username, then delete it
  540. sloodle_delete_records('sloodle_login_notifications', 'username', $username);
  541. // Add the new details
  542. $notification = new stdClass();
  543. $notification->destination = $destination;
  544. $notification->avatar = $avatar;
  545. $notification->username = $username;
  546. $notification->password = $password;
  547. return (bool)sloodle_insert_record('sloodle_login_notifications', $notification);
  548. }
  549. /**
  550. * Send a login notification.
  551. * @param string $destination Identifies the destination of the notification (for SL, this will be the object UUID. The target email address will be constructed)
  552. * @param string $avatar Identifier for the avatar being notified
  553. * @param string $username The username to notify the user of
  554. * @param string $password The (plaintext) password to notify the user of
  555. * @return bool True if successful, or false otherwise
  556. */
  557. function sloodle_send_login_notification($destination, $avatar, $username, $password)
  558. {
  559. global $CFG;
  560. return sloodle_text_email_sl($destination, 'SLOODLE_LOGIN', "$avatar|{$CFG->wwwroot}|$username|$password");
  561. }
  562. /**
  563. * Processes pending login notifications, up to a certain limit.
  564. * Retrieves the requests one-at-a-time for processing.
  565. * This is slower, but ensures minimal damage if the process is terminated, e.g. due to server timeout.
  566. * @param int $limit The maximum number of pending requests to process.
  567. * @return void
  568. */
  569. function sloodle_process_login_notifications($limit = 25)
  570. {
  571. global $CFG;
  572. // Validate the limit
  573. $limit = (int)$limit;
  574. if ($limit < 1) return;
  575. // Go through each one
  576. for ($i = 0; $i < $limit; $i++) {
  577. // Obtain the first record
  578. $recs = sloodle_get_records('sloodle_login_notifications', '', '', 'id', '*', 0, $limit);
  579. if (!$recs) return false;
  580. reset($recs);
  581. $rec = current($recs);
  582. // Determine the user ID of the person who requested this
  583. $userid = 0;
  584. if (!($sloodleuser = sloodle_get_record('sloodle_users', 'uuid', $rec->avatar))) {
  585. // Failed to the user - get the guest user instead
  586. $guestdata = guest_user();
  587. $userid = $guestdata->id;
  588. } else {
  589. // Got the data - store the user ID
  590. $userid = $sloodleuser->userid;
  591. }
  592. // Send the notification
  593. if (sloodle_send_login_notification($rec->destination, $rec->avatar, $rec->username, $rec->password)) {
  594. // Log the notification
  595. add_to_log(SITEID, 'sloodle', 'view', '', 'Sent login details by email to avatar in-world', 0, $userid);
  596. } else {
  597. // Log the failed notification (but don't keep trying the same one)
  598. add_to_log(SITEID, 'sloodle', 'view failed', '', 'Failed to send login details by email to avatar in-world', 0, $userid);
  599. }
  600. // Delete the record from the data
  601. sloodle_delete_records('sloodle_login_notifications', 'id', $rec->id);
  602. }
  603. }
  604. /**
  605. * Extracts a value from a name-value associative array if it is set.
  606. * (The array should associate name to value).
  607. * @param array $settings The array of names and values
  608. * @param string $name The name of the value to retrieve
  609. * @param mixed $default The default value to return if the specified value was not found
  610. * @return mixed The value from the input array, or the $default parameter
  611. */
  612. function sloodle_get_value($settings, $name, $default = null)
  613. {
  614. if (is_array($settings) && isset($settings[$name])) return $settings[$name];
  615. return $default;
  616. }
  617. /**
  618. * Outputs the standard form elements for access levels in object configuration.
  619. * Each part can be optionally hidden, and default values can be provided.
  620. * (Note: the server access level must be communicated from the object back to Moodle... rubbish implementation, but it works!)
  621. * @param array $current_config An associative array of setting names to values, containing defaults. (Ignored if null).
  622. * @param bool $show_use_object Determines whether or not the "Use Object" setting is shown
  623. * @param bool $show_control_object Determines whether or not the "Control Object" setting is shown
  624. * @param bool $show_server Determines whether or not the server access setting is shown
  625. * @return void
  626. */
  627. function sloodle_print_access_level_options($current_config, $show_use_object = true, $show_control_object = true, $show_server = true)
  628. {
  629. // Quick-escape: if everything is being suppressed, then do nothing
  630. if (!($show_use_object || $show_control_object || $show_server)) return;
  631. // Fetch default values from the configuration, if possible
  632. $sloodleobjectaccessleveluse = sloodle_get_value($current_config, 'sloodleobjectaccessleveluse', SLOODLE_OBJECT_ACCESS_LEVEL_PUBLIC);
  633. $sloodleobjectaccesslevelctrl = sloodle_get_value($current_config, 'sloodleobjectaccesslevelctrl', SLOODLE_OBJECT_ACCESS_LEVEL_OWNER);
  634. $sloodleserveraccesslevel = sloodle_get_value($current_config, 'sloodleserveraccesslevel', SLOODLE_SERVER_ACCESS_LEVEL_PUBLIC);
  635. // Define our object access level array
  636. $object_access_levels = array( SLOODLE_OBJECT_ACCESS_LEVEL_PUBLIC => get_string('accesslevel:public','sloodle'),
  637. SLOODLE_OBJECT_ACCESS_LEVEL_GROUP => get_string('accesslevel:group','sloodle'),
  638. SLOODLE_OBJECT_ACCESS_LEVEL_OWNER => get_string('accesslevel:owner','sloodle') );
  639. // Define our server access level array
  640. $server_access_levels = array( SLOODLE_SERVER_ACCESS_LEVEL_PUBLIC => get_string('accesslevel:public','sloodle'),
  641. SLOODLE_SERVER_ACCESS_LEVEL_COURSE => get_string('accesslevel:course','sloodle'),
  642. SLOODLE_SERVER_ACCESS_LEVEL_SITE => get_string('accesslevel:site','sloodle'),
  643. SLOODLE_SERVER_ACCESS_LEVEL_STAFF => get_string('accesslevel:staff','sloodle') );
  644. // Display box and a heading
  645. sloodle_print_box_start('generalbox boxaligncenter');
  646. echo '<h3>'.get_string('accesslevel','sloodle').'</h3>';
  647. // Print the object settings
  648. if ($show_use_object || $show_control_object) {
  649. // Object access
  650. echo '<b>'.get_string('accesslevelobject','sloodle').'</b><br><i>'.get_string('accesslevelobject:desc','sloodle').'</i><br><br>';
  651. // Use object
  652. if ($show_use_object) {
  653. echo get_string('accesslevelobject:use','sloodle').': ';
  654. choose_from_menu($object_access_levels, 'sloodleobjectaccessleveluse', $sloodleobjectaccessleveluse, '');
  655. echo '<br><br>';
  656. }
  657. // Control object
  658. if ($show_control_object) {
  659. echo get_string('accesslevelobject:control','sloodle').': ';
  660. choose_from_menu($object_access_levels, 'sloodleobjectaccesslevelctrl', $sloodleobjectaccesslevelctrl, '');
  661. echo '<br><br>';
  662. }
  663. }
  664. // Print the server settings
  665. if ($show_server) {
  666. // Server access
  667. echo '<b>'.get_string('accesslevelserver','sloodle').'</b><br><i>'.get_string('accesslevelserver:desc','sloodle').'</i><br><br>';
  668. echo get_string('accesslevel','sloodle').': ';
  669. choose_from_menu($server_access_levels, 'sloodleserveraccesslevel', $sloodleserveraccesslevel, '');
  670. echo '<br>';
  671. }
  672. sloodle_print_box_end();
  673. }
  674. function sloodle_access_level_option_choice($option, $current_config, $show, $prefix = '', $suffix = '') {
  675. $access_levels = array();
  676. if ($option == 'sloodleserveraccesslevel') {
  677. $access_levels = array( SLOODLE_SERVER_ACCESS_LEVEL_PUBLIC => get_string('accesslevel:public','sloodle'),
  678. SLOODLE_SERVER_ACCESS_LEVEL_COURSE => get_string('accesslevel:course','sloodle'),
  679. SLOODLE_SERVER_ACCESS_LEVEL_SITE => get_string('accesslevel:site','sloodle'),
  680. SLOODLE_SERVER_ACCESS_LEVEL_STAFF => get_string('accesslevel:staff','sloodle')
  681. );
  682. } else {
  683. $access_levels = array( SLOODLE_OBJECT_ACCESS_LEVEL_PUBLIC => get_string('accesslevel:public','sloodle'),
  684. SLOODLE_OBJECT_ACCESS_LEVEL_GROUP => get_string('accesslevel:group','sloodle'),
  685. SLOODLE_OBJECT_ACCESS_LEVEL_OWNER => get_string('accesslevel:owner','sloodle')
  686. );
  687. }
  688. $defaults = array(
  689. 'sloodleobjectaccessleveluse' => SLOODLE_OBJECT_ACCESS_LEVEL_PUBLIC,
  690. 'sloodleobjectaccesslevelctrl' => SLOODLE_OBJECT_ACCESS_LEVEL_OWNER,
  691. 'sloodleserveraccesslevel' => SLOODLE_SERVER_ACCESS_LEVEL_PUBLIC
  692. );
  693. // Fetch default values from the configuration, if possible
  694. $selected_value = sloodle_get_value($current_config, $option, $defaults[$option]);
  695. if ($show) {
  696. return choose_from_menu($access_levels, $prefix.$option.$suffix, $selected_value, '', '', 0, $return = true);
  697. } else {
  698. return '&nbsp;';
  699. }
  700. }
  701. /**
  702. * Returns a very approximate natural language description of a period of time (in minutes, hours, days, or weeks).
  703. * Can also be used to describe how long ago something happened, in which case anything less than 1 minute is treated as 'now'.
  704. * @param int $secs Number of seconds in period of time
  705. * @param bool $ago If true (not default), then the time will be described in past tense, e.g. "3 days ago", as opposed to simply "3 days".
  706. * @return string
  707. */
  708. function sloodle_describe_approx_time($secs, $ago = false)
  709. {
  710. // Make sure the time is a positive integer
  711. $secs = (int)$secs;
  712. if ($secs < 0) $secs *= -1;
  713. // Less than a minute
  714. if ($secs < 60) {
  715. // If we are describing a past time, then approximate to 'now'
  716. if ($ago) return ucwords(get_string('now', 'sloodle'));
  717. // Give the number of seconds
  718. if ($secs == 1) return '1 '. get_string('second', 'sloodle');
  719. return $secs.' '. get_string('seconds', 'sloodle');
  720. }
  721. // This variable will hold the time description
  722. $desc = '';
  723. // Roughly 1 minute
  724. if ($secs < 120) $desc = '1 '. get_string('minute', 'sloodle');
  725. // Several minutes (up to 1 hour)
  726. else if ($secs < 3600) $desc = ((string)(int)($secs / 60)).' '. get_string('minutes', 'sloodle');
  727. // Roughly 1 hour
  728. else if ($secs < 7200) $desc = '1 '. get_string('hour', 'sloodle');
  729. // Several hours (up to 1 day)
  730. else if ($secs < 86400) $desc = ((string)(int)($secs / 3600)).' '. get_string('hours', 'sloodle');
  731. // Roughly 1 day
  732. else if ($secs < 172800) $desc = '1 '. get_string('day', 'sloodle');
  733. // Several days (up to 1 week)
  734. else if ($secs < 604800) $desc = ((string)(int)($secs / 86400)).' '. get_string('days', 'sloodle');
  735. // Roughly 1 week
  736. else if ($secs < 1209600) $desc = '1 '. get_string('week', 'sloodle');
  737. // Several weeks (up to 2 months)
  738. else if ($secs < 5184000) $desc = ((string)(int)($secs / 604800)).' '. get_string('weeks', 'sloodle');
  739. // Several months (up to 11 months)
  740. else if ($secs < 29462400) $desc = ((string)(int)($secs / 2592000)).' '. get_string('months', 'sloodle');
  741. // 1 year
  742. else if ($secs < 63072000) $desc = '1 '. get_string('year', 'sloodle');
  743. // Several years
  744. else $desc = ((string)(int)($secs / 31536000)).' '. get_string('years', 'sloodle');
  745. // Add 'ago' if necessary
  746. if ($ago) return get_string('timeago', 'sloodle', $desc);
  747. return $desc;
  748. }
  749. /**
  750. * Gets the basic URL of the current web-page being accessed.
  751. * Includes the protocol, hostname, and script path/name.
  752. * @return string
  753. */
  754. function sloodle_get_web_path()
  755. {
  756. // Check for the protocol
  757. if (empty($_SERVER['HTTPS']) || $_SERVER['HTTPS'] == 'off') $protocol = "http";
  758. else $protocol = "https";
  759. // Get the host name (e.g. domain)
  760. $host = $_SERVER['SERVER_NAME'];
  761. // Finally, get the script path/name
  762. $file = $_SERVER['SCRIPT_NAME'];
  763. return $protocol.'://'.$host.$file;
  764. }
  765. /**
  766. * Gets an array of subdirectories within the given directory.
  767. * Ignores anything which starts with a .
  768. * @param string $dir The directory to search WITHOUT a trailing slash. (Note: cannot search the current directory or higher in the file hierarchy)
  769. * @param bool $relative If TRUE (default) the array of results will be relative to the input directory. Otherwise, they will include the input directory path.
  770. * @return array|false A numeric array of subdirectory names sorted alphabetically, or false if an error occurred (such as the input value not being a directory)
  771. */
  772. function sloodle_get_subdirectories($dir, $relative = true)
  773. {
  774. // Make sure we have a valid directory
  775. if (empty($dir)) return false;
  776. // Open the directory
  777. if (!is_dir($dir)) return false;
  778. if (!$dh = opendir($dir)) return false;
  779. // Go through each item
  780. $output = array();
  781. while (($file = readdir($dh)) !== false) {
  782. // Ignore anything starting with a . and anything which isn't a directory
  783. if (strpos($file, '.') == 0) continue;
  784. $filetype = @filetype($dir.'/'.$file);
  785. if (empty($filetype) || $filetype != 'dir') continue;
  786. // Store it
  787. if ($relative) $output[] = $file;
  788. else $output[] = $dir.'/'.$file;
  789. }
  790. closedir($dh);
  791. natcasesort($output);
  792. return $output;
  793. }
  794. /**
  795. * Gets an array of files within the given directory.
  796. * Ignores anything which starts with a .
  797. * @param string $dir The directory to search WITHOUT a trailing slash. (Note: cannot search the current directory or higher in the file hierarchy)
  798. * @param bool $relative If TRUE (default) the array of results will be relative to the input directory. Otherwise, they will include the input directory path.
  799. * @return array|false A numeric array of file names sorted alphabetically, or false if an error occurred (such as the input value not being a directory)
  800. */
  801. function sloodle_get_files($dir, $relative = true)
  802. {
  803. // Make sure we have a valid directory
  804. if (empty($dir)) return false;
  805. // Open the directory
  806. if (!is_dir($dir)) return false;
  807. if (!$dh = opendir($dir)) return false;
  808. // Go through each item
  809. $output = array();
  810. while (($file = readdir($dh)) !== false) {
  811. // Ignore anything starting with a . and anything which isn't a file
  812. if (strpos($file, '.') == 0) continue;
  813. $filetype = @filetype($dir.'/'.$file);
  814. if (empty($filetype) || $filetype != 'file') continue;
  815. // Store it
  816. if ($relative) $output[] = $file;
  817. else $output[] = $dir.'/'.$file;
  818. }
  819. closedir($dh);
  820. natcasesort($output);
  821. return $output;
  822. }
  823. /**
  824. * Gets all object types and versions available in this installation.
  825. * Creates a 2-dimensional associative array.
  826. * The top level is the object name, and the second is the object version (both as strings).
  827. * The associated value is the path to the configuration form script, or boolean false
  828. * if the object has no configuration options.
  829. * @return array|false Returns a 2d associative array if successful, or false if an error occurs
  830. */
  831. function sloodle_get_installed_object_types()
  832. {
  833. // Fetch all sub-directories of the "mod" directory
  834. // Go through each object to parse names and version numbers.
  835. // Object names should have format "name-version" (e.g. "chat-1.0").
  836. // We will skip anything that does not match this format.
  837. // We will also skip anything with a "noshow" file in the folder.
  838. return SloodleObjectConfig::AllAvailableAsNameVersionHash();
  839. }
  840. /**
  841. * Render a page viewing a particular feature, or a SLOODLE module.
  842. * Outputs error text in SLOODLE debug mode.
  843. * @param string $feature The name of a feature to view ("course", "user", "users"), or "module" to indicate that we are viewing some kind of module. Note: features should contain only alphanumric characters.
  844. * @return bool True if successful, or false if not.
  845. */
  846. function sloodle_view($feature)
  847. {
  848. global $CFG, $USER;
  849. // Make sure the parameter is safe -- nothing but alphanumeric characters.
  850. if (!ctype_alnum($feature)) {
  851. sloodle_debug('sloodle_view(..): Invalid characters in view feature, "'.$feature.'"');
  852. return false;
  853. }
  854. if (empty($feature)) {
  855. sloodle_debug('sloodle_view(..): No feature name specified.');
  856. return false;
  857. }
  858. $feature = trim($feature);
  859. // Has a module been requested?
  860. if (strcasecmp($feature, 'module') == 0) {
  861. // We should have an ID parameter, indicating which module has been requested
  862. $id = required_param('id', PARAM_INT);
  863. // Query the database for the SLOODLE module sub-type
  864. $instanceid = sloodle_get_field('course_modules', 'instance', 'id', $id);
  865. if ($instanceid === false) error('Course module instance '.$id.' not found.');
  866. $type = sloodle_get_field('sloodle', 'type', 'id', $instanceid);
  867. if ($type === false) error('SLOODLE module instance '.$instanceid.' not found.');
  868. // We will just use the type as a feature name now.
  869. // This means the following words are unavailable as module sub-types: course, user, users
  870. $feature = $type;
  871. }
  872. // Attempt to include the relevant viewing class
  873. $filename = SLOODLE_DIRROOT."/view/{$feature}.php";
  874. if (!file_exists($filename)) {
  875. error("SLOODLE file not found: view/{$feature}.php");
  876. exit();
  877. }
  878. require_once($filename);
  879. // Create and execute the viewing instance
  880. $classname = 'sloodle_view_'.$feature;
  881. if (!class_exists($classname)) {
  882. error("SLOODLE class missing: {$classname}");
  883. exit();
  884. }
  885. $viewer = new $classname();
  886. $viewer->view();
  887. return true;
  888. }
  889. /**
  890. * Returns the given string, 'cleaned' and ready for output to SL as UTF-8.
  891. * Removes tags and slash-characters.
  892. * @param string str The string to clean.
  893. * @return string
  894. */
  895. function sloodle_clean_for_output($str)
  896. {
  897. return sloodle_strip_new_lines(strip_tags(stripcslashes(@html_entity_decode($str, ENT_QUOTES, 'UTF-8'))));
  898. }
  899. /**
  900. * Returns the src of the first <img> tag found in the html
  901. * @param string str The string to clean.
  902. * @return string
  903. */
  904. function sloodle_extract_first_image_url($html) {
  905. if (preg_match("/<img .*?(?=src)src=\"([^\"]+)\"/si", $html, $m)) {
  906. return $m[1];
  907. }
  908. return '';
  909. }
  910. /**
  911. * Returns the given string with new line characters removed
  912. * Removes new line characters
  913. * @param string data The string to clean.
  914. * @return string
  915. */
  916. function sloodle_strip_new_lines($data) {
  917. $data=str_replace("\r","",$data);
  918. $data=str_replace("\n","",$data);
  919. return $data;
  920. }
  921. /**
  922. * Returns the given string, 'cleaned' and ready for storage in the database.
  923. * Note: removes tags and slash-characters.
  924. * @param string str The string to clean.
  925. * @return string
  926. */
  927. function sloodle_clean_for_db($str)
  928. {
  929. return htmlentities($str, ENT_QUOTES, 'UTF-8');
  930. }
  931. /**
  932. * Converts a shorthand file size to a number of bytes, if necessary.
  933. * This follows PHP shorthand, with K for Kilobytes, M for Megabytes, and G for Gigabytes.
  934. * @param string size The shorthand size to conert
  935. * @return integer The size specified in bytes
  936. */
  937. function sloodle_convert_file_size_shorthand($size)
  938. {
  939. $size = trim($size);
  940. $num = (int)$size;
  941. $char = strtolower($size{strlen($size)-1});
  942. switch ($char)
  943. {
  944. case 'g': $num *= 1024;
  945. case 'm': $num *= 1024;
  946. case 'k': $num *= 1024;
  947. }
  948. return $num;
  949. }
  950. /**
  951. * Converts a file size to plain text.
  952. * For example, will convert "1024" to "1 kilobyte".
  953. * @param integer|string size If an integer, then it is the number of bytes. If a string, then it can be PHP shorthand, such as "1M" for 1 megabyte.
  954. * @return string A text string describing the specified size.
  955. */
  956. function sloodle_get_size_description($size)
  957. {
  958. // Make sure we have a number of bytes
  959. $bytes = 0;
  960. if (is_int($size)) $bytes = $size;
  961. else $bytes = sloodle_convert_file_size_shorthand($size);
  962. $desc = '';
  963. // Keep the number small by going with the largest possible units
  964. if ($bytes >= 1073741824) $desc = ($bytes / 1073741824)." GB";
  965. else if ($bytes >= 1048576) $desc = ($bytes / 1048576). " MB";
  966. else if ($bytes >= 1024) $desc = ($bytes / 1024). " KB";
  967. else $desc = $bytes . " bytes";
  968. return $desc;
  969. }
  970. /**
  971. * Gets the maximum size of a file (in bytes) that can be uploaded using POST.
  972. * @return integer
  973. */
  974. function sloodle_get_max_post_upload()
  975. {
  976. // Get the sizes of the relevant limits
  977. $upload_max_filesize = sloodle_convert_file_size_shorthand(ini_get('upload_max_filesize'));
  978. $post_max_size = sloodle_convert_file_size_shorthand(ini_get('post_max_size'));
  979. // Use the smaller limit
  980. return min($upload_max_filesize, $post_max_size);
  981. }
  982. /*
  983. Used to sign a piece of data to ensure that data passed to the user was issued by us.
  984. Made for the presenter image upload, where we can't use the session because the flash component that talks to the server won't pass a cookie.
  985. */
  986. function sloodle_signature($data) {
  987. global $CFG;
  988. $salt = '';
  989. if ( isset($CFG->sloodle_signature_salt) && ($CFG->sloodle_signature_salt != '' ) ) {
  990. $salt = $CFG->sloodle_signature_salt;
  991. } else {
  992. $salt = random_string(40);
  993. set_config('sloodle_signature_salt', $salt);
  994. }
  995. if (function_exists('hash_hmac')) {
  996. return hash_hmac('sha256', $data, $salt);
  997. }
  998. return sloodle_custom_hmac('sha1', $data, $salt);
  999. }
  1000. // From http://php.net/manual/en/function.hash-hmac.php
  1001. // For use if the php hash_hmac isn't available
  1002. function sloodle_custom_hmac($algo, $data, $key, $raw_output = false)
  1003. {
  1004. $algo = strtolower($algo);
  1005. $pack = 'H'.strlen($algo('test'));
  1006. $size = 64;
  1007. $opad = str_repeat(chr(0x5C), $size);
  1008. $ipad = str_repeat(chr(0x36), $size);
  1009. if (strlen($key) > $size) {
  1010. $key = str_pad(pack($pack, $algo($key)), $size, chr(0x00));
  1011. } else {
  1012. $key = str_pad($key, $size, chr(0x00));
  1013. }
  1014. for ($i = 0; $i < strlen($key) - 1; $i++) {
  1015. $opad[$i] = $opad[$i] ^ $key[$i];
  1016. $ipad[$i] = $ipad[$i] ^ $key[$i];
  1017. }
  1018. $output = $algo($opad.pack($pack, $algo($ipad.$data)));
  1019. return ($raw_output) ? pack($pack, $output) : $output;
  1020. }
  1021. ?>