PostfixAdmin - web based virtual user administration interface for Postfix mail servers https://postfixadmin.github.io/postfixadmin/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2392 lines
68 KiB

reverting most changes from SVN r572 aka https://sourceforge.net/tracker/index.php?func=detail&aid=2567466&group_id=191583&atid=937966 because - it undermines the $CONF[*alias_control*] settings more or less - mailbox aliases with non-default targets are always shown in the "Aliases" section - see comment from 2009-05-04 on https://sourceforge.net/tracker/?func=detail&aid=1902476&group_id=191583&atid=937964 - it introduced some "funny" bugs - a nice example is http://sourceforge.net/tracker/?func=detail&aid=2786284&group_id=191583&atid=937964 Files / sections affected by the revert: - list-virtual.php: all numbers (alias count etc.) correct? (the changes in this file are the largest ones) - functions.inc.php: SQL queries in get_domain_properties() - delete.php: the only change since r572 affected code that was inserted in r572 (and is now deleted again) - nothing should break here - create-alias.php: had no changes since r572 - therefore nothing should break here Exceptions (not reverted): - edit-alias: this change looks useful (hide mailbox alias target from admins if they don't have permissions to change it). The actual code has changed in the meantime, but the functionality stays. Additionally, reverting this would be very hard or throw useful later changes away. BUT: shouldn't the page completely forbid to edit a mailbox alias if the admin doesn't have permissions for it? - functions.inc.php: comment for pacrypt() ;-) - linebreaks in long SQL queries Please check if everything is still working as expected (especially the domain list and the virtual list) - I did only some quick tests. git-svn-id: https://svn.code.sf.net/p/postfixadmin/code/trunk@652 a1433add-5e2c-0410-b055-b7f2511e0802
17 years ago
reverting most changes from SVN r572 aka https://sourceforge.net/tracker/index.php?func=detail&aid=2567466&group_id=191583&atid=937966 because - it undermines the $CONF[*alias_control*] settings more or less - mailbox aliases with non-default targets are always shown in the "Aliases" section - see comment from 2009-05-04 on https://sourceforge.net/tracker/?func=detail&aid=1902476&group_id=191583&atid=937964 - it introduced some "funny" bugs - a nice example is http://sourceforge.net/tracker/?func=detail&aid=2786284&group_id=191583&atid=937964 Files / sections affected by the revert: - list-virtual.php: all numbers (alias count etc.) correct? (the changes in this file are the largest ones) - functions.inc.php: SQL queries in get_domain_properties() - delete.php: the only change since r572 affected code that was inserted in r572 (and is now deleted again) - nothing should break here - create-alias.php: had no changes since r572 - therefore nothing should break here Exceptions (not reverted): - edit-alias: this change looks useful (hide mailbox alias target from admins if they don't have permissions to change it). The actual code has changed in the meantime, but the functionality stays. Additionally, reverting this would be very hard or throw useful later changes away. BUT: shouldn't the page completely forbid to edit a mailbox alias if the admin doesn't have permissions for it? - functions.inc.php: comment for pacrypt() ;-) - linebreaks in long SQL queries Please check if everything is still working as expected (especially the domain list and the virtual list) - I did only some quick tests. git-svn-id: https://svn.code.sf.net/p/postfixadmin/code/trunk@652 a1433add-5e2c-0410-b055-b7f2511e0802
17 years ago
  1. <?php
  2. /**
  3. * Postfix Admin
  4. *
  5. * LICENSE
  6. * This source file is subject to the GPL license that is bundled with
  7. * this package in the file LICENSE.TXT.
  8. *
  9. * Further details on the project are available at :
  10. * http://www.postfixadmin.com or http://postfixadmin.sf.net
  11. *
  12. * @version $Id$
  13. * @license GNU GPL v2 or later.
  14. *
  15. * File: functions.inc.php
  16. * Contains re-usable code.
  17. */
  18. $version = '2.3.3';
  19. /**
  20. * check_session
  21. * Action: Check if a session already exists, if not redirect to login.php
  22. * Call: check_session ()
  23. * @return String username (e.g. foo@example.com)
  24. */
  25. function authentication_get_username()
  26. {
  27. global $CONF;
  28. if (!isset($_SESSION['sessid'])) {
  29. header ("Location: " . $CONF['postfix_admin_url'] . "/login.php");
  30. exit(0);
  31. }
  32. $SESSID_USERNAME = $_SESSION['sessid']['username'];
  33. return $SESSID_USERNAME;
  34. }
  35. /**
  36. * Returns the type of user - either 'user' or 'admin'
  37. * Returns false if neither (E.g. if not logged in)
  38. * @return String admin or user or (boolean) false.
  39. */
  40. function authentication_get_usertype() {
  41. if(isset($_SESSION['sessid'])) {
  42. if(isset($_SESSION['sessid']['type'])) {
  43. return $_SESSION['sessid']['type'];
  44. }
  45. }
  46. return false;
  47. }
  48. /**
  49. *
  50. * Used to determine whether a user has a particular role.
  51. * @param String role-name. (E.g. admin, global-admin or user)
  52. * @return boolean True if they have the requested role in their session.
  53. * Note, user < admin < global-admin
  54. */
  55. function authentication_has_role($role) {
  56. global $CONF;
  57. if(isset($_SESSION['sessid'])) {
  58. if(isset($_SESSION['sessid']['roles'])) {
  59. if(in_array($role, $_SESSION['sessid']['roles'])) {
  60. return true;
  61. }
  62. }
  63. }
  64. return false;
  65. }
  66. /**
  67. * Used to enforce that $user has a particular role when
  68. * viewing a page.
  69. * If they are lacking a role, redirect them to
  70. * $CONF['postfix_admin_url']/login.php
  71. *
  72. * Note, user < admin < global-admin
  73. */
  74. function authentication_require_role($role) {
  75. global $CONF;
  76. // redirect to appropriate page?
  77. if(authentication_has_role($role)) {
  78. return True;
  79. }
  80. if($role === 'user') {
  81. header("Location: " . $CONF['postfix_admin_url'] . '/users/login.php');
  82. }
  83. else {
  84. header("Location: " . $CONF['postfix_admin_url'] . "/login.php");
  85. }
  86. exit(0);
  87. }
  88. /**
  89. * @return boolean TRUE if a admin, FALSE otherwise.
  90. */
  91. function authentication_is_admin() {
  92. return authentication_get_usertype() == 'admin';
  93. }
  94. /**
  95. * @return boolean TRUE if a user, FALSE otherwise.
  96. */
  97. function authentication_is_user() {
  98. return authentication_get_usertype() == 'user';
  99. }
  100. /**
  101. * Add an error message for display on the next page that is rendered.
  102. * @param String message to show.
  103. *
  104. * Stores string in session. Flushed through header template.
  105. * @see _flash_string()
  106. */
  107. function flash_error($string) {
  108. _flash_string('error', $string);
  109. }
  110. /**
  111. * Used to display an info message on successful update.
  112. * @param String $string
  113. * Stores data in sessio.
  114. * @see _flash_string()
  115. */
  116. function flash_info($string) {
  117. _flash_string('info', $string);
  118. }
  119. /**
  120. * 'Private' method used for flash_info() and flash_error().
  121. */
  122. function _flash_string($type, $string) {
  123. if(!isset($_SESSION['flash'])) {
  124. $_SESSION['flash'] = array();
  125. }
  126. if(!isset($_SESSION['flash'][$type])) {
  127. $_SESSION['flash'][$type] = array();
  128. }
  129. $_SESSION['flash'][$type][] = $string;
  130. }
  131. //
  132. // check_language
  133. // Action: checks what language the browser uses
  134. // Call: check_language
  135. // Parameter: $use_post - set to 0 if $_POST should NOT be read
  136. //
  137. function check_language ($use_post = 1)
  138. {
  139. global $CONF;
  140. global $supported_languages; # from languages/languages.php
  141. $lang = $CONF['default_language'];
  142. if(isset($_SERVER['HTTP_ACCEPT_LANGUAGE']))
  143. {
  144. $lang_array = preg_split ('/(\s*,\s*)/', $_SERVER['HTTP_ACCEPT_LANGUAGE']);
  145. if (safecookie('lang')) {
  146. array_unshift($lang_array, safecookie('lang')); # prefer language from cookie
  147. }
  148. if ( $use_post && safepost('lang')) {
  149. array_unshift($lang_array, safepost('lang')); # but prefer $_POST['lang'] even more
  150. }
  151. for($i = 0; $i < count($lang_array); $i++)
  152. {
  153. $lang_next = $lang_array[$i];
  154. $lang_next = strtolower(trim($lang_next));
  155. if(array_key_exists($lang_next, $supported_languages))
  156. {
  157. $lang = $lang_next;
  158. break;
  159. }
  160. }
  161. }
  162. return $lang;
  163. }
  164. //
  165. // language_selector
  166. // Action: returns a language selector dropdown with the browser (or cookie) language preselected
  167. // Call: language_selector()
  168. //
  169. function language_selector()
  170. {
  171. global $supported_languages; # from languages/languages.php
  172. $current_lang = check_language();
  173. $selector = '<select name="lang" xml:lang="en" dir="ltr">';
  174. foreach($supported_languages as $lang => $lang_name) {
  175. if ($lang == $current_lang) {
  176. $selected = ' selected="selected"';
  177. } else {
  178. $selected = '';
  179. }
  180. $selector .= "<option value='$lang'$selected>$lang_name</option>";
  181. }
  182. $selector .= "</select>";
  183. return $selector;
  184. }
  185. //
  186. // check_string
  187. // Action: checks if a string is valid and returns TRUE if this is the case.
  188. // Call: check_string (string var)
  189. //
  190. function check_string ($var)
  191. {
  192. if (preg_match ('/^([A-Za-z0-9 ]+)+$/', $var))
  193. {
  194. return true;
  195. }
  196. else
  197. {
  198. return false;
  199. }
  200. }
  201. //
  202. // check_domain
  203. // Action: Checks if domain is valid and returns TRUE if this is the case.
  204. // Call: check_domain (string domain)
  205. //
  206. // TODO: make check_domain able to handle as example .local domains
  207. function check_domain ($domain)
  208. {
  209. global $CONF;
  210. global $PALANG;
  211. if (!preg_match ('/^([-0-9A-Z]+\.)+' . '([0-9A-Z]){2,6}$/i', ($domain)))
  212. {
  213. flash_error(sprintf($PALANG['pInvalidDomainRegex'], htmlentities($domain)));
  214. return false;
  215. }
  216. if (isset($CONF['emailcheck_resolve_domain']) && 'YES' == $CONF['emailcheck_resolve_domain'] && 'WINDOWS'!=(strtoupper(substr(php_uname('s'), 0, 7))))
  217. {
  218. // Look for an AAAA, A, or MX record for the domain
  219. if(function_exists('checkdnsrr')) {
  220. // AAAA (IPv6) is only available in PHP v. >= 5
  221. if (version_compare(phpversion(), "5.0.0", ">="))
  222. {
  223. if (checkdnsrr($domain,'AAAA')) return true;
  224. }
  225. if (checkdnsrr($domain,'A')) return true;
  226. if (checkdnsrr($domain,'MX')) return true;
  227. flash_error(sprintf($PALANG['pInvalidDomainDNS'], htmlentities($domain)));
  228. return false;
  229. }
  230. else {
  231. flash_error("emailcheck_resolve_domain is enabled, but function (checkdnsrr) missing!");
  232. }
  233. }
  234. return true;
  235. }
  236. /**
  237. * check_email
  238. * Checks if an email is valid - if it is, return true, else false.
  239. * @param String $email - a string that may be an email address.
  240. * @return boolean true if it's an email address, else false.
  241. * TODO: make check_email able to handle already added domains
  242. */
  243. function check_email ($email)
  244. {
  245. global $CONF;
  246. global $PALANG;
  247. $ce_email=$email;
  248. //strip the vacation domain out if we are using it
  249. //and change from blah#foo.com@autoreply.foo.com to blah@foo.com
  250. if ($CONF['vacation'] == 'YES')
  251. {
  252. $vacation_domain = $CONF['vacation_domain'];
  253. $ce_email = preg_replace("/@$vacation_domain/", '', $ce_email);
  254. $ce_email = preg_replace("/#/", '@', $ce_email);
  255. }
  256. // Perform non-domain-part sanity checks
  257. if (!preg_match ('/^[-!#$%&\'*+\\.\/0-9=?A-Z^_{|}~]+' . '@' . '[^@]+$/i', trim ($ce_email)))
  258. {
  259. flash_error($PALANG['pInvalidMailRegex']);
  260. return false;
  261. }
  262. // Determine domain name
  263. $matches=array();
  264. if (!preg_match('|@(.+)$|',$ce_email,$matches))
  265. {
  266. flash_error($PALANG['pInvalidMailRegex']);
  267. return false;
  268. }
  269. $domain=$matches[1];
  270. # check domain name
  271. return check_domain($domain);
  272. }
  273. /**
  274. * Clean a string, escaping any meta characters that could be
  275. * used to disrupt an SQL string. i.e. "'" => "\'" etc.
  276. *
  277. * @param String (or Array)
  278. * @return String (or Array) of cleaned data, suitable for use within an SQL
  279. * statement.
  280. */
  281. function escape_string ($string)
  282. {
  283. global $CONF;
  284. // if the string is actually an array, do a recursive cleaning.
  285. // Note, the array keys are not cleaned.
  286. if(is_array($string)) {
  287. $clean = array();
  288. foreach(array_keys($string) as $row) {
  289. $clean[$row] = escape_string($string[$row]);
  290. }
  291. return $clean;
  292. }
  293. if (get_magic_quotes_gpc ())
  294. {
  295. $string = stripslashes($string);
  296. }
  297. if (!is_numeric($string))
  298. {
  299. $link = db_connect();
  300. if ($CONF['database_type'] == "mysql")
  301. {
  302. $escaped_string = mysql_real_escape_string($string, $link);
  303. }
  304. if ($CONF['database_type'] == "mysqli")
  305. {
  306. $escaped_string = mysqli_real_escape_string($link, $string);
  307. }
  308. if ($CONF['database_type'] == "pgsql")
  309. {
  310. // php 5.2+ allows for $link to be specified.
  311. if (version_compare(phpversion(), "5.2.0", ">="))
  312. {
  313. $escaped_string = pg_escape_string($link, $string);
  314. }
  315. else
  316. {
  317. $escaped_string = pg_escape_string($string);
  318. }
  319. }
  320. }
  321. else
  322. {
  323. $escaped_string = $string;
  324. }
  325. return $escaped_string;
  326. }
  327. /**
  328. * safeget
  329. * Action: get value from $_GET[$param], or $default if $_GET[$param] is not set
  330. * Call: $param = safeget('param') # replaces $param = $_GET['param']
  331. * - or -
  332. * $param = safeget('param', 'default')
  333. *
  334. * @param String parameter name.
  335. * @param String (optional) - default value if key is not set.
  336. * @return String
  337. */
  338. function safeget ($param, $default="") {
  339. $retval=$default;
  340. if (isset($_GET[$param])) $retval=$_GET[$param];
  341. return $retval;
  342. }
  343. /**
  344. * safepost - similar to safeget()
  345. * @see safeget()
  346. * @param String parameter name
  347. * @param String (optional) default value (defaults to "")
  348. * @return String - value in $_POST[$param] or $default
  349. * same as safeget, but for $_POST
  350. */
  351. function safepost ($param, $default="") {
  352. $retval=$default;
  353. if (isset($_POST[$param])) $retval=$_POST[$param];
  354. return $retval;
  355. }
  356. /**
  357. * safeserver
  358. * @see safeget()
  359. * @param String $param
  360. * @param String $default (optional)
  361. * @return String value from $_SERVER[$param] or $default
  362. */
  363. function safeserver ($param, $default="") {
  364. $retval=$default;
  365. if (isset($_SERVER[$param])) $retval=$_SERVER[$param];
  366. return $retval;
  367. }
  368. /**
  369. * safecookie
  370. * @see safeget()
  371. * @param String $param
  372. * @param String $default (optional)
  373. * @return String value from $_COOKIE[$param] or $default
  374. */
  375. function safecookie ($param, $default="") {
  376. $retval=$default;
  377. if (isset($_COOKIE[$param])) $retval=$_COOKIE[$param];
  378. return $retval;
  379. }
  380. //
  381. // get_domain_properties
  382. // Action: Get all the properties of a domain.
  383. // Call: get_domain_properties (string domain)
  384. //
  385. function get_domain_properties ($domain)
  386. {
  387. global $CONF;
  388. global $table_alias, $table_mailbox, $table_domain;
  389. $list = array ();
  390. $result = db_query ("SELECT COUNT(*) FROM $table_alias WHERE domain='$domain'");
  391. $row = db_row ($result['result']);
  392. $list['alias_count'] = $row[0];
  393. $result = db_query ("SELECT COUNT(*) FROM $table_mailbox WHERE domain='$domain'");
  394. $row = db_row ($result['result']);
  395. $list['mailbox_count'] = $row[0];
  396. $result = db_query ("SELECT SUM(quota) FROM $table_mailbox WHERE domain='$domain'");
  397. $row = db_row ($result['result']);
  398. $list['quota_sum'] = $row[0];
  399. $list['alias_count'] = $list['alias_count'] - $list['mailbox_count'];
  400. $list['alias_pgindex']=array ();
  401. $list['mbox_pgindex']=array ();
  402. $list['mbox_pgindex_count'] = 0;
  403. //while loop to figure index names. use page_size and loop of queries
  404. $i=0;
  405. $current=0;
  406. $page_size = (int) $CONF['page_size'];
  407. if ($page_size < 1) die ("\$CONF['page_size'] = '$page_size' is invalid (it may only contain digits and must be >= 1)");
  408. $tmpstr="";
  409. $idxlabel="";
  410. $list['alias_pgindex_count'] = 0;
  411. if ( $list['alias_count'] > $page_size )
  412. {
  413. while ( $current < $list['alias_count'] )
  414. {
  415. $limitSql=('pgsql'==$CONF['database_type']) ? "1 OFFSET $current" : "$current, 1";
  416. $query = "SELECT $table_alias.address
  417. FROM $table_alias
  418. LEFT JOIN $table_mailbox ON $table_alias.address=$table_mailbox.username
  419. WHERE ($table_alias.domain='$domain' AND $table_mailbox.maildir IS NULL)
  420. ORDER BY $table_alias.address LIMIT $limitSql";
  421. $result = db_query ("$query");
  422. $row = db_array ($result['result']);
  423. $tmpstr = $row['address'];
  424. //get first 2 chars
  425. $idxlabel = $tmpstr[0] . $tmpstr[1] . "-";
  426. ($current + $page_size - 1 <= $list['alias_count']) ? $current = $current + $page_size - 1 : $current = $list['alias_count'] - 1;
  427. $limitSql=('pgsql'==$CONF['database_type']) ? "1 OFFSET $current" : "$current, 1";
  428. $query = "SELECT $table_alias.address
  429. FROM $table_alias
  430. LEFT JOIN $table_mailbox ON $table_alias.address=$table_mailbox.username
  431. WHERE ($table_alias.domain='$domain' AND $table_mailbox.maildir IS NULL)
  432. ORDER BY $table_alias.address LIMIT $limitSql";
  433. $result = db_query ("$query");
  434. $row = db_array ($result['result']);
  435. $tmpstr = $row['address'];
  436. $idxlabel = $idxlabel . $tmpstr[0] . $tmpstr[1];
  437. $current = $current + 1;
  438. $list['alias_pgindex'][]=$idxlabel;
  439. $i++;
  440. }
  441. $list['alias_pgindex_count']=$i;
  442. }
  443. $i=0;
  444. $current=0;
  445. $page_size = $CONF['page_size'];
  446. $tmpstr="";
  447. $idxlabel="";
  448. if ( $list['mailbox_count'] > $page_size )
  449. {
  450. while ( $current < $list['mailbox_count'] )
  451. {
  452. $limitSql=('pgsql'==$CONF['database_type']) ? "1 OFFSET $current" : "$current, 1";
  453. $query = "SELECT $table_mailbox.username FROM $table_mailbox WHERE $table_mailbox.domain='$domain' ORDER BY $table_mailbox.username LIMIT $limitSql";
  454. $result = db_query ("$query");
  455. $row = db_array ($result['result']);
  456. $tmpstr = $row['username'];
  457. //get first 2 chars
  458. $idxlabel = $tmpstr[0] . $tmpstr[1] . "-";
  459. ($current + $page_size - 1 <= $list['mailbox_count']) ? $current = $current + $page_size - 1 : $current = $list['mailbox_count'] - 1;
  460. $limitSql=('pgsql'==$CONF['database_type']) ? "1 OFFSET $current" : "$current, 1";
  461. $query = "SELECT $table_mailbox.username FROM $table_mailbox WHERE $table_mailbox.domain='$domain' ORDER BY $table_mailbox.username LIMIT $limitSql";
  462. $result = db_query ("$query");
  463. $row = db_array ($result['result']);
  464. $tmpstr = $row['username'];
  465. $idxlabel = $idxlabel . $tmpstr[0] . $tmpstr[1];
  466. $current = $current + 1;
  467. $list['mbox_pgindex'][]=$idxlabel;
  468. $i++;
  469. }
  470. $list['mbox_pgindex_count']=$i;
  471. }
  472. // end mod
  473. $query="SELECT * FROM $table_domain WHERE domain='$domain'";
  474. if ('pgsql'==$CONF['database_type'])
  475. {
  476. $query=" SELECT *, EXTRACT(epoch FROM created) AS uts_created, EXTRACT(epoch FROM modified) AS uts_modified FROM $table_domain WHERE domain='$domain' ";
  477. }
  478. $result = db_query ($query);
  479. $row = db_array ($result['result']);
  480. $list['description'] = $row['description'];
  481. $list['aliases'] = $row['aliases'];
  482. $list['mailboxes'] = $row['mailboxes'];
  483. $list['maxquota'] = $row['maxquota'];
  484. $list['quota'] = $row['quota'];
  485. $list['transport'] = $row['transport'];
  486. $list['backupmx'] = $row['backupmx'];
  487. $list['created'] = $row['created'];
  488. $list['modified'] = $row['modified'];
  489. $list['active'] = $row['active'];
  490. if ($CONF['database_type'] == "pgsql")
  491. {
  492. $list['active']=('t'==$row['active']) ? 1 : 0;
  493. $list['backupmx']=('t'==$row['backupmx']) ? 1 : 0;
  494. $list['created']= gmstrftime('%c %Z',$row['uts_created']);
  495. $list['modified']= gmstrftime('%c %Z',$row['uts_modified']);
  496. }
  497. else
  498. {
  499. $list['active'] = $row['active'];
  500. $list['backupmx'] = $row['backupmx'];
  501. }
  502. return $list;
  503. }
  504. //
  505. // get_mailbox_properties
  506. // Action: Get all the properties of a mailbox.
  507. // Call: get_mailbox_properties (string mailbox)
  508. //
  509. function get_mailbox_properties ($username)
  510. {
  511. global $CONF;
  512. global $table_mailbox;
  513. $query="SELECT * FROM $table_mailbox WHERE username='$username'";
  514. if ('pgsql'==$CONF['database_type'])
  515. {
  516. $query="
  517. SELECT
  518. *,
  519. EXTRACT(epoch FROM created) AS uts_created,
  520. EXTRACT(epoch FROM modified) AS uts_modified
  521. FROM $table_mailbox
  522. WHERE username='$username'
  523. ";
  524. }
  525. $result = db_query ($query);
  526. $row = db_array ($result['result']);
  527. $list['name'] = $row['name'];
  528. $list['maildir'] = $row['maildir'];
  529. $list['quota'] = $row['quota'];
  530. $list['domain'] = $row['domain'];
  531. $list['created'] = $row['created'];
  532. $list['modified'] = $row['modified'];
  533. $list['active'] = $row['active'];
  534. if ($CONF['database_type'] == "pgsql")
  535. {
  536. $list['active']=('t'==$row['active']) ? 1 : 0;
  537. $list['created']= gmstrftime('%c %Z',$row['uts_created']);
  538. $list['modified']= gmstrftime('%c %Z',$row['uts_modified']);
  539. }
  540. else
  541. {
  542. $list['active'] = $row['active'];
  543. }
  544. return $list;
  545. }
  546. //
  547. // check_alias
  548. // Action: Checks if the domain is still able to create aliases.
  549. // Call: check_alias (string domain)
  550. //
  551. function check_alias ($domain)
  552. {
  553. $limit = get_domain_properties ($domain);
  554. if ($limit['aliases'] == 0)
  555. {
  556. # 0 = unlimited, -1 = disabled
  557. return true;
  558. }
  559. if ($limit['aliases'] < 0)
  560. {
  561. return false;
  562. }
  563. if ($limit['alias_count'] >= $limit['aliases'])
  564. {
  565. return false;
  566. }
  567. else
  568. {
  569. return true;
  570. }
  571. }
  572. //
  573. // check_mailbox
  574. // Action: Checks if the domain is still able to create mailboxes.
  575. // Call: check_mailbox (string domain)
  576. //
  577. function check_mailbox ($domain)
  578. {
  579. $limit = get_domain_properties ($domain);
  580. /* -1 = disable, 0 = unlimited */
  581. if ($limit['mailboxes'] == 0)
  582. {
  583. return true;
  584. }
  585. if ($limit['mailboxes'] < 0)
  586. {
  587. return false;
  588. }
  589. if ($limit['mailbox_count'] >= $limit['mailboxes'])
  590. {
  591. return false;
  592. }
  593. else
  594. {
  595. return true;
  596. }
  597. }
  598. //
  599. // check_quota
  600. // Action: Checks if the user is creating a mailbox with the correct quota
  601. // Call: check_quota (string domain)
  602. //
  603. function check_quota ($quota, $domain)
  604. {
  605. $limit = get_domain_properties ($domain);
  606. if ($limit['maxquota'] == 0)
  607. {
  608. return true;
  609. }
  610. if (($limit['maxquota'] < 0) and ($quota < 0))
  611. {
  612. return true;
  613. }
  614. if (($limit['maxquota'] > 0) and ($quota == 0))
  615. {
  616. return false;
  617. }
  618. if ($quota > $limit['maxquota'])
  619. {
  620. return false;
  621. }
  622. else
  623. {
  624. return true;
  625. }
  626. }
  627. //
  628. // multiply_quota
  629. // Action: Recalculates the quota from bytes to MBs (multiply, *)
  630. // Call: multiply_quota (string $quota)
  631. //
  632. function multiply_quota ($quota)
  633. {
  634. global $CONF;
  635. if ($quota == -1) return $quota;
  636. $value = $quota * $CONF['quota_multiplier'];
  637. return $value;
  638. }
  639. //
  640. // divide_quota
  641. // Action: Recalculates the quota from MBs to bytes (divide, /)
  642. // Call: divide_quota (string $quota)
  643. //
  644. function divide_quota ($quota)
  645. {
  646. global $CONF;
  647. if ($quota == -1) return $quota;
  648. $value = round($quota / $CONF['quota_multiplier'],2);
  649. return $value;
  650. }
  651. //
  652. // check_owner
  653. // Action: Checks if the admin is the owner of the domain (or global-admin)
  654. // Call: check_owner (string admin, string domain)
  655. //
  656. function check_owner ($username, $domain)
  657. {
  658. global $table_domain_admins;
  659. $result = db_query ("SELECT 1 FROM $table_domain_admins WHERE username='$username' AND (domain='$domain' OR domain='ALL') AND active='1'");
  660. if ($result['rows'] != 1)
  661. {
  662. if ($result['rows'] > 1) { # "ALL" + specific domain permissions. 2.3 doesn't create such entries, but they are available as leftover from older versions
  663. flash_error("Permission check returned more than one result. Please go to 'edit admin' for your username and press the save "
  664. . "button once to fix the database. If this doesn't help, open a bugreport.");
  665. }
  666. return false;
  667. }
  668. else
  669. {
  670. return true;
  671. }
  672. }
  673. //
  674. // check_alias_owner
  675. // Action: Checks if the admin is the owner of the alias.
  676. // Call: check_alias_owner (string admin, string alias)
  677. //
  678. function check_alias_owner ($username, $alias)
  679. {
  680. global $CONF;
  681. if (authentication_has_role('global-admin')) return true;
  682. $tmp = preg_split('/\@/', $alias);
  683. if (($CONF['special_alias_control'] == 'NO') && array_key_exists($tmp[0], $CONF['default_aliases']))
  684. {
  685. return false;
  686. }
  687. else
  688. {
  689. return true;
  690. }
  691. }
  692. /**
  693. * List domains for an admin user.
  694. * @param String $username
  695. * @return array of domain names.
  696. */
  697. function list_domains_for_admin ($username)
  698. {
  699. global $CONF;
  700. global $table_domain, $table_domain_admins;
  701. $list = array ();
  702. // does $username need escaping here?
  703. $active_sql = db_get_boolean(True);
  704. $backupmx_sql = db_get_boolean(False);
  705. $query = "SELECT $table_domain.domain, $table_domain_admins.username FROM $table_domain
  706. LEFT JOIN $table_domain_admins ON $table_domain.domain=$table_domain_admins.domain
  707. WHERE $table_domain_admins.username='$username'
  708. AND $table_domain.active='$active_sql'
  709. AND $table_domain.backupmx='$backupmx_sql'
  710. ORDER BY $table_domain_admins.domain";
  711. $result = db_query ($query);
  712. if ($result['rows'] > 0)
  713. {
  714. $i = 0;
  715. while ($row = db_array ($result['result']))
  716. {
  717. $list[$i] = $row['domain'];
  718. $i++;
  719. }
  720. }
  721. return $list;
  722. }
  723. //
  724. // list_domains
  725. // Action: List all available domains.
  726. // Call: list_domains ()
  727. //
  728. function list_domains ()
  729. {
  730. global $table_domain;
  731. $list = array();
  732. $result = db_query ("SELECT domain FROM $table_domain WHERE domain!='ALL' ORDER BY domain");
  733. if ($result['rows'] > 0)
  734. {
  735. $i = 0;
  736. while ($row = db_array ($result['result']))
  737. {
  738. $list[$i] = $row['domain'];
  739. $i++;
  740. }
  741. }
  742. return $list;
  743. }
  744. //
  745. // admin_exist
  746. // Action: Checks if the admin already exists.
  747. // Call: admin_exist (string admin)
  748. //
  749. function admin_exist ($username)
  750. {
  751. $result = db_query ("SELECT 1 FROM " . table_by_key ('admin') . " WHERE username='$username'");
  752. if ($result['rows'] != 1)
  753. {
  754. return false;
  755. }
  756. else
  757. {
  758. return true;
  759. }
  760. }
  761. //
  762. // domain_exist
  763. // Action: Checks if the domain already exists.
  764. // Call: domain_exist (string domain)
  765. //
  766. function domain_exist ($domain)
  767. {
  768. global $table_domain;
  769. $result = db_query("SELECT 1 FROM $table_domain WHERE domain='$domain'");
  770. if ($result['rows'] != 1)
  771. {
  772. return false;
  773. }
  774. else
  775. {
  776. return true;
  777. }
  778. }
  779. //
  780. // list_admins
  781. // Action: Lists all the admins
  782. // Call: list_admins ()
  783. //
  784. // was admin_list_admins
  785. //
  786. function list_admins ()
  787. {
  788. global $table_admin;
  789. $list = "";
  790. $result = db_query ("SELECT username FROM $table_admin ORDER BY username");
  791. if ($result['rows'] > 0)
  792. {
  793. $i = 0;
  794. while ($row = db_array ($result['result']))
  795. {
  796. $list[$i] = $row['username'];
  797. $i++;
  798. }
  799. }
  800. return $list;
  801. }
  802. //
  803. // get_admin_properties
  804. // Action: Get all the admin properties.
  805. // Call: get_admin_properties (string admin)
  806. //
  807. function get_admin_properties ($username)
  808. {
  809. global $CONF;
  810. global $table_admin, $table_domain_admins;
  811. $list = array ();
  812. $result = db_query ("SELECT * FROM $table_domain_admins WHERE username='$username' AND domain='ALL'");
  813. if ($result['rows'] == 1)
  814. {
  815. $list['domain_count'] = 'ALL';
  816. }
  817. else
  818. {
  819. $result = db_query ("SELECT COUNT(*) FROM $table_domain_admins WHERE username='$username'");
  820. $row = db_row ($result['result']);
  821. $list['domain_count'] = $row[0];
  822. }
  823. $query = "SELECT * FROM $table_admin WHERE username='$username'";
  824. if ('pgsql'==$CONF['database_type']) {
  825. $query="
  826. SELECT
  827. *,
  828. EXTRACT(epoch FROM created) AS uts_created,
  829. EXTRACT (epoch FROM modified) AS uts_modified
  830. FROM $table_admin
  831. WHERE username='$username'
  832. ";
  833. }
  834. $result = db_query ($query);
  835. $row = db_array ($result['result']);
  836. $list['created'] = $row['created'];
  837. $list['modified'] = $row['modified'];
  838. $list['active'] = $row['active'];
  839. if ('pgsql'==$CONF['database_type']) {
  840. $list['active'] = ('t'==$row['active']) ? 1 : 0;
  841. $list['created']= gmstrftime('%c %Z',$row['uts_created']);
  842. $list['modified']= gmstrftime('%c %Z',$row['uts_modified']);
  843. }
  844. return $list;
  845. }
  846. //
  847. // encode_header
  848. // Action: Encode a string according to RFC 1522 for use in headers if it contains 8-bit characters.
  849. // Call: encode_header (string header, string charset)
  850. //
  851. function encode_header ($string, $default_charset = "utf-8")
  852. {
  853. if (strtolower ($default_charset) == 'iso-8859-1')
  854. {
  855. $string = str_replace ("\240",' ',$string);
  856. }
  857. $j = strlen ($string);
  858. $max_l = 75 - strlen ($default_charset) - 7;
  859. $aRet = array ();
  860. $ret = '';
  861. $iEncStart = $enc_init = false;
  862. $cur_l = $iOffset = 0;
  863. for ($i = 0; $i < $j; ++$i)
  864. {
  865. switch ($string{$i})
  866. {
  867. case '=':
  868. case '<':
  869. case '>':
  870. case ',':
  871. case '?':
  872. case '_':
  873. if ($iEncStart === false)
  874. {
  875. $iEncStart = $i;
  876. }
  877. $cur_l+=3;
  878. if ($cur_l > ($max_l-2))
  879. {
  880. $aRet[] = substr ($string,$iOffset,$iEncStart-$iOffset);
  881. $aRet[] = "=?$default_charset?Q?$ret?=";
  882. $iOffset = $i;
  883. $cur_l = 0;
  884. $ret = '';
  885. $iEncStart = false;
  886. }
  887. else
  888. {
  889. $ret .= sprintf ("=%02X",ord($string{$i}));
  890. }
  891. break;
  892. case '(':
  893. case ')':
  894. if ($iEncStart !== false)
  895. {
  896. $aRet[] = substr ($string,$iOffset,$iEncStart-$iOffset);
  897. $aRet[] = "=?$default_charset?Q?$ret?=";
  898. $iOffset = $i;
  899. $cur_l = 0;
  900. $ret = '';
  901. $iEncStart = false;
  902. }
  903. break;
  904. case ' ':
  905. if ($iEncStart !== false)
  906. {
  907. $cur_l++;
  908. if ($cur_l > $max_l)
  909. {
  910. $aRet[] = substr ($string,$iOffset,$iEncStart-$iOffset);
  911. $aRet[] = "=?$default_charset?Q?$ret?=";
  912. $iOffset = $i;
  913. $cur_l = 0;
  914. $ret = '';
  915. $iEncStart = false;
  916. }
  917. else
  918. {
  919. $ret .= '_';
  920. }
  921. }
  922. break;
  923. default:
  924. $k = ord ($string{$i});
  925. if ($k > 126)
  926. {
  927. if ($iEncStart === false)
  928. {
  929. // do not start encoding in the middle of a string, also take the rest of the word.
  930. $sLeadString = substr ($string,0,$i);
  931. $aLeadString = explode (' ',$sLeadString);
  932. $sToBeEncoded = array_pop ($aLeadString);
  933. $iEncStart = $i - strlen ($sToBeEncoded);
  934. $ret .= $sToBeEncoded;
  935. $cur_l += strlen ($sToBeEncoded);
  936. }
  937. $cur_l += 3;
  938. // first we add the encoded string that reached it's max size
  939. if ($cur_l > ($max_l-2))
  940. {
  941. $aRet[] = substr ($string,$iOffset,$iEncStart-$iOffset);
  942. $aRet[] = "=?$default_charset?Q?$ret?= ";
  943. $cur_l = 3;
  944. $ret = '';
  945. $iOffset = $i;
  946. $iEncStart = $i;
  947. }
  948. $enc_init = true;
  949. $ret .= sprintf ("=%02X", $k);
  950. }
  951. else
  952. {
  953. if ($iEncStart !== false)
  954. {
  955. $cur_l++;
  956. if ($cur_l > $max_l)
  957. {
  958. $aRet[] = substr ($string,$iOffset,$iEncStart-$iOffset);
  959. $aRet[] = "=?$default_charset?Q?$ret?=";
  960. $iEncStart = false;
  961. $iOffset = $i;
  962. $cur_l = 0;
  963. $ret = '';
  964. }
  965. else
  966. {
  967. $ret .= $string{$i};
  968. }
  969. }
  970. }
  971. break;
  972. }
  973. }
  974. if ($enc_init)
  975. {
  976. if ($iEncStart !== false)
  977. {
  978. $aRet[] = substr ($string,$iOffset,$iEncStart-$iOffset);
  979. $aRet[] = "=?$default_charset?Q?$ret?=";
  980. }
  981. else
  982. {
  983. $aRet[] = substr ($string,$iOffset);
  984. }
  985. $string = implode ('',$aRet);
  986. }
  987. return $string;
  988. }
  989. //
  990. // generate_password
  991. // Action: Generates a random password
  992. // Call: generate_password ()
  993. //
  994. function generate_password ()
  995. {
  996. $password = substr (md5 (mt_rand ()), 0, 8);
  997. return $password;
  998. }
  999. /**
  1000. * Encrypt a password, using the apparopriate hashing mechanism as defined in
  1001. * config.inc.php ($CONF['encrypt']).
  1002. * When wanting to compare one pw to another, it's necessary to provide the salt used - hence
  1003. * the second parameter ($pw_db), which is the existing hash from the DB.
  1004. *
  1005. * @param string $pw
  1006. * @param string $encrypted password
  1007. * @return string encrypted password.
  1008. */
  1009. function pacrypt ($pw, $pw_db="")
  1010. {
  1011. global $CONF;
  1012. $pw = stripslashes($pw);
  1013. $password = "";
  1014. $salt = "";
  1015. if ($CONF['encrypt'] == 'md5crypt') {
  1016. $split_salt = preg_split ('/\$/', $pw_db);
  1017. if (isset ($split_salt[2])) {
  1018. $salt = $split_salt[2];
  1019. }
  1020. $password = md5crypt ($pw, $salt);
  1021. }
  1022. elseif ($CONF['encrypt'] == 'md5') {
  1023. $password = md5($pw);
  1024. }
  1025. elseif ($CONF['encrypt'] == 'system') {
  1026. if (preg_match("/\\$1\\$/", $pw_db)) {
  1027. $split_salt = preg_split ('/\$/', $pw_db);
  1028. $salt = "\$1\$${split_salt[2]}\$";
  1029. }
  1030. else {
  1031. if (strlen($pw_db) == 0) {
  1032. $salt = substr (md5 (mt_rand ()), 0, 2);
  1033. }
  1034. else {
  1035. $salt = substr ($pw_db, 0, 2);
  1036. }
  1037. }
  1038. $password = crypt ($pw, $salt);
  1039. }
  1040. elseif ($CONF['encrypt'] == 'cleartext') {
  1041. $password = $pw;
  1042. }
  1043. // See https://sourceforge.net/tracker/?func=detail&atid=937966&aid=1793352&group_id=191583
  1044. // this is apparently useful for pam_mysql etc.
  1045. elseif ($CONF['encrypt'] == 'mysql_encrypt')
  1046. {
  1047. if ($pw_db!="") {
  1048. $salt=substr($pw_db,0,2);
  1049. $res=db_query("SELECT ENCRYPT('".$pw."','".$salt."');");
  1050. } else {
  1051. $res=db_query("SELECT ENCRYPT('".$pw."');");
  1052. }
  1053. $l = db_row($res["result"]);
  1054. $password = $l[0];
  1055. }
  1056. elseif ($CONF['encrypt'] == 'authlib') {
  1057. $flavor = $CONF['authlib_default_flavor'];
  1058. $salt = substr(create_salt(), 0, 2); # courier-authlib supports only two-character salts
  1059. if(preg_match('/^{.*}/', $pw_db)) {
  1060. // we have a flavor in the db -> use it instead of default flavor
  1061. $result = preg_split('/[{}]/', $pw_db, 3); # split at { and/or }
  1062. $flavor = $result[1];
  1063. $salt = substr($result[2], 0, 2);
  1064. }
  1065. if(stripos($flavor, 'md5raw') === 0) {
  1066. $password = '{' . $flavor . '}' . md5($pw);
  1067. } elseif(stripos($flavor, 'md5') === 0) {
  1068. $password = '{' . $flavor . '}' . base64_encode(md5($pw, TRUE));
  1069. } elseif(stripos($flavor, 'crypt') === 0) {
  1070. $password = '{' . $flavor . '}' . crypt($pw, $salt);
  1071. } elseif(stripos($flavor, 'SHA') === 0) {
  1072. $password = '{' . $flavor . '}' . base64_encode(sha1($pw, TRUE));
  1073. } else {
  1074. die("authlib_default_flavor '" . $flavor . "' unknown. Valid flavors are 'md5raw', 'md5', 'SHA' and 'crypt'");
  1075. }
  1076. }
  1077. elseif (preg_match("/^dovecot:/", $CONF['encrypt'])) {
  1078. $split_method = preg_split ('/:/', $CONF['encrypt']);
  1079. $method = strtoupper($split_method[1]);
  1080. if (! preg_match("/^[A-Z0-9-]+$/", $method)) { die("invalid dovecot encryption method"); } # TODO: check against a fixed list?
  1081. if (strtolower($method) == 'md5-crypt') die("\$CONF['encrypt'] = 'dovecot:md5-crypt' will not work because dovecotpw generates a random salt each time. Please use \$CONF['encrypt'] = 'md5crypt' instead.");
  1082. $dovecotpw = "dovecotpw";
  1083. if (!empty($CONF['dovecotpw'])) $dovecotpw = $CONF['dovecotpw'];
  1084. # Use proc_open call to avoid safe_mode problems and to prevent showing plain password in process table
  1085. $spec = array(
  1086. 0 => array("pipe", "r"), // stdin
  1087. 1 => array("pipe", "w") // stdout
  1088. );
  1089. $pipe = proc_open("$dovecotpw '-s' $method", $spec, $pipes);
  1090. if (!$pipe) {
  1091. die("can't proc_open $dovecotpw");
  1092. } else {
  1093. // use dovecot's stdin, it uses getpass() twice
  1094. // Write pass in pipe stdin
  1095. fwrite($pipes[0], $pw . "\n", 1+strlen($pw)); usleep(1000);
  1096. fwrite($pipes[0], $pw . "\n", 1+strlen($pw));
  1097. fclose($pipes[0]);
  1098. // Read hash from pipe stdout
  1099. $password = fread($pipes[1], "200");
  1100. fclose($pipes[1]);
  1101. proc_close($pipe);
  1102. if ( !preg_match('/^\{' . $method . '\}/', $password)) { die("can't encrypt password with dovecotpw"); }
  1103. $password = trim(str_replace('{' . $method . '}', '', $password));
  1104. }
  1105. }
  1106. else {
  1107. die ('unknown/invalid $CONF["encrypt"] setting: ' . $CONF['encrypt']);
  1108. }
  1109. $password = escape_string ($password);
  1110. return $password;
  1111. }
  1112. //
  1113. // md5crypt
  1114. // Action: Creates MD5 encrypted password
  1115. // Call: md5crypt (string cleartextpassword)
  1116. //
  1117. function md5crypt ($pw, $salt="", $magic="")
  1118. {
  1119. $MAGIC = "$1$";
  1120. if ($magic == "") $magic = $MAGIC;
  1121. if ($salt == "") $salt = create_salt ();
  1122. $slist = explode ("$", $salt);
  1123. if ($slist[0] == "1") $salt = $slist[1];
  1124. $salt = substr ($salt, 0, 8);
  1125. $ctx = $pw . $magic . $salt;
  1126. $final = hex2bin (md5 ($pw . $salt . $pw));
  1127. for ($i=strlen ($pw); $i>0; $i-=16)
  1128. {
  1129. if ($i > 16)
  1130. {
  1131. $ctx .= substr ($final,0,16);
  1132. }
  1133. else
  1134. {
  1135. $ctx .= substr ($final,0,$i);
  1136. }
  1137. }
  1138. $i = strlen ($pw);
  1139. while ($i > 0)
  1140. {
  1141. if ($i & 1) $ctx .= chr (0);
  1142. else $ctx .= $pw[0];
  1143. $i = $i >> 1;
  1144. }
  1145. $final = hex2bin (md5 ($ctx));
  1146. for ($i=0;$i<1000;$i++)
  1147. {
  1148. $ctx1 = "";
  1149. if ($i & 1)
  1150. {
  1151. $ctx1 .= $pw;
  1152. }
  1153. else
  1154. {
  1155. $ctx1 .= substr ($final,0,16);
  1156. }
  1157. if ($i % 3) $ctx1 .= $salt;
  1158. if ($i % 7) $ctx1 .= $pw;
  1159. if ($i & 1)
  1160. {
  1161. $ctx1 .= substr ($final,0,16);
  1162. }
  1163. else
  1164. {
  1165. $ctx1 .= $pw;
  1166. }
  1167. $final = hex2bin (md5 ($ctx1));
  1168. }
  1169. $passwd = "";
  1170. $passwd .= to64 (((ord ($final[0]) << 16) | (ord ($final[6]) << 8) | (ord ($final[12]))), 4);
  1171. $passwd .= to64 (((ord ($final[1]) << 16) | (ord ($final[7]) << 8) | (ord ($final[13]))), 4);
  1172. $passwd .= to64 (((ord ($final[2]) << 16) | (ord ($final[8]) << 8) | (ord ($final[14]))), 4);
  1173. $passwd .= to64 (((ord ($final[3]) << 16) | (ord ($final[9]) << 8) | (ord ($final[15]))), 4);
  1174. $passwd .= to64 (((ord ($final[4]) << 16) | (ord ($final[10]) << 8) | (ord ($final[5]))), 4);
  1175. $passwd .= to64 (ord ($final[11]), 2);
  1176. return "$magic$salt\$$passwd";
  1177. }
  1178. function create_salt ()
  1179. {
  1180. srand ((double) microtime ()*1000000);
  1181. $salt = substr (md5 (rand (0,9999999)), 0, 8);
  1182. return $salt;
  1183. }
  1184. function hex2bin ($str)
  1185. {
  1186. $len = strlen ($str);
  1187. $nstr = "";
  1188. for ($i=0;$i<$len;$i+=2)
  1189. {
  1190. $num = sscanf (substr ($str,$i,2), "%x");
  1191. $nstr.=chr ($num[0]);
  1192. }
  1193. return $nstr;
  1194. }
  1195. function to64 ($v, $n)
  1196. {
  1197. $ITOA64 = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
  1198. $ret = "";
  1199. while (($n - 1) >= 0)
  1200. {
  1201. $n--;
  1202. $ret .= $ITOA64[$v & 0x3f];
  1203. $v = $v >> 6;
  1204. }
  1205. return $ret;
  1206. }
  1207. //
  1208. // smtp_mail
  1209. // Action: Sends email to new account.
  1210. // Call: smtp_mail (string To, string From, string Data)
  1211. // TODO: Replace this with something decent like PEAR::Mail or Zend_Mail.
  1212. function smtp_mail ($to, $from, $data)
  1213. {
  1214. global $CONF;
  1215. $smtpd_server = $CONF['smtp_server'];
  1216. $smtpd_port = $CONF['smtp_port'];
  1217. $smtp_server = $_SERVER["SERVER_NAME"];
  1218. $errno = "0";
  1219. $errstr = "0";
  1220. $timeout = "30";
  1221. $fh = @fsockopen ($smtpd_server, $smtpd_port, $errno, $errstr, $timeout);
  1222. if (!$fh)
  1223. {
  1224. return false;
  1225. }
  1226. else
  1227. {
  1228. $res = smtp_get_response($fh);
  1229. fputs ($fh, "EHLO $smtp_server\r\n");
  1230. $res = smtp_get_response($fh);
  1231. fputs ($fh, "MAIL FROM:<$from>\r\n");
  1232. $res = smtp_get_response($fh);
  1233. fputs ($fh, "RCPT TO:<$to>\r\n");
  1234. $res = smtp_get_response($fh);
  1235. fputs ($fh, "DATA\r\n");
  1236. $res = smtp_get_response($fh);
  1237. fputs ($fh, "$data\r\n.\r\n");
  1238. $res = smtp_get_response($fh);
  1239. fputs ($fh, "QUIT\r\n");
  1240. $res = smtp_get_response($fh);
  1241. fclose ($fh);
  1242. }
  1243. return true;
  1244. }
  1245. //
  1246. // smtp_get_response
  1247. // Action: Get response from mail server
  1248. // Call: smtp_get_response (string FileHandle)
  1249. //
  1250. function smtp_get_response ($fh)
  1251. {
  1252. $res ='';
  1253. do
  1254. {
  1255. $line = fgets($fh, 256);
  1256. $res .= $line;
  1257. }
  1258. while (preg_match("/^\d\d\d\-/", $line));
  1259. return $res;
  1260. }
  1261. $DEBUG_TEXT = "\n
  1262. <p />\n
  1263. Please check the documentation and website for more information.\n
  1264. <p />\n
  1265. <a href=\"http://postfixadmin.sf.net/\">Postfix Admin</a><br />\n
  1266. <a href='https://sourceforge.net/forum/forum.php?forum_id=676076'>Forums</a>
  1267. ";
  1268. /**
  1269. * db_connect
  1270. * Action: Makes a connection to the database if it doesn't exist
  1271. * Call: db_connect ()
  1272. * Optional parameter: $ignore_errors = TRUE, used by setup.php
  1273. *
  1274. * Return value:
  1275. * a) without $ignore_errors or $ignore_errors == 0
  1276. * - $link - the database connection -OR-
  1277. * - call die() in case of connection problems
  1278. * b) with $ignore_errors == TRUE
  1279. * array($link, $error_text);
  1280. */
  1281. function db_connect ($ignore_errors = 0)
  1282. {
  1283. global $CONF;
  1284. global $DEBUG_TEXT;
  1285. if ($ignore_errors != 0) $DEBUG_TEXT = '';
  1286. $error_text = '';
  1287. $link = 0;
  1288. if ($CONF['database_type'] == "mysql")
  1289. {
  1290. if (function_exists ("mysql_connect"))
  1291. {
  1292. $link = @mysql_connect ($CONF['database_host'], $CONF['database_user'], $CONF['database_password']) or $error_text .= ("<p />DEBUG INFORMATION:<br />Connect: " . mysql_error () . "$DEBUG_TEXT");
  1293. if ($link) {
  1294. @mysql_query("SET CHARACTER SET utf8",$link);
  1295. @mysql_query("SET COLLATION_CONNECTION='utf8_general_ci'",$link);
  1296. $succes = @mysql_select_db ($CONF['database_name'], $link) or $error_text .= ("<p />DEBUG INFORMATION:<br />MySQL Select Database: " . mysql_error () . "$DEBUG_TEXT");
  1297. }
  1298. }
  1299. else
  1300. {
  1301. $error_text .= "<p />DEBUG INFORMATION:<br />MySQL 3.x / 4.0 functions not available! (php5-mysql installed?)<br />database_type = 'mysql' in config.inc.php, are you using a different database? $DEBUG_TEXT";
  1302. }
  1303. }
  1304. elseif ($CONF['database_type'] == "mysqli")
  1305. {
  1306. if (function_exists ("mysqli_connect"))
  1307. {
  1308. $link = @mysqli_connect ($CONF['database_host'], $CONF['database_user'], $CONF['database_password']) or $error_text .= ("<p />DEBUG INFORMATION:<br />Connect: " . mysqli_connect_error () . "$DEBUG_TEXT");
  1309. if ($link) {
  1310. @mysqli_query($link,"SET CHARACTER SET utf8");
  1311. @mysqli_query($link,"SET COLLATION_CONNECTION='utf8_general_ci'");
  1312. $success = @mysqli_select_db ($link, $CONF['database_name']) or $error_text .= ("<p />DEBUG INFORMATION:<br />MySQLi Select Database: " . mysqli_error ($link) . "$DEBUG_TEXT");
  1313. }
  1314. }
  1315. else
  1316. {
  1317. $error_text .= "<p />DEBUG INFORMATION:<br />MySQL 4.1 functions not available! (php5-mysqli installed?)<br />database_type = 'mysqli' in config.inc.php, are you using a different database? $DEBUG_TEXT";
  1318. }
  1319. }
  1320. elseif ($CONF['database_type'] == "pgsql")
  1321. {
  1322. if (function_exists ("pg_pconnect"))
  1323. {
  1324. if(!isset($CONF['database_port'])) {
  1325. $CONF['database_port'] = '5432';
  1326. }
  1327. $connect_string = "host=" . $CONF['database_host'] . " port=" . $CONF['database_port'] . " dbname=" . $CONF['database_name'] . " user=" . $CONF['database_user'] . " password=" . $CONF['database_password'];
  1328. $link = @pg_pconnect ($connect_string) or $error_text .= ("<p />DEBUG INFORMATION:<br />Connect: failed to connect to database. $DEBUG_TEXT");
  1329. if ($link) pg_set_client_encoding($link, 'UNICODE');
  1330. }
  1331. else
  1332. {
  1333. $error_text .= "<p />DEBUG INFORMATION:<br />PostgreSQL functions not available! (php5-pgsql installed?)<br />database_type = 'pgsql' in config.inc.php, are you using a different database? $DEBUG_TEXT";
  1334. }
  1335. }
  1336. else
  1337. {
  1338. $error_text = "<p />DEBUG INFORMATION:<br />Invalid \$CONF['database_type']! Please fix your config.inc.php! $DEBUG_TEXT";
  1339. }
  1340. if ($ignore_errors)
  1341. {
  1342. return array($link, $error_text);
  1343. }
  1344. elseif ($error_text != "")
  1345. {
  1346. print $error_text;
  1347. die();
  1348. }
  1349. elseif ($link)
  1350. {
  1351. return $link;
  1352. }
  1353. else
  1354. {
  1355. print "DEBUG INFORMATION:<br />\n";
  1356. print "Connect: Unable to connect to database<br />\n";
  1357. print "<br />\n";
  1358. print "Make sure that you have set the correct database type in the config.inc.php file<br />\n";
  1359. print $DEBUG_TEXT;
  1360. die();
  1361. }
  1362. }
  1363. /**
  1364. * Returns the appropriate boolean value for the database.
  1365. * Currently only PostgreSQL and MySQL are supported.
  1366. * @param boolean $bool (REQUIRED)
  1367. * @return String or int as appropriate.
  1368. */
  1369. function db_get_boolean($bool) {
  1370. if(!is_bool($bool)) {
  1371. die("Invalid usage of 'db_get_boolean($bool)'");
  1372. }
  1373. global $CONF;
  1374. if($CONF['database_type']=='pgsql') {
  1375. // return either true or false (unquoted strings)
  1376. if($bool) {
  1377. return 't';
  1378. }
  1379. return 'f';
  1380. }
  1381. elseif($CONF['database_type'] == 'mysql' || $CONF['database_type'] == 'mysqli') {
  1382. if($bool) {
  1383. return 1;
  1384. }
  1385. return 0;
  1386. }
  1387. }
  1388. //
  1389. // db_query
  1390. // Action: Sends a query to the database and returns query result and number of rows
  1391. // Call: db_query (string query)
  1392. // Optional parameter: $ignore_errors = TRUE, used by upgrade.php
  1393. //
  1394. function db_query ($query, $ignore_errors = 0)
  1395. {
  1396. global $CONF;
  1397. global $DEBUG_TEXT;
  1398. $result = "";
  1399. $number_rows = "";
  1400. static $link;
  1401. $error_text = "";
  1402. if ($ignore_errors) $DEBUG_TEXT = "";
  1403. if (!is_resource($link)) $link = db_connect ();
  1404. if ($CONF['database_type'] == "mysql") $result = @mysql_query ($query, $link)
  1405. or $error_text = "<p />DEBUG INFORMATION:<br />Invalid query: " . mysql_error($link) . "$DEBUG_TEXT";
  1406. if ($CONF['database_type'] == "mysqli") $result = @mysqli_query ($link, $query)
  1407. or $error_text = "<p />DEBUG INFORMATION:<br />Invalid query: " . mysqli_error($link) . "$DEBUG_TEXT";
  1408. if ($CONF['database_type'] == "pgsql")
  1409. {
  1410. $result = @pg_query ($link, $query)
  1411. or $error_text = "<p />DEBUG INFORMATION:<br />Invalid query: " . pg_last_error() . "$DEBUG_TEXT";
  1412. }
  1413. if ($error_text != "" && $ignore_errors == 0) die($error_text);
  1414. if ($error_text == "") {
  1415. if (preg_match("/^SELECT/i", trim($query)))
  1416. {
  1417. // if $query was a SELECT statement check the number of rows with [database_type]_num_rows ().
  1418. if ($CONF['database_type'] == "mysql") $number_rows = mysql_num_rows ($result);
  1419. if ($CONF['database_type'] == "mysqli") $number_rows = mysqli_num_rows ($result);
  1420. if ($CONF['database_type'] == "pgsql") $number_rows = pg_num_rows ($result);
  1421. }
  1422. else
  1423. {
  1424. // if $query was something else, UPDATE, DELETE or INSERT check the number of rows with
  1425. // [database_type]_affected_rows ().
  1426. if ($CONF['database_type'] == "mysql") $number_rows = mysql_affected_rows ($link);
  1427. if ($CONF['database_type'] == "mysqli") $number_rows = mysqli_affected_rows ($link);
  1428. if ($CONF['database_type'] == "pgsql") $number_rows = pg_affected_rows ($result);
  1429. }
  1430. }
  1431. $return = array (
  1432. "result" => $result,
  1433. "rows" => $number_rows,
  1434. "error" => $error_text
  1435. );
  1436. return $return;
  1437. }
  1438. // db_row
  1439. // Action: Returns a row from a table
  1440. // Call: db_row (int result)
  1441. //
  1442. function db_row ($result)
  1443. {
  1444. global $CONF;
  1445. $row = "";
  1446. if ($CONF['database_type'] == "mysql") $row = mysql_fetch_row ($result);
  1447. if ($CONF['database_type'] == "mysqli") $row = mysqli_fetch_row ($result);
  1448. if ($CONF['database_type'] == "pgsql") $row = pg_fetch_row ($result);
  1449. return $row;
  1450. }
  1451. // db_array
  1452. // Action: Returns a row from a table
  1453. // Call: db_array (int result)
  1454. //
  1455. function db_array ($result)
  1456. {
  1457. global $CONF;
  1458. $row = "";
  1459. if ($CONF['database_type'] == "mysql") $row = mysql_fetch_array ($result);
  1460. if ($CONF['database_type'] == "mysqli") $row = mysqli_fetch_array ($result);
  1461. if ($CONF['database_type'] == "pgsql") $row = pg_fetch_array ($result);
  1462. return $row;
  1463. }
  1464. // db_assoc
  1465. // Action: Returns a row from a table
  1466. // Call: db_assoc(int result)
  1467. //
  1468. function db_assoc ($result)
  1469. {
  1470. global $CONF;
  1471. $row = "";
  1472. if ($CONF['database_type'] == "mysql") $row = mysql_fetch_assoc ($result);
  1473. if ($CONF['database_type'] == "mysqli") $row = mysqli_fetch_assoc ($result);
  1474. if ($CONF['database_type'] == "pgsql") $row = pg_fetch_assoc ($result);
  1475. return $row;
  1476. }
  1477. //
  1478. // db_delete
  1479. // Action: Deletes a row from a specified table
  1480. // Call: db_delete (string table, string where, string delete)
  1481. //
  1482. function db_delete ($table,$where,$delete)
  1483. {
  1484. # $table = table_by_key($table); # intentionally disabled to avoid breaking delete.php in 2.3.x
  1485. # This makes the behaviour of this function incorrect, but delete.php is the only file in 2.3.x calling db_delete and expects this (wrong) behaviour.
  1486. $query = "DELETE FROM $table WHERE " . escape_string($where) . "='" . escape_string($delete) . "'";
  1487. $result = db_query ($query);
  1488. if ($result['rows'] >= 1)
  1489. {
  1490. return $result['rows'];
  1491. }
  1492. else
  1493. {
  1494. return true;
  1495. }
  1496. }
  1497. /**
  1498. * db_insert
  1499. * Action: Inserts a row from a specified table
  1500. * Call: db_insert (string table, array values)
  1501. * @param String $table - table name
  1502. * @param array - key/value map of data to insert into the table.
  1503. * @param array (optional) - array of fields to set to now()
  1504. * @return int - number of inserted rows
  1505. */
  1506. function db_insert ($table, $values, $timestamp = array())
  1507. {
  1508. $table = table_by_key ($table);
  1509. foreach(array_keys($values) as $key) {
  1510. $values[$key] = "'" . escape_string($values[$key]) . "'";
  1511. }
  1512. foreach($timestamp as $key) {
  1513. $values[$key] = "now()";
  1514. }
  1515. $sql_values = "(" . implode(",",escape_string(array_keys($values))).") VALUES (".implode(",",$values).")";
  1516. $result = db_query ("INSERT INTO $table $sql_values");
  1517. return $result['rows'];
  1518. }
  1519. /**
  1520. * db_update
  1521. * Action: Updates a specified table
  1522. * Call: db_update (string table, array values, string where)
  1523. * @param String $table - table name
  1524. * @param String - WHERE condition
  1525. * @param array - key/value map of data to insert into the table.
  1526. * @param array (optional) - array of fields to set to now()
  1527. * @return int - number of updated rows
  1528. */
  1529. function db_update ($table, $where, $values, $timestamp = array())
  1530. {
  1531. $table = table_by_key ($table);
  1532. foreach(array_keys($values) as $key) {
  1533. $sql_values[$key] = escape_string($key) . "='" . escape_string($values[$key]) . "'";
  1534. }
  1535. foreach($timestamp as $key) {
  1536. $sql_values[$key] = escape_string($key) . "=now()";
  1537. }
  1538. $sql="UPDATE $table SET ".implode(",",$sql_values)." WHERE $where";
  1539. $result = db_query ($sql);
  1540. return $result['rows'];
  1541. }
  1542. /**
  1543. * db_log
  1544. * Action: Logs actions from admin
  1545. * Call: db_log (string username, string domain, string action, string data)
  1546. * Possible actions are:
  1547. * 'create_alias'
  1548. * 'create_alias_domain'
  1549. * 'create_mailbox'
  1550. * 'delete_alias'
  1551. * 'delete_alias_domain'
  1552. * 'delete_mailbox'
  1553. * 'edit_alias'
  1554. * 'edit_alias_state'
  1555. * 'edit_alias_domain_state'
  1556. * 'edit_mailbox'
  1557. * 'edit_mailbox_state'
  1558. * 'edit_password'
  1559. */
  1560. function db_log ($username,$domain,$action,$data)
  1561. {
  1562. global $CONF;
  1563. global $table_log;
  1564. $REMOTE_ADDR = $_SERVER['REMOTE_ADDR'];
  1565. $action_list = array('create_alias', 'create_alias_domain', 'delete_alias', 'delete_alias_domain', 'edit_alias', 'create_mailbox', 'delete_mailbox', 'edit_mailbox', 'edit_alias_state', 'edit_alias_domain_state', 'edit_mailbox_state', 'edit_password');
  1566. if(!in_array($action, $action_list)) {
  1567. die("Invalid log action : $action"); // could do with something better?
  1568. }
  1569. if ($CONF['logging'] == 'YES')
  1570. {
  1571. $logdata = array(
  1572. 'username' => "$username ($REMOTE_ADDR)",
  1573. 'domain' => $domain,
  1574. 'action' => $action,
  1575. 'data' => $data,
  1576. );
  1577. $result = db_insert('log', $logdata, array('timestamp') );
  1578. #$result = db_query ("INSERT INTO $table_log (timestamp,username,domain,action,data) VALUES (NOW(),'$username ($REMOTE_ADDR)','$domain','$action','$data')");
  1579. if ($result != 1)
  1580. {
  1581. return false;
  1582. }
  1583. else
  1584. {
  1585. return true;
  1586. }
  1587. }
  1588. }
  1589. /**
  1590. * db_in_clause
  1591. * Action: builds and returns the "field in(x, y)" clause for database queries
  1592. * Call: db_in_clause (string field, array values)
  1593. */
  1594. function db_in_clause($field, $values) {
  1595. return " $field IN ('"
  1596. . implode("','",escape_string(array_values($values)))
  1597. . "') ";
  1598. }
  1599. //
  1600. // table_by_key
  1601. // Action: Return table name for given key
  1602. // Call: table_by_key (string table_key)
  1603. //
  1604. function table_by_key ($table_key)
  1605. {
  1606. global $CONF;
  1607. $table = $CONF['database_prefix'].$CONF['database_tables'][$table_key];
  1608. if (empty($table)) $table = $table_key;
  1609. return $table;
  1610. }
  1611. /*
  1612. Called after a mailbox has been created in the DBMS.
  1613. Returns: boolean.
  1614. */
  1615. function mailbox_postcreation($username,$domain,$maildir,$quota)
  1616. {
  1617. if (empty($username) || empty($domain) || empty($maildir))
  1618. {
  1619. trigger_error('In '.__FUNCTION__.': empty username, domain and/or maildir parameter',E_USER_ERROR);
  1620. return FALSE;
  1621. }
  1622. global $CONF;
  1623. $confpar='mailbox_postcreation_script';
  1624. if (!isset($CONF[$confpar]) || empty($CONF[$confpar])) return TRUE;
  1625. $cmdarg1=escapeshellarg($username);
  1626. $cmdarg2=escapeshellarg($domain);
  1627. $cmdarg3=escapeshellarg($maildir);
  1628. if ($quota <= 0) $quota = 0;
  1629. $cmdarg4=escapeshellarg($quota);
  1630. $command=$CONF[$confpar]." $cmdarg1 $cmdarg2 $cmdarg3 $cmdarg4";
  1631. $retval=0;
  1632. $output=array();
  1633. $firstline='';
  1634. $firstline=exec($command,$output,$retval);
  1635. if (0!=$retval)
  1636. {
  1637. error_log("Running $command yielded return value=$retval, first line of output=$firstline");
  1638. print '<p>WARNING: Problems running mailbox postcreation script!</p>';
  1639. return FALSE;
  1640. }
  1641. return TRUE;
  1642. }
  1643. /*
  1644. Called after a mailbox has been altered in the DBMS.
  1645. Returns: boolean.
  1646. */
  1647. function mailbox_postedit($username,$domain,$maildir,$quota)
  1648. {
  1649. if (empty($username) || empty($domain) || empty($maildir))
  1650. {
  1651. trigger_error('In '.__FUNCTION__.': empty username, domain and/or maildir parameter',E_USER_ERROR);
  1652. return FALSE;
  1653. }
  1654. global $CONF;
  1655. $confpar='mailbox_postedit_script';
  1656. if (!isset($CONF[$confpar]) || empty($CONF[$confpar])) return TRUE;
  1657. $cmdarg1=escapeshellarg($username);
  1658. $cmdarg2=escapeshellarg($domain);
  1659. $cmdarg3=escapeshellarg($maildir);
  1660. if ($quota <= 0) $quota = 0;
  1661. $cmdarg4=escapeshellarg($quota);
  1662. $command=$CONF[$confpar]." $cmdarg1 $cmdarg2 $cmdarg3 $cmdarg4";
  1663. $retval=0;
  1664. $output=array();
  1665. $firstline='';
  1666. $firstline=exec($command,$output,$retval);
  1667. if (0!=$retval)
  1668. {
  1669. error_log("Running $command yielded return value=$retval, first line of output=$firstline");
  1670. print '<p>WARNING: Problems running mailbox postedit script!</p>';
  1671. return FALSE;
  1672. }
  1673. return TRUE;
  1674. }
  1675. /*
  1676. Called after a mailbox has been deleted in the DBMS.
  1677. Returns: boolean.
  1678. */
  1679. function mailbox_postdeletion($username,$domain)
  1680. {
  1681. global $CONF;
  1682. $confpar='mailbox_postdeletion_script';
  1683. if (!isset($CONF[$confpar]) || empty($CONF[$confpar]))
  1684. {
  1685. return true;
  1686. }
  1687. if (empty($username) || empty($domain))
  1688. {
  1689. print '<p>Warning: empty username and/or domain parameter.</p>';
  1690. return false;
  1691. }
  1692. $cmdarg1=escapeshellarg($username);
  1693. $cmdarg2=escapeshellarg($domain);
  1694. $command=$CONF[$confpar]." $cmdarg1 $cmdarg2";
  1695. $retval=0;
  1696. $output=array();
  1697. $firstline='';
  1698. $firstline=exec($command,$output,$retval);
  1699. if (0!=$retval)
  1700. {
  1701. error_log("Running $command yielded return value=$retval, first line of output=$firstline");
  1702. print '<p>WARNING: Problems running mailbox postdeletion script!</p>';
  1703. return FALSE;
  1704. }
  1705. return TRUE;
  1706. }
  1707. /*
  1708. Called after a domain has been added in the DBMS.
  1709. Returns: boolean.
  1710. */
  1711. function domain_postcreation($domain)
  1712. {
  1713. global $CONF;
  1714. $confpar='domain_postcreation_script';
  1715. if (!isset($CONF[$confpar]) || empty($CONF[$confpar]))
  1716. {
  1717. return true;
  1718. }
  1719. if (empty($domain))
  1720. {
  1721. print '<p>Warning: empty domain parameter.</p>';
  1722. return false;
  1723. }
  1724. $cmdarg1=escapeshellarg($domain);
  1725. $command=$CONF[$confpar]." $cmdarg1";
  1726. $retval=0;
  1727. $output=array();
  1728. $firstline='';
  1729. $firstline=exec($command,$output,$retval);
  1730. if (0!=$retval)
  1731. {
  1732. error_log("Running $command yielded return value=$retval, first line of output=$firstline");
  1733. print '<p>WARNING: Problems running domain postcreation script!</p>';
  1734. return FALSE;
  1735. }
  1736. return TRUE;
  1737. }
  1738. /*
  1739. Called after a domain has been deleted in the DBMS.
  1740. Returns: boolean.
  1741. */
  1742. function domain_postdeletion($domain)
  1743. {
  1744. global $CONF;
  1745. $confpar='domain_postdeletion_script';
  1746. if (!isset($CONF[$confpar]) || empty($CONF[$confpar]))
  1747. {
  1748. return true;
  1749. }
  1750. if (empty($domain))
  1751. {
  1752. print '<p>Warning: empty domain parameter.</p>';
  1753. return false;
  1754. }
  1755. $cmdarg1=escapeshellarg($domain);
  1756. $command=$CONF[$confpar]." $cmdarg1";
  1757. $retval=0;
  1758. $output=array();
  1759. $firstline='';
  1760. $firstline=exec($command,$output,$retval);
  1761. if (0!=$retval)
  1762. {
  1763. error_log("Running $command yielded return value=$retval, first line of output=$firstline");
  1764. print '<p>WARNING: Problems running domain postdeletion script!</p>';
  1765. return FALSE;
  1766. }
  1767. return TRUE;
  1768. }
  1769. /*
  1770. Called after an alias_domain has been deleted in the DBMS.
  1771. Returns: boolean.
  1772. */
  1773. function alias_domain_postdeletion($alias_domain)
  1774. {
  1775. global $CONF;
  1776. $confpar='alias_domain_postdeletion_script';
  1777. if (!isset($CONF[$confpar]) || empty($CONF[$confpar]))
  1778. {
  1779. return true;
  1780. }
  1781. if (empty($alias_domain))
  1782. {
  1783. print '<p>Warning: empty alias_domain parameter.</p>';
  1784. return false;
  1785. }
  1786. $cmdarg1=escapeshellarg($alias_domain);
  1787. $command=$CONF[$confpar]." $cmdarg1";
  1788. $retval=0;
  1789. $output=array();
  1790. $firstline='';
  1791. $firstline=exec($command,$output,$retval);
  1792. if (0!=$retval)
  1793. {
  1794. error_log("Running $command yielded return value=$retval, first line of output=$firstline");
  1795. print '<p>WARNING: Problems running alias_domain postdeletion script!</p>';
  1796. return FALSE;
  1797. }
  1798. return TRUE;
  1799. }
  1800. /*
  1801. Called by mailbox_postcreation() after a mailbox has been
  1802. created. Immediately returns, unless configuration indicates
  1803. that one or more sub-folders should be created.
  1804. Triggers E_USER_ERROR if configuration error is detected.
  1805. If IMAP login fails, the problem is logged to the system log
  1806. (such as /var/log/httpd/error_log), and the function returns
  1807. FALSE.
  1808. Returns FALSE on all other errors, or TRUE if everything
  1809. succeeds.
  1810. Doesn't clean up, if only some of the folders could be
  1811. created.
  1812. */
  1813. function create_mailbox_subfolders($login,$cleartext_password)
  1814. {
  1815. global $CONF;
  1816. if (empty($login))
  1817. {
  1818. trigger_error('In '.__FUNCTION__.': empty $login',E_USER_ERROR);
  1819. return FALSE;
  1820. }
  1821. if (!isset($CONF['create_mailbox_subdirs']) || empty($CONF['create_mailbox_subdirs'])) return TRUE;
  1822. if (!is_array($CONF['create_mailbox_subdirs']))
  1823. {
  1824. trigger_error('create_mailbox_subdirs must be an array',E_USER_ERROR);
  1825. return FALSE;
  1826. }
  1827. if (!isset($CONF['create_mailbox_subdirs_host']) || empty($CONF['create_mailbox_subdirs_host']))
  1828. {
  1829. trigger_error('An IMAP/POP server host ($CONF["create_mailbox_subdirs_host"]) must be configured, if sub-folders are to be created',E_USER_ERROR);
  1830. return FALSE;
  1831. }
  1832. $s_host=$CONF['create_mailbox_subdirs_host'];
  1833. $s_prefix=$CONF['create_mailbox_subdirs_prefix'];
  1834. $s_options='';
  1835. $s_port='';
  1836. if (
  1837. isset($CONF['create_mailbox_subdirs_hostoptions'])
  1838. && !empty($CONF['create_mailbox_subdirs_hostoptions'])
  1839. ) {
  1840. if (!is_array($CONF['create_mailbox_subdirs_hostoptions']))
  1841. {
  1842. trigger_error('The $CONF["create_mailbox_subdirs_hostoptions"] parameter must be an array',E_USER_ERROR);
  1843. return FALSE;
  1844. }
  1845. foreach ($CONF['create_mailbox_subdirs_hostoptions'] as $o)
  1846. {
  1847. $s_options.='/'.$o;
  1848. }
  1849. }
  1850. if (isset($CONF['create_mailbox_subdirs_hostport']) && !empty($CONF['create_mailbox_subdirs_hostport']))
  1851. {
  1852. $s_port=$CONF['create_mailbox_subdirs_hostport'];
  1853. if (intval($s_port)!=$s_port)
  1854. {
  1855. trigger_error('The $CONF["create_mailbox_subdirs_hostport"] parameter must be an integer',E_USER_ERROR);
  1856. return FALSE;
  1857. }
  1858. $s_port=':'.$s_port;
  1859. }
  1860. $s='{'.$s_host.$s_port.$s_options.'}';
  1861. sleep(1); # give the mail triggering the mailbox creation a chance to do its job
  1862. $i=@imap_open($s,$login,$cleartext_password);
  1863. if (FALSE==$i)
  1864. {
  1865. error_log('Could not log into IMAP/POP server: '.imap_last_error());
  1866. return FALSE;
  1867. }
  1868. foreach($CONF['create_mailbox_subdirs'] as $f)
  1869. {
  1870. $f='{'.$s_host.'}'.$s_prefix.$f;
  1871. $res=imap_createmailbox($i,$f);
  1872. if (!$res) {
  1873. error_log('Could not create IMAP folder $f: '.imap_last_error());
  1874. @imap_close($i);
  1875. return FALSE;
  1876. }
  1877. @imap_subscribe($i,$f);
  1878. }
  1879. @imap_close($i);
  1880. return TRUE;
  1881. }
  1882. //
  1883. // gen_show_status
  1884. // Action: Return a string of colored &nbsp;'s that indicate
  1885. // the if an alias goto has an error or is sent to
  1886. // addresses list in show_custom_domains
  1887. // Call: gen_show_status (string alias_address)
  1888. //
  1889. function gen_show_status ($show_alias)
  1890. {
  1891. global $CONF, $table_alias;
  1892. $stat_string = "";
  1893. $stat_goto = "";
  1894. $stat_result = db_query ("SELECT goto FROM $table_alias WHERE address='$show_alias'");
  1895. if ($stat_result['rows'] > 0)
  1896. {
  1897. $row = db_row ($stat_result['result']);
  1898. $stat_goto = $row[0];
  1899. }
  1900. // UNDELIVERABLE CHECK
  1901. if ( $CONF['show_undeliverable'] == 'YES' )
  1902. {
  1903. $gotos=array();
  1904. $gotos=explode(',',$stat_goto);
  1905. $undel_string="";
  1906. //make sure this alias goes somewhere known
  1907. $stat_ok = 1;
  1908. while ( ($g=array_pop($gotos)) && $stat_ok )
  1909. {
  1910. $stat_catchall = substr($g,strpos($g,"@"));
  1911. $stat_delimiter = "";
  1912. if (!empty($CONF['recipient_delimiter'])) {
  1913. $delimiter = preg_quote($CONF['recipient_delimiter'], "/");
  1914. $stat_delimiter = preg_replace('/' .$delimiter. '[^' .$delimiter. ']*@/', "@", $g);
  1915. $stat_delimiter = "OR address = '$stat_delimiter'";
  1916. }
  1917. $stat_result = db_query ("SELECT address FROM $table_alias WHERE address = '$g' OR address = '$stat_catchall' $stat_delimiter");
  1918. if ($stat_result['rows'] == 0)
  1919. {
  1920. $stat_ok = 0;
  1921. }
  1922. if ( $stat_ok == 0 )
  1923. {
  1924. $stat_domain = substr($g,strpos($g,"@")+1);
  1925. $stat_vacdomain = substr($stat_domain,strpos($stat_domain,"@")+1);
  1926. if ( $stat_vacdomain == $CONF['vacation_domain'] )
  1927. {
  1928. $stat_ok = 1;
  1929. break;
  1930. }
  1931. for ($i=0; $i < sizeof($CONF['show_undeliverable_exceptions']);$i++)
  1932. {
  1933. if ( $stat_domain == $CONF['show_undeliverable_exceptions'][$i] )
  1934. {
  1935. $stat_ok = 1;
  1936. break;
  1937. }
  1938. }
  1939. }
  1940. } // while
  1941. if ( $stat_ok == 0 )
  1942. {
  1943. $stat_string .= "<span style='background-color:" . $CONF['show_undeliverable_color'] .
  1944. "'>" . $CONF['show_status_text'] . "</span>&nbsp;";
  1945. }
  1946. else
  1947. {
  1948. $stat_string .= $CONF['show_status_text'] . "&nbsp;";
  1949. }
  1950. }
  1951. else
  1952. {
  1953. $stat_string .= $CONF['show_status_text'] . "&nbsp;";
  1954. }
  1955. // POP/IMAP CHECK
  1956. if ( $CONF['show_popimap'] == 'YES' )
  1957. {
  1958. $stat_delimiter = "";
  1959. if (!empty($CONF['recipient_delimiter'])) {
  1960. $delimiter = preg_quote($CONF['recipient_delimiter'], "/");
  1961. $stat_delimiter = preg_replace('/' .$delimiter. '[^' .$delimiter. '@]*@/', "@", $stat_goto);
  1962. $stat_delimiter = ',' . $stat_delimiter;
  1963. }
  1964. //if the address passed in appears in its own goto field, its POP/IMAP
  1965. if ( preg_match ('/,' . $show_alias . ',/', ',' . $stat_goto . $stat_delimiter . ',') )
  1966. {
  1967. $stat_string .= "<span style='background-color:" . $CONF['show_popimap_color'] .
  1968. "'>" . $CONF['show_status_text'] . "</span>&nbsp;";
  1969. }
  1970. else
  1971. {
  1972. $stat_string .= $CONF['show_status_text'] . "&nbsp;";
  1973. }
  1974. }
  1975. // CUSTOM DESTINATION CHECK
  1976. if ( count($CONF['show_custom_domains']) > 0 )
  1977. {
  1978. for ($i = 0; $i < sizeof ($CONF['show_custom_domains']); $i++)
  1979. {
  1980. if (preg_match ('/^.*' . $CONF['show_custom_domains'][$i] . '.*$/', $stat_goto))
  1981. {
  1982. $stat_string .= "<span style='background-color:" . $CONF['show_custom_colors'][$i] .
  1983. "'>" . $CONF['show_status_text'] . "</span>&nbsp;";
  1984. }
  1985. else
  1986. {
  1987. $stat_string .= $CONF['show_status_text'] . "&nbsp;";
  1988. }
  1989. }
  1990. }
  1991. else
  1992. {
  1993. $stat_string .= ";&nbsp;";
  1994. }
  1995. // $stat_string .= "<span style='background-color:green'> &nbsp; </span> &nbsp;" .
  1996. // "<span style='background-color:blue'> &nbsp; </span> &nbsp;";
  1997. return $stat_string;
  1998. }
  1999. /*
  2000. Called by create-admin.php and setup.php
  2001. Returns:
  2002. array(
  2003. 'error' => 0, # 0 on success, otherwise > 0
  2004. 'tMessage' => '', # success / failure message
  2005. 'pAdminCreate_admin_username_text' => '', # help text / error message for username
  2006. 'pAdminCreate_admin_password_text' => '' # error message for username
  2007. )
  2008. */
  2009. function create_admin($fUsername, $fPassword, $fPassword2, $fDomains, $no_generate_password=0)
  2010. {
  2011. global $PALANG;
  2012. global $CONF;
  2013. $error = 0;
  2014. $tMessage = '';
  2015. $pAdminCreate_admin_username_text = '';
  2016. $pAdminCreate_admin_password_text = '';
  2017. if (!check_email ($fUsername))
  2018. {
  2019. $error = 1;
  2020. $pAdminCreate_admin_username_text = $PALANG['pAdminCreate_admin_username_text_error1'];
  2021. }
  2022. if (empty ($fUsername) or admin_exist ($fUsername))
  2023. {
  2024. $error = 1;
  2025. $pAdminCreate_admin_username_text = $PALANG['pAdminCreate_admin_username_text_error2'];
  2026. }
  2027. if (empty ($fPassword) or empty ($fPassword2) or ($fPassword != $fPassword2))
  2028. {
  2029. if (empty ($fPassword) and empty ($fPassword2) and $CONF['generate_password'] == "YES" && $no_generate_password == 0)
  2030. {
  2031. $fPassword = generate_password ();
  2032. }
  2033. else
  2034. {
  2035. $error = 1;
  2036. $pAdminCreate_admin_username_text = $PALANG['pAdminCreate_admin_username_text'];
  2037. $pAdminCreate_admin_password_text = $PALANG['pAdminCreate_admin_password_text_error'];
  2038. }
  2039. }
  2040. if ($error != 1)
  2041. {
  2042. $password = pacrypt($fPassword);
  2043. $pAdminCreate_admin_username_text = $PALANG['pAdminCreate_admin_username_text'];
  2044. $result = db_query ("INSERT INTO " . table_by_key('admin') . " (username,password,created,modified) VALUES ('$fUsername','$password',NOW(),NOW())");
  2045. if ($result['rows'] != 1)
  2046. {
  2047. $tMessage = $PALANG['pAdminCreate_admin_result_error'] . "<br />($fUsername)<br />";
  2048. }
  2049. else
  2050. {
  2051. if (!empty ($fDomains[0]))
  2052. {
  2053. for ($i = 0; $i < sizeof ($fDomains); $i++)
  2054. {
  2055. $domain = $fDomains[$i];
  2056. $result = db_query ("INSERT INTO " . table_by_key ('domain_admins') . " (username,domain,created) VALUES ('$fUsername','$domain',NOW())");
  2057. }
  2058. }
  2059. $tMessage = $PALANG['pAdminCreate_admin_result_success'] . "<br />($fUsername";
  2060. if ($CONF['generate_password'] == "YES" && $no_generate_password == 0)
  2061. {
  2062. $tMessage .= " / $fPassword)</br />";
  2063. }
  2064. else
  2065. {
  2066. if ($CONF['show_password'] == "YES" && $no_generate_password == 0)
  2067. {
  2068. $tMessage .= " / $fPassword)</br />";
  2069. }
  2070. else
  2071. {
  2072. $tMessage .= ")</br />";
  2073. }
  2074. }
  2075. }
  2076. }
  2077. # TODO: should we log creation, editing and deletion of admins?
  2078. # Note: needs special handling in viewlog, because domain is empty
  2079. # db_log ($SESSID_USERNAME, '', 'create_admin', "$fUsername");
  2080. return array(
  2081. $error,
  2082. $tMessage,
  2083. $pAdminCreate_admin_username_text,
  2084. $pAdminCreate_admin_password_text
  2085. );
  2086. }
  2087. /*
  2088. Convert $CONF['whatever'] to boolean
  2089. (obviously only useful for settings that can be YES or NO)
  2090. Returns: TRUE (on YES/yes) or FALSE (on NO/no/not set/unknown value)
  2091. */
  2092. function boolconf($setting) {
  2093. global $CONF;
  2094. if (!isset($CONF[$setting])) { # not set
  2095. # TODO: show/log error message on unknown settings?
  2096. return false;
  2097. } elseif (strtoupper($CONF[$setting]) == 'YES') { # YES
  2098. return true;
  2099. } else { # NO, unknown value
  2100. # TODO: show/log error message on unknown value?
  2101. return false;
  2102. }
  2103. }
  2104. $table_admin = table_by_key ('admin');
  2105. $table_alias = table_by_key ('alias');
  2106. $table_alias_domain = table_by_key ('alias_domain');
  2107. $table_domain = table_by_key ('domain');
  2108. $table_domain_admins = table_by_key ('domain_admins');
  2109. $table_log = table_by_key ('log');
  2110. $table_mailbox = table_by_key ('mailbox');
  2111. $table_vacation = table_by_key ('vacation');
  2112. $table_vacation_notification = table_by_key('vacation_notification');
  2113. $table_quota = table_by_key ('quota');
  2114. $table_quota2 = table_by_key ('quota2');
  2115. /* vim: set expandtab softtabstop=4 tabstop=4 shiftwidth=4: */