$info) { if (isset($info['sql']) && $info['sql'] === false) continue; if ($info['type'] == 'date') $fields[] = db_format_date(empty($info['sql']) ? $field : $info['sql']) . sprintf(" AS %s", $field); elseif ($info['type'] != 'passwd') $fields[] = empty($info['sql']) ? $field : $info['sql'] . ' AS ' . $field; } $sql = sprintf('SELECT id,%s FROM %s WHERE id = %d', implode(',', $fields), $mask['table'], $_POST['id']); $row = $db->fetchAssoc($sql); if ($row === false) return false; foreach ($mask['edit'] as $field => $info) if ($info['type'] == 'boolean') $row[$field] = $row[$field]?true:false; elseif ($info['type'] == 'passwd') $row[$field] = ''; elseif (array_key_exists('format', $info)) $row[$field] = sprintf($info['format'], $row[$field]); return $row; } function details($mask) { if (empty($_POST['id'])) return array('error' => 'Missing ID'); $fields = array(); foreach ($mask['details']['list'] as $field => $info) { if ($info['type'] == 'date') $fields[] = db_format_date(empty($info['sql']) ? $field : $info['sql']) . sprintf(" AS %s", $field); elseif (!array_key_exists('fetch',$info)) $fields[] = empty($info['sql']) ? $field : $info['sql'] . ' AS ' . $field; } if (count($fields)) { $sql = sprintf('SELECT id,%s FROM %s WHERE id = %d', implode(',', $fields), $mask['table'], $_POST['id']); $sth = $db->query($sql); if (!$sth) return array('error' => db_error(), 'sql' => $sql); $row = $sth->fetch(); } else { $row = array(); } foreach ($mask['details']['list'] as $field => $info) if (array_key_exists('format', $info)) $row[$field] = sprintf($info['format'], $row[$field]); elseif (array_key_exists('fetch', $info)) $row[$field] = $info['fetch'](); return $row; } function load_edit($mask) { require_once($_SESSION['sys']['basedir'].'/lib/mask.php'); $form = build_form($mask); return array('content' => $form['form'], 'checks' => $form['checks']); } function format_date($value) { $d = explode('.', $value); if (count($d) == 3) $value = sprintf('%d-%d-%d', $d[2], $d[1], $d[0]); return $value; } function format_decimal($value) { $value = str_replace(',','.',$value); return sprintf("%.2f", $value); } $upload_error = array( 'There is no error.', 'The uploaded file exceeds the upload_max_filesize directive.', 'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the form.', 'The uploaded file was only partially uploaded.', 'No file was uploaded.', 'Missing a temporary folder.', 'Failed to write file to disk.', 'A PHP extension stopped the file upload.' ); function unique_pathname($subdir, $filename) { $base = $_SESSION['sys']['basedir'] . 'archive'; if (!is_dir($base)) ajax_error('Archivpfad nicht vorhanden'); $base .= '/' . $subdir; if (!is_dir($base)) ajax_error('Dokumentenpfad nicht vorhanden'); $base .= '/'; $path = date('Y'); if (!is_dir($base.$path)) if (mkdir($base.$path) === false) ajax_error('Kann Verzeichnis nicht anlegen: ' . $path); $path .= '/' . date('m'); if (!is_dir($base.$path)) if (mkdir($base.$path) === false) ajax_error('Kann Verzeichnis nicht anlegen: ' . $path); $parts = pathinfo($filename); $parts['filename'] = $parts['filename']; if (!empty($parts['extension'])) $parts['extension'] = strtolower($parts['extension']); $path .= '/' . $parts['filename']; $max = 100; for ($i=0; $i < $max; $i++) { $fname = $path; if ($i > 0) $fname .= '('.$i.')'; if (!empty($parts['extension'])) $fname .= '.' . $parts['extension']; if (!is_file($base.$fname)) return array($base, $fname); } ajax_error("Zu viele ähnliche Dateien"); } function store_file($field, $info) { global $upload_error; if (empty($_FILES[$field]['name'])) return array('error' => 'Kein Dateiname angegeben', 'errormsg' => 'Kein Dateiname angegeben'); if ($_FILES[$field]['error'] > 0) return array('error' => "Fehler beim Upload einer Datei:\n" . $upload_error[$_FILES[$field]['error']], 'errormsg' => 'Fehler beim Upload einer Datei'); if ($_FILES[$field]['size'] == 0) return array('error' => "Datei hat keinen Inhalt\nEventuell falsch kodierte Umlaute im Dateinamen", 'errormsg' => 'Datei hat keinen Inhalt'); if (empty($_FILES[$field]['tmp_name'])) return array('error' => 'Keine temporäre Datei erzeugt', 'errormsg' => 'Keine temporäre Datei erzeugt'); if (empty($info['path'])) return array('error' => 'Kein Pfadname konfiguriert', 'errormsg' => 'Kein Pfadname konfiguriert'); list($base, $fname) = unique_pathname($info['path'], $_FILES[$field]['name']); if (move_uploaded_file($_FILES[$field]['tmp_name'], $base.$fname) === false) return array('error' => 'Kann Datei nicht speichern ', 'errormsg' => 'Kann Datei nicht speichern'); if (filesize($base.$fname) == 0) { unlink($base.$fname); return array('error' => "Datei konnte nicht gespeichert werden,\nhat keinen Inhalt (Größe 0)", 'errormsg' => 'Datei hat keinen Inhalt'); } if ($_POST['func_use_send'] == 1) { $content = utf8_decode(file_get_contents($base.$fname)); file_put_contents($base.$fname, $content); } chmod($base.$fname, 0640); return $fname; } function save($mask) { global $db; if (array_key_exists('save', $mask)) return $mask['save']($mask); if (empty($_POST['id'])) return array('error' => 'Missing ID'); $update = array(sprintf("sys_user = %s", $db->quote($_SESSION['sys']['login'])), "sys_edit = now()"); foreach ($mask['edit'] as $field => $info) { if ($info['type'] == 'html') continue; if ($info['type'] == 'file') continue; if (isset($info['required']) && $info['required'] === true && !strlen($_POST[$field])) return array('error' => sprintf('Pflichtfeld %s nicht ausgefüllt', $info['name']), 'errormsg' => 'Pflichtfelder nicht ausgefüllt'); if (array_key_exists('ignore', $info) && $info['ignore']) continue; if ($info['type'] == 'boolean') { $update[] = sprintf("%s=%d", $field, $_POST[$field] == 'on'?1:0); } elseif ($info['type'] == 'hidden') { if (empty($_POST[$field]) && $info['null'] === true) $update[] = sprintf("%s=NULL", $field); else { if ($info['subtype'] == 'string') $update[] = sprintf("%s=%s", $field, $db->quote($_POST[$field])); else $update[] = sprintf("%s=%d", $field, $_POST[$field]); } } elseif ($info['type'] == 'number' || ($info['type'] == 'select' && $info['options_string'] !== true)) { if (empty($_POST[$field]) && $info['null'] === true) $update[] = sprintf("%s=NULL", $field); else $update[] = sprintf("%s=%d", $field, $_POST[$field]); } elseif ($info['type'] == 'decimal') { if (empty($_POST[$field]) && $info['null'] === true) $update[] = sprintf("%s=NULL", $field); else $update[] = sprintf("%s=%s", $field, format_decimal($_POST[$field])); } elseif ($info['type'] == 'date') { if (empty($_POST[$field]) && $info['null'] === true) $update[] = sprintf("%s=NULL", $field); else $update[] = sprintf("%s='%s'", $field, format_date($_POST[$field])); } elseif ($info['type'] == 'passwd') { if (!empty($_POST[$field])) $update[] = sprintf("%s=%s", $field, $db->quote(passwd(empty($_POST['login'])?$_SESSION['sys']['login']:$_POST['login'], $_POST[$field]))); } else { if (empty($_POST[$field]) && $info['null'] === true) $update[] = sprintf("%s=NULL", $field); else $update[] = sprintf("%s=%s", $field, $db->quote($_POST[$field])); } } $sql = sprintf('UPDATE %s SET %s WHERE id = %d', empty($mask['edit_table']) ? $mask['table'] : $mask['edit_table'], implode(', ', $update), intval($_POST['id'])); $sth = $db->query($sql); if ($sth === false) { error_log($sql . ': ' . db_error()); return array('error' => db_error(), 'sql' => $sql); } return array('status' => true); } function insert($mask) { global $db; if (array_key_exists('insert', $mask)) return $mask['insert']($mask); $fields = array('sys_user','sys_edit'); $values = array($db->quote($_SESSION['sys']['login']), 'now()'); foreach ($mask['edit'] as $field => $info) { if ($info['type'] == 'html') continue; if (isset($info['required']) && $info['required'] === true && !strlen($_POST[$field])) return array('error' => sprintf('Pflichtfeld %s nicht ausgefüllt', $info['name']), 'errormsg' => 'Pflichtfelder nicht ausgefüllt'); if (array_key_exists('ignore', $info) && $info['ignore']) continue; if ($info['type'] == 'boolean') { $fields[] = $field; $values[] = $_POST[$field] == 'on'?1:0; } elseif ($info['type'] == 'hidden') { $fields[] = $field; if (empty($_POST[$field]) && $info['null'] === true) $values[] = 'NULL'; else { if ($info['subtype'] == 'string') $values[] = $db->quote($_POST[$field]); else $values[] = intval($_POST[$field]); } } elseif ($info['type'] == 'number' || ($info['type'] == 'select' && $info['options_string'] !== true)) { $fields[] = $field; if (empty($_POST[$field]) && $info['null'] === true) $values[] = 'NULL'; else $values[] = intval($_POST[$field]); } elseif ($info['type'] == 'decimal') { $fields[] = $field; if (empty($_POST[$field]) && $info['null'] === true) $values[] = 'NULL'; else $values[] = format_decimal($_POST[$field]); } elseif ($info['type'] == 'date') { $fields[] = $field; if (empty($_POST[$field]) && $info['null'] === true) $values[] = 'NULL'; else $values[] = "'" . format_date($_POST[$field]) . "'"; } elseif ($info['type'] == 'passwd') { if (!empty($_POST[$field])) { $fields[] = $field; $values[] = $db->quote(passwd(empty($_POST['login'])?$_SESSION['sys']['login']:$_POST['login'], $_POST[$field])); } } elseif ($info['type'] == 'file') { $fields[] = $field; if (empty($_FILES[$field]) && $info['null'] === true) $values[] = 'NULL'; elseif (empty($_FILES[$field]) && array_key_exists('source', $info) && is_callable($info['source'])) { $fname = call_user_func_array($info['source'], array($field, $info)); if (is_array($fname)) return $fname; $values[] = $db->quote($fname); } elseif (empty($_FILES[$field])) $values[] = "''"; else { $fname = store_file($field, $info); if (is_array($fname)) return $fname; $values[] = $db->quote($fname); } } elseif (!array_key_exists('ignore', $info) || !$info['ignore']) { $fields[] = $field; if (empty($_POST[$field]) && isset($info['null']) && $info['null'] === true) $values[] = 'NULL'; else $values[] = $db->quote($_POST[$field]); } } $sql = sprintf('INSERT INTO %s (%s) VALUES (%s)', empty($mask['edit_table']) ? $mask['table'] : $mask['edit_table'], implode(',', $fields), implode(',', $values)); $sth = $db->query($sql); if ($sth === false) { error_log($sql . ': ' . db_error()); return array('error' => db_error(), 'sql' => $sql); } $id = $db->lastInsertId(); if (array_key_exists('postinsert',$mask)) $mask['postinsert']($id); return array('status' => true, 'id' => $id); } function delete_or_copy_row($table,$id,$mask) { global $db; if (array_key_exists('predelete',$mask)) $mask['predelete']($id); if (DELETE_COPY === true) { $sql = sprintf("INSERT INTO %s_deleted SELECT * FROM %s WHERE id = %d", $table, $table, $id); $sth = $db->query($sql); if ($sth === false) { error_log($sql . ': ' . db_error()); return array('error' => db_error(), 'sql' => $sql); } $sql = sprintf("UPDATE %s_deleted SET sys_user='%s',sys_edit=now() WHERE id = %d", $table, $_SESSION['sys']['login'], $id); $sth = $db->query($sql); if ($sth === false) { error_log($sql . ': ' . db_error()); return array('error' => db_error(), 'sql' => $sql); } } else { $fields = array(); foreach ($mask['edit'] as $field => $info) if ($info['type'] === 'file') $fields[] = array('name' => $field, 'path' => $info['path']); if (count($fields)) { $f = array(); foreach ($fields as $i) $f[] = $i['name']; $sql = sprintf("SELECT %s FROM %s WHERE id = %d", implode(',',$f), $table, $id); $sth = $db->query($sql); if ($sth === false) { error_log($sql . ': ' . db_error()); return array('error' => db_error(), 'sql' => $sql); } $row = $sth->fetch(); foreach ($fields as $f) { $base = $_SESSION['sys']['basedir'] . 'archive/' . $f['path']; $fname = $base . '/' . $row[$f['name']]; if (is_file($fname)) unlink($fname); } } } $sql = sprintf("DELETE FROM %s WHERE id = %d", $table, $_POST['id']); $sth = $db->query($sql); if ($sth === false) { error_log($sql . ': ' . db_error()); return array('error' => db_error(), 'sql' => $sql); } return array('status' => true); } function delete_or_copy($mask) { if (empty($_POST['id'])) return array('error' => 'Missing ID'); if (array_key_exists('delete', $mask)) return $mask['delete']($mask); if (!empty($mask['edit_table'])) return array('error' => 'Cannot handle deletion for secondary table'); return delete_or_copy_row($mask['table'],$_POST['id'],$mask); } function set_variable($name,$mask) { if (!array_key_exists('variables',$mask)) return array('error' => 'Unknown variable ' . htmlspecialchars($_POST['name'])); if (!array_key_exists($_POST['name'],$mask['variables'])) return array('error' => 'Unknown variable ' . htmlspecialchars($_POST['name'])); $_SESSION[$name . '.' . $_POST['name']] = $_POST['value']; if (array_key_exists('postcall',$mask['variables'][$_POST['name']])) $mask['variables'][$_POST['name']]['postcall'](); return array('status' => true); } function get_infos($mask) { if (!array_key_exists('info',$mask)) return array('error' => 'Unknown callback ' . htmlspecialchars($_POST['name'])); if (!array_key_exists($_POST['name'],$mask['info'])) return array('error' => 'Unknown callback ' . htmlspecialchars($_POST['name'])); if (!array_key_exists('sql',$mask['info'][$_POST['name']])) return array('error' => 'Unknown callback ' . htmlspecialchars($_POST['name'])); $sql = $mask['info'][$_POST['name']]['sql']; while (preg_match('/\{([^\}]*)\}/', $sql, $matches)) $sql = str_replace('{'.$matches[1].'}', $_POST[$matches[1]], $sql); return array('info' => query_db($sql), 'parameter' => $_POST); } function custom_function($mask) { $callback = false; if (array_key_exists('callbacks',$mask)) { if (!array_key_exists($_POST['callback'],$mask['callbacks'])) return array('error' => 'Unknown callback ' . htmlspecialchars($_POST['callback'])); $callback = $mask['callbacks'][$_POST['callback']]; } else { $callback = 'cb_' . $_POST['callback']; if (!function_exists($callback)) $callback = false; } if (!$callback) throw new Exception(sprintf("Unknown callback %s", htmlspecialchars($_POST['callback']))); return $callback(); } function menu($base, $source) { $menu = new MenuItem($base); $menu->setSource($source); return array('title' => $menu->getTitle(), 'menu' => $menu->render()); } function send_file($fname, $inline=NULL) { if ($inline === true) { $disposition = 'inline'; } elseif ($inline === false) { $disposition = 'attachment'; } else { if (isset($_GET['attachment']) && $_GET['attachment']) { $disposition = 'attachment'; } else { $disposition = 'inline'; } } if (($f = fopen($fname,'r')) === false) return sprintf("Cannot open file %s", $fname); $basename = basename($fname); $filesize = filesize($fname); $mtime = filemtime($fname); $content_type = mime_content_type($fname); if ($content_type == 'text/plain' || $content_type == 'text/html') { $data = fread($f, 1024); fseek($f, 0); if (mb_detect_encoding($data) != 'UTF-8' || mb_check_encoding($data, 'UTF-8') == false) $content_type .= '; charset=ISO-8859-1'; else $content_type .= '; charset=UTF-8'; } header(sprintf('Last-Modified: %s', date('r', $mtime))); header(sprintf('Content-disposition: %s; filename="%s"', $disposition, $basename)); header(sprintf('Content-Length: %d', $filesize)); header(sprintf('Content-Type: %s', $content_type)); header("Cache-Control: "); header("Pragma: "); usleep(5000); fpassthru($f); fclose($f); } function download_file($table,$column,$path,$id,$inline=NULL) { global $db; global $mask; $sql = sprintf("SELECT %s FROM %s WHERE id = %d", $column, $table, $id); $sth = $db->query($sql); if ($sth === false) return "Database error, Query " . $sql; $row = $sth->fetch(); $fname = $_SESSION['sys']['basedir'] . 'archive/' . $path . '/' . $row[$column]; if (!file_exists($fname)) return sprintf("File not found\n%s", $fname); return send_file($fname, $inline); } function process_file($mask) { if (!array_key_exists('files',$mask)) return array('error' => 'Unknown function ' . htmlspecialchars($_GET['name'])); if (!array_key_exists($_GET['name'],$mask['files'])) return array('error' => 'Unknown callback ' . htmlspecialchars($_GET['name'])); $mask['files'][$_GET['name']](); exit; } function process_listedit($mask, $action) { global $db; $list = false; if ($_REQUEST['second'] === true) { $secondName = substr($_REQUEST['source'],strrpos($_REQUEST['source'], '__')+2); foreach ($mask['second'] as $name => $second) if ($name == $secondName) $list = $second; } else { $list = $mask['list']; } if ($list === false) { error_log('Grid not found for ' . $_REQUEST['source']); ajax_error('Grid not found for ' . $_REQUEST['source']); exit; } $values = array(); foreach ($_POST as $k => $v) if (substr($k,0,10) == 'listinput_') $values[substr($k,10)] = $v; switch ($action) { case 'upd': $update = sprintf("sys_user='%s', sys_edit=now()", $_SESSION['sys']['login']); foreach ($values as $c => $v) { if (!array_key_exists('edit', $list['list'][$c])) continue; if (!strlen($v) && array_key_exists('isNullable', $list['list'][$c]['edit']) && $list['list'][$c]['edit']['isNullable'] === true) $v = NULL; $update .= sprintf(", %s = %s", $c, $db->quote($v)); } $sql = sprintf("UPDATE %s SET %s WHERE id = %d", $list['table_edit'], $update, $_POST['_k0']); if ($db->query($sql)) format_ajax(array('status' => true)); else ajax_error('Cannot save item'); break; } } function load_grid($mask) { require_once($_SESSION['sys']['basedir'].'/lib/mask.php'); if ($_POST['name'] == 'main') { if (!array_key_exists('list', $mask)) ajax_error('Missing grid definition'); $data = grid_details($_POST['source'], $_POST['name'], $mask); } else { if (!array_key_exists('second', $mask)) ajax_error('Missing grid definition'); if (!array_key_exists($_POST['name'], $mask['second'])) ajax_error('Missing grid definition'); $data = grid_details($_POST['source'], $_POST['name'], $mask['second'][$_POST['name']]); } return $data; } if (!empty($_REQUEST['_k0'])) { db_connect(); foreach ($_POST as $k => $v) if (substr($k,0,13) == '_action_grid_') { $_REQUEST['source'] = substr($k,13); $action = $v; } if (substr($_REQUEST['source'],-8) == '__second') { $_REQUEST['source'] = substr($_REQUEST['source'],0,-8); $_REQUEST['second'] = true; } else { $_REQUEST['second'] = false; } if (load_mask(Hallinta::instance()->module(),Hallinta::instance()->page()) === false) exit; process_listedit($mask, $action); exit; } function render_template($mask) { require_once($_SESSION['sys']['basedir'].'/lib/mask.php'); try { $template = new Template($_POST['template']); } catch (Exception $e) { ajax_error(sprintf('Template %s cannot be loaded', htmlspecialchars($_POST['template']))); } $template->addData($_POST); return $template->fillIn(); } function process_function($function, $mask) { if ($function == 'menu') { $data = menu($_POST['base'], $_POST['source']); } elseif ($function == 'fetch') { $data = fetch($mask); } elseif ($function == 'details') { $data = details($mask); } elseif ($function == 'edit') { $data = load_edit($mask); } elseif ($function == 'save') { $data = save($mask); } elseif ($function == 'insert') { $data = insert($mask); } elseif ($function == 'delete') { $data = delete_or_copy($mask); } elseif ($function == 'setvar') { $data = set_variable($_POST['source'],$mask); } elseif ($function == 'info') { $data = get_infos($mask); } elseif ($function == 'grid') { $data = load_grid($mask); } elseif ($function == 'template') { $data = render_template($mask); } elseif ($function == 'function') { $data = custom_function($mask); } else { throw new Exception("Unknown function ".htmlspecialchars($function)); } return $data; } function route_request($route, $mask) { $data = $_POST; unset($data['source']); unset($data['route']); unset($data['token']); unset($data['id']); list($class, $action) = explode('/', $_POST['route']); $reflection = new ReflectionClass($class); if (array_key_exists('id', $_POST) && strlen($_POST['id'])) { $object = $reflection->newInstanceArgs([$_POST['id']]); if (!$object->id()) throw new Exception(sprintf('Object %s not found', htmlspecialchars($_POST['id']))); } else { $object = $reflection->newInstance(); } $method = 'ajax' . $action; if (!method_exists($object, $method)) throw new Exception(sprintf("AJAX backend %s not found", htmlspecialchars($action))); return $object->$method($data); } if ($_SERVER['REQUEST_METHOD'] == 'POST' && empty($_POST) && empty($_FILES) && $_SERVER['CONTENT_LENGTH'] > 0) ajax_error(sprintf('Content size probably exeeded limit of %s', ini_get('post_max_size'))); if (empty($_REQUEST['source'])) ajax_error('Missing source'); db_connect(); if ($_REQUEST['source'] != 'start' && load_mask(Hallinta::instance()->module(),Hallinta::instance()->page()) === false) ajax_error('Loading source failed'); $data = array('error' => 'Unknown function'); if (isset($_GET['func']) && $_GET['func'] == 'file' && !empty($_GET['name'])) $data = process_file($mask); elseif (empty($_SESSION['sys']['login']) || $_POST['token'] != $_SESSION['token']) ajax_error('Your session has been expired'); try { if (isset($_POST['func'])) $data = process_function($_POST['func'], $mask); elseif (isset($_POST['route'])) $data = route_request($_POST['route'], $mask); else throw new Exception("Unknown backend call"); } catch (Exception $e) { debug($e->getMessage()); debug($e->getTraceAsString()); ajax_error($e->getMessage()); } catch (Error $e) { debug($e->getMessage()); debug($e->getTraceAsString()); ajax_error($e->getMessage()); } if (is_null($data)) $data = []; elseif (is_scalar($data)) $data = ['data' => $data]; if (is_array($data)) { if (isset($_POST['func']) && !array_key_exists('func', $data)) $data['func'] = $_POST['func']; elseif (isset($_POST['route']) && !array_key_exists('route', $data)) $data['route'] = $_POST['route']; } format_ajax($data);