]> git.mxchange.org Git - friendica.git/commitdiff
Merge pull request #7424 from TiMESPLiNTER/fix/respect-picdate
authorHypolite Petovan <hypolite@mrpetovan.com>
Tue, 23 Jul 2019 17:09:41 +0000 (13:09 -0400)
committerGitHub <noreply@github.com>
Tue, 23 Jul 2019 17:09:41 +0000 (13:09 -0400)
Respect picdate for profile pic in Frio theme

54 files changed:
bin/auth_ejabberd.php
bin/console.php
bin/daemon.php
bin/worker.php
composer.json
composer.lock
doc/Two-Factor-Authentication.md
include/api.php
index.php
src/App.php
src/App/Mode.php
src/App/Router.php
src/Console/AutomaticInstallation.php
src/Content/Text/BBCode.php
src/Core/Installer.php
src/Core/L10n/L10n.php
src/Database/Database.php
src/Factory/ConfigFactory.php
src/Factory/DBFactory.php [deleted file]
src/Factory/DependencyFactory.php
src/Factory/LoggerFactory.php
src/Factory/ProfilerFactory.php [deleted file]
src/Model/Contact.php
src/Model/Item.php
src/Model/TwoFactor/AppSpecificPassword.php [new file with mode: 0644]
src/Model/TwoFactor/RecoveryCode.php [new file with mode: 0644]
src/Model/TwoFactorRecoveryCode.php [deleted file]
src/Model/User.php
src/Module/Install.php
src/Module/Settings/TwoFactor/AppSpecific.php [new file with mode: 0644]
src/Module/Settings/TwoFactor/Index.php
src/Module/Settings/TwoFactor/Recovery.php
src/Module/TwoFactor/Recovery.php
src/Network/Probe.php
src/Protocol/ActivityPub/Processor.php
src/Protocol/Diaspora.php
src/Util/BasePath.php
src/Util/ConfigFileLoader.php
src/Util/ExAuth.php
src/Util/Profiler.php
static/dbstructure.config.php
static/dependencies.config.php [new file with mode: 0644]
tests/DatabaseTest.php
tests/functional/DependencyCheckTest.php [new file with mode: 0644]
tests/include/ApiTest.php
tests/src/App/ModeTest.php
tests/src/Database/DBATest.php
tests/src/Database/DBStructureTest.php
tests/src/Util/BasePathTest.php
tests/src/Util/Config/ConfigFileLoaderTest.php
tests/src/Util/ProfilerTest.php
view/templates/settings/twofactor/app_specific.tpl [new file with mode: 0644]
view/templates/settings/twofactor/index.tpl
view/theme/frio/templates/photo_view.tpl

index bf6d069d12bb58f1507f9d9ee6e0c14763c6e08e..ba3a9b8dd15b73b556edc34ef29470411896caa8 100755 (executable)
@@ -51,7 +51,10 @@ chdir($directory);
 
 require dirname(__DIR__) . '/vendor/autoload.php';
 
-$a = Factory\DependencyFactory::setUp('auth_ejabbered', dirname(__DIR__));
+$dice = new \Dice\Dice();
+$dice = $dice->addRules(include __DIR__ . '/../static/dependencies.config.php');
+
+$a = Factory\DependencyFactory::setUp('auth_ejabbered', $dice);
 
 if ($a->getMode()->isNormal()) {
        $oAuth = new ExAuth();
index 410eabda094639db0d3d06a9a8eb41ec92220464..6fa804fb98e3789f2596c68f9a543266abdd8810 100755 (executable)
@@ -5,7 +5,10 @@ require dirname(__DIR__) . '/vendor/autoload.php';
 
 use Friendica\Factory;
 
-$a = Factory\DependencyFactory::setUp('console', dirname(__DIR__));
+$dice = new \Dice\Dice();
+$dice = $dice->addRules(include __DIR__ . '/../static/dependencies.config.php');
+
+$a = Factory\DependencyFactory::setUp('console', $dice);
 \Friendica\BaseObject::setApp($a);
 
 (new Friendica\Core\Console($argv))->execute();
index 9051e0e6c3be227c55b407ce3260414c24bca851..1b0a7edb3ca227667ad4f39fc3c5efebfdecc870 100755 (executable)
@@ -32,7 +32,10 @@ if (!file_exists("boot.php") && (sizeof($_SERVER["argv"]) != 0)) {
 
 require dirname(__DIR__) . '/vendor/autoload.php';
 
-$a = Factory\DependencyFactory::setUp('daemon', dirname(__DIR__));
+$dice = new \Dice\Dice();
+$dice = $dice->addRules(include __DIR__ . '/../static/dependencies.config.php');
+
+$a = Factory\DependencyFactory::setUp('daemon', $dice);
 
 if ($a->getMode()->isInstall()) {
        die("Friendica isn't properly installed yet.\n");
index 2b5915f471d4fc76b9fdfdee1bad2ff42318cea0..f4b012026853e2bda7d73ed5a89dfd15eb019253 100755 (executable)
@@ -30,7 +30,10 @@ if (!file_exists("boot.php") && (sizeof($_SERVER["argv"]) != 0)) {
 
 require dirname(__DIR__) . '/vendor/autoload.php';
 
-$a = Factory\DependencyFactory::setUp('worker', dirname(__DIR__));
+$dice = new \Dice\Dice();
+$dice = $dice->addRules(include __DIR__ . '/../static/dependencies.config.php');
+
+$a = Factory\DependencyFactory::setUp('worker', $dice);
 
 // Check the database structure and possibly fixes it
 Update::check($a->getBasePath(), true, $a->getMode());
index feac6c61f77ea248b48370da1426d85a7db53959..acae66f07d66d81377cbae1c79f74934ce0a81cb 100644 (file)
@@ -58,7 +58,8 @@
                "npm-asset/fullcalendar": "^3.0.1",
                "npm-asset/cropperjs": "1.2.2",
                "npm-asset/imagesloaded": "4.1.4",
-               "pear/console_table": "^1.3"
+               "pear/console_table": "^1.3",
+               "level-2/dice": ">1.0"
        },
        "repositories": [
                {
index af51b6dfe7ef7c9ca4ba2de0e4cad4ea3501ae96..ba5b7b85d56fb923f717f96514898635cf41ac52 100644 (file)
@@ -4,7 +4,7 @@
         "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
         "This file is @generated automatically"
     ],
-    "content-hash": "eb985236d64ed0b0fe1fc2e4ac6616e2",
+    "content-hash": "b9ea7162aa7ede630a2090c883e1174b",
     "packages": [
         {
             "name": "asika/simple-console",
             ],
             "time": "2018-09-18T12:18:08+00:00"
         },
+        {
+            "name": "level-2/dice",
+            "version": "4.0.1",
+            "source": {
+                "type": "git",
+                "url": "https://github.com/Level-2/Dice.git",
+                "reference": "e631f110f0520294fec902814c61cac26566023c"
+            },
+            "dist": {
+                "type": "zip",
+                "url": "https://api.github.com/repos/Level-2/Dice/zipball/e631f110f0520294fec902814c61cac26566023c",
+                "reference": "e631f110f0520294fec902814c61cac26566023c",
+                "shasum": ""
+            },
+            "require": {
+                "php": ">=7.0.0"
+            },
+            "require-dev": {
+                "phpunit/phpunit": "^6.5"
+            },
+            "type": "library",
+            "autoload": {
+                "psr-4": {
+                    "Dice\\": "./"
+                }
+            },
+            "notification-url": "https://packagist.org/downloads/",
+            "license": [
+                "BSD-2-Clause"
+            ],
+            "authors": [
+                {
+                    "name": "Tom Butler",
+                    "email": "tom@r.je"
+                }
+            ],
+            "description": "A minimalist Dependency injection container (DIC) for PHP. Please note: 3.0+ is only compatible with PHP 7.0. The 2.0 branch is compatbile with PHP 5.6.",
+            "homepage": "http://r.je/dice.html",
+            "keywords": [
+                "dependency injection",
+                "dependency injection container",
+                "di",
+                "ioc"
+            ],
+            "time": "2019-05-01T12:55:36+00:00"
+        },
         {
             "name": "lightopenid/lightopenid",
             "version": "dev-master",
index 32aa7308a357ae826138637ed08e0fce0212fb74..eca6f598c9f847f782894989125e4e693081cedf 100644 (file)
@@ -58,3 +58,17 @@ In this case you will have to configure your authenticator app again using the p
 When two-factor authentication is enabled, you can show your recovery codes, including the ones you've already used.
 
 You can freely regenerate a new set of fresh recovery codes, just be sure to replace the previous ones where you saved them as they won't be active anymore.
+
+## Third-party applications and API
+
+Third-party applications using the Friendica API can't accept two-factor time-based authentication codes.
+Instead, if you enabled two-factor authentication, you have to generate app-specific randomly generated long passwords to use in your apps instead of your regular account password.
+
+**Note**: Your regular password won't work at all when prompted in third-party apps if you enabled two-factor authentication.
+
+You can generate as many app-specific passwords as you want, they will be shown once to you just after you generated it.
+Just copy and paste it in your third-party app in the Friendica account password input field at this point.
+We recommend generating a single app-specific password for each separate third-party app you are using, using a meaningul description of the target app (like "Frienqa on my Fairphone 2").
+
+You can also revoke any and all app-specific password you generated this way.
+This may log you out of the third-party application(s) you used the revoked app-specific password to log in with.
\ No newline at end of file
index 51ca1e4e17b794c7ba687e03b1c04891dc60c68a..b1bb49c517116b6c31841169bd65f1811c0ffd1e 100644 (file)
@@ -236,7 +236,7 @@ function api_login(App $a)
        if ($addon_auth['authenticated'] && count($addon_auth['user_record'])) {
                $record = $addon_auth['user_record'];
        } else {
-               $user_id = User::authenticate(trim($user), trim($password));
+               $user_id = User::authenticate(trim($user), trim($password), true);
                if ($user_id !== false) {
                        $record = DBA::selectFirst('user', [], ['uid' => $user_id]);
                }
index ddc851cd4e36654e02d6f3d73cf6e68270ffeef1..7b7146aeb2be13eea17a618e372ae412cfb35538 100644 (file)
--- a/index.php
+++ b/index.php
@@ -12,7 +12,9 @@ if (!file_exists(__DIR__ . '/vendor/autoload.php')) {
 
 require __DIR__ . '/vendor/autoload.php';
 
-$a = Factory\DependencyFactory::setUp('index', __DIR__, false);
+$dice = new \Dice\Dice();
+$dice = $dice->addRules(include __DIR__ . '/static/dependencies.config.php');
 
-$a->runFrontend();
+$a = Factory\DependencyFactory::setUp('index', $dice, false);
 
+$a->runFrontend();
index 1475d60df83a108bc16b5def133792e619d1ea59..58ab451b379c606d18750e3764b4ac36f7be8afe 100644 (file)
@@ -376,12 +376,10 @@ class App
                $this->getMode()->determine($this->getBasePath());
 
                if ($this->getMode()->has(App\Mode::DBAVAILABLE)) {
-                       $this->profiler->update(
-                               $this->config->get('system', 'profiler', false),
-                               $this->config->get('rendertime', 'callstack', false));
+                       $this->profiler->update($this->config);
 
                        Core\Hook::loadHooks();
-                       $loader = new ConfigFileLoader($this->getBasePath(), $this->mode);
+                       $loader = new ConfigFileLoader($this->getBasePath());
                        Core\Hook::callAll('load_config', $loader);
                }
 
index 8d82ffc0b499ee57b24df6716d5e11bda4cde783..6cb79b6edd4532ac25d4ffeb9791162b82b4ee33 100644 (file)
@@ -2,8 +2,9 @@
 
 namespace Friendica\App;
 
-use Friendica\Core\Config;
-use Friendica\Database\DBA;
+use Friendica\Core\Config\Cache\ConfigCache;
+use Friendica\Database\Database;
+use Friendica\Util\BasePath;
 
 /**
  * Mode of the current Friendica Node
@@ -12,9 +13,9 @@ use Friendica\Database\DBA;
  */
 class Mode
 {
-       const LOCALCONFIGPRESENT = 1;
-       const DBAVAILABLE = 2;
-       const DBCONFIGAVAILABLE = 4;
+       const LOCALCONFIGPRESENT  = 1;
+       const DBAVAILABLE         = 2;
+       const DBCONFIGAVAILABLE   = 4;
        const MAINTENANCEDISABLED = 8;
 
        /***
@@ -28,10 +29,22 @@ class Mode
         */
        private $basepath;
 
-       public function __construct($basepath = '')
+       /**
+        * @var Database
+        */
+       private $database;
+
+       /**
+        * @var ConfigCache
+        */
+       private $configCache;
+
+       public function __construct(BasePath $basepath, Database $database, ConfigCache $configCache)
        {
-               $this->basepath = $basepath;
-               $this->mode = 0;
+               $this->basepath    = $basepath->getPath();
+               $this->database    = $database;
+               $this->configCache = $configCache;
+               $this->mode        = 0;
        }
 
        /**
@@ -41,42 +54,50 @@ class Mode
         * - App::MODE_MAINTENANCE: The maintenance mode has been set
         * - App::MODE_NORMAL     : Normal run with all features enabled
         *
-        * @param string $basepath the Basepath of the Application
-        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
+        * @param string $basePath the Basepath of the Application
+        *
+        * @return Mode returns itself
+        *
+        * @throws \Exception
         */
-       public function determine($basepath = null)
+       public function determine($basePath = null)
        {
-               if (!empty($basepath)) {
-                       $this->basepath = $basepath;
+               if (!empty($basePath)) {
+                       $this->basepath = $basePath;
                }
 
                $this->mode = 0;
 
                if (!file_exists($this->basepath . '/config/local.config.php')
-                       && !file_exists($this->basepath . '/config/local.ini.php')
-                       && !file_exists($this->basepath . '/.htconfig.php')) {
-                       return;
+                   && !file_exists($this->basepath . '/config/local.ini.php')
+                   && !file_exists($this->basepath . '/.htconfig.php')) {
+                       return $this;
                }
 
                $this->mode |= Mode::LOCALCONFIGPRESENT;
 
-               if (!DBA::connected()) {
-                       return;
+               if (!$this->database->connected()) {
+                       return $this;
                }
 
                $this->mode |= Mode::DBAVAILABLE;
 
-               if (DBA::fetchFirst("SHOW TABLES LIKE 'config'") === false) {
-                       return;
+               if ($this->database->fetchFirst("SHOW TABLES LIKE 'config'") === false) {
+                       return $this;
                }
 
                $this->mode |= Mode::DBCONFIGAVAILABLE;
 
-               if (Config::get('system', 'maintenance')) {
-                       return;
+               if (!empty($this->configCache->get('system', 'maintenance')) ||
+                   // Don't use Config or Configuration here because we're possibly BEFORE initializing the Configuration,
+                   // so this could lead to a dependency circle
+                   !empty($this->database->selectFirst('config', ['v'], ['cat' => 'system', 'k' => 'maintenance'])['v'])) {
+                       return $this;
                }
 
                $this->mode |= Mode::MAINTENANCEDISABLED;
+
+               return $this;
        }
 
        /**
@@ -100,7 +121,7 @@ class Mode
        public function isInstall()
        {
                return !$this->has(Mode::LOCALCONFIGPRESENT) ||
-                       !$this->has(MODE::DBCONFIGAVAILABLE);
+                      !$this->has(MODE::DBCONFIGAVAILABLE);
        }
 
        /**
@@ -111,8 +132,8 @@ class Mode
        public function isNormal()
        {
                return $this->has(Mode::LOCALCONFIGPRESENT) &&
-                       $this->has(Mode::DBAVAILABLE) &&
-                       $this->has(Mode::DBCONFIGAVAILABLE) &&
-                       $this->has(Mode::MAINTENANCEDISABLED);
+                      $this->has(Mode::DBAVAILABLE) &&
+                      $this->has(Mode::DBCONFIGAVAILABLE) &&
+                      $this->has(Mode::MAINTENANCEDISABLED);
        }
 }
index 6720f5443710dedbc142e881f3e5ed14fa74e3bf..a54f3a711ee6cf87581b6fee082f4342cf7f2854 100644 (file)
@@ -202,6 +202,7 @@ class Router
                        $collector->addGroup('/2fa', function (RouteCollector $collector) {
                                $collector->addRoute(['GET', 'POST'], '[/]'                    , Module\Settings\TwoFactor\Index::class);
                                $collector->addRoute(['GET', 'POST'], '/recovery'              , Module\Settings\TwoFactor\Recovery::class);
+                               $collector->addRoute(['GET', 'POST'], '/app_specific'          , Module\Settings\TwoFactor\AppSpecific::class);
                                $collector->addRoute(['GET', 'POST'], '/verify'                , Module\Settings\TwoFactor\Verify::class);
                        });
                });
@@ -220,13 +221,9 @@ class Router
                $this->routeCollector->addRoute(['GET'],         '/xrd',                 Module\Xrd::class);
        }
 
-       public function __construct(RouteCollector $routeCollector = null)
+       public function __construct()
        {
-               if (!$routeCollector) {
-                       $routeCollector = new RouteCollector(new Std(), new GroupCountBased());
-               }
-
-               $this->routeCollector = $routeCollector;
+               $this->routeCollector = new RouteCollector(new Std(), new GroupCountBased());
        }
 
        public function getRouteCollector()
index 401d9c292c87cc758712c7e43a91932b9deb6154..d594b2605e19375d25c8a1b577fcdfaff03782bc 100644 (file)
@@ -79,7 +79,8 @@ HELP;
                $installer = new Installer();
 
                $configCache = $a->getConfigCache();
-               $installer->setUpCache($configCache, BasePath::create($a->getBasePath(), $_SERVER));
+               $basepath = new BasePath($a->getBasePath());
+               $installer->setUpCache($configCache, $basepath->getPath());
 
                $this->out(" Complete!\n\n");
 
index da09e13dd9b8dfc5a243a9480d4b3de5b9207186..e2a4fa603cf42dc959fe95fcfb5efbc5d186cc87 100644 (file)
@@ -905,7 +905,17 @@ class BBCode extends BaseObject
                                // We only call this so that a previously unknown contact can be added.
                                // This is important for the function "Model\Contact::getDetailsByURL()".
                                // This function then can fetch an entry from the contact table.
-                               Contact::getIdForURL($attributes['profile'], 0, true);
+                               $default['url'] = $attributes['profile'];
+
+                               if (!empty($attributes['author'])) {
+                                       $default['name'] = $attributes['author'];
+                               }
+
+                               if (!empty($attributes['avatar'])) {
+                                       $default['photo'] = $attributes['avatar'];
+                               }
+
+                               Contact::getIdForURL($attributes['profile'], 0, true, $default);
 
                                $author_contact = Contact::getDetailsByURL($attributes['profile']);
                                $author_contact['addr'] = defaults($author_contact, 'addr' , Protocol::getAddrFromProfileUrl($attributes['profile']));
index de041773ef6b88b7525b7a35cad64ce043e9e798..8163d546adeb62ed1934617bf9eeaadc2b0a9128 100644 (file)
@@ -7,8 +7,8 @@ namespace Friendica\Core;
 use DOMDocument;
 use Exception;
 use Friendica\Core\Config\Cache\ConfigCache;
+use Friendica\Database\Database;
 use Friendica\Database\DBStructure;
-use Friendica\Factory\DBFactory;
 use Friendica\Object\Image;
 use Friendica\Util\Logger\VoidLogger;
 use Friendica\Util\Network;
@@ -600,7 +600,7 @@ class Installer
         */
        public function checkDB(ConfigCache $configCache, Profiler $profiler)
        {
-               $database = DBFactory::init($configCache, $profiler, [], new VoidLogger());
+               $database = new Database($configCache, $profiler, new VoidLogger());
 
                if ($database->connected()) {
                        if (DBStructure::existsTable('user')) {
index a7bbed9af738ceec5fe77392e1b4df59cb8c68a0..355f4c6ed4666c9dfdb51c0cd14e0e681fbcbaf9 100644 (file)
@@ -2,6 +2,7 @@
 
 namespace Friendica\Core\L10n;
 
+use Friendica\Core\Config\Configuration;
 use Friendica\Core\Hook;
 use Friendica\Core\Session;
 use Friendica\Database\Database;
@@ -52,12 +53,12 @@ class L10n
         */
        private $logger;
 
-       public function __construct(string $lang, Database $dba, LoggerInterface $logger)
+       public function __construct(Configuration $config, Database $dba, LoggerInterface $logger)
        {
                $this->dba    = $dba;
                $this->logger = $logger;
 
-               $this->loadTranslationTable($lang);
+               $this->loadTranslationTable(L10n::detectLanguage($config->get('system', 'language', 'en')));
 
                \Friendica\Core\L10n::init($this);
        }
index 0d1540f7cd94fca392d76c194e65ead662c84723..c503416912287fd22bfc5516c28c35e9c85fedbb 100644 (file)
@@ -9,7 +9,6 @@ use Friendica\Util\Profiler;
 use mysqli;
 use mysqli_result;
 use mysqli_stmt;
-use ParagonIE\HiddenString\HiddenString;
 use PDO;
 use PDOException;
 use PDOStatement;
@@ -46,30 +45,47 @@ class Database
        private $in_transaction = false;
        private $in_retrial     = false;
        private $relation       = [];
-       private $db_serveraddr;
-       private $db_user;
-       /**
-        * @var HiddenString
-        */
-       private $db_pass;
-       private $db_name;
-       private $db_charset;
 
-       public function __construct(ConfigCache $configCache, Profiler $profiler, LoggerInterface $logger, $serveraddr, $user, HiddenString $pass, $db, $charset = null)
+       public function __construct(ConfigCache $configCache, Profiler $profiler, LoggerInterface $logger, array $server = [])
        {
                // We are storing these values for being able to perform a reconnect
                $this->configCache   = $configCache;
                $this->profiler      = $profiler;
                $this->logger        = $logger;
-               $this->db_serveraddr = $serveraddr;
-               $this->db_user       = $user;
-               $this->db_pass       = $pass;
-               $this->db_name       = $db;
-               $this->db_charset    = $charset;
 
+               $this->readServerVariables($server);
                $this->connect();
 
                DBA::init($this);
+
+               if ($this->isConnected()) {
+                       // Loads DB_UPDATE_VERSION constant
+                       DBStructure::definition($configCache->get('system', 'basepath'), false);
+               }
+       }
+
+       private function readServerVariables(array $server)
+       {
+               // Use environment variables for mysql if they are set beforehand
+               if (!empty($server['MYSQL_HOST'])
+                   && !empty($server['MYSQL_USERNAME'] || !empty($server['MYSQL_USER']))
+                   && $server['MYSQL_PASSWORD'] !== false
+                   && !empty($server['MYSQL_DATABASE']))
+               {
+                       $db_host = $server['MYSQL_HOST'];
+                       if (!empty($server['MYSQL_PORT'])) {
+                               $db_host .= ':' . $server['MYSQL_PORT'];
+                       }
+                       $this->configCache->set('database', 'hostname', $db_host);
+                       unset($db_host);
+                       if (!empty($server['MYSQL_USERNAME'])) {
+                               $this->configCache->set('database', 'username', $server['MYSQL_USERNAME']);
+                       } else {
+                               $this->configCache->set('database', 'username', $server['MYSQL_USER']);
+                       }
+                       $this->configCache->set('database', 'password', (string) $server['MYSQL_PASSWORD']);
+                       $this->configCache->set('database', 'database', $server['MYSQL_DATABASE']);
+               }
        }
 
        public function connect()
@@ -79,20 +95,17 @@ class Database
                }
 
                $port       = 0;
-               $serveraddr = trim($this->db_serveraddr);
-
+               $serveraddr = trim($this->configCache->get('database', 'hostname'));
                $serverdata = explode(':', $serveraddr);
                $server     = $serverdata[0];
-
                if (count($serverdata) > 1) {
                        $port = trim($serverdata[1]);
                }
-
                $server  = trim($server);
-               $user    = trim($this->db_user);
-               $pass    = trim($this->db_pass);
-               $db      = trim($this->db_name);
-               $charset = trim($this->db_charset);
+               $user    = trim($this->configCache->get('database', 'username'));
+               $pass    = trim($this->configCache->get('database', 'password'));
+               $db      = trim($this->configCache->get('database', 'database'));
+               $charset = trim($this->configCache->get('database', 'charset'));
 
                if (!(strlen($server) && strlen($user))) {
                        return false;
index 1aba8fc824ae6c7b51ed2b1b15d471814766043f..08fcddacead6d67e386c0ee80e418b61437fb1db 100644 (file)
@@ -5,9 +5,9 @@ namespace Friendica\Factory;
 use Friendica\Core;
 use Friendica\Core\Config;
 use Friendica\Core\Config\Cache;
-use Friendica\Util\ConfigFileLoader;
 use Friendica\Model\Config\Config as ConfigModel;
 use Friendica\Model\Config\PConfig as PConfigModel;
+use Friendica\Util\ConfigFileLoader;
 
 class ConfigFactory
 {
@@ -16,7 +16,7 @@ class ConfigFactory
         *
         * @return Cache\ConfigCache
         */
-       public static function createCache(ConfigFileLoader $loader)
+       public function createCache(ConfigFileLoader $loader)
        {
                $configCache = new Cache\ConfigCache();
                $loader->setupCache($configCache);
@@ -26,11 +26,11 @@ class ConfigFactory
 
        /**
         * @param Cache\ConfigCache $configCache The config cache of this adapter
-        * @param ConfigModel $configModel The configuration model
+        * @param ConfigModel       $configModel The configuration model
         *
         * @return Config\Configuration
         */
-       public static function createConfig(Cache\ConfigCache $configCache, ConfigModel $configModel)
+       public function createConfig(Cache\ConfigCache $configCache, ConfigModel $configModel)
        {
                if ($configCache->get('system', 'config_adapter') === 'preload') {
                        $configuration = new Config\PreloadConfiguration($configCache, $configModel);
@@ -46,13 +46,13 @@ class ConfigFactory
        }
 
        /**
-        * @param Cache\ConfigCache $configCache The config cache
-        * @param Cache\PConfigCache  $pConfigCache The personal config cache
-        * @param PConfigModel $configModel The configuration model
+        * @param Cache\ConfigCache  $configCache  The config cache
+        * @param Cache\PConfigCache $pConfigCache The personal config cache
+        * @param PConfigModel       $configModel  The configuration model
         *
         * @return Config\PConfiguration
         */
-       public static function createPConfig(Cache\ConfigCache $configCache, Cache\PConfigCache $pConfigCache, PConfigModel $configModel)
+       public function createPConfig(Cache\ConfigCache $configCache, Cache\PConfigCache $pConfigCache, PConfigModel $configModel)
        {
                if ($configCache->get('system', 'config_adapter') === 'preload') {
                        $configuration = new Config\PreloadPConfiguration($pConfigCache, $configModel);
diff --git a/src/Factory/DBFactory.php b/src/Factory/DBFactory.php
deleted file mode 100644 (file)
index f24961c..0000000
+++ /dev/null
@@ -1,61 +0,0 @@
-<?php
-
-namespace Friendica\Factory;
-
-use Friendica\Core\Config\Cache;
-use Friendica\Database;
-use Friendica\Util\Logger\VoidLogger;
-use Friendica\Util\Profiler;
-use ParagonIE\HiddenString\HiddenString;
-
-class DBFactory
-{
-       /**
-        * Initialize the DBA connection
-        *
-        * @param Cache\ConfigCache $configCache The configuration cache
-        * @param Profiler          $profiler    The profiler
-        * @param array             $server      The $_SERVER variables
-        *
-        * @return Database\Database
-        * @throws \Exception if connection went bad
-        */
-       public static function init(Cache\ConfigCache $configCache, Profiler $profiler, array $server)
-       {
-               $db_host = $configCache->get('database', 'hostname');
-               $db_user = $configCache->get('database', 'username');
-               $db_pass = $configCache->get('database', 'password');
-               $db_data = $configCache->get('database', 'database');
-               $charset = $configCache->get('database', 'charset');
-
-               // Use environment variables for mysql if they are set beforehand
-               if (!empty($server['MYSQL_HOST'])
-                       && !empty($server['MYSQL_USERNAME'] || !empty($server['MYSQL_USER']))
-                       && $server['MYSQL_PASSWORD'] !== false
-                       && !empty($server['MYSQL_DATABASE']))
-               {
-                       $db_host = $server['MYSQL_HOST'];
-                       if (!empty($server['MYSQL_PORT'])) {
-                               $db_host .= ':' . $server['MYSQL_PORT'];
-                       }
-                       if (!empty($server['MYSQL_USERNAME'])) {
-                               $db_user = $server['MYSQL_USERNAME'];
-                       } else {
-                               $db_user = $server['MYSQL_USER'];
-                       }
-                       $db_pass = new HiddenString((string) $server['MYSQL_PASSWORD']);
-                       $db_data = $server['MYSQL_DATABASE'];
-               }
-
-               $database = new Database\Database($configCache, $profiler, new VoidLogger(), $db_host, $db_user, $db_pass, $db_data, $charset);
-
-               if ($database->connected()) {
-                       // Loads DB_UPDATE_VERSION constant
-                       Database\DBStructure::definition($configCache->get('system', 'basepath'), false);
-               }
-
-               unset($db_host, $db_user, $db_pass, $db_data, $charset);
-
-               return $database;
-       }
-}
index 9d1af15a0fcba64eaa031a3c78ea0e125debbc93..8ead2f61563bf81ba3115edd617890e7213ef001 100644 (file)
@@ -2,13 +2,10 @@
 
 namespace Friendica\Factory;
 
+use Dice\Dice;
 use Friendica\App;
-use Friendica\Core\Config\Cache\PConfigCache;
-use Friendica\Core\L10n\L10n;
-use Friendica\Factory;
-use Friendica\Util\BasePath;
-use Friendica\Util\BaseURL;
-use Friendica\Util\ConfigFileLoader;
+use Friendica\Core\Config\PConfiguration;
+use Psr\Log\LoggerInterface;
 
 class DependencyFactory
 {
@@ -16,34 +13,18 @@ class DependencyFactory
         * Setting all default-dependencies of a friendica execution
         *
         * @param string $channel   The channel of this execution
-        * @param string $directory The base directory
         * @param bool   $isBackend True, if it's a backend execution, otherwise false (Default true)
         *
         * @return App The application
         *
         * @throws \Exception
         */
-       public static function setUp($channel, $directory, $isBackend = true)
+       public static function setUp($channel, Dice $dice, $isBackend = true)
        {
-               $basePath = BasePath::create($directory, $_SERVER);
-               $mode = new App\Mode($basePath);
-               $router = new App\Router();
-               $configLoader = new ConfigFileLoader($basePath, $mode);
-               $configCache = Factory\ConfigFactory::createCache($configLoader);
-               $profiler = Factory\ProfilerFactory::create($configCache);
-               $database = Factory\DBFactory::init($configCache, $profiler, $_SERVER);
-               $configModel = new \Friendica\Model\Config\Config($database);
-               $config = Factory\ConfigFactory::createConfig($configCache, $configModel);
-               // needed to call PConfig::init()
-               $pconfigModel = new \Friendica\Model\Config\PConfig($database);
-               Factory\ConfigFactory::createPConfig($configCache, new PConfigCache(), $pconfigModel);
-               $logger = Factory\LoggerFactory::create($channel, $database, $config, $profiler);
-               Factory\LoggerFactory::createDev($channel, $config, $profiler);
-               $baseURL = new BaseURL($config, $_SERVER);
-               $l10n = new L10n(L10n::detectLanguage($config->get('system', 'language')),
-                       $database,
-                       $logger);
+               $pConfig = $dice->create(PConfiguration::class);
+               $logger = $dice->create(LoggerInterface::class, [$channel]);
+               $devLogger = $dice->create('$devLogger', [$channel]);
 
-               return new App($database, $config, $mode, $router, $baseURL, $logger, $profiler, $l10n, $isBackend);
+               return $dice->create(App::class, [$isBackend]);
        }
 }
index 67829546e91f1a9a1f5b4bf86f99c9ad602a203f..2a054607cade11bc25dc486c5893641bd7cb851d 100644 (file)
@@ -48,7 +48,7 @@ class LoggerFactory
         * @throws \Exception
         * @throws InternalServerErrorException
         */
-       public static function create($channel, Database $database, Configuration $config, Profiler $profiler)
+       public function create($channel, Database $database, Configuration $config, Profiler $profiler)
        {
                if (empty($config->get('system', 'debugging', false))) {
                        $logger = new VoidLogger();
diff --git a/src/Factory/ProfilerFactory.php b/src/Factory/ProfilerFactory.php
deleted file mode 100644 (file)
index 522e8fe..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-<?php
-
-namespace Friendica\Factory;
-
-use Friendica\Core\Config\Cache\ConfigCache;
-use Friendica\Util\Profiler;
-
-class ProfilerFactory
-{
-       /**
-        * Creates a Profiler for the current execution
-        *
-        * @param ConfigCache     $configCache The configuration cache
-        *
-        * @return Profiler
-        */
-       public static function create(ConfigCache $configCache)
-       {
-               $enabled = $configCache->get('system', 'profiler');
-               $enabled = isset($enabled) && $enabled !== '0';
-               $renderTime = $configCache->get('rendertime', 'callstack');
-               $renderTime = isset($renderTime) && $renderTime !== '0';
-
-               return new Profiler($enabled, $renderTime);
-       }
-}
index 559236001eee0da25d74a62875b4c38fff0c1128..398acc2d7d06e65ce4cda577d4c74c035204ade9 100644 (file)
@@ -1419,7 +1419,7 @@ class Contact extends BaseObject
                        // When we don't want to update, we look if we know this contact in any way
                        $data = self::getProbeDataFromDatabase($url, $contact_id);
                        $background_update = true;
-               } elseif ($no_update && !empty($default)) {
+               } elseif ($no_update && !empty($default['network'])) {
                        // If there are default values, take these
                        $data = $default;
                        $background_update = false;
index ddfc5e0b35566a9fa4119dee016d473d46776923..81c60b30b038387f2711a32bc9551c70bcb61ce1 100644 (file)
@@ -20,6 +20,7 @@ use Friendica\Core\Renderer;
 use Friendica\Core\System;
 use Friendica\Core\Worker;
 use Friendica\Database\DBA;
+use Friendica\Protocol\ActivityPub;
 use Friendica\Protocol\Diaspora;
 use Friendica\Protocol\OStatus;
 use Friendica\Util\DateTimeFormat;
@@ -29,7 +30,6 @@ use Friendica\Util\Security;
 use Friendica\Util\Strings;
 use Friendica\Util\XML;
 use Friendica\Worker\Delivery;
-use Friendica\Protocol\ActivityPub;
 use Text_LanguageDetect;
 
 class Item extends BaseObject
@@ -3628,11 +3628,12 @@ class Item extends BaseObject
                        return $item_id;
                }
 
-               ActivityPub\Processor::fetchMissingActivity($uri);
-
-               /// @todo add Diaspora as well
+               if (ActivityPub\Processor::fetchMissingActivity($uri)) {
+                       $item_id = self::searchByLink($uri, $uid);
+               } else {
+                       $item_id = Diaspora::fetchByURL($uri);
+               }
 
-               $item_id = self::searchByLink($uri, $uid);
                if (!empty($item_id)) {
                        return $item_id;
                }
diff --git a/src/Model/TwoFactor/AppSpecificPassword.php b/src/Model/TwoFactor/AppSpecificPassword.php
new file mode 100644 (file)
index 0000000..41b1e3b
--- /dev/null
@@ -0,0 +1,137 @@
+<?php
+
+namespace Friendica\Model\TwoFactor;
+
+use Friendica\BaseObject;
+use Friendica\Database\DBA;
+use Friendica\Model\User;
+use Friendica\Util\DateTimeFormat;
+use Friendica\Util\Temporal;
+use PragmaRX\Random\Random;
+
+/**
+ * Manages users' two-factor recovery hashed_passwords in the 2fa_app_specific_passwords table
+ *
+ * @package Friendica\Model
+ */
+class AppSpecificPassword extends BaseObject
+{
+       public static function countForUser($uid)
+       {
+               return DBA::count('2fa_app_specific_password', ['uid' => $uid]);
+       }
+
+       public static function checkDuplicateForUser($uid, $description)
+       {
+               return DBA::exists('2fa_app_specific_password', ['uid' => $uid, 'description' => $description]);
+       }
+
+       /**
+        * Checks the provided hashed_password is available to use for login by the provided user
+        *
+        * @param int    $uid User ID
+        * @param string $plaintextPassword
+        * @return bool
+        * @throws \Exception
+        */
+       public static function authenticateUser($uid, $plaintextPassword)
+       {
+               $appSpecificPasswords = self::getListForUser($uid);
+
+               $return = false;
+
+               foreach ($appSpecificPasswords as $appSpecificPassword) {
+                       if (password_verify($plaintextPassword, $appSpecificPassword['hashed_password'])) {
+                               $fields = ['last_used' => DateTimeFormat::utcNow()];
+                               if (password_needs_rehash($appSpecificPassword['hashed_password'], PASSWORD_DEFAULT)) {
+                                       $fields['hashed_password'] = User::hashPassword($plaintextPassword);
+                               }
+
+                               self::update($appSpecificPassword['id'], $fields);
+
+                               $return |= true;
+                       }
+               }
+
+               return $return;
+       }
+
+    /**
+     * Returns a complete list of all recovery hashed_passwords for the provided user, including the used status
+     *
+     * @param  int $uid User ID
+     * @return array
+     * @throws \Exception
+     */
+       public static function getListForUser($uid)
+       {
+               $appSpecificPasswordsStmt = DBA::select('2fa_app_specific_password', ['id', 'description', 'hashed_password', 'last_used'], ['uid' => $uid]);
+
+               $appSpecificPasswords = DBA::toArray($appSpecificPasswordsStmt);
+
+               array_walk($appSpecificPasswords, function (&$value) {
+                       $value['ago'] = Temporal::getRelativeDate($value['last_used']);
+               });
+
+               return $appSpecificPasswords;
+       }
+
+    /**
+     * Generates a new app specific password for the provided user and hashes it in the database.
+     *
+     * @param  int    $uid         User ID
+     * @param  string $description Password description
+     * @return array The new app-specific password data structure with the plaintext password added
+     * @throws \Exception
+     */
+       public static function generateForUser(int $uid, $description)
+       {
+               $Random = (new Random())->size(40);
+
+               $plaintextPassword = $Random->get();
+
+               $generated = DateTimeFormat::utcNow();
+
+               $fields = [
+                       'uid' => $uid,
+                       'description' => $description,
+                       'hashed_password' => User::hashPassword($plaintextPassword),
+                       'generated' => $generated,
+               ];
+
+               DBA::insert('2fa_app_specific_password', $fields);
+
+               $fields['id'] = DBA::lastInsertId();
+               $fields['plaintext_password'] = $plaintextPassword;
+
+               return $fields;
+       }
+
+       private static function update($appSpecificPasswordId, $fields)
+       {
+               return DBA::update('2fa_app_specific_password', $fields, ['id' => $appSpecificPasswordId]);
+       }
+
+       /**
+        * Deletes all the recovery hashed_passwords for the provided user.
+        *
+        * @param int $uid User ID
+        * @return bool
+        * @throws \Exception
+        */
+       public static function deleteAllForUser(int $uid)
+       {
+               return DBA::delete('2fa_app_specific_password', ['uid' => $uid]);
+       }
+
+       /**
+        * @param int $uid
+        * @param int $app_specific_password_id
+        * @return bool
+        * @throws \Exception
+        */
+       public static function deleteForUser(int $uid, int $app_specific_password_id)
+       {
+               return DBA::delete('2fa_app_specific_password', ['id' => $app_specific_password_id, 'uid' => $uid]);
+       }
+}
diff --git a/src/Model/TwoFactor/RecoveryCode.php b/src/Model/TwoFactor/RecoveryCode.php
new file mode 100644 (file)
index 0000000..676b538
--- /dev/null
@@ -0,0 +1,125 @@
+<?php
+
+namespace Friendica\Model\TwoFactor;
+
+use Friendica\BaseObject;
+use Friendica\Database\DBA;
+use Friendica\Util\DateTimeFormat;
+use PragmaRX\Random\Random;
+use PragmaRX\Recovery\Recovery;
+
+/**
+ * Manages users' two-factor recovery codes in the 2fa_recovery_codes table
+ *
+ * @package Friendica\Model
+ */
+class RecoveryCode extends BaseObject
+{
+    /**
+     * Returns the number of code the provided users can still use to replace a TOTP code
+     *
+     * @param int $uid User ID
+     * @return int
+     * @throws \Exception
+     */
+    public static function countValidForUser($uid)
+       {
+               return DBA::count('2fa_recovery_codes', ['uid' => $uid, 'used' => null]);
+       }
+
+    /**
+     * Checks the provided code is available to use for login by the provided user
+     *
+     * @param  int $uid User ID
+     * @param string $code
+     * @return bool
+     * @throws \Exception
+     */
+       public static function existsForUser($uid, $code)
+       {
+               return DBA::exists('2fa_recovery_codes', ['uid' => $uid, 'code' => $code, 'used' => null]);
+       }
+
+    /**
+     * Returns a complete list of all recovery codes for the provided user, including the used status
+     *
+     * @param  int $uid User ID
+     * @return array
+     * @throws \Exception
+     */
+       public static function getListForUser($uid)
+       {
+               $codesStmt = DBA::select('2fa_recovery_codes', ['code', 'used'], ['uid' => $uid]);
+
+               return DBA::toArray($codesStmt);
+       }
+
+    /**
+     * Marks the provided code as used for the provided user.
+     * Returns false if the code doesn't exist for the user or it has been used already.
+     *
+     * @param  int $uid User ID
+     * @param string $code
+     * @return bool
+     * @throws \Exception
+     */
+       public static function markUsedForUser($uid, $code)
+       {
+               DBA::update('2fa_recovery_codes', ['used' => DateTimeFormat::utcNow()], ['uid' => $uid, 'code' => $code, 'used' => null]);
+
+               return DBA::affectedRows() > 0;
+       }
+
+    /**
+     * Generates a fresh set of recovery codes for the provided user.
+     * Generates 12 codes constituted of 2 blocks of 6 characters separated by a dash.
+     *
+     * @param  int $uid User ID
+     * @throws \Exception
+     */
+       public static function generateForUser($uid)
+       {
+               $Random = (new Random())->pattern('[a-z0-9]');
+
+               $RecoveryGenerator = new Recovery($Random);
+
+               $codes = $RecoveryGenerator
+                       ->setCount(12)
+                       ->setBlocks(2)
+                       ->setChars(6)
+                       ->lowercase(true)
+                       ->toArray();
+
+               $generated = DateTimeFormat::utcNow();
+               foreach ($codes as $code) {
+                       DBA::insert('2fa_recovery_codes', [
+                               'uid' => $uid,
+                               'code' => $code,
+                               'generated' => $generated
+                       ]);
+               }
+       }
+
+    /**
+     * Deletes all the recovery codes for the provided user.
+     *
+     * @param  int $uid User ID
+     * @throws \Exception
+     */
+       public static function deleteForUser($uid)
+       {
+               DBA::delete('2fa_recovery_codes', ['uid' => $uid]);
+       }
+
+    /**
+     * Replaces the existing recovery codes for the provided user by a freshly generated set.
+     *
+     * @param  int $uid User ID
+     * @throws \Exception
+     */
+       public static function regenerateForUser($uid)
+       {
+               self::deleteForUser($uid);
+               self::generateForUser($uid);
+       }
+}
diff --git a/src/Model/TwoFactorRecoveryCode.php b/src/Model/TwoFactorRecoveryCode.php
deleted file mode 100644 (file)
index 74dead3..0000000
+++ /dev/null
@@ -1,125 +0,0 @@
-<?php
-
-namespace Friendica\Model;
-
-use Friendica\BaseObject;
-use Friendica\Database\DBA;
-use Friendica\Util\DateTimeFormat;
-use PragmaRX\Random\Random;
-use PragmaRX\Recovery\Recovery;
-
-/**
- * Manages users' two-factor recovery codes in the 2fa_recovery_codes table
- *
- * @package Friendica\Model
- */
-class TwoFactorRecoveryCode extends BaseObject
-{
-    /**
-     * Returns the number of code the provided users can still use to replace a TOTP code
-     *
-     * @param int $uid User ID
-     * @return int
-     * @throws \Exception
-     */
-    public static function countValidForUser($uid)
-       {
-               return DBA::count('2fa_recovery_codes', ['uid' => $uid, 'used' => null]);
-       }
-
-    /**
-     * Checks the provided code is available to use for login by the provided user
-     *
-     * @param  int $uid User ID
-     * @param string $code
-     * @return bool
-     * @throws \Exception
-     */
-       public static function existsForUser($uid, $code)
-       {
-               return DBA::exists('2fa_recovery_codes', ['uid' => $uid, 'code' => $code, 'used' => null]);
-       }
-
-    /**
-     * Returns a complete list of all recovery codes for the provided user, including the used status
-     *
-     * @param  int $uid User ID
-     * @return array
-     * @throws \Exception
-     */
-       public static function getListForUser($uid)
-       {
-               $codesStmt = DBA::select('2fa_recovery_codes', ['code', 'used'], ['uid' => $uid]);
-
-               return DBA::toArray($codesStmt);
-       }
-
-    /**
-     * Marks the provided code as used for the provided user.
-     * Returns false if the code doesn't exist for the user or it has been used already.
-     *
-     * @param  int $uid User ID
-     * @param string $code
-     * @return bool
-     * @throws \Exception
-     */
-       public static function markUsedForUser($uid, $code)
-       {
-               DBA::update('2fa_recovery_codes', ['used' => DateTimeFormat::utcNow()], ['uid' => $uid, 'code' => $code, 'used' => null]);
-
-               return DBA::affectedRows() > 0;
-       }
-
-    /**
-     * Generates a fresh set of recovery codes for the provided user.
-     * Generates 12 codes constituted of 2 blocks of 6 characters separated by a dash.
-     *
-     * @param  int $uid User ID
-     * @throws \Exception
-     */
-       public static function generateForUser($uid)
-       {
-               $Random = (new Random())->pattern('[a-z0-9]');
-
-               $RecoveryGenerator = new Recovery($Random);
-
-               $codes = $RecoveryGenerator
-                       ->setCount(12)
-                       ->setBlocks(2)
-                       ->setChars(6)
-                       ->lowercase(true)
-                       ->toArray();
-
-               $generated = DateTimeFormat::utcNow();
-               foreach ($codes as $code) {
-                       DBA::insert('2fa_recovery_codes', [
-                               'uid' => $uid,
-                               'code' => $code,
-                               'generated' => $generated
-                       ]);
-               }
-       }
-
-    /**
-     * Deletes all the recovery codes for the provided user.
-     *
-     * @param  int $uid User ID
-     * @throws \Exception
-     */
-       public static function deleteForUser($uid)
-       {
-               DBA::delete('2fa_recovery_codes', ['uid' => $uid]);
-       }
-
-    /**
-     * Replaces the existing recovery codes for the provided user by a freshly generated set.
-     *
-     * @param  int $uid User ID
-     * @throws \Exception
-     */
-       public static function regenerateForUser($uid)
-       {
-               self::deleteForUser($uid);
-               self::generateForUser($uid);
-       }
-}
index de6931052f50bb3c46a302fd654a921ff50be399..141ecf059802d88d7956801262b593e6ec8c925d 100644 (file)
@@ -17,6 +17,7 @@ use Friendica\Core\System;
 use Friendica\Core\Worker;
 use Friendica\Database\DBA;
 use Friendica\Model\Photo;
+use Friendica\Model\TwoFactor\AppSpecificPassword;
 use Friendica\Object\Image;
 use Friendica\Util\Crypto;
 use Friendica\Util\DateTimeFormat;
@@ -267,17 +268,18 @@ class User
        /**
         * Authenticate a user with a clear text password
         *
-        * @brief Authenticate a user with a clear text password
-        * @param mixed $user_info
+        * @brief      Authenticate a user with a clear text password
+        * @param mixed  $user_info
         * @param string $password
+        * @param bool   $third_party
         * @return int|boolean
         * @deprecated since version 3.6
-        * @see User::getIdFromPasswordAuthentication()
+        * @see        User::getIdFromPasswordAuthentication()
         */
-       public static function authenticate($user_info, $password)
+       public static function authenticate($user_info, $password, $third_party = false)
        {
                try {
-                       return self::getIdFromPasswordAuthentication($user_info, $password);
+                       return self::getIdFromPasswordAuthentication($user_info, $password, $third_party);
                } catch (Exception $ex) {
                        return false;
                }
@@ -287,16 +289,22 @@ class User
         * Returns the user id associated with a successful password authentication
         *
         * @brief Authenticate a user with a clear text password
-        * @param mixed $user_info
+        * @param mixed  $user_info
         * @param string $password
+        * @param bool   $third_party
         * @return int User Id if authentication is successful
         * @throws Exception
         */
-       public static function getIdFromPasswordAuthentication($user_info, $password)
+       public static function getIdFromPasswordAuthentication($user_info, $password, $third_party = false)
        {
                $user = self::getAuthenticationInfo($user_info);
 
-               if (strpos($user['password'], '$') === false) {
+               if ($third_party && PConfig::get($user['uid'], '2fa', 'verified')) {
+                       // Third-party apps can't verify two-factor authentication, we use app-specific passwords instead
+                       if (AppSpecificPassword::authenticateUser($user['uid'], $password)) {
+                               return $user['uid'];
+                       }
+               } elseif (strpos($user['password'], '$') === false) {
                        //Legacy hash that has not been replaced by a new hash yet
                        if (self::hashPasswordLegacy($password) === $user['password']) {
                                self::updatePasswordHashed($user['uid'], self::hashPassword($password));
index 54c6141cc725fcda67362dabab8284aa674ee5ba..a7427ead0dbb226de0b1d0c851a5684ecfcde127 100644 (file)
@@ -67,7 +67,8 @@ class Install extends BaseModule
 
                // get basic installation information and save them to the config cache
                $configCache = $a->getConfigCache();
-               self::$installer->setUpCache($configCache, BasePath::create($a->getBasePath(), $_SERVER));
+               $basePath = new BasePath($a->getBasePath());
+               self::$installer->setUpCache($configCache, $basePath->getPath());
 
                // We overwrite current theme css, because during install we may not have a working mod_rewrite
                // so we may not have a css at all. Here we set a static css file for the install procedure pages
diff --git a/src/Module/Settings/TwoFactor/AppSpecific.php b/src/Module/Settings/TwoFactor/AppSpecific.php
new file mode 100644 (file)
index 0000000..c62b0bb
--- /dev/null
@@ -0,0 +1,116 @@
+<?php
+
+
+namespace Friendica\Module\Settings\TwoFactor;
+
+
+use Friendica\Core\L10n;
+use Friendica\Core\PConfig;
+use Friendica\Core\Renderer;
+use Friendica\Model\TwoFactor\AppSpecificPassword;
+use Friendica\Module\BaseSettingsModule;
+use Friendica\Module\Login;
+
+/**
+ * // Page 5: 2FA enabled, app-specific password generation
+ *
+ * @package Friendica\Module\TwoFactor
+ */
+class AppSpecific extends BaseSettingsModule
+{
+       private static $appSpecificPassword = null;
+
+       public static function init()
+       {
+               if (!local_user()) {
+                       return;
+               }
+
+               $verified = PConfig::get(local_user(), '2fa', 'verified');
+
+               if (!$verified) {
+                       self::getApp()->internalRedirect('settings/2fa');
+               }
+
+               if (!self::checkFormSecurityToken('settings_2fa_password', 't')) {
+                       notice(L10n::t('Please enter your password to access this page.'));
+                       self::getApp()->internalRedirect('settings/2fa');
+               }
+       }
+
+       public static function post()
+       {
+               if (!local_user()) {
+                       return;
+               }
+
+               if (!empty($_POST['action'])) {
+                       self::checkFormSecurityTokenRedirectOnError('settings/2fa/app_specific', 'settings_2fa_app_specific');
+
+                       switch ($_POST['action']) {
+                               case 'generate':
+                                       $description = $_POST['description'] ?? '';
+                                       if (empty($description)) {
+                                               notice(L10n::t('App-specific password generation failed: The description is empty.'));
+                                               self::getApp()->internalRedirect('settings/2fa/app_specific?t=' . self::getFormSecurityToken('settings_2fa_password'));
+                                       } elseif (AppSpecificPassword::checkDuplicateForUser(local_user(), $description)) {
+                                               notice(L10n::t('App-specific password generation failed: This description already exists.'));
+                                               self::getApp()->internalRedirect('settings/2fa/app_specific?t=' . self::getFormSecurityToken('settings_2fa_password'));
+                                       } else {
+                                               self::$appSpecificPassword = AppSpecificPassword::generateForUser(local_user(), $_POST['description'] ?? '');
+                                               notice(L10n::t('New app-specific password generated.'));
+                                       }
+
+                                       break;
+                               case 'revoke_all' :
+                                       AppSpecificPassword::deleteAllForUser(local_user());
+                                       notice(L10n::t('App-specific passwords successfully revoked.'));
+                                       self::getApp()->internalRedirect('settings/2fa/app_specific?t=' . self::getFormSecurityToken('settings_2fa_password'));
+                                       break;
+                       }
+               }
+
+               if (!empty($_POST['revoke_id'])) {
+                       self::checkFormSecurityTokenRedirectOnError('settings/2fa/app_specific', 'settings_2fa_app_specific');
+
+                       if (AppSpecificPassword::deleteForUser(local_user(), $_POST['revoke_id'])) {
+                               notice(L10n::t('App-specific password successfully revoked.'));
+                       }
+
+                       self::getApp()->internalRedirect('settings/2fa/app_specific?t=' . self::getFormSecurityToken('settings_2fa_password'));
+               }
+       }
+
+       public static function content()
+       {
+               if (!local_user()) {
+                       return Login::form('settings/2fa/app_specific');
+               }
+
+               parent::content();
+
+               $appSpecificPasswords = AppSpecificPassword::getListForUser(local_user());
+
+               return Renderer::replaceMacros(Renderer::getMarkupTemplate('settings/twofactor/app_specific.tpl'), [
+                       '$form_security_token'     => self::getFormSecurityToken('settings_2fa_app_specific'),
+                       '$password_security_token' => self::getFormSecurityToken('settings_2fa_password'),
+
+                       '$title'                  => L10n::t('Two-factor app-specific passwords'),
+                       '$help_label'             => L10n::t('Help'),
+                       '$message'                => L10n::t('<p>App-specific passwords are randomly generated passwords used instead your regular password to authenticate your account on third-party applications that don\'t support two-factor authentication.</p>'),
+                       '$generated_message'      => L10n::t('Make sure to copy your new app-specific password now. You won’t be able to see it again!'),
+                       '$generated_app_specific_password' => self::$appSpecificPassword,
+
+                       '$description_label'      => L10n::t('Description'),
+                       '$last_used_label'        => L10n::t('Last Used'),
+                       '$revoke_label'           => L10n::t('Revoke'),
+                       '$revoke_all_label'       => L10n::t('Revoke All'),
+
+                       '$app_specific_passwords' => $appSpecificPasswords,
+                       '$generate_message'       => L10n::t('When you generate a new app-specific password, you must use it right away, it will be shown to you once after you generate it.'),
+                       '$generate_title'         => L10n::t('Generate new app-specific password'),
+                       '$description_placeholder_label' => L10n::t('Friendiqa on my Fairphone 2...'),
+                       '$generate_label' => L10n::t('Generate'),
+               ]);
+       }
+}
index 9f02692706a0c72e3343702a731bf914236237f5..79b92f159291c2e94d3d434aaf5a33009fa460c2 100644 (file)
@@ -8,7 +8,8 @@ use Friendica\Core\L10n;
 use Friendica\Core\PConfig;
 use Friendica\Core\Renderer;
 use Friendica\Core\Session;
-use Friendica\Model\TwoFactorRecoveryCode;
+use Friendica\Model\TwoFactor\AppSpecificPassword;
+use Friendica\Model\TwoFactor\RecoveryCode;
 use Friendica\Model\User;
 use Friendica\Module\BaseSettingsModule;
 use Friendica\Module\Login;
@@ -42,7 +43,7 @@ class Index extends BaseSettingsModule
                                        break;
                                case 'disable':
                                        if ($has_secret) {
-                                               TwoFactorRecoveryCode::deleteForUser(local_user());
+                                               RecoveryCode::deleteForUser(local_user());
                                                PConfig::delete(local_user(), '2fa', 'secret');
                                                PConfig::delete(local_user(), '2fa', 'verified');
                                                Session::remove('2fa');
@@ -56,6 +57,11 @@ class Index extends BaseSettingsModule
                                                self::getApp()->internalRedirect('settings/2fa/recovery?t=' . self::getFormSecurityToken('settings_2fa_password'));
                                        }
                                        break;
+                               case 'app_specific':
+                                       if ($has_secret) {
+                                               self::getApp()->internalRedirect('settings/2fa/app_specific?t=' . self::getFormSecurityToken('settings_2fa_password'));
+                                       }
+                                       break;
                                case 'configure':
                                        if (!$verified) {
                                                self::getApp()->internalRedirect('settings/2fa/verify?t=' . self::getFormSecurityToken('settings_2fa_password'));
@@ -94,14 +100,20 @@ class Index extends BaseSettingsModule
 
                        '$recovery_codes_title'     => L10n::t('Recovery codes'),
                        '$recovery_codes_remaining' => L10n::t('Remaining valid codes'),
-                       '$recovery_codes_count'     => TwoFactorRecoveryCode::countValidForUser(local_user()),
+                       '$recovery_codes_count'     => RecoveryCode::countValidForUser(local_user()),
                        '$recovery_codes_message'   => L10n::t('<p>These one-use codes can replace an authenticator app code in case you have lost access to it.</p>'),
 
+                       '$app_specific_passwords_title'     => L10n::t('App-specific passwords'),
+                       '$app_specific_passwords_remaining' => L10n::t('Generated app-specific passwords'),
+                       '$app_specific_passwords_count'     => AppSpecificPassword::countForUser(local_user()),
+                       '$app_specific_passwords_message'   => L10n::t('<p>These randomly generated passwords allow you to authenticate on apps not supporting two-factor authentication.</p>'),
+
                        '$action_title'         => L10n::t('Actions'),
                        '$password'             => ['password', L10n::t('Current password:'), '', L10n::t('You need to provide your current password to change two-factor authentication settings.'), 'required', 'autofocus'],
                        '$enable_label'         => L10n::t('Enable two-factor authentication'),
                        '$disable_label'        => L10n::t('Disable two-factor authentication'),
                        '$recovery_codes_label' => L10n::t('Show recovery codes'),
+                       '$app_specific_passwords_label' => L10n::t('Manage app-specific passwords'),
                        '$configure_label'      => L10n::t('Finish app configuration'),
                ]);
        }
index a2d08cda86cb4def517a817d7a32d4007248de43..6937fa503f4eec65a0bc3bfe92cb8fff668d84c5 100644 (file)
@@ -7,7 +7,7 @@ namespace Friendica\Module\Settings\TwoFactor;
 use Friendica\Core\L10n;
 use Friendica\Core\PConfig;
 use Friendica\Core\Renderer;
-use Friendica\Model\TwoFactorRecoveryCode;
+use Friendica\Model\TwoFactor\RecoveryCode;
 use Friendica\Module\BaseSettingsModule;
 use Friendica\Module\Login;
 
@@ -46,7 +46,7 @@ class Recovery extends BaseSettingsModule
                        self::checkFormSecurityTokenRedirectOnError('settings/2fa/recovery', 'settings_2fa_recovery');
 
                        if ($_POST['action'] == 'regenerate') {
-                               TwoFactorRecoveryCode::regenerateForUser(local_user());
+                               RecoveryCode::regenerateForUser(local_user());
                                notice(L10n::t('New recovery codes successfully generated.'));
                                self::getApp()->internalRedirect('settings/2fa/recovery?t=' . self::getFormSecurityToken('settings_2fa_password'));
                        }
@@ -61,11 +61,11 @@ class Recovery extends BaseSettingsModule
 
                parent::content();
 
-               if (!TwoFactorRecoveryCode::countValidForUser(local_user())) {
-                       TwoFactorRecoveryCode::generateForUser(local_user());
+               if (!RecoveryCode::countValidForUser(local_user())) {
+                       RecoveryCode::generateForUser(local_user());
                }
 
-               $recoveryCodes = TwoFactorRecoveryCode::getListForUser(local_user());
+               $recoveryCodes = RecoveryCode::getListForUser(local_user());
 
                $verified = PConfig::get(local_user(), '2fa', 'verified');
                
index 4952d220b7ac755bd7210aed3e57aa4422c7e091..60f443c35ffe88bc54cf0658c52625c8b394f40d 100644 (file)
@@ -6,7 +6,7 @@ use Friendica\BaseModule;
 use Friendica\Core\L10n;
 use Friendica\Core\Renderer;
 use Friendica\Core\Session;
-use Friendica\Model\TwoFactorRecoveryCode;
+use Friendica\Model\TwoFactor\RecoveryCode;
 
 /**
  * // Page 1a: Recovery code verification
@@ -35,10 +35,10 @@ class Recovery extends BaseModule
 
                        $recovery_code = defaults($_POST, 'recovery_code', '');
 
-                       if (TwoFactorRecoveryCode::existsForUser(local_user(), $recovery_code)) {
-                               TwoFactorRecoveryCode::markUsedForUser(local_user(), $recovery_code);
+                       if (RecoveryCode::existsForUser(local_user(), $recovery_code)) {
+                               RecoveryCode::markUsedForUser(local_user(), $recovery_code);
                                Session::set('2fa', true);
-                               notice(L10n::t('Remaining recovery codes: %d', TwoFactorRecoveryCode::countValidForUser(local_user())));
+                               notice(L10n::t('Remaining recovery codes: %d', RecoveryCode::countValidForUser(local_user())));
 
                                // Resume normal login workflow
                                Session::setAuthenticatedForUser($a, $a->user, true, true);
index 7a8e8de3502356782b22c5a36c9778378665059f..15235c7c26d5969f2bc58caa67721e3e560474b1 100644 (file)
@@ -471,7 +471,7 @@ class Probe
                        }
 
                        if ($host == 'twitter.com') {
-                               return ["network" => Protocol::TWITTER];
+                               return self::twitter($uri);
                        }
                        $lrdd = self::hostMeta($host);
 
@@ -512,7 +512,7 @@ class Probe
                        $nick = substr($uri, 0, strpos($uri, '@'));
 
                        if (strpos($uri, '@twitter.com')) {
-                               return ["network" => Protocol::TWITTER];
+                               return self::twitter($uri);
                        }
                        $lrdd = self::hostMeta($host);
 
@@ -1411,6 +1411,37 @@ class Probe
                return $data;
        }
 
+       /**
+        * @brief Check for twitter contact
+        *
+        * @param string $uri
+        *
+        * @return array twitter data
+        */
+       private static function twitter($uri)
+       {
+               if (preg_match('=(.*)@twitter.com=i', $uri, $matches)) {
+                       $nick = $matches[1];
+               } elseif (preg_match('=https?://twitter.com/(.*)=i', $uri, $matches)) {
+                       $nick = $matches[1];
+               } else {
+                       return [];
+               }
+
+               $data = [];
+               $data['url'] = 'https://twitter.com/' . $nick;
+               $data['addr'] = $nick . '@twitter.com';
+               $data['nick'] = $data['name'] = $nick;
+               $data['network'] = Protocol::TWITTER;
+               $data['baseurl'] = 'https://twitter.com';
+
+               $curlResult = Network::curl($data['url'], false);
+               if ($curlResult->isSuccess()) {
+                       return $data;
+               }
+               return [];
+       }
+
        /**
         * @brief Check page for feed link
         *
index c1ab6bc1af58cdd5381f0e1584d33ebf57493951..dadadecde9af1c97789404cce9d51292a2f64df6 100644 (file)
@@ -534,8 +534,9 @@ class Processor
        /**
         * Fetches missing posts
         *
-        * @param $url
-        * @param $child
+        * @param string $url message URL
+        * @param array $child activity array with the child of this message
+        * @return boolean success
         * @throws \Friendica\Network\HTTPException\InternalServerErrorException
         */
        public static function fetchMissingActivity($url, $child = [])
@@ -549,12 +550,12 @@ class Processor
                $object = ActivityPub::fetchContent($url, $uid);
                if (empty($object)) {
                        Logger::log('Activity ' . $url . ' was not fetchable, aborting.');
-                       return;
+                       return false;
                }
 
                if (empty($object['id'])) {
                        Logger::log('Activity ' . $url . ' has got not id, aborting. ' . json_encode($object));
-                       return;
+                       return false;
                }
 
                if (!empty($child['author'])) {
@@ -593,6 +594,8 @@ class Processor
 
                ActivityPub\Receiver::processActivity($ldactivity);
                Logger::log('Activity ' . $url . ' had been fetched and processed.');
+
+               return true;
        }
 
        /**
index 8e2e487796a300c373edddebc880cd9833e3af7f..18dcd759a1053c3742f992f93fce25b20631df0f 100644 (file)
@@ -1414,6 +1414,39 @@ class Diaspora
                return $msg;
        }
 
+       /**
+        * @brief Fetches an item with a given URL
+        *
+        * @param string $url the message url
+        *
+        * @return int the message id of the stored message or false
+        * @throws \Friendica\Network\HTTPException\InternalServerErrorException
+        * @throws \ImagickException
+        */
+       public static function fetchByURL($url, $uid = 0)
+       {
+               // Check for Diaspora (and Friendica) typical paths
+               if (!preg_match("=(https?://.+)/(?:posts|display)/([a-zA-Z0-9-_@.:%]+[a-zA-Z0-9])=i", $url, $matches)) {
+                       return false;
+               }
+
+               $guid = urldecode($matches[2]);
+
+               $item = Item::selectFirst(['id'], ['guid' => $guid, 'uid' => $uid]);
+               if (DBA::isResult($item)) {
+                       return $item['id'];
+               }
+
+               self::storeByGuid($guid, $matches[1], $uid);
+
+               $item = Item::selectFirst(['id'], ['guid' => $guid, 'uid' => $uid]);
+               if (DBA::isResult($item)) {
+                       return $item['id'];
+               } else {
+                       return false;
+               }
+       }
+
        /**
         * @brief Fetches the item record of a given guid
         *
index f29c2e864ec414b7d1219344e4e14d6fd0b622f9..06fa5246797028fb1bbee1dc75a5b7184db7726c 100644 (file)
@@ -4,36 +4,55 @@ namespace Friendica\Util;
 
 class BasePath
 {
+       /**
+        * @var string
+        */
+       private $baseDir;
+       /**
+        * @var array
+        */
+       private $server;
+
+       /**
+        * @param string|null $baseDir The default base path
+        * @param array       $server  server arguments
+        */
+       public function __construct(string $baseDir, array $server = [])
+       {
+               $this->baseDir = $baseDir;
+               $this->server = $server;
+       }
+
        /**
         * @brief Returns the base filesystem path of the App
         *
         * It first checks for the internal variable, then for DOCUMENT_ROOT and
         * finally for PWD
         *
-        * @param string|null $basePath The default base path
-        * @param array       $server   server arguments
-        *
         * @return string
         *
         * @throws \Exception if directory isn't usable
         */
-       public static function create($basePath, array $server = [])
+       public function getPath()
        {
-               if ((!$basePath || !is_dir($basePath)) && !empty($server['DOCUMENT_ROOT'])) {
-                       $basePath = $server['DOCUMENT_ROOT'];
+               $baseDir = $this->baseDir;
+               $server = $this->server;
+
+               if ((!$baseDir || !is_dir($baseDir)) && !empty($server['DOCUMENT_ROOT'])) {
+                       $baseDir = $server['DOCUMENT_ROOT'];
                }
 
-               if ((!$basePath || !is_dir($basePath)) && !empty($server['PWD'])) {
-                       $basePath = $server['PWD'];
+               if ((!$baseDir || !is_dir($baseDir)) && !empty($server['PWD'])) {
+                       $baseDir = $server['PWD'];
                }
 
-               $basePath = self::getRealPath($basePath);
+               $baseDir = self::getRealPath($baseDir);
 
-               if (!is_dir($basePath)) {
-                       throw new \Exception(sprintf('\'%s\' is not a valid basepath', $basePath));
+               if (!is_dir($baseDir)) {
+                       throw new \Exception(sprintf('\'%s\' is not a valid basepath', $baseDir));
                }
 
-               return $basePath;
+               return $baseDir;
        }
 
        /**
index 8ca76f79c1c370c8f69e3e2167db9d29900a031f..80da4018c09e63efed1edd83dca011c53fbe0b09 100644 (file)
@@ -3,7 +3,6 @@
 namespace Friendica\Util;
 
 use Exception;
-use Friendica\App;
 use Friendica\Core\Addon;
 use Friendica\Core\Config\Cache\ConfigCache;
 
@@ -52,10 +51,6 @@ class ConfigFileLoader
         */
        const SAMPLE_END = '-sample';
 
-       /**
-        * @var App\Mode
-        */
-       private $appMode;
        /**
         * @var string
         */
@@ -69,12 +64,11 @@ class ConfigFileLoader
         */
        private $staticDir;
 
-       public function __construct($baseDir, App\Mode $mode)
+       public function __construct(string $basePath)
        {
-               $this->baseDir   = $baseDir;
-               $this->configDir = $baseDir . DIRECTORY_SEPARATOR . self::CONFIG_DIR;
-               $this->staticDir = $baseDir . DIRECTORY_SEPARATOR . self::STATIC_DIR;
-               $this->appMode   = $mode;
+               $this->baseDir   = $basePath;
+               $this->configDir = $this->baseDir . DIRECTORY_SEPARATOR . self::CONFIG_DIR;
+               $this->staticDir = $this->baseDir . DIRECTORY_SEPARATOR . self::STATIC_DIR;
        }
 
        /**
@@ -102,7 +96,7 @@ class ConfigFileLoader
                $this->loadCoreConfig($config);
 
                // In case of install mode, add the found basepath (because there isn't a basepath set yet
-               if (!$raw && ($this->appMode->isInstall() || empty($config->get('system', 'basepath')))) {
+               if (!$raw && empty($config->get('system', 'basepath'))) {
                        // Setting at least the basepath we know
                        $config->set('system', 'basepath', $this->baseDir);
                }
index d341682e806d04419743f9d5f698b87d375f395e..46452f6e4e156132bbf68e9bd2044d0265cc8800 100644 (file)
@@ -228,7 +228,7 @@ class ExAuth
                        $aUser = DBA::selectFirst('user', ['uid', 'password', 'legacy_password'], ['nickname' => $sUser]);
                        if (DBA::isResult($aUser)) {
                                $uid = $aUser['uid'];
-                               $success = User::authenticate($aUser, $aCommand[3]);
+                               $success = User::authenticate($aUser, $aCommand[3], true);
                                $Error = $success === false;
                        } else {
                                $this->writeLog(LOG_WARNING, 'user not found: ' . $sUser);
index fe72efce40beec2e623598e3c6efd35c050d3f54..dc140469c671e945e42408a1b41ff5bd67fd14f9 100644 (file)
@@ -2,6 +2,8 @@
 
 namespace Friendica\Util;
 
+use Friendica\Core\Config\Cache\ConfigCache;
+use Friendica\Core\Config\Configuration;
 use Psr\Container\ContainerExceptionInterface;
 use Psr\Container\ContainerInterface;
 use Psr\Container\NotFoundExceptionInterface;
@@ -45,23 +47,21 @@ class Profiler implements ContainerInterface
        /**
         * Updates the enabling of the current profiler
         *
-        * @param bool $enabled
-        * @param bool $renderTime
+        * @param Configuration $config
         */
-       public function update($enabled = false, $renderTime = false)
+       public function update(Configuration $config)
        {
-               $this->enabled = $enabled;
-               $this->rendertime = $renderTime;
+               $this->enabled = $config->get('system', 'profiler');
+               $this->rendertime = $config->get('rendertime', 'callstack');
        }
 
        /**
-        * @param bool $enabled           True, if the Profiler is enabled
-        * @param bool $renderTime        True, if the Profiler should measure the whole rendertime including functions
+        * @param ConfigCache $configCache The configuration cache
         */
-       public function __construct($enabled = false, $renderTime = false)
+       public function __construct(ConfigCache $configCache)
        {
-               $this->enabled = $enabled;
-               $this->rendertime = $renderTime;
+               $this->enabled = $configCache->get('system', 'profiler');
+               $this->rendertime = $configCache->get('rendertime', 'callstack');
                $this->reset();
        }
 
index 4f75eac467cb5a0c85d48ed7ddf4a3163da2abec..aca6cf16b3d65489adb812990f3b87a38270370f 100755 (executable)
 use Friendica\Database\DBA;
 
 if (!defined('DB_UPDATE_VERSION')) {
-       define('DB_UPDATE_VERSION', 1319);
+       define('DB_UPDATE_VERSION', 1320);
 }
 
 return [
+       "2fa_app_specific_password" => [
+               "comment" => "Two-factor app-specific _password",
+               "fields" => [
+                       "id" => ["type" => "mediumint unsigned", "not null" => "1", "extra" => "auto_increment", "primary" => "1", "comment" => "Password ID for revocation"],
+                       "uid" => ["type" => "mediumint unsigned", "not null" => "1", "relation" => ["user" => "uid"], "comment" => "User ID"],
+                       "description" => ["type" => "varchar(255)", "comment" => "Description of the usage of the password"],
+                       "hashed_password" => ["type" => "varchar(255)", "not null" => "1", "primary" => "1", "comment" => "Hashed password"],
+                       "generated" => ["type" => "datetime", "not null" => "1", "comment" => "Datetime the password was generated"],
+                       "last_used" => ["type" => "datetime", "comment" => "Datetime the password was last used"],
+               ],
+               "indexes" => [
+                       "PRIMARY" => ["id"],
+                       "uid_description" => ["uid", "description"],
+               ]
+       ],
        "2fa_recovery_codes" => [
                "comment" => "Two-factor authentication recovery codes",
                "fields" => [
diff --git a/static/dependencies.config.php b/static/dependencies.config.php
new file mode 100644 (file)
index 0000000..c1ce4f3
--- /dev/null
@@ -0,0 +1,125 @@
+<?php
+
+use Dice\Dice;
+use Friendica\App;
+use Friendica\Core\Config;
+use Friendica\Database\Database;
+use Friendica\Factory;
+use Friendica\Util;
+use Psr\Log\LoggerInterface;
+
+/**
+ * The configuration defines "complex" dependencies inside Friendica
+ * So this classes shouldn't be simple or their dependencies are already defined here.
+ *
+ * This kind of dependencies are NOT required to be defined here:
+ *   - $a = new ClassA(new ClassB());
+ *   - $a = new ClassA();
+ *   - $a = new ClassA(Configuration $configuration);
+ *
+ * This kind of dependencies SHOULD be defined here:
+ *   - $a = new ClassA();
+ *     $b = $a->create();
+ *
+ *   - $a = new ClassA($creationPassedVariable);
+ *
+ */
+return [
+       '$basepath' => [
+               'instanceOf' => Util\BasePath::class,
+               'call' => [
+                       ['getPath', [], Dice::CHAIN_CALL],
+               ],
+               'constructParams' => [
+                       dirname(__FILE__, 2),
+                       $_SERVER
+               ]
+       ],
+       Util\BasePath::class => [
+               'shared'          => true,
+               'constructParams' => [
+                       dirname(__FILE__, 2),
+                       $_SERVER
+               ]
+       ],
+       Util\ConfigFileLoader::class => [
+               'shared' => true,
+               'constructParams' => [
+                       [Dice::INSTANCE => '$basepath'],
+               ],
+       ],
+       Config\Cache\ConfigCache::class => [
+               'instanceOf' => Factory\ConfigFactory::class,
+               'call'       => [
+                       ['createCache', [], Dice::CHAIN_CALL],
+               ],
+               'shared'     => true,
+       ],
+       App\Mode::class => [
+               'call'   => [
+                       ['determine', [], Dice::CHAIN_CALL],
+               ],
+               // marks the result as shared for other creations, so there's just
+               // one instance for the whole execution
+               'shared' => true,
+       ],
+       Config\Configuration::class => [
+               'shared' => true,
+               'instanceOf' => Factory\ConfigFactory::class,
+               'call' => [
+                       ['createConfig', [], Dice::CHAIN_CALL],
+               ],
+       ],
+       Config\PConfiguration::class => [
+               'shared' => true,
+               'instanceOf' => Factory\ConfigFactory::class,
+               'call' => [
+                       ['createPConfig', [], Dice::CHAIN_CALL],
+               ]
+       ],
+       Database::class => [
+               'shared' => true,
+               'constructParams' => [
+                       [DICE::INSTANCE => \Psr\Log\NullLogger::class],
+                       $_SERVER,
+               ],
+       ],
+       /**
+        * Creates the Util\BaseURL
+        *
+        * Same as:
+        *   $baseURL = new Util\BaseURL($configuration, $_SERVER);
+        */
+       Util\BaseURL::class => [
+               'shared'          => true,
+               'constructParams' => [
+                       $_SERVER,
+               ],
+       ],
+       /**
+        * Create a Logger, which implements the LoggerInterface
+        *
+        * Same as:
+        *   $loggerFactory = new Factory\LoggerFactory();
+        *   $logger = $loggerFactory->create($channel, $configuration, $profiler);
+        *
+        * Attention1: We can use DICE for detecting dependencies inside "chained" calls too
+        * Attention2: The variable "$channel" is passed inside the creation of the dependencies per:
+        *    $app = $dice->create(App::class, [], ['$channel' => 'index']);
+        *    and is automatically passed as an argument with the same name
+        */
+       LoggerInterface::class => [
+               'shared'     => true,
+               'instanceOf' => Factory\LoggerFactory::class,
+               'call'       => [
+                       ['create', [], Dice::CHAIN_CALL],
+               ],
+       ],
+       '$devLogger' => [
+               'shared'     => true,
+               'instanceOf' => Factory\LoggerFactory::class,
+               'call'       => [
+                       ['createDev', [], Dice::CHAIN_CALL],
+               ]
+       ]
+];
index 950da5af6f3d82dc211f846b023a52eb4307b200..5f9d3bf72cdc05b835e18443463f01c5bc49a296 100644 (file)
@@ -9,14 +9,13 @@ use Friendica\App\Mode;
 use Friendica\Core\Config\Cache\ConfigCache;
 use Friendica\Database\Database;
 use Friendica\Factory\ConfigFactory;
-use Friendica\Factory\DBFactory;
-use Friendica\Factory\ProfilerFactory;
 use Friendica\Util\BasePath;
 use Friendica\Util\ConfigFileLoader;
 use Friendica\Util\Profiler;
 use PHPUnit\DbUnit\DataSet\YamlDataSet;
 use PHPUnit\DbUnit\TestCaseTrait;
 use PHPUnit_Extensions_Database_DB_IDatabaseConnection;
+use Psr\Log\NullLogger;
 
 require_once __DIR__ . '/../boot.php';
 
@@ -46,12 +45,13 @@ abstract class DatabaseTest extends MockedTest
        {
                parent::setUpBeforeClass();
 
-               self::$basePath = BasePath::create(dirname(__DIR__));
-               self::$mode = new Mode(self::$basePath);
-               $configLoader = new ConfigFileLoader(self::$basePath, self::$mode);
-               self::$configCache = ConfigFactory::createCache($configLoader);
-               self::$profiler = ProfilerFactory::create(self::$configCache);
-               self::$dba = DBFactory::init(self::$configCache, self::$profiler, $_SERVER);
+               self::$basePath = new BasePath(dirname(__DIR__));
+               $configLoader = new ConfigFileLoader(self::$basePath->getPath());
+               $configFactory = new ConfigFactory();
+               self::$configCache = $configFactory->createCache($configLoader);
+               self::$profiler = new Profiler(self::$configCache);
+               self::$dba = new Database(self::$configCache, self::$profiler, new NullLogger(), $_SERVER);
+               self::$mode = new Mode(self::$basePath, self::$dba, self::$configCache);
        }
 
        /**
diff --git a/tests/functional/DependencyCheckTest.php b/tests/functional/DependencyCheckTest.php
new file mode 100644 (file)
index 0000000..4a1ab5a
--- /dev/null
@@ -0,0 +1,136 @@
+<?php
+
+namespace functional;
+
+use Dice\Dice;
+use Friendica\App;
+use Friendica\Core\Config\Cache\ConfigCache;
+use Friendica\Core\Config\Configuration;
+use Friendica\Database\Database;
+use Friendica\Test\Util\VFSTrait;
+use Friendica\Util\BasePath;
+use Friendica\Util\ConfigFileLoader;
+use Friendica\Util\Profiler;
+use PHPUnit\Framework\TestCase;
+use Psr\Log\LoggerInterface;
+
+class dependencyCheck extends TestCase
+{
+       use VFSTrait;
+
+       /**
+        * @var Dice
+        */
+       private $dice;
+
+       protected function setUp()
+       {
+               parent::setUp();
+
+               $this->setUpVfsDir();
+
+               $this->dice = new Dice();
+               $this->dice = $this->dice->addRules(include __DIR__ . '/../../static/dependencies.config.php');
+       }
+
+       /**
+        * Test the creation of the BasePath
+        */
+       public function testBasePath()
+       {
+               /** @var BasePath $basePath */
+               $basePath = $this->dice->create(BasePath::class, [$this->root->url()]);
+
+               $this->assertInstanceOf(BasePath::class, $basePath);
+               $this->assertEquals($this->root->url(), $basePath->getPath());
+       }
+
+       /**
+        * Test the initial config cache
+        * Should not need any other files
+        */
+       public function testConfigFileLoader()
+       {
+               /** @var ConfigFileLoader $configFileLoader */
+               $configFileLoader = $this->dice->create(ConfigFileLoader::class);
+
+               $this->assertInstanceOf(ConfigFileLoader::class, $configFileLoader);
+
+               $configCache = new ConfigCache();
+               $configFileLoader->setupCache($configCache);
+
+               $this->assertNotEmpty($configCache->getAll());
+               $this->assertArrayHasKey('database', $configCache->getAll());
+               $this->assertArrayHasKey('system', $configCache->getAll());
+       }
+
+       /**
+        * Test the construction of a profiler class with DI
+        */
+       public function testProfiler()
+       {
+               /** @var Profiler $profiler */
+               $profiler = $this->dice->create(Profiler::class);
+
+               $this->assertInstanceOf(Profiler::class, $profiler);
+
+               $configCache = new ConfigCache([
+                       'system' => [
+                               'profiler' => true,
+                       ],
+                       'rendertime' => [
+                               'callstack' => true,
+                       ]
+               ]);
+
+               $profiler = $this->dice->create(Profiler::class, [$configCache]);
+
+               $this->assertInstanceOf(Profiler::class, $profiler);
+               $this->assertTrue($profiler->isRendertime());
+       }
+
+       public function testDatabase()
+       {
+               /** @var Database $database */
+               $database = $this->dice->create(Database::class);
+
+               $this->assertInstanceOf(Database::class, $database);
+               $this->assertTrue($database->connected());
+       }
+
+       public function testAppMode()
+       {
+               /** @var App\Mode $mode */
+               $mode = $this->dice->create(App\Mode::class);
+
+               $this->assertInstanceOf(App\Mode::class, $mode);
+
+               $this->assertTrue($mode->isNormal());
+       }
+
+       public function testConfiguration()
+       {
+               /** @var Configuration $config */
+               $config = $this->dice->create(Configuration::class);
+
+               $this->assertInstanceOf(Configuration::class, $config);
+
+               $this->assertNotEmpty($config->get('database', 'username'));
+       }
+
+       public function testLogger()
+       {
+               /** @var LoggerInterface $logger */
+               $logger = $this->dice->create(LoggerInterface::class, ['test']);
+
+               $this->assertInstanceOf(LoggerInterface::class, $logger);
+       }
+
+       public function testDevLogger()
+       {
+               /** @var LoggerInterface $logger */
+               $logger = $this->dice->create('$devLogger', ['dev']);
+
+               self::assertInstanceOf(LoggerInterface::class, $logger);
+       }
+}
index 801857a4531d746298ab100914332662926c571e..a7193c8872dc6ad0280171a15636d1fc557358e7 100644 (file)
@@ -15,7 +15,6 @@ use Friendica\Core\System;
 use Friendica\Factory;
 use Friendica\Network\HTTPException;
 use Friendica\Util\BaseURL;
-use Friendica\Util\ConfigFileLoader;
 use Monolog\Handler\TestHandler;
 
 require_once __DIR__ . '/../../include/api.php';
@@ -51,13 +50,15 @@ class ApiTest extends DatabaseTest
        public function setUp()
        {
                $configModel = new \Friendica\Model\Config\Config(self::$dba);
-               $config = Factory\ConfigFactory::createConfig(self::$configCache, $configModel);
+               $configFactory = new Factory\ConfigFactory();
+               $config = $configFactory->createConfig(self::$configCache, $configModel);
                $pconfigModel = new \Friendica\Model\Config\PConfig(self::$dba);
-               Factory\ConfigFactory::createPConfig(self::$configCache, new PConfigCache(), $pconfigModel);
-               $logger = Factory\LoggerFactory::create('test', self::$dba, $config, self::$profiler);
+               $configFactory->createPConfig(self::$configCache, new PConfigCache(), $pconfigModel);
+               $loggerFactory = new Factory\LoggerFactory();
+               $logger = $loggerFactory->create('test', self::$dba, $config, self::$profiler);
                $baseUrl = new BaseURL($config, $_SERVER);
                $router = new App\Router();
-               $l10n = new L10n(L10n::detectLanguage($config->get('system', 'language')),
+               $l10n = new L10n($config,
                        self::$dba,
                        $logger);
                $this->app = new App(self::$dba, $config, self::$mode, $router, $baseUrl, $logger, self::$profiler, $l10n, false);
index 9059e8bebf4ad6b6d1ec29812dcc239a9e774f7e..06aad106614f578c435490baad5c4b34a36020f5 100644 (file)
@@ -4,32 +4,56 @@ namespace Friendica\Test\src\App;
 
 use Friendica\App\Mode;
 use Friendica\Core\Config;
+use Friendica\Database\Database;
 use Friendica\Test\MockedTest;
 use Friendica\Test\Util\DBAMockTrait;
 use Friendica\Test\Util\VFSTrait;
+use Friendica\Util\BasePath;
+use Mockery\MockInterface;
 
 class ModeTest extends MockedTest
 {
        use VFSTrait;
        use DBAMockTrait;
 
+       /**
+        * @var BasePath|MockInterface
+        */
+       private $basePathMock;
+
+       /**
+        * @var Database|MockInterface
+        */
+       private $databaseMock;
+
+       /**
+        * @var Config\Cache\ConfigCache|MockInterface
+        */
+       private $configCacheMock;
+
        public function setUp()
        {
                parent::setUp();
 
                $this->setUpVfsDir();
+
+               $this->basePathMock = \Mockery::mock(BasePath::class);
+               $this->basePathMock->shouldReceive('getPath')->andReturn($this->root->url())->once();
+
+               $this->databaseMock = \Mockery::mock(Database::class);
+               $this->configCacheMock = \Mockery::mock(Config\Cache\ConfigCache::class);
        }
 
        public function testItEmpty()
        {
-               $mode = new Mode($this->root->url());
+               $mode = new Mode($this->basePathMock, $this->databaseMock, $this->configCacheMock);
                $this->assertTrue($mode->isInstall());
                $this->assertFalse($mode->isNormal());
        }
 
        public function testWithoutConfig()
        {
-               $mode = new Mode($this->root->url());
+               $mode = new Mode($this->basePathMock, $this->databaseMock, $this->configCacheMock);
 
                $this->assertTrue($this->root->hasChild('config/local.config.php'));
 
@@ -45,15 +69,11 @@ class ModeTest extends MockedTest
                $this->assertFalse($mode->has(Mode::LOCALCONFIGPRESENT));
        }
 
-       /**
-        * @runInSeparateProcess
-        * @preserveGlobalState disabled
-        */
        public function testWithoutDatabase()
        {
-               $this->mockConnected(false, 1);
+               $this->databaseMock->shouldReceive('connected')->andReturn(false)->once();
 
-               $mode = new Mode($this->root->url());
+               $mode = new Mode($this->basePathMock, $this->databaseMock, $this->configCacheMock);
                $mode->determine();
 
                $this->assertFalse($mode->isNormal());
@@ -63,16 +83,13 @@ class ModeTest extends MockedTest
                $this->assertFalse($mode->has(Mode::DBAVAILABLE));
        }
 
-       /**
-        * @runInSeparateProcess
-        * @preserveGlobalState disabled
-        */
        public function testWithoutDatabaseSetup()
        {
-               $this->mockConnected(true, 1);
-               $this->mockFetchFirst('SHOW TABLES LIKE \'config\'', false, 1);
+               $this->databaseMock->shouldReceive('connected')->andReturn(true)->once();
+               $this->databaseMock->shouldReceive('fetchFirst')
+                                  ->with('SHOW TABLES LIKE \'config\'')->andReturn(false)->once();
 
-               $mode = new Mode($this->root->url());
+               $mode = new Mode($this->basePathMock, $this->databaseMock, $this->configCacheMock);
                $mode->determine();
 
                $this->assertFalse($mode->isNormal());
@@ -81,25 +98,15 @@ class ModeTest extends MockedTest
                $this->assertTrue($mode->has(Mode::LOCALCONFIGPRESENT));
        }
 
-       /**
-        * @runInSeparateProcess
-        * @preserveGlobalState disabled
-        */
        public function testWithMaintenanceMode()
        {
-               $this->mockConnected(true, 1);
-               $this->mockFetchFirst('SHOW TABLES LIKE \'config\'', true, 1);
-
-               $config = \Mockery::mock(Config\Configuration::class);
-               $config
-                       ->shouldReceive('get')
-                       ->with('system', 'maintenance', null, false)
-                       ->andReturn(true)
-                       ->once();
-               // Initialize empty Config
-               Config::init($config);
-
-               $mode = new Mode($this->root->url());
+               $this->databaseMock->shouldReceive('connected')->andReturn(true)->once();
+               $this->databaseMock->shouldReceive('fetchFirst')
+                                  ->with('SHOW TABLES LIKE \'config\'')->andReturn(true)->once();
+               $this->configCacheMock->shouldReceive('get')->with('system', 'maintenance')
+                                     ->andReturn(true)->once();
+
+               $mode = new Mode($this->basePathMock, $this->databaseMock, $this->configCacheMock);
                $mode->determine();
 
                $this->assertFalse($mode->isNormal());
@@ -109,25 +116,42 @@ class ModeTest extends MockedTest
                $this->assertFalse($mode->has(Mode::MAINTENANCEDISABLED));
        }
 
+       public function testNormalMode()
+       {
+               $this->databaseMock->shouldReceive('connected')->andReturn(true)->once();
+               $this->databaseMock->shouldReceive('fetchFirst')
+                                  ->with('SHOW TABLES LIKE \'config\'')->andReturn(true)->once();
+               $this->configCacheMock->shouldReceive('get')->with('system', 'maintenance')
+                                     ->andReturn(false)->once();
+               $this->databaseMock->shouldReceive('selectFirst')
+                                  ->with('config', ['v'], ['cat' => 'system', 'k' => 'maintenance'])
+                                  ->andReturn(['v' => null])->once();
+
+               $mode = new Mode($this->basePathMock, $this->databaseMock, $this->configCacheMock);
+               $mode->determine();
+
+               $this->assertTrue($mode->isNormal());
+               $this->assertFalse($mode->isInstall());
+
+               $this->assertTrue($mode->has(Mode::DBCONFIGAVAILABLE));
+               $this->assertTrue($mode->has(Mode::MAINTENANCEDISABLED));
+       }
+
        /**
-        * @runInSeparateProcess
-        * @preserveGlobalState disabled
+        * Test explicit disabled maintenance (in case you manually disable it)
         */
-       public function testNormalMode()
+       public function testDisabledMaintenance()
        {
-               $this->mockConnected(true, 1);
-               $this->mockFetchFirst('SHOW TABLES LIKE \'config\'', true, 1);
-
-               $config = \Mockery::mock(Config\Configuration::class);
-               $config
-                       ->shouldReceive('get')
-                       ->with('system', 'maintenance', null, false)
-                       ->andReturn(false)
-                       ->once();
-               // Initialize empty Config
-               Config::init($config);
-
-               $mode = new Mode($this->root->url());
+               $this->databaseMock->shouldReceive('connected')->andReturn(true)->once();
+               $this->databaseMock->shouldReceive('fetchFirst')
+                                  ->with('SHOW TABLES LIKE \'config\'')->andReturn(true)->once();
+               $this->configCacheMock->shouldReceive('get')->with('system', 'maintenance')
+                                     ->andReturn(false)->once();
+               $this->databaseMock->shouldReceive('selectFirst')
+                                  ->with('config', ['v'], ['cat' => 'system', 'k' => 'maintenance'])
+                                  ->andReturn(['v' => '0'])->once();
+
+               $mode = new Mode($this->basePathMock, $this->databaseMock, $this->configCacheMock);
                $mode->determine();
 
                $this->assertTrue($mode->isNormal());
index cc25373e1aceba9d04bbb160ca7c1b6aa11591aa..69ea267bd2890999ab0501cb3d54350f5533f51d 100644 (file)
@@ -15,13 +15,15 @@ class DBATest extends DatabaseTest
        public function setUp()
        {
                $configModel = new \Friendica\Model\Config\Config(self::$dba);
-               $config = Factory\ConfigFactory::createConfig(self::$configCache, $configModel);
+               $configFactory = new Factory\ConfigFactory();
+               $config = $configFactory->createConfig(self::$configCache, $configModel);
                $pconfigModel = new \Friendica\Model\Config\PConfig(self::$dba);
-               Factory\ConfigFactory::createPConfig(self::$configCache, new PConfigCache(), $pconfigModel);
-               $logger = Factory\LoggerFactory::create('test', self::$dba, $config, self::$profiler);
+               $configFactory->createPConfig(self::$configCache, new PConfigCache(), $pconfigModel);
+               $loggerFactory = new Factory\LoggerFactory();
+               $logger = $loggerFactory->create('test', self::$dba, $config, self::$profiler);
                $baseUrl = new BaseURL($config, $_SERVER);
                $router = new App\Router();
-               $l10n = new L10n(L10n::detectLanguage($config->get('system', 'language')),
+               $l10n = new L10n($config,
                        self::$dba,
                        $logger);
                $this->app = new App(self::$dba, $config, self::$mode, $router, $baseUrl, $logger, self::$profiler, $l10n, false);
index 3cecec62a9bd40dcdd80d0986b7faa8bd5ecf609..a951d1c0914f894b1551164a263ac5bd10501633 100644 (file)
@@ -19,13 +19,15 @@ class DBStructureTest extends DatabaseTest
        public function setUp()
        {
                $configModel = new Config(self::$dba);
-               $config = Factory\ConfigFactory::createConfig(self::$configCache, $configModel);
+               $configFactory = new Factory\ConfigFactory();
+               $config = $configFactory->createConfig(self::$configCache, $configModel);
                $pconfigModel = new \Friendica\Model\Config\PConfig(self::$dba);
-               Factory\ConfigFactory::createPConfig(self::$configCache, new PConfigCache(), $pconfigModel);
-               $logger = Factory\LoggerFactory::create('test', self::$dba, $config, self::$profiler);
+               $configFactory->createPConfig(self::$configCache, new PConfigCache(), $pconfigModel);
+               $loggerFactory = new Factory\LoggerFactory();
+               $logger = $loggerFactory->create('test', self::$dba, $config, self::$profiler);
                $baseUrl = new BaseURL($config, $_SERVER);
                $router = new App\Router();
-               $l10n = new L10n(L10n::detectLanguage($config->get('system', 'language')),
+               $l10n = new L10n($config,
                        self::$dba,
                        $logger);
                $this->app = new App(self::$dba, $config, self::$mode, $router, $baseUrl, $logger, self::$profiler, $l10n, false);
index 3c619255cfe959c419760eedb9d7d4c9d778e437..c9c7b079f9aa78cd933817c94c5efde7aa603835 100644 (file)
@@ -58,7 +58,8 @@ class BasePathTest extends MockedTest
         */
        public function testDetermineBasePath(array $server, $input, $output)
        {
-               $this->assertEquals($output, BasePath::create($input, $server));
+               $basepath = new BasePath($input, $server);
+               $this->assertEquals($output, $basepath->getPath());
        }
 
        /**
@@ -68,6 +69,7 @@ class BasePathTest extends MockedTest
         */
        public function testFailedBasePath()
        {
-               BasePath::create('/now23452sgfgas', []);
+               $basepath = new BasePath('/now23452sgfgas', []);
+               $basepath->getPath();
        }
 }
index 1680f2fdb6f164f14e10986e54e6536ebf8d8f3b..aa9a11cb080aeb02fcc86a2cf3277228c915782e 100644 (file)
@@ -2,31 +2,21 @@
 
 namespace Friendica\Test\src\Util\Config;
 
-use Friendica\App;
 use Friendica\Core\Config\Cache\ConfigCache;
 use Friendica\Test\MockedTest;
 use Friendica\Test\Util\VFSTrait;
 use Friendica\Util\ConfigFileLoader;
-use Mockery\MockInterface;
 use org\bovigo\vfs\vfsStream;
 
 class ConfigFileLoaderTest extends MockedTest
 {
        use VFSTrait;
 
-       /**
-        * @var App\Mode|MockInterface
-        */
-       private $mode;
-
        protected function setUp()
        {
                parent::setUp();
 
                $this->setUpVfsDir();
-
-               $this->mode = \Mockery::mock(App\Mode::class);
-               $this->mode->shouldReceive('isInstall')->andReturn(true);
        }
 
        /**
@@ -34,7 +24,9 @@ class ConfigFileLoaderTest extends MockedTest
         */
        public function testLoadConfigFiles()
        {
-               $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode);
+               $this->delConfigFile('local.config.php');
+
+               $configFileLoader = new ConfigFileLoader($this->root->url());
                $configCache = new ConfigCache();
 
                $configFileLoader->setupCache($configCache);
@@ -55,7 +47,7 @@ class ConfigFileLoaderTest extends MockedTest
                        ->at($this->root->getChild('config'))
                        ->setContent('<?php return true;');
 
-               $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode);
+               $configFileLoader = new ConfigFileLoader($this->root->url());
                $configCache = new ConfigCache();
 
                $configFileLoader->setupCache($configCache);
@@ -79,7 +71,7 @@ class ConfigFileLoaderTest extends MockedTest
                        ->at($this->root->getChild('config'))
                        ->setContent(file_get_contents($file));
 
-               $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode);
+               $configFileLoader = new ConfigFileLoader($this->root->url());
                $configCache = new ConfigCache();
 
                $configFileLoader->setupCache($configCache);
@@ -111,7 +103,7 @@ class ConfigFileLoaderTest extends MockedTest
                        ->at($this->root->getChild('config'))
                        ->setContent(file_get_contents($file));
 
-               $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode);
+               $configFileLoader = new ConfigFileLoader($this->root->url());
                $configCache = new ConfigCache();
 
                $configFileLoader->setupCache($configCache);
@@ -142,7 +134,7 @@ class ConfigFileLoaderTest extends MockedTest
                        ->at($this->root)
                        ->setContent(file_get_contents($file));
 
-               $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode);
+               $configFileLoader = new ConfigFileLoader($this->root->url());
                $configCache = new ConfigCache();
 
                $configFileLoader->setupCache($configCache);
@@ -191,7 +183,7 @@ class ConfigFileLoaderTest extends MockedTest
                        ->at($this->root->getChild('addon')->getChild('test')->getChild('config'))
                        ->setContent(file_get_contents($file));
 
-               $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode);
+               $configFileLoader = new ConfigFileLoader($this->root->url());
 
                $conf = $configFileLoader->loadAddonConfig('test');
 
@@ -223,7 +215,7 @@ class ConfigFileLoaderTest extends MockedTest
                                ->at($this->root->getChild('config'))
                         ->setContent(file_get_contents($fileDir . 'B.config.php'));
 
-               $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode);
+               $configFileLoader = new ConfigFileLoader($this->root->url());
                $configCache = new ConfigCache();
 
                $configFileLoader->setupCache($configCache);
@@ -252,7 +244,7 @@ class ConfigFileLoaderTest extends MockedTest
                         ->at($this->root->getChild('config'))
                         ->setContent(file_get_contents($fileDir . 'B.ini.php'));
 
-               $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode);
+               $configFileLoader = new ConfigFileLoader($this->root->url());
                $configCache = new ConfigCache();
 
                $configFileLoader->setupCache($configCache);
@@ -281,7 +273,7 @@ class ConfigFileLoaderTest extends MockedTest
                         ->at($this->root->getChild('config'))
                         ->setContent(file_get_contents($fileDir . 'B.ini.php'));
 
-               $configFileLoader = new ConfigFileLoader($this->root->url(), $this->mode);
+               $configFileLoader = new ConfigFileLoader($this->root->url());
                $configCache = new ConfigCache();
 
                $configFileLoader->setupCache($configCache);
index 1dbf36f38bb1f11e8bc5e6db99fc65f563db0004..0790bc30ac3aad45da8de12d45be1bdd68e4bf0e 100644 (file)
@@ -2,6 +2,8 @@
 
 namespace Friendica\Test\src\Util;
 
+use Friendica\Core\Config\Cache\ConfigCache;
+use Friendica\Core\Config\Configuration;
 use Friendica\Test\MockedTest;
 use Friendica\Util\Profiler;
 use Mockery\MockInterface;
@@ -26,7 +28,12 @@ class ProfilerTest extends MockedTest
         */
        public function testSetUp()
        {
-               $profiler = new Profiler(true, true);
+               $configCache = \Mockery::mock(ConfigCache::class);
+               $configCache->shouldReceive('get')
+                           ->withAnyArgs()
+                           ->andReturn(true)
+                           ->twice();
+               $profiler = new Profiler($configCache);
        }
 
        /**
@@ -96,7 +103,13 @@ class ProfilerTest extends MockedTest
         */
        public function testSaveTimestamp($timestamp, $name, array $functions)
        {
-               $profiler = new Profiler(true, true);
+               $configCache = \Mockery::mock(ConfigCache::class);
+               $configCache->shouldReceive('get')
+                           ->withAnyArgs()
+                           ->andReturn(true)
+                           ->twice();
+
+               $profiler = new Profiler($configCache);
 
                foreach ($functions as $function) {
                        $profiler->saveTimestamp($timestamp, $name, $function);
@@ -111,7 +124,13 @@ class ProfilerTest extends MockedTest
         */
        public function testReset($timestamp, $name, array $functions)
        {
-               $profiler = new Profiler(true, true);
+               $configCache = \Mockery::mock(ConfigCache::class);
+               $configCache->shouldReceive('get')
+                           ->withAnyArgs()
+                           ->andReturn(true)
+                           ->twice();
+
+               $profiler = new Profiler($configCache);
 
                $profiler->saveTimestamp($timestamp, $name);
                $profiler->reset();
@@ -168,7 +187,13 @@ class ProfilerTest extends MockedTest
                        ->shouldReceive('info')
                        ->once();
 
-               $profiler = new Profiler(true, true);
+               $configCache = \Mockery::mock(ConfigCache::class);
+               $configCache->shouldReceive('get')
+                           ->withAnyArgs()
+                           ->andReturn(true)
+                           ->twice();
+
+               $profiler = new Profiler($configCache);
 
                foreach ($data as $perf => $items) {
                        foreach ($items['functions'] as $function) {
@@ -193,19 +218,48 @@ class ProfilerTest extends MockedTest
         */
        public function testEnableDisable()
        {
-               $profiler = new Profiler(true, false);
+               $configCache = \Mockery::mock(ConfigCache::class);
+               $configCache->shouldReceive('get')
+                           ->with('system', 'profiler')
+                           ->andReturn(true)
+                           ->once();
+               $configCache->shouldReceive('get')
+                           ->with('rendertime', 'callstack')
+                           ->andReturn(false)
+                           ->once();
+
+               $profiler = new Profiler($configCache);
 
                $this->assertFalse($profiler->isRendertime());
                $this->assertEmpty($profiler->getRendertimeString());
 
                $profiler->saveTimestamp(time(), 'network', 'test1');
 
-               $profiler->update(false, false);
+               $config = \Mockery::mock(Configuration::class);
+               $config->shouldReceive('get')
+                           ->with('system', 'profiler')
+                           ->andReturn(false)
+                           ->once();
+               $config->shouldReceive('get')
+                           ->with('rendertime', 'callstack')
+                           ->andReturn(false)
+                           ->once();
+
+               $profiler->update($config);
 
                $this->assertFalse($profiler->isRendertime());
                $this->assertEmpty($profiler->getRendertimeString());
 
-               $profiler->update(true, true);
+               $config->shouldReceive('get')
+                      ->with('system', 'profiler')
+                      ->andReturn(true)
+                      ->once();
+               $config->shouldReceive('get')
+                      ->with('rendertime', 'callstack')
+                      ->andReturn(true)
+                      ->once();
+
+               $profiler->update($config);
 
                $profiler->saveTimestamp(time(), 'database', 'test2');
 
diff --git a/view/templates/settings/twofactor/app_specific.tpl b/view/templates/settings/twofactor/app_specific.tpl
new file mode 100644 (file)
index 0000000..1f58267
--- /dev/null
@@ -0,0 +1,57 @@
+<div class="generic-page-wrapper">
+       <h1>{{$title}} <a href="help/Two-Factor-Authentication" title="{{$help_label}}" class="btn btn-default btn-sm"><i aria-hidden="true" class="fa fa-question fa-2x"></i></a></h1>
+       <div>{{$message nofilter}}</div>
+
+{{if $generated_app_specific_password}}
+       <div class="panel panel-success">
+               <div class="panel-heading">
+                       ✅ {{$generated_app_specific_password.plaintext_password}}
+               </div>
+               <div class="panel-body">
+            {{$generated_message}}
+               </div>
+       </div>
+{{/if}}
+
+       <form action="settings/2fa/app_specific?t={{$password_security_token}}" method="post">
+               <input type="hidden" name="form_security_token" value="{{$form_security_token}}">
+               <table class="app-specific-passwords table table-hover table-condensed table-striped">
+                       <thead>
+                               <tr>
+                                       <th>{{$description_label}}</th>
+                                       <th>{{$last_used_label}}</th>
+                                       <th><button type="submit" name="action" class="btn btn-primary btn-small" value="revoke_all">{{$revoke_all_label}}</button></th>
+                               </tr>
+                       </thead>
+                       <tbody>
+{{foreach $app_specific_passwords as $app_specific_password}}
+                               <tr{{if $generated_app_specific_password && $app_specific_password.id == $generated_app_specific_password.id}} class="success"{{/if}}>
+                                       <td>
+                               {{$app_specific_password.description}}
+                                       </td>
+                                       <td>
+                                               <span class="time" title="{{$app_specific_password.last_used}}" data-toggle="tooltip">
+                                                       <time datetime="{{$app_specific_password.last_used}}">{{$app_specific_password.ago}}</time>
+                                               </span>
+                                       </td>
+                                       <td>
+                                               <button type="submit" name="revoke_id" class="btn btn-default btn-small" value="{{$app_specific_password.id}}">{{$revoke_label}}</button>
+                                       </td>
+                               </tr>
+{{/foreach}}
+                       </tbody>
+               </table>
+       </form>
+       <form action="settings/2fa/app_specific?t={{$password_security_token}}" method="post">
+               <input type="hidden" name="form_security_token" value="{{$form_security_token}}">
+               <h3>{{$generate_title}}</h3>
+               <p>{{$generate_message}}</p>
+               <div class="form-group">
+                       <label for="app-specific-password-description">{{$description_label}}</label>
+                       <input type="text" maxlength="255" name="description" id="app-specific-password-description" class="form-control" placeholder="{{$description_placeholder_label}}" required/>
+               </div>
+               <p>
+                       <button type="submit" name="action" class="btn btn-large btn-primary" value="generate">{{$generate_label}}</button>
+               </p>
+       </form>
+</div>
index 57bcab19eae90e11d2e084a18efaff953255f916..6cf3fac11976e9a84110fddab33b580623b04240 100644 (file)
 
                {{include file="field_password.tpl" field=$password}}
 
-               <div class="form-group settings-submit-wrapper" >
 {{if !$has_secret}}
-                       <button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="enable">{{$enable_label}}</button>
+               <p><button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="enable">{{$enable_label}}</button></p>
 {{else}}
-                       <button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="disable">{{$disable_label}}</button>
+               <p><button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="disable">{{$disable_label}}</button></p>
 {{/if}}
 {{if $has_secret && $verified}}
-                       <button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="recovery">{{$recovery_codes_label}}</button>
+               <p><button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="recovery">{{$recovery_codes_label}}</button></p>
+               <p><button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="app_specific">{{$app_specific_passwords_label}}</button></p>
 {{/if}}
 {{if $has_secret && !$verified}}
-                       <button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="configure">{{$configure_label}}</button>
+               <p><button type="submit" name="action" id="confirm-submit-button" class="btn btn-primary confirm-button" value="configure">{{$configure_label}}</button></p>
 {{/if}}
-               </div>
        </form>
 </div>
index 82bddcfc61c2870596fbefe523ef2377ccac7460..91e9dafe4426dad38458ea974a9ab23372df57f6 100644 (file)
@@ -70,7 +70,7 @@
                <div id="photo-tags">{{$tags.title}}
                        {{foreach $tags.tags as $t}}
                        <span class="category label btn-success sm">
-                               <span class="p-category">{{$t.name}}</span>
+                               <span class="p-category">{{$t.name nofilter}}</span>
                                {{if $t.removeurl}} <a href="{{$t.removeurl}}">(X)</a> {{/if}}
                        </span>
                        {{/foreach}}