From e18a037fb254cdb462838463e020ae32a6110011 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 14 Sep 2017 05:16:23 +0000 Subject: [PATCH 01/12] API: Improvements for clients with bad HTML support --- include/api.php | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/include/api.php b/include/api.php index e7c320e62..3c84f46f7 100644 --- a/include/api.php +++ b/include/api.php @@ -2233,7 +2233,7 @@ $called_api = null; //don't send title to regular StatusNET requests to avoid confusing these apps if (x($_GET, 'getText')) { - $ret['title'] = $item['title'] ; + $ret['title'] = $item['title']; if ($_GET['getText'] == 'html') { $ret['text'] = bbcode($item['body'], false, false); } elseif ($_GET['getText'] == 'plain') { @@ -2280,14 +2280,31 @@ $called_api = null; "

", "

", "

", "

", "

", "

", "

", "

", "
", "
", "
", "
"); - $replace = array("
\n", "\n
", "
\n", - "\n

", "

\n", "\n

", "

\n", - "\n

", "

\n", "\n

", "

\n", - "\n
", "
\n", "\n
", "
\n"); +// $replace = array(" \n
", " \n
", "
\n ", +// " \n

", "

\n ", " \n

", "

\n ", +// " \n

", "

\n ", " \n

", "

\n ", +// " \n
", "
\n ", " \n
", "
\n "); + $replace = array("
", "
", "

", + "

", "


", "

", "


", + "

", "


", "

", "


", + "
", "

", "
", "

"); $statushtml = str_replace($search, $replace, $statushtml); if ($item['title'] != "") { - $statushtml = "

" . bbcode($item['title']) . "

\n" . $statushtml; + $statushtml = "

" . bbcode($item['title']) . "


" . $statushtml; + } + + do { + $oldtext = $statushtml; + $statushtml = str_replace("

", "
", $statushtml); + } while ($oldtext != $statushtml); + + if (substr($statushtml, 0, 4) == '
') { + $statushtml = substr($statushtml, 4); + } + + if (substr($statushtml, 0, -4) == '
') { + $statushtml = substr($statushtml, -4); } // feeds without body should contain the link From c6ceae52250877c966a3ede6c6e9d2c49bb8ca8a Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 14 Sep 2017 05:19:05 +0000 Subject: [PATCH 02/12] The old database function is now replaced with a wrapper --- include/dba.php | 233 ++++++++++------------------------------ include/dbstructure.php | 18 ++-- mod/directory.php | 6 +- 3 files changed, 66 insertions(+), 191 deletions(-) diff --git a/include/dba.php b/include/dba.php index c94522829..53ee3e965 100644 --- a/include/dba.php +++ b/include/dba.php @@ -214,171 +214,22 @@ class dba { } } - public function q($sql, $onlyquery = false) { - $a = get_app(); + public function q($sql) { + $ret = self::p($sql); - if (!$this->db || !$this->connected) { - return false; + if (is_bool($ret)) { + return $ret; } - $this->error = ''; + $columns = self::columnCount($ret); - $connstr = ($this->connected() ? "Connected" : "Disonnected"); + $data = self::inArray($ret); - $stamp1 = microtime(true); - - $orig_sql = $sql; - - if (x($a->config,'system') && x($a->config['system'], 'db_callstack')) { - $sql = "/*".System::callstack()." */ ".$sql; - } - - $columns = 0; - - switch ($this->driver) { - case 'pdo': - $result = @$this->db->query($sql); - // Is used to separate between queries that returning data - or not - if (!is_bool($result)) { - $columns = $result->columnCount(); - } - break; - case 'mysqli': - $result = @$this->db->query($sql); - break; - case 'mysql': - $result = @mysql_query($sql,$this->db); - break; - } - $stamp2 = microtime(true); - $duration = (float)($stamp2 - $stamp1); - - $a->save_timestamp($stamp1, "database"); - - if (strtolower(substr($orig_sql, 0, 6)) != "select") { - $a->save_timestamp($stamp1, "database_write"); - } - if (x($a->config,'system') && x($a->config['system'],'db_log')) { - if (($duration > $a->config["system"]["db_loglimit"])) { - $duration = round($duration, 3); - $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS); - @file_put_contents($a->config["system"]["db_log"], datetime_convert()."\t".$duration."\t". - basename($backtrace[1]["file"])."\t". - $backtrace[1]["line"]."\t".$backtrace[2]["function"]."\t". - substr($sql, 0, 2000)."\n", FILE_APPEND); - } - } - - switch ($this->driver) { - case 'pdo': - $errorInfo = $this->db->errorInfo(); - if ($errorInfo) { - $this->error = $errorInfo[2]; - $this->errorno = $errorInfo[1]; - } - break; - case 'mysqli': - if ($this->db->errno) { - $this->error = $this->db->error; - $this->errorno = $this->db->errno; - } - break; - case 'mysql': - if (mysql_errno($this->db)) { - $this->error = mysql_error($this->db); - $this->errorno = mysql_errno($this->db); - } - break; - } - if (strlen($this->error)) { - logger('DB Error ('.$connstr.') '.$this->errorno.': '.$this->error); - } - - if ($this->debug) { - - $mesg = ''; - - if ($result === false) { - $mesg = 'false'; - } elseif ($result === true) { - $mesg = 'true'; - } else { - switch ($this->driver) { - case 'pdo': - $mesg = $result->rowCount().' results'.EOL; - break; - case 'mysqli': - $mesg = $result->num_rows.' results'.EOL; - break; - case 'mysql': - $mesg = mysql_num_rows($result).' results'.EOL; - break; - } - } - - $str = 'SQL = ' . printable($sql) . EOL . 'SQL returned ' . $mesg - . (($this->error) ? ' error: ' . $this->error : '') - . EOL; - - logger('dba: ' . $str ); - } - - /** - * If dbfail.out exists, we will write any failed calls directly to it, - * regardless of any logging that may or may nor be in effect. - * These usually indicate SQL syntax errors that need to be resolved. - */ - - if ($result === false) { - logger('dba: ' . printable($sql) . ' returned false.' . "\n" . $this->error); - if (file_exists('dbfail.out')) { - file_put_contents('dbfail.out', datetime_convert() . "\n" . printable($sql) . ' returned false' . "\n" . $this->error . "\n", FILE_APPEND); - } - } - - if (is_bool($result)) { - return $result; - } - if ($onlyquery) { - $this->result = $result; + if ((count($data) == 0) && ($columns == 0)) { return true; } - $r = array(); - switch ($this->driver) { - case 'pdo': - while ($x = $result->fetch(PDO::FETCH_ASSOC)) { - $r[] = $x; - } - $result->closeCursor(); - break; - case 'mysqli': - while ($x = $result->fetch_array(MYSQLI_ASSOC)) { - $r[] = $x; - } - $result->free_result(); - break; - case 'mysql': - while ($x = mysql_fetch_array($result, MYSQL_ASSOC)) { - $r[] = $x; - } - mysql_free_result($result); - break; - } - - // PDO doesn't return "true" on successful operations - like mysqli does - // Emulate this behaviour by checking if the query returned data and had columns - // This should be reliable enough - if (($this->driver == 'pdo') && (count($r) == 0) && ($columns == 0)) { - return true; - } - - //$a->save_timestamp($stamp1, "database"); - - if ($this->debug) { - logger('dba: ' . printable(print_r($r, true))); - } - return($r); + return $data; } public function dbg($dbg) { @@ -820,6 +671,26 @@ class dba { return self::$dbo->affected_rows; } + /** + * @brief Returns the number of columns of a statement + * + * @param object Statement object + * @return int Number of columns + */ + public static function columnCount($stmt) { + if (!is_object($stmt)) { + return 0; + } + switch (self::$dbo->driver) { + case 'pdo': + return $stmt->columnCount(); + case 'mysqli': + return $stmt->field_count; + case 'mysql': + return mysql_affected_rows($stmt); + } + return 0; + } /** * @brief Returns the number of rows of a statement * @@ -1415,38 +1286,44 @@ function dbesc($str) { // 'user', 1); function q($sql) { global $db; + $args = func_get_args(); unset($args[0]); - if ($db && $db->connected) { - $sql = $db->clean_query($sql); - $sql = $db->any_value_fallback($sql); - $stmt = @vsprintf($sql,$args); // Disabled warnings - //logger("dba: q: $stmt", LOGGER_ALL); - if ($stmt === false) - logger('dba: vsprintf error: ' . print_r(debug_backtrace(),true), LOGGER_DEBUG); - - $db->log_index($stmt); - - return $db->q($stmt); + if (!$db || !$db->connected) { + return false; } - /** - * - * This will happen occasionally trying to store the - * session data after abnormal program termination - * - */ - logger('dba: no database: ' . print_r($args,true)); - return false; + $sql = $db->clean_query($sql); + $sql = $db->any_value_fallback($sql); + + $stmt = @vsprintf($sql, $args); + + $ret = dba::p($stmt); + + if (is_bool($ret)) { + return $ret; + } + + $columns = dba::columnCount($ret); + + $data = dba::inArray($ret); + + if ((count($data) == 0) && ($columns == 0)) { + return true; + } + + return $data; } /** - * @brief Performs a query with "dirty reads" + * @brief Performs a query with "dirty reads" - deprecated * * By doing dirty reads (reading uncommitted data) no locks are performed * This function can be used to fetch data that doesn't need to be reliable. * + * Hadn't worked like expected and does now the same like the other function. + * * @param $args Query parameters (1 to N parameters of different types) * @return array Query array */ @@ -1465,9 +1342,7 @@ function qu($sql) { $db->log_index($stmt); - $db->q("SET SESSION TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;"); $retval = $db->q($stmt); - $db->q("SET SESSION TRANSACTION ISOLATION LEVEL REPEATABLE READ;"); return $retval; } diff --git a/include/dbstructure.php b/include/dbstructure.php index d87f3948d..4d615a2f1 100644 --- a/include/dbstructure.php +++ b/include/dbstructure.php @@ -31,7 +31,7 @@ function convert_to_innodb() { $sql = sprintf("ALTER TABLE `%s` engine=InnoDB;", dbesc($table['TABLE_NAME'])); echo $sql."\n"; - $result = $db->q($sql); + $result = dba::e($sql); if (!dbm::is_result($result)) { print_update_error($db, $sql); } @@ -442,9 +442,9 @@ function update_structure($verbose, $action, $tables=null, $definition=null) { // Ensure index conversion to unique removes duplicates if ($is_unique) { if ($ignore != "") { - $db->q("SET session old_alter_table=1;"); + dba::e("SET session old_alter_table=1;"); } else { - $r = $db->q("CREATE TABLE `".$temp_name."` LIKE `".$name."`;"); + $r = dba::e("CREATE TABLE `".$temp_name."` LIKE `".$name."`;"); if (!dbm::is_result($r)) { $errors .= print_update_error($db, $sql3); return $errors; @@ -452,25 +452,25 @@ function update_structure($verbose, $action, $tables=null, $definition=null) { } } - $r = @$db->q($sql3); + $r = @dba::e($sql3); if (!dbm::is_result($r)) { $errors .= print_update_error($db, $sql3); } if ($is_unique) { if ($ignore != "") { - $db->q("SET session old_alter_table=0;"); + dba::e("SET session old_alter_table=0;"); } else { - $r = $db->q("INSERT INTO `".$temp_name."` SELECT ".$field_list." FROM `".$name."`".$group_by.";"); + $r = dba::e("INSERT INTO `".$temp_name."` SELECT ".$field_list." FROM `".$name."`".$group_by.";"); if (!dbm::is_result($r)) { $errors .= print_update_error($db, $sql3); return $errors; } - $r = $db->q("DROP TABLE `".$name."`;"); + $r = dba::e("DROP TABLE `".$name."`;"); if (!dbm::is_result($r)) { $errors .= print_update_error($db, $sql3); return $errors; } - $r = $db->q("RENAME TABLE `".$temp_name."` TO `".$name."`;"); + $r = dba::e("RENAME TABLE `".$temp_name."` TO `".$name."`;"); if (!dbm::is_result($r)) { $errors .= print_update_error($db, $sql3); return $errors; @@ -551,7 +551,7 @@ function db_create_table($name, $fields, $verbose, $action, $indexes=null) { echo $sql.";\n"; if ($action) - $r = @$db->q($sql); + $r = @dba::e($sql); return $r; } diff --git a/mod/directory.php b/mod/directory.php index cb0b1f4cd..9e004ab5d 100644 --- a/mod/directory.php +++ b/mod/directory.php @@ -71,7 +71,7 @@ function directory_content(App $a) { $publish = ((get_config('system','publish_all')) ? '' : " AND `publish` = 1 " ); - $r = $db->q("SELECT COUNT(*) AS `total` FROM `profile` + $r = q("SELECT COUNT(*) AS `total` FROM `profile` LEFT JOIN `user` ON `user`.`uid` = `profile`.`uid` WHERE `is-default` = 1 $publish AND `user`.`blocked` = 0 $sql_extra "); if (dbm::is_result($r)) @@ -81,11 +81,11 @@ function directory_content(App $a) { $limit = intval($a->pager['start']).",".intval($a->pager['itemspage']); - $r = $db->q("SELECT `profile`.*, `profile`.`uid` AS `profile_uid`, `user`.`nickname`, `user`.`timezone` , `user`.`page-flags`, + $r = q("SELECT `profile`.*, `profile`.`uid` AS `profile_uid`, `user`.`nickname`, `user`.`timezone` , `user`.`page-flags`, `contact`.`addr`, `contact`.`url` AS profile_url FROM `profile` LEFT JOIN `user` ON `user`.`uid` = `profile`.`uid` LEFT JOIN `contact` ON `contact`.`uid` = `user`.`uid` - WHERE `is-default` = 1 $publish AND `user`.`blocked` = 0 AND `contact`.`self` $sql_extra $order LIMIT ".$limit); + WHERE `is-default` $publish AND `user`.`blocked` = 0 AND `contact`.`self` $sql_extra $order LIMIT ".$limit); if (dbm::is_result($r)) { if (in_array('small', $a->argv)) { From 7a3bb02f1189aafa5a6d95331e52ec438c86b03f Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 14 Sep 2017 05:40:23 +0000 Subject: [PATCH 03/12] Issue 3700: Security and Privacy related Headers --- doc/htconfig.md | 1 + include/session.php | 6 ++++++ index.php | 13 +++++++++++++ 3 files changed, 20 insertions(+) diff --git a/doc/htconfig.md b/doc/htconfig.md index aeb0c9202..dd7ab8047 100644 --- a/doc/htconfig.md +++ b/doc/htconfig.md @@ -44,6 +44,7 @@ Example: To set the directory value please add this line to your .htconfig.php: * **dlogfile - location of the developer log file * **event_input_format** - Default value is "ymd". * **frontend_worker_timeout** - Value in minutes after we think that a frontend task was killed by the webserver. Default value is 10. +* **hsts** (Boolean) - Enables the sending of HTTP Strict Transport Security headers * **ignore_cache** (Boolean) - For development only. Disables the item cache. * **like_no_comment** (Boolean) - Don't update the "commented" value of an item when it is liked. * **local_block** (Boolean) - Used in conjunction with "block_public". diff --git a/include/session.php b/include/session.php index 5194e7ef0..af871b28a 100644 --- a/include/session.php +++ b/include/session.php @@ -2,6 +2,8 @@ // Session management functions. These provide database storage of PHP // session info. +use Friendica\Core\Config; + require_once('include/cache.php'); $session_exists = 0; @@ -114,6 +116,10 @@ ini_set('session.gc_probability', $gc_probability); ini_set('session.use_only_cookies', 1); ini_set('session.cookie_httponly', 1); +if (Config::get('system', 'ssl_policy') == SSL_POLICY_FULL) { + ini_set('session.cookie_secure', 1); +} + if (!get_config('system', 'disable_database_session')) { session_set_save_handler('ref_session_open', 'ref_session_close', 'ref_session_read', 'ref_session_write', diff --git a/index.php b/index.php index 5b4cea756..d3d2e42ae 100644 --- a/index.php +++ b/index.php @@ -489,6 +489,19 @@ $profile = $a->profile; header("X-Friendica-Version: " . FRIENDICA_VERSION); header("Content-type: text/html; charset=utf-8"); +if (Config::get('system', 'hsts') && (Config::get('system', 'ssl_policy') == SSL_POLICY_FULL)) { + header("Strict-Transport-Security: max-age=31536000"); +} + +// Some security stuff +header('X-Content-Type-Options: nosniff'); +header('X-XSS-Protection: 1; mode=block'); +header('X-Permitted-Cross-Domain-Policies: none'); +header('X-Frame-Options: sameorigin'); + +// Things like embedded OSM maps don't work, when this is enabled +// header("Content-Security-Policy: default-src 'self'; script-src 'self' 'unsafe-inline' 'unsafe-eval'; connect-src 'self'; style-src 'self' 'unsafe-inline'; font-src 'self'; img-src 'self' https: data:; media-src 'self' https:; child-src 'self' https:; object-src 'none'"); + /* * We use $_GET["mode"] for special page templates. So we will check if we have * to load another page template than the default one. From 4b99d122dd7734c8f1c6aad3b6ab4316f3c5738d Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 14 Sep 2017 05:48:43 +0000 Subject: [PATCH 04/12] Added explanation --- include/api.php | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/include/api.php b/include/api.php index 3c84f46f7..45c9ed255 100644 --- a/include/api.php +++ b/include/api.php @@ -2276,14 +2276,11 @@ $called_api = null; $statushtml = trim(bbcode($body, false, false)); + // Workaround for clients with limited HTML parser functionality $search = array("
", "
", "
", "

", "

", "

", "

", "

", "

", "

", "

", "
", "
", "
", "
"); -// $replace = array(" \n
", " \n
", "
\n ", -// " \n

", "

\n ", " \n

", "

\n ", -// " \n

", "

\n ", " \n

", "

\n ", -// " \n
", "
\n ", " \n
", "
\n "); $replace = array("
", "
", "

", "

", "


", "

", "


", "

", "


", "

", "


", From 9468b7427d82d4ee5da7fc8f36d37f49c37c2bc0 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 14 Sep 2017 10:00:45 +0000 Subject: [PATCH 05/12] Allow replies as well --- include/ostatus.php | 37 ++++++++++++++++++------------------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/include/ostatus.php b/include/ostatus.php index 0c99905fa..099c0516e 100644 --- a/include/ostatus.php +++ b/include/ostatus.php @@ -418,27 +418,26 @@ class ostatus { self::processPost($xpath, $entry, $item, $importer); if ($initialize && (count(self::$itemlist) > 0)) { - // We will import it everytime, when it is started by our contacts - $valid = !empty(self::$itemlist[0]['contact-id']); - if (!$valid) { - // If not, then it depends on this setting - $valid = !Config::get('system','ostatus_full_threads'); - } - - if ($valid) { - // But we will only import complete threads - $valid = self::$itemlist[0]['uri'] == self::$itemlist[0]['parent-uri']; - } - - if ($valid) { - // Never post a thread when the only interaction by our contact was a like - $valid = false; - $verbs = array(ACTIVITY_POST, ACTIVITY_SHARE); - foreach (self::$itemlist AS $item) { - if (!empty($item['contact-id']) && in_array($item['verb'], $verbs)) { - $valid = true; + if (self::$itemlist[0]['uri'] == self::$itemlist[0]['parent-uri']) { + // We will import it everytime, when it is started by our contacts + $valid = !empty(self::$itemlist[0]['contact-id']); + if (!$valid) { + // If not, then it depends on this setting + $valid = !Config::get('system','ostatus_full_threads'); + } + if ($valid) { + // Never post a thread when the only interaction by our contact was a like + $valid = false; + $verbs = array(ACTIVITY_POST, ACTIVITY_SHARE); + foreach (self::$itemlist AS $item) { + if (!empty($item['contact-id']) && in_array($item['verb'], $verbs)) { + $valid = true; + } } } + } else { + // But we will only import complete threads + $valid = dba::exists('item', array('uid' => $importer["uid"], 'uri' => self::$itemlist[0]['parent-uri'])); } if ($valid) { From 9dfb9c1c456458b6db3bcf471a4f7dda1dca9bfa Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 14 Sep 2017 19:23:14 +0000 Subject: [PATCH 06/12] Only fetch items that don't exist in the system --- include/ostatus.php | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/include/ostatus.php b/include/ostatus.php index 099c0516e..716935955 100644 --- a/include/ostatus.php +++ b/include/ostatus.php @@ -476,6 +476,12 @@ class ostatus { */ private static function processPost($xpath, $entry, &$item, $importer) { $item["uri"] = $xpath->query('atom:id/text()', $entry)->item(0)->nodeValue; + + if (dba::exists('item', array('uid' => $importer["uid"], 'uri' => $item["uri"]))) { + logger('Post with URI '.$item["uri"].' already existed for user '.$importer["uid"].'.'); + return; + } + $item["body"] = html2bbcode($xpath->query('atom:content/text()', $entry)->item(0)->nodeValue); $item["object-type"] = $xpath->query('activity:object-type/text()', $entry)->item(0)->nodeValue; if (($item["object-type"] == ACTIVITY_OBJ_BOOKMARK) || ($item["object-type"] == ACTIVITY_OBJ_EVENT)) { @@ -590,7 +596,12 @@ class ostatus { } if (isset($item["parent-uri"]) && ($related != '')) { - self::fetchRelated($related, $item["parent-uri"], $importer); + if (!dba::exists('item', array('uid' => $importer["uid"], 'uri' => $item['parent-uri']))) { + self::fetchRelated($related, $item["parent-uri"], $importer); + } else { + logger('Reply with URI '.$item["uri"].' already existed for user '.$importer["uid"].'.'); + } + $item["type"] = 'remote-comment'; $item["gravity"] = GRAVITY_COMMENT; } else { From 87fffde80ec986d135c28816ad59f3afb102b085 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 14 Sep 2017 20:40:05 +0000 Subject: [PATCH 07/12] Documented some unknown verbs --- include/ostatus.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/ostatus.php b/include/ostatus.php index 716935955..45ac8061c 100644 --- a/include/ostatus.php +++ b/include/ostatus.php @@ -373,7 +373,7 @@ class ostatus { $item["verb"] = $xpath->query('activity:verb/text()', $entry)->item(0)->nodeValue; /// Delete a message - if ($item["verb"] == "qvitter-delete-notice" || $item["verb"] == ACTIVITY_DELETE) { + if (in_array($item["verb"], array('qvitter-delete-notice', ACTIVITY_DELETE, 'delete'))) { // ignore "Delete" messages (by now) logger("Ignore delete message ".print_r($item, true)); continue; @@ -411,6 +411,8 @@ class ostatus { } // http://activitystrea.ms/schema/1.0/rsvp-yes + // http://activitystrea.ms/schema/1.0/unfavorite + // http://mastodon.social/schema/1.0/block if (!in_array($item["verb"], array(ACTIVITY_POST, ACTIVITY_LIKE, ACTIVITY_SHARE))) { logger("Unhandled verb ".$item["verb"]." ".print_r($item, true)); } From d10eaefaa967c3f024acb4b66f40e6bce86700d7 Mon Sep 17 00:00:00 2001 From: Michael Date: Thu, 14 Sep 2017 22:04:37 +0000 Subject: [PATCH 08/12] Improved regular expression for the Diaspora link detection --- include/bbcode.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/bbcode.php b/include/bbcode.php index be4667a29..a14e3ee8e 100644 --- a/include/bbcode.php +++ b/include/bbcode.php @@ -987,7 +987,8 @@ function bbcode($Text, $preserve_nl = false, $tryoembed = true, $simplehtml = fa // Server independent link to posts and comments // See issue: https://github.com/diaspora/diaspora_federation/issues/75 - $Text = preg_replace("=diaspora://(.*?)/([^\s\]]*)=ism", System::baseUrl()."/display/$2", $Text); + $expression = "=diaspora://.*?/post/([0-9A-Za-z\-_@.:]{15,254}[0-9A-Za-z])=ism"; + $Text = preg_replace($expression, System::baseUrl()."/display/$1", $Text); // if the HTML is used to generate plain text, then don't do this search, but replace all URL of that kind to text // if ($simplehtml != 7) { From f29e21eb5c8d35a30bd470d739ef1049ce5f3e5d Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 15 Sep 2017 03:00:38 +0000 Subject: [PATCH 09/12] We now avoid prepared statements without an argument --- include/dba.php | 35 ++++++++++++++++++++++++++--------- 1 file changed, 26 insertions(+), 9 deletions(-) diff --git a/include/dba.php b/include/dba.php index 53ee3e965..48f195542 100644 --- a/include/dba.php +++ b/include/dba.php @@ -423,6 +423,19 @@ class dba { switch (self::$dbo->driver) { case 'pdo': + // If there are no arguments we use "query" + if (count($args) == 0) { + if (!$retval = self::$dbo->db->query($sql)) { + $errorInfo = self::$dbo->db->errorInfo(); + self::$dbo->error = $errorInfo[2]; + self::$dbo->errorno = $errorInfo[1]; + $retval = false; + break; + } + self::$dbo->affected_rows = $retval->rowCount(); + break; + } + if (!$stmt = self::$dbo->db->prepare($sql)) { $errorInfo = self::$dbo->db->errorInfo(); self::$dbo->error = $errorInfo[2]; @@ -451,8 +464,8 @@ class dba { $command = strtolower($parts[0]); $can_be_prepared = in_array($command, array('select', 'update', 'insert', 'delete')); - // The fallback routine currently only works with statements that doesn't return values - if (!$can_be_prepared && $called_from_e) { + // The fallback routine is called as well when there are no arguments + if (!$can_be_prepared || (count($args) == 0)) { $retval = self::$dbo->db->query(self::replace_parameters($sql, $args)); if (self::$dbo->db->errno) { self::$dbo->error = self::$dbo->db->error; @@ -727,6 +740,10 @@ class dba { case 'pdo': return $stmt->fetch(PDO::FETCH_ASSOC); case 'mysqli': + if (get_class($stmt) == 'mysqli_result') { + return $stmt->fetch_assoc(); + } + // This code works, but is slow // Bind the result to a result array @@ -890,13 +907,13 @@ class dba { $definition = db_definition(); foreach ($definition AS $table => $structure) { - foreach ($structure['fields'] AS $field => $field_struct) { - if (isset($field_struct['relation'])) { - foreach ($field_struct['relation'] AS $rel_table => $rel_field) { - self::$relation[$rel_table][$rel_field][$table][] = $field; - } - } - } + foreach ($structure['fields'] AS $field => $field_struct) { + if (isset($field_struct['relation'])) { + foreach ($field_struct['relation'] AS $rel_table => $rel_field) { + self::$relation[$rel_table][$rel_field][$table][] = $field; + } + } + } } } From cd16155c96f7ea4ead1a0ec87198913d3414dc84 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 15 Sep 2017 03:10:04 +0000 Subject: [PATCH 10/12] Only warn when there are arguments at all --- include/dba.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/dba.php b/include/dba.php index 48f195542..16261efd2 100644 --- a/include/dba.php +++ b/include/dba.php @@ -391,7 +391,7 @@ class dba { return false; } - if (substr_count($sql, '?') != count($args)) { + if ((substr_count($sql, '?') != count($args)) && (count($args) > 0)) { // Question: Should we continue or stop the query here? logger('Parameter mismatch. Query "'.$sql.'" - Parameters '.print_r($args, true), LOGGER_DEBUG); } From 88ada982452ece7d355b207dd64056761e2df223 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 15 Sep 2017 06:07:34 +0000 Subject: [PATCH 11/12] Added documentation --- include/dba.php | 43 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 35 insertions(+), 8 deletions(-) diff --git a/include/dba.php b/include/dba.php index 16261efd2..d6a1727d6 100644 --- a/include/dba.php +++ b/include/dba.php @@ -214,6 +214,16 @@ class dba { } } + /** + * @brief execute SQL query - deprecated + * + * Please use the dba:: functions instead: + * dba::select, dba::exists, dba::insert + * dba::delete, dba::update, dba::p, dba::e + * + * @param string $sql SQL query + * @return array Query array + */ public function q($sql) { $ret = self::p($sql); @@ -366,6 +376,10 @@ class dba { /** * @brief Executes a prepared statement that returns data * @usage Example: $r = p("SELECT * FROM `item` WHERE `guid` = ?", $guid); + * + * Please only use it with complicated queries. + * For all regular queries please use dba::select or dba::exists + * * @param string $sql SQL statement * @return object statement object */ @@ -574,6 +588,8 @@ class dba { /** * @brief Executes a prepared statement like UPDATE or INSERT that doesn't return data * + * Please use dba::delete, dba::insert, dba::update, ... instead + * * @param string $sql SQL statement * @return boolean Was the query successfull? False is returned only if an error occurred */ @@ -656,6 +672,8 @@ class dba { /** * @brief Fetches the first row * + * Please use dba::select or dba::select whenever this is possible. + * * @param string $sql SQL statement * @return array first row of query */ @@ -1159,7 +1177,11 @@ class dba { * Example: * $table = "item"; * $fields = array("id", "uri", "uid", "network"); + * * $condition = array("uid" => 1, "network" => 'dspr'); + * or: + * $condition = array("`uid` = ? AND `network` IN (?, ?)", 1, 'dfrn', 'dspr'); + * * $params = array("order" => array("id", "received" => true), "limit" => 1); * * $data = dba::select($table, $fields, $condition, $params); @@ -1297,10 +1319,16 @@ function dbesc($str) { } } -// Function: q($sql,$args); -// Description: execute SQL query with printf style args. -// Example: $r = q("SELECT * FROM `%s` WHERE `uid` = %d", -// 'user', 1); +/** + * @brief execute SQL query with printf style args - deprecated + * + * Please use the dba:: functions instead: + * dba::select, dba::exists, dba::insert + * dba::delete, dba::update, dba::p, dba::e + * + * @param $args Query parameters (1 to N parameters of different types) + * @return array Query array + */ function q($sql) { global $db; @@ -1336,10 +1364,9 @@ function q($sql) { /** * @brief Performs a query with "dirty reads" - deprecated * - * By doing dirty reads (reading uncommitted data) no locks are performed - * This function can be used to fetch data that doesn't need to be reliable. - * - * Hadn't worked like expected and does now the same like the other function. + * Please use the dba:: functions instead: + * dba::select, dba::exists, dba::insert + * dba::delete, dba::update, dba::p, dba::e * * @param $args Query parameters (1 to N parameters of different types) * @return array Query array From f8d34e35e7b720dae2bd7acf9b1d9499564c8814 Mon Sep 17 00:00:00 2001 From: Michael Date: Fri, 15 Sep 2017 06:15:25 +0000 Subject: [PATCH 12/12] "exists", "select" whre is the difference? --- include/dba.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/dba.php b/include/dba.php index d6a1727d6..9bf7a67d4 100644 --- a/include/dba.php +++ b/include/dba.php @@ -672,7 +672,7 @@ class dba { /** * @brief Fetches the first row * - * Please use dba::select or dba::select whenever this is possible. + * Please use dba::select or dba::exists whenever this is possible. * * @param string $sql SQL statement * @return array first row of query