data = $data; $driver = $data->conf->dbDriver; $this->db = $driver::create($data, INSTALL); $ver = $this->db->schemaVersion(); if(!INSTALL && $ver < self::SCHEMA_VERSION) { $this->db->update(self::SCHEMA_VERSION); } } static public function listDrivers(): array { $sep = \DIRECTORY_SEPARATOR; $path = __DIR__.$sep."Db".$sep; $classes = []; foreach(glob($path."Driver?*.php") as $file) { $name = basename($file, ".php"); if(substr($name,-3) != "PDO") { $name = NS_BASE."Db\\$name"; if(class_exists($name)) { $classes[$name] = $name::driverName(); } } } return $classes; } public function schemaVersion(): int { return $this->db->schemaVersion(); } public function dbUpdate(): bool { if($this->db->schemaVersion() < self::SCHEMA_VERSION) return $this->db->update(self::SCHEMA_VERSION); return false; } public function settingGet(string $key) { $row = $this->db->prepare("SELECT value, type from newssync_settings where key = ?", "str")->run($key)->get(); if(!$row) return null; switch($row['type']) { case "int": return (int) $row['value']; case "numeric": return (float) $row['value']; case "text": return $row['value']; case "json": return json_decode($row['value']); case "timestamp": return date_create_from_format("!".self::FORMAT_TS, $row['value'], new DateTimeZone("UTC")); case "date": return date_create_from_format("!".self::FORMAT_DATE, $row['value'], new DateTimeZone("UTC")); case "time": return date_create_from_format("!".self::FORMAT_TIME, $row['value'], new DateTimeZone("UTC")); case "bool": return (bool) $row['value']; case "null": return null; default: return $row['value']; } } public function settingSet(string $key, $in, string $type = null): bool { if(!$type) { switch(gettype($in)) { case "boolean": $type = "bool"; break; case "integer": $type = "int"; break; case "double": $type = "numeric"; break; case "string": case "array": $type = "json"; break; case "resource": case "unknown type": case "NULL": $type = "null"; break; case "object": if($in instanceof DateTimeInterface) { $type = "timestamp"; } else { $type = "text"; } break; default: $type = 'null'; break; } } $type = strtolower($type); switch($type) { case "integer": $type = "int"; case "int": $value =& $in; break; case "float": case "double": case "real": $type = "numeric"; case "numeric": $value =& $in; break; case "str": case "string": $type = "text"; case "text": $value =& $in; break; case "json": if(is_array($in) || is_object($in)) { $value = json_encode($in); } else { $value =& $in; } break; case "datetime": $type = "timestamp"; case "timestamp": if($in instanceof DateTimeInterface) { $value = gmdate(self::FORMAT_TS, $in->format("U")); } else if(is_numeric($in)) { $value = gmdate(self::FORMAT_TS, $in); } else { $value = gmdate(self::FORMAT_TS, gmstrftime($in)); } break; case "date": if($in instanceof DateTimeInterface) { $value = gmdate(self::FORMAT_DATE, $in->format("U")); } else if(is_numeric($in)) { $value = gmdate(self::FORMAT_DATE, $in); } else { $value = gmdate(self::FORMAT_DATE, gmstrftime($in)); } break; case "time": if($in instanceof DateTimeInterface) { $value = gmdate(self::FORMAT_TIME, $in->format("U")); } else if(is_numeric($in)) { $value = gmdate(self::FORMAT_TIME, $in); } else { $value = gmdate(self::FORMAT_TIME, gmstrftime($in)); } break; case "boolean": case "bit": $type = "bool"; case "bool": $value = (int) $in; break; case "null": $value = null; break; default: $type = "text"; $value =& $in; break; } $this->db->prepare("REPLACE INTO newssync_settings(key,value,type) values(?,?,?)", "str", (($type=="null") ? "null" : "str"), "str")->run($key, $value, "text"); } public function settingRemove(string $key): bool { $this->db->prepare("DELETE from newssync_settings where key = ?", "str")->run($key); return true; } public function userExists(string $user): bool { if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); return (bool) $this->db->prepare("SELECT count(*) from newssync_users where id is ?", "str")->run($user)->getSingle(); } public function userAdd(string $user, string $password = null): bool { if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); if($this->userExists($user)) return false; if(strlen($password) > 0) $password = password_hash($password, \PASSWORD_DEFAULT); $this->db->prepare("INSERT INTO newssync_users(id,password) values(?,?)", "str", "str")->run($user,$password); return true; } public function userRemove(string $user): bool { if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); $this->db->prepare("DELETE from newssync_users where id is ?", "str")->run($user); return true; } public function userList(string $domain = null): array { if($domain !== null) { if(!$this->data->user->authorize("@".$domain, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $domain]); $domain = str_replace(["\\","%","_"],["\\\\", "\\%", "\\_"], $domain); $domain = "%@".$domain; $set = $this->db->prepare("SELECT id from newssync_users where id like ?", "str")->run($domain); } else { if(!$this->data->user->authorize("", __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => "all users"]); $set = $this->db->prepare("SELECT id from newssync_users")->run(); } $out = []; foreach($set as $row) { $out[] = $row["id"]; } return $out; } public function userPasswordGet(string $user): string { if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); if(!$this->userExists($user)) return ""; return (string) $this->db->prepare("SELECT password from newssync_users where id is ?", "str")->run($user)->getSingle(); } public function userPasswordSet(string $user, string $password = null): bool { if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); if(!$this->userExists($user)) return false; if(strlen($password > 0)) $password = password_hash($password, \PASSWORD_DEFAULT); $this->db->prepare("UPDATE newssync_users set password = ? where id is ?", "str", "str")->run($password, $user); return true; } public function userPropertiesGet(string $user): array { if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); $prop = $this->db->prepare("SELECT name,rights from newssync_users where id is ?", "str")->run($user)->get(); if(!$prop) return []; return $prop; } public function userPropertiesSet(string $user, array &$properties): array { if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); $valid = [ // FIXME: add future properties "name" => "str", ]; if(!$this->userExists($user)) return []; $this->db->begin(); foreach($valid as $prop => $type) { if(!array_key_exists($prop, $properties)) continue; $this->db->prepare("UPDATE newssync_users set $prop = ? where id is ?", $type, "str")->run($properties[$prop], $user); } $this->db->commit(); return $this->userPropertiesGet($user); } public function userRightsGet(string $user): int { if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); return (int) $this->db->prepare("SELECT rights from newssync_users where id is ?", "str")->run($user)->getSingle(); } public function userRightsSet(string $user, int $rights): bool { if(!$this->data->user->authorize($user, __FUNCTION__, $rights)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); if(!$this->userExists($user)) return false; $this->db->prepare("UPDATE newssync_users set rights = ? where id is ?", "int", "str")->run($rights, $user); return true; } public function subscriptionAdd(string $user, string $url, string $fetchUser = "", string $fetchPassword = ""): int { if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); if(!$this->userExists($user)) throw new User\Exception("doesNotExist", ["user" => $user, "action" => __FUNCTION__]); $this->db->begin(); $qFeed = $this->db->prepare("SELECT id from newssync_feeds where url is ? and username is ? and password is ?", "str", "str", "str"); $feed = $qFeed->run($url, $fetchUser, $fetchPassword)->getSingle(); if($feed===null) { $this->db->prepare("INSERT INTO newssync_feeds(url,username,password) values(?,?,?)", "str", "str", "str")->run($url, $fetchUser, $fetchPassword); $feed = $qFeed->run($url, $fetchUser, $fetchPassword)->getSingle(); } $this->db->prepare("INSERT INTO newssync_subscriptions(owner,feed) values(?,?)", "str", "int")->run($user,$feed); $sub = $this->db->prepare("SELECT id from newssync_subscriptions where owner is ? and feed is ?", "str", "int")->run($user, $feed)->getSingle(); $this->db->commit(); return $sub; } public function subscriptionRemove(int $id): bool { $this->db->begin(); $user = $this->db->prepare("SELECT owner from newssync_subscriptions where id is ?", "int")->run($id)->getSingle(); if($user===null) return false; if(!$this->data->user->authorize($user, __FUNCTION__)) throw new User\ExceptionAuthz("notAuthorized", ["action" => __FUNCTION__, "user" => $user]); return (bool) $this->db->prepare("DELETE from newssync_subscriptions where id is ?", "int")->run($id)->changes(); } }