root/releases/0.9/lib/elgglib.php

Revision 1416, 135.9 kB (checked in by rho, 1 year ago)

missing commit

  • Property svn:eol-style set to native
Line 
1 <?php
2
3 /**
4  * Library of functions for handling input validation
5  * and some HTML generation
6  *
7  * This library incorporates portions of bits of lib/weblib.php
8  * and lib/moodlelib.php from moodle
9  * http://moodle.org || http://sourceforge.net/projects/moodle
10  *
11  * @copyright Copyright (C) 2001-2003  Martin Dougiamas  http://dougiamas.com
12  * @author Curverider Ltd
13  * @author Martin Dougiamas and many others
14  * @link http://elgg.org/
15  * @license http://www.gnu.org/copyleft/gpl.html GNU Public License
16  * @package elgg
17  * @subpackage elgg.lib
18  */
19
20
21 /// PARAMETER HANDLING ////////////////////////////////////////////////////
22
23 /**
24  * Returns a particular value for the named variable, taken from
25  * POST or GET.  If the parameter doesn't exist then an error is
26  * thrown because we require this variable.
27  *
28  * This function should be used to initialise all required values
29  * in a script that are based on parameters.  Usually it will be
30  * used like this:
31  *    $id = required_param('id');
32  *
33  * @param string $varname the name of the parameter variable we want
34  * @param int $options a bit field that specifies any cleaning needed
35  * @return mixed
36  */
37 function required_param($varname, $options=PARAM_CLEAN) {
38
39     // detect_unchecked_vars addition
40     global $CFG;
41     if (!empty($CFG->detect_unchecked_vars)) {
42         global $UNCHECKED_VARS;
43         unset ($UNCHECKED_VARS->vars[$varname]);
44     }
45
46     if (isset($_POST[$varname])) {       // POST has precedence
47         $param = $_POST[$varname];
48     } else if (isset($_GET[$varname])) {
49         $param = $_GET[$varname];
50     } else {
51         error('A required parameter ('.$varname.') was missing');
52     }
53
54     return clean_param($param, $options);
55 }
56
57 /**
58  * Returns a particular value for the named variable, taken from
59  * POST or GET, otherwise returning a given default.
60  *
61  * This function should be used to initialise all optional values
62  * in a script that are based on parameters.  Usually it will be
63  * used like this:
64  *    $name = optional_param('name', 'Fred');
65  *
66  * @param string $varname the name of the parameter variable we want
67  * @param mixed  $default the default value to return if nothing is found
68  * @param int $options a bit field that specifies any cleaning needed
69  * @return mixed
70  */
71 function optional_param($varname, $default=NULL, $options=PARAM_CLEAN) {
72
73     // detect_unchecked_vars addition
74     global $CFG;
75     if (!empty($CFG->detect_unchecked_vars)) {
76         global $UNCHECKED_VARS;
77         unset ($UNCHECKED_VARS->vars[$varname]);
78     }
79
80     if (isset($_POST[$varname])) {       // POST has precedence
81         $param = $_POST[$varname];
82     } else if (isset($_GET[$varname])) {
83         $param = $_GET[$varname];
84     } else {
85         return $default;
86     }
87
88     return clean_param($param, $options);
89 }
90
91
92 /**
93  * Used by {@link optional_param()} and {@link required_param()} to
94  * clean the variables and/or cast to specific types, based on
95  * an options field.
96  * <code>
97  * $course->format = clean_param($course->format, PARAM_ALPHA);
98  * $selectedgrade_item = clean_param($selectedgrade_item, PARAM_CLEAN);
99  * </code>
100  *
101  * @uses $CFG
102  * @uses PARAM_CLEAN
103  * @uses PARAM_INT
104  * @uses PARAM_INTEGER
105  * @uses PARAM_ALPHA
106  * @uses PARAM_ALPHANUM
107  * @uses PARAM_NOTAGS
108  * @uses PARAM_ALPHATEXT
109  * @uses PARAM_BOOL
110  * @uses PARAM_SAFEDIR
111  * @uses PARAM_CLEANFILE
112  * @uses PARAM_FILE
113  * @uses PARAM_PATH
114  * @uses PARAM_HOST
115  * @uses PARAM_URL
116  * @uses PARAM_LOCALURL
117  * @uses PARAM_CLEANHTML
118  * @param mixed $param the variable we are cleaning
119  * @param int $options a bit field that specifies the cleaning needed. This field is specified by combining PARAM_* definitions together with a logical or.
120  * @return mixed
121  */
122 function clean_param($param, $options) {
123
124     global $CFG;
125
126     if (is_array($param)) {              // Let's loop
127         $newparam = array();
128         foreach ($param as $key => $value) {
129             $newparam[$key] = clean_param($value, $options);
130         }
131         return $newparam;
132     }
133
134     if (!$options) {
135         return $param;                   // Return raw value
136     }
137
138     //this corrupts data - Sven
139     //if ((string)$param == (string)(int)$param) {  // It's just an integer
140     //    return (int)$param;
141     //}
142
143     if ($options & PARAM_CLEAN) {
144 // this breaks backslashes in user input
145 //        $param = stripslashes($param);   // Needed by kses to work fine
146         $param = clean_text($param);     // Sweep for scripts, etc
147 // and this unnecessarily escapes quotes, etc in user input
148 //        $param = addslashes($param);     // Restore original request parameter slashes
149     }
150
151     if ($options & PARAM_INT) {
152         $param = (int)$param;            // Convert to integer
153     }
154
155     if ($options & PARAM_ALPHA) {        // Remove everything not a-z
156         $param = eregi_replace('[^a-zA-Z]', '', $param);
157     }
158
159     if ($options & PARAM_ALPHANUM) {     // Remove everything not a-zA-Z0-9
160         $param = eregi_replace('[^A-Za-z0-9]', '', $param);
161     }
162
163     if ($options & PARAM_ALPHAEXT) {     // Remove everything not a-zA-Z/_-
164         $param = eregi_replace('[^a-zA-Z/_-]', '', $param);
165     }
166
167     if ($options & PARAM_BOOL) {         // Convert to 1 or 0
168         $tempstr = strtolower($param);
169         if ($tempstr == 'on') {
170             $param = 1;
171         } else if ($tempstr == 'off') {
172             $param = 0;
173         } else {
174             $param = empty($param) ? 0 : 1;
175         }
176     }
177
178     if ($options & PARAM_NOTAGS) {       // Strip all tags completely
179         $param = strip_tags($param);
180     }
181
182     if ($options & PARAM_SAFEDIR) {     // Remove everything not a-zA-Z0-9_-
183         $param = eregi_replace('[^a-zA-Z0-9_-]', '', $param);
184     }
185
186     if ($options & PARAM_CLEANFILE) {    // allow only safe characters
187         $param = clean_filename($param);
188     }
189
190     if ($options & PARAM_FILE) {         // Strip all suspicious characters from filename
191         $param = ereg_replace('[[:cntrl:]]|[<>"`\|\':\\/]', '', $param);
192         $param = ereg_replace('\.\.+', '', $param);
193         if($param == '.') {
194             $param = '';
195         }
196     }
197
198     if ($options & PARAM_PATH) {         // Strip all suspicious characters from file path
199         $param = str_replace('\\\'', '\'', $param);
200         $param = str_replace('\\"', '"', $param);
201         $param = str_replace('\\', '/', $param);
202         $param = ereg_replace('[[:cntrl:]]|[<>"`\|\':]', '', $param);
203         $param = ereg_replace('\.\.+', '', $param);
204         $param = ereg_replace('//+', '/', $param);
205         $param = ereg_replace('/(\./)+', '/', $param);
206     }
207
208     if ($options & PARAM_HOST) {         // allow FQDN or IPv4 dotted quad
209         preg_replace('/[^\.\d\w-]/','', $param ); // only allowed chars
210         // match ipv4 dotted quad
211         if (preg_match('/(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})/',$param, $match)){
212             // confirm values are ok
213             if ( $match[0] > 255
214                  || $match[1] > 255
215                  || $match[3] > 255
216                  || $match[4] > 255 ) {
217                 // hmmm, what kind of dotted quad is this?
218                 $param = '';
219             }
220         } elseif ( preg_match('/^[\w\d\.-]+$/', $param) // dots, hyphens, numbers
221                    && !preg_match('/^[\.-]/'$param) // no leading dots/hyphens
222                    && !preg_match('/[\.-]$/'$param) // no trailing dots/hyphens
223                    ) {
224             // all is ok - $param is respected
225         } else {
226             // all is not ok...
227             $param='';
228         }
229     }
230
231     if ($options & PARAM_URL) { // allow safe ftp, http, mailto urls
232
233         include_once($CFG->dirroot . 'lib/validateurlsyntax.php');
234
235         //
236         // Parameters to validateurlsyntax()
237         //
238         // s? scheme is optional
239         //   H? http optional
240         //   S? https optional
241         //   F? ftp   optional
242         //   E? mailto optional
243         // u- user section not allowed
244         //   P- password not allowed
245         // a? address optional
246         //   I? Numeric IP address optional (can use IP or domain)
247         //   p-  port not allowed -- restrict to default port
248         // f? "file" path section optional
249         //   q? query section optional
250         //   r? fragment (anchor) optional
251         //
252         if (!empty($param) && validateUrlSyntax($param, 's?H?S?F?E?u-P-a?I?p-f?q?r?')) {
253             // all is ok, param is respected
254         } else {
255             $param =''; // not really ok
256         }
257         $options ^= PARAM_URL; // Turn off the URL bit so that simple PARAM_URLs don't test true for PARAM_LOCALURL
258     }
259
260     if ($options & PARAM_LOCALURL) {
261         // assume we passed the PARAM_URL test...
262         // allow http absolute, root relative and relative URLs within wwwroot
263         if (!empty($param)) {
264             if (preg_match(':^/:', $param)) {
265                 // root-relative, ok!
266             } elseif (preg_match('/^'.preg_quote($CFG->wwwroot, '/').'/i',$param)) {
267                 // absolute, and matches our wwwroot
268             } else {
269                 // relative - let's make sure there are no tricks
270                 if (validateUrlSyntax($param, 's-u-P-a-p-f+q?r?')) {
271                     // looks ok.
272                 } else {
273                     $param = '';
274                 }
275             }
276         }
277     }
278
279     if ($options & PARAM_CLEANHTML) {
280 //        $param = stripslashes($param);         // Remove any slashes
281         $param = clean_text($param);           // Sweep for scripts, etc
282 //        $param = trim($param);                 // Sweep for scripts, etc
283     }
284
285     return $param;
286 }
287
288 /**
289  * Retrieves the list of plugins available in the $plugin
290  * directory. Defaults to 'mod'.
291  *
292  * NOTE: To get the list of enabled modules, do
293  * get_records('modules', 'enabled', true) instead.
294  *
295  * @return array
296  **/
297 function get_list_of_plugins($plugin='mod', $exclude='') {
298     
299     global $CFG;
300     if ($plugin == 'mod') {
301         $plugin = $CFG->dirroot . $plugin;
302     }
303     static $plugincache = array();
304     $plugincachename = $plugin . "_" . $exclude;
305     
306     if (isset($plugincache[$plugincachename])) {
307         $plugins = $plugincache[$plugincachename];
308     } else {
309         $plugins = array();
310         if ($basedir = opendir($plugin)) {
311             while (false !== ($dir = readdir($basedir))) {
312                 $firstchar = substr($dir, 0, 1);
313                 if ($firstchar == '.' or $dir == 'CVS' or $dir == '_vti_cnf' or $dir == $exclude) {
314                     continue;
315                 }
316                 if ((filetype($plugin .'/'. $dir) != 'dir') && ((filetype($plugin .'/'. $dir) != 'link'))) {
317                     continue;
318                 }
319                 $plugins[] = $dir;
320             }
321         }
322         if ($plugins) {
323             asort($plugins);
324         }
325         $plugincache[$plugincachename] = $plugins;
326     }
327     return $plugins;
328 }
329
330 // Adds a function to the variables used to cycle through plugin extensions
331 // to actions on objects
332 function listen_for_event($object_type, $event, $function) {
333     
334     global $CFG;
335     $CFG->event_hooks[$object_type][$event][] = $function;
336     
337 }
338
339 function plugin_hook($object_type,$event,$object = null) {
340     
341     global $CFG;
342     
343     if (!empty($CFG->event_hooks['all']['all']) && is_array($CFG->event_hooks['all']['all'])) {
344         foreach($CFG->event_hooks['all']['all'] as $hook) {
345             $object = $hook($object_type,$event,$object);
346         }
347     }
348     if (!empty($CFG->event_hooks[$object_type]['all']) && is_array($CFG->event_hooks[$object_type]['all'])) {
349         foreach($CFG->event_hooks[$object_type]['all'] as $hook) {
350             $object = $hook($object_type,$event,$object);
351         }
352     }
353     if (!empty($CFG->event_hooks['all'][$event]) && is_array($CFG->event_hooks['all'][$event])) {
354         foreach($CFG->event_hooks['all'][$event] as $hook) {
355             $object = $hook($object_type,$event,$object);
356         }
357     }
358     if (!empty($CFG->event_hooks[$object_type][$event]) && is_array($CFG->event_hooks[$object_type][$event])) {
359         foreach($CFG->event_hooks[$object_type][$event] as $hook) {
360             $object = $hook($object_type,$event,$object);
361         }
362     }
363     
364     return $object;
365     
366 }
367
368 function report_session_error() {
369     global $CFG, $FULLME;
370
371     //clear session cookies
372     setcookie('ElggSession'.$CFG->sessioncookie, '', time() - 3600, $CFG->cookiepath);
373     setcookie('ElggSessionTest'.$CFG->sessioncookie, '', time() - 3600, $CFG->cookiepath);
374     //increment database error counters
375     //if (isset($CFG->session_error_counter)) {
376     //    set_config('session_error_counter', 1 + $CFG->session_error_counter);
377     //} else {
378     //    set_config('session_error_counter', 1);
379     //}
380     //called from setup.php, so gettext module hasn't been loaded yet
381     redirect($FULLME, '', 1);
382 }
383
384 // never called
385 /**
386  * For security purposes, this function will check that the currently
387  * given sesskey (passed as a parameter to the script or this function)
388  * matches that of the current user.
389  *
390  * @param string $sesskey optionally provided sesskey
391  * @return bool
392  */
393 // function confirm_sesskey($sesskey=NULL) {
394 //     global $USER;
395
396 //     if (!empty($USER->ignoresesskey) || !empty($CFG->ignoresesskey)) {
397 //         return true;
398 //     }
399
400 //     if (empty($sesskey)) {
401 //         $sesskey = required_param('sesskey');  // Check script parameters
402 //     }
403
404 //     if (!isset($USER->sesskey)) {
405 //         return false;
406 //     }
407
408 //     return ($USER->sesskey === $sesskey);
409 // }
410
411
412 /**
413  * Makes sure that $USER->sesskey exists, if $USER itself exists. It sets a new sesskey
414  * if one does not already exist, but does not overwrite existing sesskeys. Returns the
415  * sesskey string if $USER exists, or boolean false if not.
416  *
417  * @uses $USER
418  * @return string
419  */
420 function sesskey() {
421     global $USER;
422
423     if(!isset($USER)) {
424         return false;
425     }
426
427     if (empty($USER->sesskey)) {
428         $USER->sesskey = random_string(10);
429     }
430
431     return $USER->sesskey;
432 }
433
434
435 /**
436  * Send an email to a specified user
437  *
438  * @uses $CFG
439  * @param user $user  A {@link $USER} object
440  * @param user $from A {@link $USER} object
441  * @param string $subject plain text subject line of the email
442  * @param string $messagetext plain text version of the message
443  * @param string $messagehtml complete html version of the message (optional)
444  * @param string $attachment a file on the filesystem
445  * @param string $attachname the name of the file (extension indicates MIME)
446  * @param bool $usetrueaddress determines whether $from email address should
447  *          be sent out. Will be overruled by user profile setting for maildisplay
448  * @return bool|string Returns "true" if mail was sent OK, "emailstop" if email
449  *          was blocked by user and "false" if there was another sort of error.
450  */
451 function email_to_user($user, $from, $subject, $messagetext, $messagehtml='', $attachment='', $attachname='', $usetrueaddress=true, $replyto='', $replytoname='') {
452
453     global $CFG;
454     $textlib = textlib_get_instance();
455
456     include_once($CFG->libdir .'/phpmailer/class.phpmailer.php');
457
458     if (empty($user) || empty($user->email)) {
459         return false;
460     }
461
462     /*
463     if (over_bounce_threshold($user)) {
464         error_log("User $user->id (".fullname($user).") is over bounce threshold! Not sending.");
465         return false;
466     }
467     */ // this doesn't exist right now, we may bring it in later though.
468
469     $mail = new phpmailer;
470
471     $mail->Version = 'Elgg ';           // mailer version (should have $CFG->version on here but we don't have it yet)
472     $mail->PluginDir = $CFG->libdir .'/phpmailer/';      // plugin directory (eg smtp plugin)
473
474
475     $mail->CharSet = 'UTF-8'; // everything is now uft8
476
477     if (empty($CFG->smtphosts)) {
478         $mail->IsMail();                               // use PHP mail() = sendmail
479     } else if ($CFG->smtphosts == 'qmail') {
480         $mail->IsQmail();                              // use Qmail system
481     } else {
482         $mail->IsSMTP();                               // use SMTP directly
483         if ($CFG->debug > 7) {
484             echo '<pre>' . "\n";
485             $mail->SMTPDebug = true;
486         }
487         $mail->Host = $CFG->smtphosts;               // specify main and backup servers
488
489         if ($CFG->smtpuser) {                          // Use SMTP authentication
490             $mail->SMTPAuth = true;
491             $mail->Username = $CFG->smtpuser;
492             $mail->Password = $CFG->smtppass;
493         }
494     }
495
496     /* not here yet, leave it in just in case.
497     // make up an email address for handling bounces
498     if (!empty($CFG->handlebounces)) {
499         $modargs = 'B'.base64_encode(pack('V',$user->ident)).substr(md5($user->email),0,16);
500         $mail->Sender = generate_email_processing_address(0,$modargs);
501     }
502     else {
503         $mail->Sender   =  $CFG->sysadminemail;
504     }
505     */
506     $mail->Sender = $CFG->sysadminemail; // for elgg. delete if we change the above.
507
508     // TODO add a preference for maildisplay
509     if (is_string($from)) { // So we can pass whatever we want if there is need
510         $mail->From     = $CFG->noreplyaddress;
511         $mail->FromName = $from;
512     } else if (empty($from)) { // make stuff up
513         $mail->From     = $CFG->sysadminemail;
514         $mail->FromName = $CFG->sitename.' '.__gettext('Administrator');
515     } else if ($usetrueaddress and !empty($from->maildisplay)) {
516         $mail->From     = $from->email;
517         $mail->FromName = $from->name;
518     } else {
519         $mail->From     = $CFG->noreplyaddress;
520         $mail->FromName = $from->name;
521         if (empty($replyto)) {
522             $mail->AddReplyTo($CFG->noreplyaddress,__gettext('Do not reply'));
523         }
524     }
525
526     if (!empty($replyto)) {
527         $mail->AddReplyTo($replyto,$replytoname);
528     }
529
530     $mail->Subject = $textlib->substr(stripslashes($subject), 0, 900);
531
532     $mail->AddAddress($user->email, $user->name);
533
534     $mail->WordWrap = 79;                               // set word wrap
535
536     if (!empty($from->customheaders)) {                 // Add custom headers
537         if (is_array($from->customheaders)) {
538             foreach ($from->customheaders as $customheader) {
539                 $mail->AddCustomHeader($customheader);
540             }
541         } else {
542             $mail->AddCustomHeader($from->customheaders);
543         }
544     }
545
546     if (!empty($from->priority)) {
547         $mail->Priority = $from->priority;
548     }
549
550     //TODO add a user preference for this. right now just send plaintext
551     $user->mailformat = 0;
552     if ($messagehtml && $user->mailformat == 1) { // Don't ever send HTML to users who don't want it
553         $mail->IsHTML(true);
554         $mail->Encoding = 'quoted-printable';           // Encoding to use
555         $mail->Body    $messagehtml;
556         $mail->AltBody