/** * Execute an sql query in the database. The correct database connection * will be chosen and the query will be logged with the success status. * * As third parameter an alternative query can be passed, which should be * displayed instead of the executed query. This prevents leakage of * confidential information like password salts. The logfile will still * contain the executed query. * * @param $query query to execute as string * @param bool $errorProcessing true if it's an error when the query fails. * @param null $displayQuery */ function exec_query($query, $errorProcessing = true, $displayQuery = null) { global $database, $kga, $errors, $executed_queries; $conn = $database->getConnectionHandler(); $executed_queries++; $success = $conn->Query($query); Kimai_Logger::logfile($query); $err = $conn->Error(); $query = htmlspecialchars($query); $displayQuery = htmlspecialchars($displayQuery); if ($success) { $level = 'green'; } else { if ($errorProcessing) { $level = 'red'; $errors++; } else { $level = 'orange'; // something went wrong but it's not an error } } printLine($level, $displayQuery == null ? $query : $displayQuery, $err); if (!$success) { Kimai_Logger::logfile("An error has occured in query [{$query}]: " . $conn->Error()); } }
/** * @param string $name * @return string * @throws \Zend_Mail_Exception */ public function forgotPassword($name) { $kga = $this->getKga(); $database = $this->getDatabase(); $is_customer = $database->is_customer_name($name); $mail = new Zend_Mail('utf-8'); $mail->setFrom($kga['conf']['adminmail'], 'Kimai - Open Source Time Tracking'); $mail->setSubject($kga['lang']['passwordReset']['mailSubject']); $transport = new Zend_Mail_Transport_Sendmail(); $passwordResetHash = str_shuffle(MD5(microtime())); if ($is_customer) { $customerId = $database->customer_nameToID($name); $customer = $database->customer_get_data($customerId); $database->customer_edit($customerId, array('passwordResetHash' => $passwordResetHash)); $mail->addTo($customer['mail']); } else { $userId = $database->user_name2id($name); $user = $database->user_get_data($userId); $database->user_edit($userId, array('passwordResetHash' => $passwordResetHash)); $mail->addTo($user['mail']); } Kimai_Logger::logfile('password reset: ' . $name . ($is_customer ? ' as customer' : ' as user')); $ssl = !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] != 'off'; $url = ($ssl ? 'https://' : 'http://') . $_SERVER['SERVER_NAME'] . dirname($_SERVER['SCRIPT_NAME']) . '/forgotPassword.php?name=' . urlencode($name) . '&key=' . $passwordResetHash; $message = $kga['lang']['passwordReset']['mailMessage']; $message = str_replace('%{URL}', $url, $message); $mail->setBodyText($message); try { $mail->send($transport); return $kga['lang']['passwordReset']['mailConfirmation']; } catch (Zend_Mail_Transport_Exception $e) { return $e->getMessage(); } }
/** * Execute an sql query in the database. The correct database connection * will be chosen and the query will be logged with the success status. * * @param $query query to execute as string */ function exec_query($query) { global $conn, $kga, $errors, $executed_queries; $success = $conn->Query($query); $errorInfo = serialize($conn->Error()); Kimai_Logger::logfile($query); if (!$success) { Kimai_Logger::logfile($errorInfo); $errors = true; } }
/** * Execute an sql query in the database. The correct database connection * will be chosen and the query will be logged with the success status. * * @param string $query string query to execute */ function exec_query($query) { global $database, $errors; $conn = $database->getConnectionHandler(); $success = $conn->Query($query); //Kimai_Logger::logfile($query); if (!$success) { $errorInfo = serialize($conn->Error()); Kimai_Logger::logfile('[ERROR] in [' . $query . '] => ' . $errorInfo); $errors = true; } }
/** * Adds the translations for the given language. * * @param $language * @throws Exception */ public function addTranslations($language) { // no need to load the default or already requested language again! $default = Kimai_Config::getDefault(Kimai_Config::DEFAULT_LANGUAGE); if (empty($language) || $language == $default || $language == $this->language) { return; } $languageFile = WEBROOT . 'language/' . $language . '.php'; if (!file_exists($languageFile)) { Kimai_Logger::logfile('Requested translation is missing: ' . $language); return; } $this->language = $language; $data = array_replace_recursive($this->getArrayCopy(), include $languageFile); $this->exchangeArray($data); }
function expenseAccessAllowed($entry, $action, &$errors) { global $database, $kga; if (!isset($kga['user'])) { $errors[''] = $kga['lang']['errorMessages']['permissionDenied']; return false; } // check if expense is too far in the past to allow editing (or deleting) if ($kga->isEditLimit() && time() - $entry['timestamp'] > $kga->getEditLimit()) { $errors[''] = $kga['lang']['editLimitError']; return false; } $groups = $database->getGroupMemberships($entry['userID']); if ($entry['userID'] == $kga['user']['userID']) { $permissionName = 'ki_expenses-ownEntry-' . $action; if ($database->global_role_allows($kga['user']['globalRoleID'], $permissionName)) { return true; } else { Kimai_Logger::logfile("missing global permission {$permissionName} for user " . $kga['user']['name'] . " to access expense"); $errors[''] = $kga['lang']['errorMessages']['permissionDenied']; return false; } } $assignedOwnGroups = array_intersect($groups, $database->getGroupMemberships($kga['user']['userID'])); if (count($assignedOwnGroups) > 0) { $permissionName = 'ki_expenses-otherEntry-ownGroup-' . $action; if ($database->checkMembershipPermission($kga['user']['userID'], $assignedOwnGroups, $permissionName)) { return true; } else { Kimai_Logger::logfile("missing membership permission {$permissionName} of own group(s) " . implode(", ", $assignedOwnGroups) . " for user " . $kga['user']['name']); $errors[''] = $kga['lang']['errorMessages']['permissionDenied']; return false; } } $permissionName = 'ki_expenses-otherEntry-otherGroup-' . $action; if ($database->global_role_allows($kga['user']['globalRoleID'], $permissionName)) { return true; } else { Kimai_Logger::logfile("missing global permission {$permissionName} for user " . $kga['user']['name'] . " to access expense"); $errors[''] = $kga['lang']['errorMessages']['permissionDenied']; return false; } }
/** * create exp entry * * @param $userID * @param array $data array with record data * @return bool|int */ function expense_create($userID, $data) { global $kga, $database; $conn = $database->getConnectionHandler(); $data = $database->clean_data($data); $values['projectID'] = MySQL::SQLValue($data['projectID'], MySQL::SQLVALUE_NUMBER); $values['designation'] = MySQL::SQLValue($data['designation']); $values['comment'] = MySQL::SQLValue($data['comment']); $values['commentType'] = MySQL::SQLValue($data['commentType'], MySQL::SQLVALUE_NUMBER); $values['timestamp'] = MySQL::SQLValue($data['timestamp'], MySQL::SQLVALUE_NUMBER); $values['multiplier'] = MySQL::SQLValue($data['multiplier'], MySQL::SQLVALUE_NUMBER); $values['value'] = MySQL::SQLValue($data['value'], MySQL::SQLVALUE_NUMBER); $values['userID'] = MySQL::SQLValue($userID, MySQL::SQLVALUE_NUMBER); $values['refundable'] = MySQL::SQLValue($data['refundable'], MySQL::SQLVALUE_NUMBER); $table = $kga['server_prefix'] . "expenses"; $result = $conn->InsertRow($table, $values); if (!$result) { Kimai_Logger::logfile('expense_create: ' . $conn->Error()); return false; } return $result; }
Kimai_Logger::logfile("-- update to r1392"); $charset = ''; if ($kga['utf8']) { $charset = 'utf8'; } $success = write_config_file($kga['server_database'], $kga['server_hostname'], $kga['server_username'], $kga['server_password'], $charset, $kga['server_prefix'], $kga['language'], $kga['password_salt'], $kga['defaultTimezone']); if ($success) { $level = 'green'; $additional = 'charset: ' . $charset; } else { $level = 'red'; $additional = 'Unable to write config file.'; } printLine($level, 'Store charset in configuration file <i>autoconf.php</i>.', $additional); } if ((int) $revisionDB < 1393) { Kimai_Logger::logfile("-- update to r1393"); exec_query("ALTER TABLE `{$p}users` CHANGE `mail` `mail` VARCHAR(160) NULL"); exec_query("ALTER TABLE `{$p}timeSheet` CHANGE `fixedRate` `fixedRate` DECIMAL(10,2) NULL"); } // ================================================================================ // FINALIZATION: update DB version number // ================================================================================ if ((int) $revisionDB < $kga['revision'] && !$errors) { $query = sprintf("UPDATE `{$p}configuration` SET value = '%s' WHERE `option` = 'version';", $kga['version']); exec_query($query, 0); $query = sprintf("UPDATE `{$p}configuration` SET value = '%d' WHERE `option` = 'revision';", $kga['revision']); exec_query($query, 0); } Kimai_Logger::logfile("-- update finished --------------------------------"); require_once 'update_footer.php';
$view = new Zend_View(); $view->setBasePath(WEBROOT . '/templates'); $authPlugin = Kimai_Registry::getAuthenticator(); $view->assign('kga', $kga); // current database setup correct? checkDBversion("."); // processing login and displaying either login screen or errors $name = htmlspecialchars(trim($name)); $is_customer = $database->is_customer_name($name); if ($is_customer) { $id = $database->customer_nameToID($name); $customer = $database->customer_get_data($id); $keyCorrect = $key === $customer['passwordResetHash']; } else { $id = $database->user_name2id($name); $user = $database->user_get_data($id); $keyCorrect = $key === $user['passwordResetHash']; } switch ($_REQUEST['a']) { case "request": Kimai_Logger::logfile("password reset: " . $name . ($is_customer ? " as customer" : " as user")); break; // Show password reset page // Show password reset page default: $view->assign('devtimespan', '2006-' . date('y')); $view->assign('keyCorrect', $keyCorrect); $view->assign('requestData', array('key' => $key, 'name' => $name)); echo $view->render('login/forgotPassword.php'); break; }
$view->assign('kga', $kga); // ================== // = security check = // ================== if (isset($_REQUEST['axAction']) && !is_array($_REQUEST['axAction']) && $_REQUEST['axAction'] != "") { $axAction = strip_tags($_REQUEST['axAction']); } else { $axAction = ''; } $axValue = isset($_REQUEST['axValue']) ? strip_tags($_REQUEST['axValue']) : ''; $id = isset($_REQUEST['id']) ? strip_tags($_REQUEST['id']) : null; // ============================================ // = initialize currently displayed timeframe = // ============================================ $timeframe = get_timeframe(); $in = $timeframe[0]; $out = $timeframe[1]; if (isset($_REQUEST['first_day'])) { $in = (int) $_REQUEST['first_day']; } if (isset($_REQUEST['last_day'])) { $out = mktime(23, 59, 59, date("n", $_REQUEST['last_day']), date("j", $_REQUEST['last_day']), date("Y", $_REQUEST['last_day'])); } if ($axAction != "reloadLogfile") { Kimai_Logger::logfile("KSPI axAction (" . (array_key_exists('customer', $kga) ? $kga['customer']['name'] : $kga['user']['name']) . "): " . $axAction); } // prevent IE from caching the response header("Last-Modified: " . gmdate("D, d M Y H:i:s") . " GMT"); header("Cache-Control: no-store, no-cache, must-revalidate"); header("Cache-Control: post-check=0, pre-check=0", false); header("Pragma: no-cache");
public static function errorHandler($errno, $errstr, $errfile, $errline) { // If the @ error-control operator is set don't log the error. if (error_reporting() === 0) { return false; } $line = ''; switch ($errno) { case E_WARNING: $line .= 'E_WARNING'; break; case E_NOTICE: $line .= 'E_NOTICE'; break; case E_USER_ERROR: $line .= 'E_USER_ERROR'; break; case E_USER_WARNING: $line .= 'E_USER_WARNING'; break; case E_USER_NOTICE: $line .= 'E_USER_NOTICE'; break; case E_STRICT: $line .= 'E_STRICT'; break; case E_RECOVERABLE_ERROR: $line .= 'E_RECOVERABLE_ERROR'; break; } $line .= ' ' . $errstr; $line .= " @{$errfile} line {$errline}"; Kimai_Logger::logfile($line); return false; }
* ================== * * Called via AJAX from the Kimai user interface. Depending on $axAction * actions are performed, e.g. editing preferences or returning a list * of customers. */ // insert KSPI $isCoreProcessor = 1; $dir_templates = "templates/core/"; require "../includes/kspi.php"; switch ($axAction) { /** * Append a new entry to the logfile. */ case 'logfile': Kimai_Logger::logfile("JavaScript: " . $axValue); break; /** * Remember which project and activity the user has selected for * the quick recording via the buzzer. */ /** * Remember which project and activity the user has selected for * the quick recording via the buzzer. */ case 'saveBuzzerPreselection': if (!isset($kga['user'])) { return; } $data = array(); if (isset($_REQUEST['project'])) {
/** * Check if a parset string matches with the following time-formatting: 20.08.2008-19:00:00. * * @param string $timestring * @return boolean */ public static function check_time_format($timestring) { if (!preg_match("/([0-9]{1,2})\\.([0-9]{1,2})\\.([0-9]{2,4})-([0-9]{1,2}):([0-9]{1,2}):([0-9]{1,2})/", $timestring)) { return false; // WRONG format } $ok = 1; $hours = substr($timestring, 11, 2); $minutes = substr($timestring, 14, 2); $seconds = substr($timestring, 17, 2); if ((int) $hours >= 24) { $ok = 0; } if ((int) $minutes >= 60) { $ok = 0; } if ((int) $seconds >= 60) { $ok = 0; } Kimai_Logger::logfile("timecheck: " . $ok); $day = substr($timestring, 0, 2); $month = substr($timestring, 3, 2); $year = substr($timestring, 6, 4); if (!checkdate((int) $month, (int) $day, (int) $year)) { $ok = 0; } Kimai_Logger::logfile("time/datecheck: " . $ok); if ($ok) { return true; } return false; }
$logdatei = fopen(WEBROOT . "temporary/logfile.txt", "w"); fwrite($logdatei, ""); fclose($logdatei); echo $kga['lang']['log_delete']; } else { die; } break; /** * Write some message to the logfile. */ /** * Write some message to the logfile. */ case "shoutbox": Kimai_Logger::logfile("[" . Kimai_Registry::getUser()->getName() . "] " . $axValue); break; /** * Return the $kga variable (Kimai Global Array). Strip out some sensitive * information if not configured otherwise. */ /** * Return the $kga variable (Kimai Global Array). Strip out some sensitive * information if not configured otherwise. */ case "reloadKGA": $output = $kga; $filter = array('server_hostname' => "xxx", 'server_database' => "xxx", 'server_username' => "xxx", 'server_password' => "xxx", 'password_salt' => "xxx", 'user' => array('secure' => "xxx", 'userID' => "xxx", 'pw' => "xxx", 'password' => "xxx", 'apikey' => "xxx")); switch ($axValue) { case 'plain': $output = $kga;
$database->setGroupMemberships($userId, array($authPlugin->getDefaultGroups())); } $userData = $database->user_get_data($userId); $loginKey = random_code(30); setcookie('kimai_key', $loginKey); setcookie('kimai_user', $userData['name']); $database->user_loginSetKey($userId, $loginKey); header('Location: core/kimai.php'); } // ================================================================= // = processing login and displaying either login screen or errors = // ================================================================= switch ($_REQUEST['a']) { case 'checklogin': $is_customer = $database->is_customer_name($name); Kimai_Logger::logfile('login: '******' as customer' : ' as user')); if ($is_customer) { // perform login of customer $passCrypt = encode_password($password); $customerId = $database->customer_nameToID($name); $data = $database->customer_get_data($customerId); // TODO: add BAN support if ($data['password'] == $passCrypt && $name != '' && $passCrypt != '') { $loginKey = random_code(30); setcookie('kimai_key', $loginKey); setcookie('kimai_user', 'customer_' . $name); $database->customer_loginSetKey($customerId, $loginKey); header('Location: core/kimai.php'); } else { setcookie('kimai_key', '0'); setcookie('kimai_user', '0');
/** * Query the database for the best fitting rate for the given user, project and activity. * * @author sl * @param $userID * @param $projectID * @param $activityID * @return bool */ public function get_best_fitting_rate($userID, $projectID, $activityID) { // validate input if ($userID == null || !is_numeric($userID)) { $userID = "NULL"; } if ($projectID == null || !is_numeric($projectID)) { $projectID = "NULL"; } if ($activityID == null || !is_numeric($activityID)) { $activityID = "NULL"; } $query = "SELECT rate FROM " . $this->kga['server_prefix'] . "rates WHERE\n (userID = {$userID} OR userID IS NULL) AND\n (projectID = {$projectID} OR projectID IS NULL) AND\n (activityID = {$activityID} OR activityID IS NULL)\n ORDER BY userID DESC, activityID DESC, projectID DESC\n LIMIT 1;"; $result = $this->conn->Query($query); if ($result === false) { $this->logLastError('get_best_fitting_rate'); return false; } if ($this->conn->RowCount() == -1) { // no error, but no best fitting rate, return default value Kimai_Logger::logfile("get_best_fitting_rate - using default rate 0.00"); return 0.0; } $data = $this->conn->rowArray(0, MYSQLI_ASSOC); return $data['rate']; }
$beginDate = $in; $endDate = $out; $invoiceID = $customer['name'] . "-" . date("y", $in) . "-" . date("m", $in); $today = time(); $dueDate = mktime(0, 0, 0, date("m") + 1, date("d"), date("Y")); $round = 0; // do we have to round the time ? if (isset($_REQUEST['roundValue']) && (double) $_REQUEST['roundValue'] > 0) { $round = (double) $_REQUEST['roundValue']; $time_index = 0; $amount = count($invoiceArray); while ($time_index < $amount) { if ($invoiceArray[$time_index]['type'] == 'timeSheet') { $rounded = ext_invoice_round_value($invoiceArray[$time_index]['hour'], $round / 10); // Write a logfile entry for each value that is rounded. Kimai_Logger::logfile("Round " . $invoiceArray[$time_index]['hour'] . " to " . $rounded . " with " . $round); if ($invoiceArray[$time_index]['hour'] == 0) { // make sure we do not raise a "divison by zero" - there might be entries with the zero seconds $rate = 0; } else { $rate = ext_invoice_round_value($invoiceArray[$time_index]['amount'] / $invoiceArray[$time_index]['hour'], 0.05); } $invoiceArray[$time_index]['hour'] = $rounded; $invoiceArray[$time_index]['amount'] = $invoiceArray[$time_index]['hour'] * $rate; } $time_index++; } } // calculate invoice sums $ttltime = 0; $rawTotalTime = 0;
/** * @brief Check the permission to access an object. * * This method is meant to check permissions for adding, editing and deleting customers, * projects, activities and users. The input is not checked whether it falls within those boundaries since * it can also work with others, if the permissions match the pattern. * * @param string $objectTypeName name of the object type being edited (e.g. Project) * @param string $action the action being performed (e.g. add) * @param array $oldGroups the old groups of the object (empty array for new objects) * @param array $newGroups the new groups of the object (same as oldGroups if nothing should be changed in group assignment) * @return boolean if the permission is granted, false otherwise */ function checkGroupedObjectPermission($objectTypeName, $action, $oldGroups, $newGroups) { global $database, $kga; if (!isset($kga['user'])) { return false; } $assignedOwnGroups = array_intersect($oldGroups, $database->getGroupMemberships($kga['user']['userID'])); $assignedOtherGroups = array_diff($oldGroups, $database->getGroupMemberships($kga['user']['userID'])); if (count($assignedOtherGroups) > 0) { $permissionName = "core-{$objectTypeName}-otherGroup-{$action}"; if (!$database->global_role_allows($kga['user']['globalRoleID'], $permissionName)) { Kimai_Logger::logfile("missing global permission {$permissionName} for user " . $kga['user']['name'] . " to access {$objectTypeName}"); return false; } } if (count($assignedOwnGroups) > 0) { $permissionName = "core-{$objectTypeName}-{$action}"; if (!$database->checkMembershipPermission($kga['user']['userID'], $assignedOwnGroups, $permissionName)) { Kimai_Logger::logfile("missing membership permission {$permissionName} of current own group(s) " . implode(", ", $assignedOwnGroups) . " for user " . $kga['user']['name'] . " to access {$objectTypeName}"); return false; } } if (count($oldGroups) != array_intersect($oldGroups, $newGroups)) { // group assignment has changed $addToGroups = array_diff($newGroups, $oldGroups); $removeFromGroups = array_diff($oldGroups, $newGroups); $addToOtherGroups = array_diff($addToGroups, $database->getGroupMemberships($kga['user']['userID'])); $addToOwnGroups = array_intersect($addToGroups, $database->getGroupMemberships($kga['user']['userID'])); $removeFromOtherGroups = array_diff($removeFromGroups, $database->getGroupMemberships($kga['user']['userID'])); $removeFromOwnGroups = array_intersect($removeFromGroups, $database->getGroupMemberships($kga['user']['userID'])); $action = 'assign'; if (count($addToOtherGroups) > 0) { $permissionName = "core-{$objectTypeName}-otherGroup-{$action}"; if (!$database->global_role_allows($kga['user']['globalRoleID'], $permissionName)) { Kimai_Logger::logfile("missing global permission {$permissionName} for user " . $kga['user']['name'] . " to access {$objectTypeName}"); return false; } } if (count($addToOwnGroups) > 0) { $permissionName = "core-{$objectTypeName}-{$action}"; if (!$database->checkMembershipPermission($kga['user']['userID'], $addToOwnGroups, $permissionName)) { Kimai_Logger::logfile("missing membership permission {$permissionName} of new own group(s) " . implode(", ", $addToOwnGroups) . " for user " . $kga['user']['name'] . " to access {$objectTypeName}"); return false; } } $action = 'unassign'; if (count($removeFromOtherGroups) > 0) { $permissionName = "core-{$objectTypeName}-otherGroup-{$action}"; if (!$database->global_role_allows($kga['user']['globalRoleID'], $permissionName)) { Kimai_Logger::logfile("missing global permission {$permissionName} for user " . $kga['user']['name'] . " to access {$objectTypeName}"); return false; } } if (count($removeFromOwnGroups) > 0) { $permissionName = "core-{$objectTypeName}-{$action}"; if (!$database->checkMembershipPermission($kga['user']['userID'], $removeFromOwnGroups, $permissionName)) { Kimai_Logger::logfile("missing membership permission {$permissionName} of old own group(s) " . implode(", ", $removeFromOwnGroups) . " for user " . $kga['user']['name'] . " to access {$objectTypeName}"); return false; } } } return true; }