RoundCube Webmail
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.

327 lines
12 KiB

20 years ago
10 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
17 years ago
12 years ago
17 years ago
20 years ago
17 years ago
12 years ago
12 years ago
10 years ago
20 years ago
10 years ago
20 years ago
12 years ago
12 years ago
20 years ago
12 years ago
9 years ago
12 years ago
12 years ago
12 years ago
9 years ago
12 years ago
12 years ago
12 years ago
9 years ago
12 years ago
12 years ago
12 years ago
12 years ago
20 years ago
12 years ago
20 years ago
12 years ago
12 years ago
20 years ago
12 years ago
12 years ago
9 years ago
12 years ago
12 years ago
12 years ago
20 years ago
12 years ago
12 years ago
20 years ago
12 years ago
12 years ago
12 years ago
12 years ago
20 years ago
12 years ago
20 years ago
12 years ago
  1. <?php
  2. /**
  3. +-------------------------------------------------------------------------+
  4. | Roundcube Webmail IMAP Client |
  5. | Version 1.4-git |
  6. | |
  7. | Copyright (C) The Roundcube Dev Team |
  8. | |
  9. | This program is free software: you can redistribute it and/or modify |
  10. | it under the terms of the GNU General Public License (with exceptions |
  11. | for skins & plugins) as published by the Free Software Foundation, |
  12. | either version 3 of the License, or (at your option) any later version. |
  13. | |
  14. | This file forms part of the Roundcube Webmail Software for which the |
  15. | following exception is added: Plugins and Skins which merely make |
  16. | function calls to the Roundcube Webmail Software, and for that purpose |
  17. | include it by reference shall not be considered modifications of |
  18. | the software. |
  19. | |
  20. | If you wish to use this file in another project or create a modified |
  21. | version that will not be part of the Roundcube Webmail Software, you |
  22. | may remove the exception above and use this source code under the |
  23. | original version of the license. |
  24. | |
  25. | This program is distributed in the hope that it will be useful, |
  26. | but WITHOUT ANY WARRANTY; without even the implied warranty of |
  27. | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
  28. | GNU General Public License for more details. |
  29. | |
  30. | You should have received a copy of the GNU General Public License |
  31. | along with this program. If not, see http://www.gnu.org/licenses/. |
  32. | |
  33. +-------------------------------------------------------------------------+
  34. | Author: Thomas Bruederli <roundcube@gmail.com> |
  35. | Author: Aleksander Machniak <alec@alec.pl> |
  36. +-------------------------------------------------------------------------+
  37. */
  38. // include environment
  39. require_once 'program/include/iniset.php';
  40. // init application, start session, init output class, etc.
  41. $RCMAIL = rcmail::get_instance(0, $GLOBALS['env']);
  42. // Make the whole PHP output non-cacheable (#1487797)
  43. $RCMAIL->output->nocacheing_headers();
  44. $RCMAIL->output->common_headers(!empty($_SESSION['user_id']));
  45. // turn on output buffering
  46. ob_start();
  47. // check if config files had errors
  48. if ($err_str = $RCMAIL->config->get_error()) {
  49. rcmail::raise_error(array(
  50. 'code' => 601,
  51. 'type' => 'php',
  52. 'message' => $err_str), false, true);
  53. }
  54. // check DB connections and exit on failure
  55. if ($err_str = $RCMAIL->db->is_error()) {
  56. rcmail::raise_error(array(
  57. 'code' => 603,
  58. 'type' => 'db',
  59. 'message' => $err_str), false, true);
  60. }
  61. // error steps
  62. if ($RCMAIL->action == 'error' && !empty($_GET['_code'])) {
  63. rcmail::raise_error(array('code' => hexdec($_GET['_code'])), false, true);
  64. }
  65. // check if https is required (for login) and redirect if necessary
  66. if (empty($_SESSION['user_id']) && ($force_https = $RCMAIL->config->get('force_https', false))) {
  67. // force_https can be true, <hostname>, <hostname>:<port>, <port>
  68. if (!is_bool($force_https)) {
  69. list($host, $port) = explode(':', $force_https);
  70. if (is_numeric($host) && empty($port)) {
  71. $port = $host;
  72. $host = '';
  73. }
  74. }
  75. if (!rcube_utils::https_check($port ?: 443)) {
  76. if (empty($host)) {
  77. $host = preg_replace('/:[0-9]+$/', '', $_SERVER['HTTP_HOST']);
  78. }
  79. if ($port && $port != 443) {
  80. $host .= ':' . $port;
  81. }
  82. header('Location: https://' . $host . $_SERVER['REQUEST_URI']);
  83. exit;
  84. }
  85. }
  86. // trigger startup plugin hook
  87. $startup = $RCMAIL->plugins->exec_hook('startup', array('task' => $RCMAIL->task, 'action' => $RCMAIL->action));
  88. $RCMAIL->set_task($startup['task']);
  89. $RCMAIL->action = $startup['action'];
  90. // try to log in
  91. if ($RCMAIL->task == 'login' && $RCMAIL->action == 'login') {
  92. $request_valid = $_SESSION['temp'] && $RCMAIL->check_request();
  93. $pass_charset = $RCMAIL->config->get('password_charset', 'UTF-8');
  94. // purge the session in case of new login when a session already exists
  95. if ($request_valid) {
  96. $RCMAIL->kill_session();
  97. }
  98. $auth = $RCMAIL->plugins->exec_hook('authenticate', array(
  99. 'host' => $RCMAIL->autoselect_host(),
  100. 'user' => trim(rcube_utils::get_input_value('_user', rcube_utils::INPUT_POST)),
  101. 'pass' => rcube_utils::get_input_value('_pass', rcube_utils::INPUT_POST, true, $pass_charset),
  102. 'valid' => $request_valid,
  103. 'cookiecheck' => true,
  104. ));
  105. // Login
  106. if ($auth['valid'] && !$auth['abort']
  107. && $RCMAIL->login($auth['user'], $auth['pass'], $auth['host'], $auth['cookiecheck'])
  108. ) {
  109. // create new session ID, don't destroy the current session
  110. // it was destroyed already by $RCMAIL->kill_session() above
  111. $RCMAIL->session->remove('temp');
  112. $RCMAIL->session->regenerate_id(false);
  113. // send auth cookie if necessary
  114. $RCMAIL->session->set_auth_cookie();
  115. // log successful login
  116. $RCMAIL->log_login();
  117. // restore original request parameters
  118. $query = array();
  119. if ($url = rcube_utils::get_input_value('_url', rcube_utils::INPUT_POST)) {
  120. parse_str($url, $query);
  121. // prevent endless looping on login page
  122. if ($query['_task'] == 'login') {
  123. unset($query['_task']);
  124. }
  125. // prevent redirect to compose with specified ID (#1488226)
  126. if ($query['_action'] == 'compose' && !empty($query['_id'])) {
  127. $query = array('_action' => 'compose');
  128. }
  129. }
  130. // allow plugins to control the redirect url after login success
  131. $redir = $RCMAIL->plugins->exec_hook('login_after', $query + array('_task' => 'mail'));
  132. unset($redir['abort'], $redir['_err']);
  133. // send redirect
  134. $OUTPUT->redirect($redir, 0, true);
  135. }
  136. else {
  137. if (!$auth['valid']) {
  138. $error_code = rcmail::ERROR_INVALID_REQUEST;
  139. }
  140. else {
  141. $error_code = is_numeric($auth['error']) ? $auth['error'] : $RCMAIL->login_error();
  142. }
  143. $error_labels = array(
  144. rcmail::ERROR_STORAGE => 'storageerror',
  145. rcmail::ERROR_COOKIES_DISABLED => 'cookiesdisabled',
  146. rcmail::ERROR_INVALID_REQUEST => 'invalidrequest',
  147. rcmail::ERROR_INVALID_HOST => 'invalidhost',
  148. rcmail::ERROR_RATE_LIMIT => 'accountlocked',
  149. );
  150. $error_message = !empty($auth['error']) && !is_numeric($auth['error']) ? $auth['error'] : ($error_labels[$error_code] ?: 'loginfailed');
  151. $OUTPUT->show_message($error_message, 'warning');
  152. // log failed login
  153. $RCMAIL->log_login($auth['user'], true, $error_code);
  154. $RCMAIL->plugins->exec_hook('login_failed', array(
  155. 'code' => $error_code, 'host' => $auth['host'], 'user' => $auth['user']));
  156. if (!isset($_SESSION['user_id'])) {
  157. $RCMAIL->kill_session();
  158. }
  159. }
  160. }
  161. // end session
  162. else if ($RCMAIL->task == 'logout' && isset($_SESSION['user_id'])) {
  163. $RCMAIL->request_security_check(rcube_utils::INPUT_GET | rcube_utils::INPUT_POST);
  164. $userdata = array(
  165. 'user' => $_SESSION['username'],
  166. 'host' => $_SESSION['storage_host'],
  167. 'lang' => $RCMAIL->user->language,
  168. );
  169. $OUTPUT->show_message('loggedout');
  170. $RCMAIL->logout_actions();
  171. $RCMAIL->kill_session();
  172. $RCMAIL->plugins->exec_hook('logout_after', $userdata);
  173. }
  174. // check session and auth cookie
  175. else if ($RCMAIL->task != 'login' && $_SESSION['user_id']) {
  176. if (!$RCMAIL->session->check_auth()) {
  177. $RCMAIL->kill_session();
  178. $session_error = 'sessionerror';
  179. }
  180. }
  181. // not logged in -> show login page
  182. if (empty($RCMAIL->user->ID)) {
  183. if ($session_error || $_REQUEST['_err'] === 'session' || ($session_error = $RCMAIL->session_error())) {
  184. $OUTPUT->show_message($session_error ?: 'sessionerror', 'error', null, true, -1);
  185. }
  186. if ($OUTPUT->ajax_call || $OUTPUT->get_env('framed')) {
  187. $OUTPUT->command('session_error', $RCMAIL->url(array('_err' => 'session')));
  188. $OUTPUT->send('iframe');
  189. }
  190. // check if installer is still active
  191. if ($RCMAIL->config->get('enable_installer') && is_readable('./installer/index.php')) {
  192. $OUTPUT->add_footer(html::div(array('id' => 'login-addon', 'style' => "background:#ef9398; border:2px solid #dc5757; padding:0.5em; margin:2em auto; width:50em"),
  193. html::tag('h2', array('style' => "margin-top:0.2em"), "Installer script is still accessible") .
  194. html::p(null, "The install script of your Roundcube installation is still stored in its default location!") .
  195. html::p(null, "Please <b>remove</b> the whole <tt>installer</tt> folder from the Roundcube directory because
  196. these files may expose sensitive configuration data like server passwords and encryption keys
  197. to the public. Make sure you cannot access the <a href=\"./installer/\">installer script</a> from your browser.")
  198. ));
  199. }
  200. $plugin = $RCMAIL->plugins->exec_hook('unauthenticated', array(
  201. 'task' => 'login',
  202. 'error' => $session_error,
  203. // Return 401 only on failed logins (#7010)
  204. 'http_code' => empty($session_error) && !empty($error_message) ? 401 : 200
  205. ));
  206. $RCMAIL->set_task($plugin['task']);
  207. if ($plugin['http_code'] == 401) {
  208. header('HTTP/1.0 401 Unauthorized');
  209. }
  210. $OUTPUT->send($plugin['task']);
  211. }
  212. else {
  213. // CSRF prevention
  214. $RCMAIL->request_security_check();
  215. // check access to disabled actions
  216. $disabled_actions = (array) $RCMAIL->config->get('disabled_actions');
  217. if (in_array($RCMAIL->task . '.' . ($RCMAIL->action ?: 'index'), $disabled_actions)) {
  218. rcube::raise_error(array(
  219. 'code' => 404, 'type' => 'php',
  220. 'message' => "Action disabled"), true, true);
  221. }
  222. }
  223. // we're ready, user is authenticated and the request is safe
  224. $plugin = $RCMAIL->plugins->exec_hook('ready', array('task' => $RCMAIL->task, 'action' => $RCMAIL->action));
  225. $RCMAIL->set_task($plugin['task']);
  226. $RCMAIL->action = $plugin['action'];
  227. // handle special actions
  228. if ($RCMAIL->action == 'keep-alive') {
  229. $OUTPUT->reset();
  230. $RCMAIL->plugins->exec_hook('keep_alive', array());
  231. $OUTPUT->send();
  232. }
  233. else if ($RCMAIL->action == 'save-pref') {
  234. include INSTALL_PATH . 'program/steps/utils/save_pref.inc';
  235. }
  236. // include task specific functions
  237. if (is_file($incfile = INSTALL_PATH . 'program/steps/'.$RCMAIL->task.'/func.inc')) {
  238. include_once $incfile;
  239. }
  240. // allow 5 "redirects" to another action
  241. $redirects = 0;
  242. while ($redirects < 5) {
  243. // execute a plugin action
  244. if (preg_match('/^plugin\./', $RCMAIL->action)) {
  245. $RCMAIL->plugins->exec_action($RCMAIL->action);
  246. break;
  247. }
  248. // execute action registered to a plugin task
  249. else if ($RCMAIL->plugins->is_plugin_task($RCMAIL->task)) {
  250. if (!$RCMAIL->action) $RCMAIL->action = 'index';
  251. $RCMAIL->plugins->exec_action($RCMAIL->task.'.'.$RCMAIL->action);
  252. break;
  253. }
  254. // try to include the step file
  255. else if (($stepfile = $RCMAIL->get_action_file())
  256. && is_file($incfile = INSTALL_PATH . 'program/steps/'.$RCMAIL->task.'/'.$stepfile)
  257. ) {
  258. // include action file only once (in case it don't exit)
  259. include_once $incfile;
  260. $redirects++;
  261. }
  262. else {
  263. break;
  264. }
  265. }
  266. if ($RCMAIL->action == 'refresh') {
  267. $RCMAIL->plugins->exec_hook('refresh', array('last' => intval(rcube_utils::get_input_value('_last', rcube_utils::INPUT_GPC))));
  268. }
  269. // parse main template (default)
  270. $OUTPUT->send($RCMAIL->task);
  271. // if we arrive here, something went wrong
  272. rcmail::raise_error(array(
  273. 'code' => 404,
  274. 'type' => 'php',
  275. 'line' => __LINE__,
  276. 'file' => __FILE__,
  277. 'message' => "Invalid request"), true, true);