root/trunk/gui/include/input-checks.php

Revision 1413, 20.0 kB (checked in by scitech, 8 hours ago)

Add support for alias subdomain mail (part II)

Line 
1 <?php
2 /**
3  * ispCP ω (OMEGA) a Virtual Hosting Control System
4  *
5  * @copyright     2001-2006 by moleSoftware GmbH
6  * @copyright     2006-2008 by ispCP | http://isp-control.net
7  * @version     SVN: $Id$
8  * @link         http://isp-control.net
9  * @author         ispCP Team
10  *
11  * @license
12  *   This program is free software; you can redistribute it and/or modify it under
13  *   the terms of the MPL General Public License as published by the Free Software
14  *   Foundation; either version 1.1 of the License, or (at your option) any later
15  *   version.
16  *   You should have received a copy of the MPL Mozilla Public License along with
17  *   this program; if not, write to the Open Source Initiative (OSI)
18  *   http://opensource.org | osi@opensource.org
19  */
20
21 // -- check if they are trying to hack
22 $CHECK_VARS = array();
23 $CHECK_VARS[] = "/wget /i";
24 $CHECK_VARS[] = "/chmod /i";
25 $CHECK_VARS[] = "/chown /i";
26 $CHECK_VARS[] = "/lnyx /i";
27 $CHECK_VARS[] = "/curl /i";
28 $CHECK_VARS[] = "/fopen /i";
29 $CHECK_VARS[] = "/mkdir /i";
30 $CHECK_VARS[] = "/passwd /i";
31 $CHECK_VARS[] = "/http:/i";
32 $CHECK_VARS[] = "/ftp:/i";
33
34 $CHECK_VARS[] = "/content-type:/i";
35 $CHECK_VARS[] = "/content-transfer-encoding:/i";
36 $CHECK_VARS[] = "/mime-version:/i";
37 $CHECK_VARS[] = "/subject:/i";
38 $CHECK_VARS[] = "/to:/i";
39 $CHECK_VARS[] = "/cc:/i";
40 $CHECK_VARS[] = "/bcc:/i";
41 $CHECK_VARS[] = "/\r/";
42 $CHECK_VARS[] = "/\n/";
43 $CHECK_VARS[] = "/%0a/";
44 $CHECK_VARS[] = "/%0d/";
45
46 function check_input($value = '') {
47     global $CHECK_VARS;
48
49     if (!empty($value)) {
50         $value = strtolower($value);
51
52         foreach($CHECK_VARS as $VAR) {
53             if (preg_match($VAR, $value) > 0) {
54                 $message = "Possible hacking attempt. Script terminated.";
55                 write_log($message);
56                 system_message(tr($message));
57                 die();
58             }
59         }
60     }
61 }
62
63 function clean_html($text) {
64     $suche = array ('@<script[^>]*?>.*?</script>@si', // remove JavaScript
65         '@<[\/\!]*?[^<>]*?>@si', // remove HTML tags
66         '@([\r\n])[\s]+@', // remove spaces,
67         '@&(quot|#34);@i', // change HTML entities
68         '@&(amp|#38);@i',
69         '@&(lt|#60);@i',
70         '@&(gt|#62);@i',
71         '@&(nbsp|#160);@i',
72         '@&(iexcl|#161);@i',
73         '@&(cent|#162);@i',
74         '@&(pound|#163);@i',
75         '@&(copy|#169);@i',
76         '@&#(\d+);@e'); // handle als php
77
78     $ersetze = array ('',
79         '',
80         '\1',
81         '"',
82         '&',
83         '<',
84         '>',
85         ' ',
86         chr(161),
87         chr(162),
88         chr(163),
89         chr(169),
90         'chr(\1)');
91
92     $text = preg_replace($suche, $ersetze, $text);
93     // and second one...
94     $text = strip_tags($text);
95
96     return $text;
97 }
98
99 /**
100  *
101  * @function clean_input
102  * @description
103  * @param String $input input data (eg. post-var) to be cleaned
104  * @param boolean $htmlencode should return value be html encoded (& -> &amp;)
105  * @return String {|} trimmed, stripslashed, ev htmlencoded input string
106  */
107 function clean_input($input, $htmlencode = false) {
108     if ((strpos($input, "{") == 0) && (strpos($input, "}") == strlen($input)-1)) {
109         $input = trim($input, "{..}");
110     }
111
112     $input = stripslashes($input);
113
114     if ($htmlencode) {
115         return htmlentities($input, ENT_QUOTES, "UTF-8");
116     } else {
117         return $input;
118     }
119 }
120
121 /**
122  * Passwort check
123  *
124  * Check if an password is valid
125  *
126  * @author        ispCP Team
127  * @author        Benedikt Heintel
128  * @copyright     2006-2008 by ispCP | http://isp-control.net
129  * @version        1.01
130  *
131  * @access    public
132  * @param     String     $data        username to be checked
133  * @param     int        $num        number of max. chars
134  * @param    String    $permitted    RegExp of permitted chars
135  * @return     boolean                valid password or not
136  */
137 function chk_password($password, $num = 50, $permitted = "") {
138     global $cfg;
139
140     if ($num > 255) {
141         $num = 255;
142     } else if ($num < 6) {
143         $num = 6;
144     }
145
146     $len = strlen($password);
147     if ($len < Config::get('PASSWD_CHARS') || $len > $num) {
148         return false;
149     }
150
151     if (!empty($permitted) && (bool)preg_match($permitted, $password)) {
152         return false;
153     }
154
155     if (Config::get('PASSWD_STRONG')) {
156         return (bool)(preg_match("/[0-9]/", $password) && preg_match("/[a-zA-Z]/", $password));
157     } else {
158         return true;
159     }
160 }
161
162 /**
163  *
164  * @function chk_username
165  * @description
166  * @param String $data username to be checked
167  * @param int $num number of max. chars
168  * @return boolean valid username or not
169  */
170 function chk_username($username, $length = null) {
171     // Username contains only allowed chars
172     if (!preg_match("/^[A-Za-z0-9][A-Za-z0-9\.\-_]*[A-Za-z0-9]$/D", $username))
173         return false;
174     // Username has not two times .,- or _
175     if (preg_match("/(\.){2,}|(\-){3,}|(\_){2,}/", $username))
176         return false;
177     // Username has no not allowed concardination in it
178     if (preg_match("/(\.\-)|(\-\.)|(\.\_)|(\_\.)|(\-\_)|(\_\-)/", $username))
179         return false;
180     // String is not to long
181     if ($length !== null && strlen($username) > $length)
182         return false;
183
184     return true;
185 }
186
187 function chk_email($email, $num = 50) {
188     if (strlen($email) > $num)
189         return false;
190
191     // RegEx begin
192     $nonascii = "\x80-\xff"; # non ASCII chars are not allowed
193
194     $nqtext = "[^\\\\$nonascii\015\012\"]"; # all not qouteable chars
195     $qchar = "\\\\[^$nonascii]";            # matched quoted chars
196
197     $normuser = '[a-zA-Z0-9][a-zA-Z0-9_.-]*';
198     $quotedstring = "\"(?:$nqtext|$qchar)+\"";
199     $user_part = "(?:$normuser|$quotedstring)";
200
201     $dom_mainpart = '[a-zA-Z0-9][a-zA-Z0-9.-]*[a-zA-Z0-9]\\.';
202     $dom_subpart = '(?:[a-zA-Z0-9][a-zA-Z0-9.-]*\\.)*';
203     $dom_tldpart = '[a-zA-Z]{2,5}';
204     $domain_part = "$dom_subpart$dom_mainpart$dom_tldpart";
205
206     $regex = "$user_part\@$domain_part";
207     // RegEx end
208     return (bool) preg_match("/^$regex$/", $email);
209 }
210
211 function ispcp_check_local_part($email, $num = 50) {
212     if (strlen($email) > $num)
213         return false;
214
215     // RegEx begin
216     $nonascii = "\x80-\xff"; # non ASCII chars are not allowed
217
218     $nqtext = "[^\\\\$nonascii\015\012\"]";
219     $qchar = "\\\\[^$nonascii]";
220
221     $normuser = "[a-zA-Z0-9][a-zA-Z0-9_.-]*";
222     $quotedstring = "\"(?:$nqtext|$qchar)+\"";
223     $user_part = "(?:$normuser|$quotedstring)";
224
225     $regex = $user_part;
226     // RegEx end
227     return (bool) preg_match("/^$regex$/", $email);
228 }
229
230 function full_domain_check($data) {
231     $data .= ".";
232     $match = array();
233
234     $res = preg_match_all("/([^\.]*\.)/", $data, $match, PREG_PATTERN_ORDER);
235
236     if (!$res) {
237         return false;
238     }
239
240     $last = $res - 1;
241
242     for ($i = 0; $i < $last ; $i++) {
243         $token = chop($match[0][$i], ".");
244
245         $res = check_dn_token($token);
246
247         if (!$res)
248             return false;
249     }
250
251     $res = preg_match("/^[A-Za-z0-9][A-Za-z0-9]*[A-Za-z0-9]\.$/", $match[0][$last]);
252
253     if (!$res)
254         return false;
255
256     return true;
257 }
258
259 function check_dn_token($data) {
260     if (!preg_match("/^([A-Za-z0-9])([A-Za-z0-9\-]*)([A-Za-z0-9])$/D", $data))
261         return false;
262     // Username has not two times .,- or _
263     if (preg_match("/(\.){2,}|(\-){3,}|(\_){2,}/", $data))
264         return false;
265     // Username has no not allowed concardination in it
266     if (preg_match("/(\.\-)|(\-\.)|(\.\_)|(\_\.)|(\-\_)|(\_\-)/", $data))
267         return false;
268
269     return true;
270 }
271
272 /**
273  *
274  * @function ispcp_limit_check
275  * @description Function for checking ispcp limits.
276  * @param string $data ispcp 'limit' field data (by default valids are numbers greater equal 0)
277  * @param misc $extra single extra permitted value or array of permitted values
278  * @return boolean false    incorrect syntax (ranges)
279  *                                         true    correct syntax (ranges)
280  * @example ispcp_limit_check($_POST['domains_limit'], null)
281  * @example ispcp_limit_check($_POST['ftp_accounts_limit'])
282  */
283 function ispcp_limit_check($data, $extra = -1) {
284     if ($extra !== null && !is_bool($extra)) {
285         if (is_array($extra)) {
286             $nextra = '';
287             $max = count($extra);
288
289             foreach ($extra as $n => $element) {
290                 $nextra = $element . ($n < $max)? '|' : '';
291             }
292
293             $extra = $nextra;
294         } else {
295             $extra .= '|';
296         }
297     } else {
298         $extra = '';
299     }
300
301     return (bool)preg_match("/^(${extra}0|[1-9][0-9]*)$/D", $data);
302 }
303
304 /**
305  *
306  * @function check_dn_rsl_token
307  * @description Function for checking domain name tokens; Internel function,
308  *                     for usage in ispcp_* functions
309  * @param String $data token data. Without '\n' at the end
310  * @param int $num number of max. chars
311  * @return boolean false    incorrect syntax
312  *                                         true    correct syntax
313  */
314 function check_dn_rsl_token($data) {
315     $match = array();
316     if (!preg_match("/^([A-Za-z0-9])([a-z0-9A-Z\-]*)([A-Za-z0-9])$/D", $data, $match))
317         return false;
318
319     /*if (preg_match("/\-\-/", $match[2]))
320         return FALSE;*/
321
322     return true;
323 }
324
325 /**
326  *
327  * @function chk_dname
328  * @description Function for checking ispCP domains syntax. Here domains are
329  *                     limited to {dname}.{ext} parts
330  * @param String $dname ispcp domain data
331  * @param int $num number of max. chars
332  * @return boolean false    incorrect syntax
333  *                                         true    correct syntax
334  */
335 function chk_dname($dname) {
336     // Check for invalid characters first
337     if (preg_match('/[^a-z0-9\.\-]+/', $dname)) {
338         return false;
339     }
340
341     if (!rsl_full_domain_check($dname))
342         return false;
343
344     $match = array();
345
346     if (preg_match_all("/\./", $dname, $match, PREG_PATTERN_ORDER) <= 0)
347         return false;
348
349     return true;
350 }
351
352 /**
353  *
354  * @function chk_forward_url
355  * @description Function for checking URL syntax
356  * @param String $url URL data
357  * @return boolean false    incorrect syntax
358  *                                         true    correct syntax
359  */
360 function chk_forward_url($url) {
361     $dom_mainpart = '[a-zA-Z0-9][a-zA-Z0-9-]*[a-zA-Z0-9]\.';
362     $dom_subpart = '(?:[a-zA-Z0-9][a-zA-Z0-9.-]*\.)*';
363     $dom_tldpart = '[a-zA-Z]{2,5}';
364     $domain = $dom_subpart . $dom_mainpart . $dom_tldpart;
365
366     if (!preg_match("/^(http|https|ftp)\:\/\/" . $domain . "/", $url))
367         return false;
368
369     return true;
370 }
371
372 /**
373  *
374  * @function chk_mountp
375  * @description Function checking for valid mount point
376  * @param String $data mountpoint data
377  * @param int $num number of max. chars
378  * @return boolean false    incorrect syntax
379  *                                         true    correct syntax
380  */
381 function chk_mountp($data, $num = 50) {
382     if (!preg_match("/^\/(.*)$/D", $data))
383         return false;
384
385     if (preg_match("/^\/htdocs$/D", $data))
386         return false;
387
388     if (preg_match("/^\/backups$/D", $data))
389         return false;
390
391     if (preg_match("/^\/cgi-bin$/D", $data))
392         return false;
393
394     if (preg_match("/^\/errors$/D", $data))
395         return false;
396
397     if (preg_match("/^\/logs$/D", $data))
398         return false;
399
400     /*$res = explode("/", trim($data));
401     $cnt_res = count($res);
402     if ($cnt_res > 2)
403         return FALSE;*/
404
405     $match = array();
406     $count = preg_match_all("(\/[^\/]*)", $data, $match, PREG_PATTERN_ORDER);
407
408     if (!$count)
409         return false;
410
411     for ($i = 0; $i < $count; $i++) {
412         $token = substr($match[0][$i], 1);
413
414         if (!chk_username($token, $num))
415             return false;
416     }
417
418     return true;
419 }
420
421 function get_post($value) {
422     if (array_key_exists($value, $_POST))
423         return $_POST[$value];
424     else
425         return null;
426 }
427
428 function get_session($value) {
429     if (array_key_exists($value, $_SESSION))
430         return $_SESSION[$value];
431     else
432         return null;
433 }
434
435 function is_subdir_of($base_domain, $subdomain, $realPath = true) {
436     if ($realPath) {
437         $base_domain = realpath($base_domain);
438         $subdomain = realpath($subdomain);
439     }
440
441     $t = explode($base_domain, $subdomain);
442
443     return (count($t) > 1 && $t[0] === '');
444 }
445
446 /**
447  * Description:
448  *
449  * Function for checking ispCP subdomain syntax. Here subdomains are
450  * limited to {subname}.{dname}.{ext} parts. Data passed to this
451  * function must be in the upper form, not only subdomain part for
452  * example.
453  *
454  * Input:
455  *
456  * $data - ispcp subdomain data;
457  *
458  * Output:
459  *
460  * false - incorrect syntax;
461  *
462  * true - correct syntax;
463  */
464 function chk_subdname($subdname) {
465     if (!full_domain_check($subdname)) {
466         return false;
467     }
468
469     $match = array();
470
471     $res = preg_match_all("/\./", $subdname, $match, PREG_PATTERN_ORDER);
472
473     if ($res <= 1) {
474         return false;
475     }
476
477     $res = preg_match("/^(www|ftp|mail|ns)\./", $subdname);
478
479     return !($res == 1);
480 }
481
482 /**
483  * All in one function to check who owns what =)
484  *
485  * @param misc $id FTP/mail/domain/alias/subdomain/etc id to check
486  * @param string $type What kind of id $id is
487  * @param bool $forcefinal Ignore the resolver's is_final value (force as yes)
488  * @return numeric The id of the admin who owns the id $id of $type type
489  */
490 function who_owns_this($id, $type = 'dmn', $forcefinal = false) {
491     $sql = Database::getInstance();
492
493     $who = null;
494     // Fix $type according to type or by alias
495     switch ($type) {
496         case 'dmn_id':
497             $type = 'domain_id';
498             break;
499         case 'sub_id':
500             $type = 'subdomain_id';
501             break;
502         case 'als_id':
503             $type = 'alias_id';
504             break;
505         case 'user':
506             $type = 'client';
507             break;
508         case 'domain_uid':
509             $type = 'uid';
510             break;
511         case 'ticket':
512             $type = 'ticket_id';
513             break;
514         case 'domain_gid':
515             $type = 'gid';
516             break;
517         case 'sqlu_id':
518         case 'sqluser_id':
519             $type = 'sql_user_id';
520             break;
521         case 'sqld_id':
522         case 'sqldatabase_id':
523             $type = 'sql_database_id';
524             break;
525         case 'ftpuser':
526         case 'ftpuserid':
527         case 'ftp_userid':
528             $type = 'ftp_user';
529             break;
530         case 'sqluser':
531         case 'sqlu':
532         case 'sqlu_name':
533             // Can't guess by type
534             $type = 'sql_user';
535             break;
536         case 'sqldatabase':
537         case 'sqld':
538         case 'sqld_name':
539             // Can't guess by type
540             $type = 'sql_database';
541             break;
542         case 'dmn':
543         case 'normal':
544         case 'domain':
545             if (!is_numeric($id)) {
546                 $type = 'domain';
547             } else {
548                 $type = 'domain_id';
549             }
550             break;
551         case 'als':
552         case 'alias':
553         case 'domain_alias':
554             if (!is_numeric($id)) {
555                 $type = 'alias';
556             } else {
557                 $type = 'alias_id';
558             }
559             break;
560         case 'sub':
561         case 'subdom':
562         case 'subdomain':
563             if (!is_numeric($id)) {
564                 $type = 'subdomain';
565             } else {
566                 $type = 'subdomain_id';
567             }
568             break;
569         case 'alssub':
570             if (!is_numeric($id)) {
571                 $type = 'subdomain_alias';
572             } else {
573                 $type = 'subdomain_alias_id';
574             }
575             break;
576     }
577
578     $resolvers = array();
579     /**
580      * $resolvers is a multi-dimensional array.
581      * Its elements keys are the value that will be matched by $type.
582      * Each element is an array, containing at least two elements:
583      *   'query' and 'is_final'
584      * The former is the SQL query that should only SELECT one item; or false in case a query isn't used.
585      * The latter is a boolean which specifies whether the result of that 'resolver' is an admin id or not
586      *
587      * Other elements might be:
588      *   'next', 'separator', 'pos'
589      *
590      * 'next' is the $type value for the next call to who_owns_this (only used when 'is_final' is false)
591      * 'separator' is the separator to be used when exploding the $id (only used when 'query' is false)
592      * 'post' is the position in the array/result of exploding $id (only used when 'query' is false)
593      *
594      * NOTE: 'query' MUST be formated like: 'SELECT something FROM...' in order to correctly detect the field being selected
595      */
596     $resolvers['domain_id'] = array();
597     $resolvers['domain_id']['query'] = 'SELECT domain_admin_id FROM domain WHERE domain_id = ? LIMIT 1;';
598     $resolvers['domain_id']['is_final'] = true;
599
600     $resolvers['alias_id'] = array();
601     $resolvers['alias_id']['query'] = 'SELECT domain_id FROM domain_aliasses WHERE alias_id = ? LIMIT 1;';
602     $resolvers['alias_id']['is_final'] = false;
603     $resolvers['alias_id']['next'] = 'dmn';