root/releases/0.673/lib/elgglib.php

Revision 734, 129.8 kB (checked in by ben, 2 years ago)

validate_email now allows for multiple email address domains to validate against.

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