Passed
Push — master ( 23df99...f86f59 )
by Joas
16:43 queued 15s
created
lib/public/Security/ISecureRandom.php 2 patches
Indentation   +24 added lines, -24 removed lines patch added patch discarded remove patch
@@ -40,30 +40,30 @@
 block discarded – undo
40 40
  */
41 41
 interface ISecureRandom {
42 42
 
43
-	/**
44
-	 * Flags for characters that can be used for <code>generate($length, $characters)</code>
45
-	 */
46
-	public const CHAR_UPPER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
47
-	public const CHAR_LOWER = 'abcdefghijklmnopqrstuvwxyz';
48
-	public const CHAR_DIGITS = '0123456789';
49
-	public const CHAR_SYMBOLS = '!\"#$%&\\\'()*+,-./:;<=>?@[\]^_`{|}~';
50
-	public const CHAR_ALPHANUMERIC = self::CHAR_UPPER . self::CHAR_LOWER . self::CHAR_DIGITS;
43
+    /**
44
+     * Flags for characters that can be used for <code>generate($length, $characters)</code>
45
+     */
46
+    public const CHAR_UPPER = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ';
47
+    public const CHAR_LOWER = 'abcdefghijklmnopqrstuvwxyz';
48
+    public const CHAR_DIGITS = '0123456789';
49
+    public const CHAR_SYMBOLS = '!\"#$%&\\\'()*+,-./:;<=>?@[\]^_`{|}~';
50
+    public const CHAR_ALPHANUMERIC = self::CHAR_UPPER . self::CHAR_LOWER . self::CHAR_DIGITS;
51 51
 
52
-	/**
53
-	 * Characters that can be used for <code>generate($length, $characters)</code>, to
54
-	 * generate human readable random strings. Lower- and upper-case characters and digits
55
-	 * are included. Characters which are ambiguous are excluded, such as I, l, and 1 and so on.
56
-	 */
57
-	public const CHAR_HUMAN_READABLE = 'abcdefgijkmnopqrstwxyzABCDEFGHJKLMNPQRSTWXYZ23456789';
52
+    /**
53
+     * Characters that can be used for <code>generate($length, $characters)</code>, to
54
+     * generate human readable random strings. Lower- and upper-case characters and digits
55
+     * are included. Characters which are ambiguous are excluded, such as I, l, and 1 and so on.
56
+     */
57
+    public const CHAR_HUMAN_READABLE = 'abcdefgijkmnopqrstwxyzABCDEFGHJKLMNPQRSTWXYZ23456789';
58 58
 
59
-	/**
60
-	 * Generate a random string of specified length.
61
-	 * @param int $length The length of the generated string
62
-	 * @param string $characters An optional list of characters to use if no character list is
63
-	 * 							specified all valid base64 characters are used.
64
-	 * @return string
65
-	 * @since 8.0.0
66
-	 */
67
-	public function generate(int $length,
68
-							 string $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'): string;
59
+    /**
60
+     * Generate a random string of specified length.
61
+     * @param int $length The length of the generated string
62
+     * @param string $characters An optional list of characters to use if no character list is
63
+     * 							specified all valid base64 characters are used.
64
+     * @return string
65
+     * @since 8.0.0
66
+     */
67
+    public function generate(int $length,
68
+                                string $characters = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'): string;
69 69
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -47,7 +47,7 @@
 block discarded – undo
47 47
 	public const CHAR_LOWER = 'abcdefghijklmnopqrstuvwxyz';
48 48
 	public const CHAR_DIGITS = '0123456789';
49 49
 	public const CHAR_SYMBOLS = '!\"#$%&\\\'()*+,-./:;<=>?@[\]^_`{|}~';
50
-	public const CHAR_ALPHANUMERIC = self::CHAR_UPPER . self::CHAR_LOWER . self::CHAR_DIGITS;
50
+	public const CHAR_ALPHANUMERIC = self::CHAR_UPPER.self::CHAR_LOWER.self::CHAR_DIGITS;
51 51
 
52 52
 	/**
53 53
 	 * Characters that can be used for <code>generate($length, $characters)</code>, to
Please login to merge, or discard this patch.
lib/private/Setup/PostgreSQL.php 2 patches
Indentation   +124 added lines, -124 removed lines patch added patch discarded remove patch
@@ -34,141 +34,141 @@
 block discarded – undo
34 34
 use OCP\Security\ISecureRandom;
35 35
 
36 36
 class PostgreSQL extends AbstractDatabase {
37
-	public $dbprettyname = 'PostgreSQL';
37
+    public $dbprettyname = 'PostgreSQL';
38 38
 
39
-	/**
40
-	 * @param string $username
41
-	 * @throws \OC\DatabaseSetupException
42
-	 */
43
-	public function setupDatabase($username) {
44
-		try {
45
-			$connection = $this->connect([
46
-				'dbname' => 'postgres'
47
-			]);
48
-			//check for roles creation rights in postgresql
49
-			$builder = $connection->getQueryBuilder();
50
-			$builder->automaticTablePrefix(false);
51
-			$query = $builder
52
-				->select('rolname')
53
-				->from('pg_roles')
54
-				->where($builder->expr()->eq('rolcreaterole', new Literal('TRUE')))
55
-				->andWhere($builder->expr()->eq('rolname', $builder->createNamedParameter($this->dbUser)));
39
+    /**
40
+     * @param string $username
41
+     * @throws \OC\DatabaseSetupException
42
+     */
43
+    public function setupDatabase($username) {
44
+        try {
45
+            $connection = $this->connect([
46
+                'dbname' => 'postgres'
47
+            ]);
48
+            //check for roles creation rights in postgresql
49
+            $builder = $connection->getQueryBuilder();
50
+            $builder->automaticTablePrefix(false);
51
+            $query = $builder
52
+                ->select('rolname')
53
+                ->from('pg_roles')
54
+                ->where($builder->expr()->eq('rolcreaterole', new Literal('TRUE')))
55
+                ->andWhere($builder->expr()->eq('rolname', $builder->createNamedParameter($this->dbUser)));
56 56
 
57
-			try {
58
-				$result = $query->execute();
59
-				$canCreateRoles = $result->rowCount() > 0;
60
-			} catch (DatabaseException $e) {
61
-				$canCreateRoles = false;
62
-			}
57
+            try {
58
+                $result = $query->execute();
59
+                $canCreateRoles = $result->rowCount() > 0;
60
+            } catch (DatabaseException $e) {
61
+                $canCreateRoles = false;
62
+            }
63 63
 
64
-			if ($canCreateRoles) {
65
-				//use the admin login data for the new database user
64
+            if ($canCreateRoles) {
65
+                //use the admin login data for the new database user
66 66
 
67
-				//add prefix to the postgresql user name to prevent collisions
68
-				$this->dbUser = 'oc_' . strtolower($username);
69
-				//create a new password so we don't need to store the admin config in the config file
70
-				$this->dbPassword = \OC::$server->getSecureRandom()->generate(30, ISecureRandom::CHAR_ALPHANUMERIC);
67
+                //add prefix to the postgresql user name to prevent collisions
68
+                $this->dbUser = 'oc_' . strtolower($username);
69
+                //create a new password so we don't need to store the admin config in the config file
70
+                $this->dbPassword = \OC::$server->getSecureRandom()->generate(30, ISecureRandom::CHAR_ALPHANUMERIC);
71 71
 
72
-				$this->createDBUser($connection);
73
-			}
72
+                $this->createDBUser($connection);
73
+            }
74 74
 
75
-			$this->config->setValues([
76
-				'dbuser' => $this->dbUser,
77
-				'dbpassword' => $this->dbPassword,
78
-			]);
75
+            $this->config->setValues([
76
+                'dbuser' => $this->dbUser,
77
+                'dbpassword' => $this->dbPassword,
78
+            ]);
79 79
 
80
-			//create the database
81
-			$this->createDatabase($connection);
82
-			// the connection to dbname=postgres is not needed anymore
83
-			$connection->close();
84
-		} catch (\Exception $e) {
85
-			$this->logger->warning('Error trying to connect as "postgres", assuming database is setup and tables need to be created', [
86
-				'exception' => $e,
87
-			]);
88
-			$this->config->setValues([
89
-				'dbuser' => $this->dbUser,
90
-				'dbpassword' => $this->dbPassword,
91
-			]);
92
-		}
80
+            //create the database
81
+            $this->createDatabase($connection);
82
+            // the connection to dbname=postgres is not needed anymore
83
+            $connection->close();
84
+        } catch (\Exception $e) {
85
+            $this->logger->warning('Error trying to connect as "postgres", assuming database is setup and tables need to be created', [
86
+                'exception' => $e,
87
+            ]);
88
+            $this->config->setValues([
89
+                'dbuser' => $this->dbUser,
90
+                'dbpassword' => $this->dbPassword,
91
+            ]);
92
+        }
93 93
 
94
-		// connect to the database (dbname=$this->dbname) and check if it needs to be filled
95
-		$this->dbUser = $this->config->getValue('dbuser');
96
-		$this->dbPassword = $this->config->getValue('dbpassword');
97
-		$connection = $this->connect();
98
-		try {
99
-			$connection->connect();
100
-		} catch (\Exception $e) {
101
-			$this->logger->error($e->getMessage(), [
102
-				'exception' => $e,
103
-			]);
104
-			throw new \OC\DatabaseSetupException($this->trans->t('PostgreSQL username and/or password not valid'),
105
-				$this->trans->t('You need to enter details of an existing account.'), 0, $e);
106
-		}
107
-	}
94
+        // connect to the database (dbname=$this->dbname) and check if it needs to be filled
95
+        $this->dbUser = $this->config->getValue('dbuser');
96
+        $this->dbPassword = $this->config->getValue('dbpassword');
97
+        $connection = $this->connect();
98
+        try {
99
+            $connection->connect();
100
+        } catch (\Exception $e) {
101
+            $this->logger->error($e->getMessage(), [
102
+                'exception' => $e,
103
+            ]);
104
+            throw new \OC\DatabaseSetupException($this->trans->t('PostgreSQL username and/or password not valid'),
105
+                $this->trans->t('You need to enter details of an existing account.'), 0, $e);
106
+        }
107
+    }
108 108
 
109
-	private function createDatabase(Connection $connection) {
110
-		if (!$this->databaseExists($connection)) {
111
-			//The database does not exists... let's create it
112
-			$query = $connection->prepare("CREATE DATABASE " . addslashes($this->dbName) . " OWNER " . addslashes($this->dbUser));
113
-			try {
114
-				$query->execute();
115
-			} catch (DatabaseException $e) {
116
-				$this->logger->error('Error while trying to create database', [
117
-					'exception' => $e,
118
-				]);
119
-			}
120
-		} else {
121
-			$query = $connection->prepare("REVOKE ALL PRIVILEGES ON DATABASE " . addslashes($this->dbName) . " FROM PUBLIC");
122
-			try {
123
-				$query->execute();
124
-			} catch (DatabaseException $e) {
125
-				$this->logger->error('Error while trying to restrict database permissions', [
126
-					'exception' => $e,
127
-				]);
128
-			}
129
-		}
130
-	}
109
+    private function createDatabase(Connection $connection) {
110
+        if (!$this->databaseExists($connection)) {
111
+            //The database does not exists... let's create it
112
+            $query = $connection->prepare("CREATE DATABASE " . addslashes($this->dbName) . " OWNER " . addslashes($this->dbUser));
113
+            try {
114
+                $query->execute();
115
+            } catch (DatabaseException $e) {
116
+                $this->logger->error('Error while trying to create database', [
117
+                    'exception' => $e,
118
+                ]);
119
+            }
120
+        } else {
121
+            $query = $connection->prepare("REVOKE ALL PRIVILEGES ON DATABASE " . addslashes($this->dbName) . " FROM PUBLIC");
122
+            try {
123
+                $query->execute();
124
+            } catch (DatabaseException $e) {
125
+                $this->logger->error('Error while trying to restrict database permissions', [
126
+                    'exception' => $e,
127
+                ]);
128
+            }
129
+        }
130
+    }
131 131
 
132
-	private function userExists(Connection $connection) {
133
-		$builder = $connection->getQueryBuilder();
134
-		$builder->automaticTablePrefix(false);
135
-		$query = $builder->select('*')
136
-			->from('pg_roles')
137
-			->where($builder->expr()->eq('rolname', $builder->createNamedParameter($this->dbUser)));
138
-		$result = $query->execute();
139
-		return $result->rowCount() > 0;
140
-	}
132
+    private function userExists(Connection $connection) {
133
+        $builder = $connection->getQueryBuilder();
134
+        $builder->automaticTablePrefix(false);
135
+        $query = $builder->select('*')
136
+            ->from('pg_roles')
137
+            ->where($builder->expr()->eq('rolname', $builder->createNamedParameter($this->dbUser)));
138
+        $result = $query->execute();
139
+        return $result->rowCount() > 0;
140
+    }
141 141
 
142
-	private function databaseExists(Connection $connection) {
143
-		$builder = $connection->getQueryBuilder();
144
-		$builder->automaticTablePrefix(false);
145
-		$query = $builder->select('datname')
146
-			->from('pg_database')
147
-			->where($builder->expr()->eq('datname', $builder->createNamedParameter($this->dbName)));
148
-		$result = $query->execute();
149
-		return $result->rowCount() > 0;
150
-	}
142
+    private function databaseExists(Connection $connection) {
143
+        $builder = $connection->getQueryBuilder();
144
+        $builder->automaticTablePrefix(false);
145
+        $query = $builder->select('datname')
146
+            ->from('pg_database')
147
+            ->where($builder->expr()->eq('datname', $builder->createNamedParameter($this->dbName)));
148
+        $result = $query->execute();
149
+        return $result->rowCount() > 0;
150
+    }
151 151
 
152
-	private function createDBUser(Connection $connection) {
153
-		$dbUser = $this->dbUser;
154
-		try {
155
-			$i = 1;
156
-			while ($this->userExists($connection)) {
157
-				$i++;
158
-				$this->dbUser = $dbUser . $i;
159
-			}
152
+    private function createDBUser(Connection $connection) {
153
+        $dbUser = $this->dbUser;
154
+        try {
155
+            $i = 1;
156
+            while ($this->userExists($connection)) {
157
+                $i++;
158
+                $this->dbUser = $dbUser . $i;
159
+            }
160 160
 
161
-			// create the user
162
-			$query = $connection->prepare("CREATE USER " . addslashes($this->dbUser) . " CREATEDB PASSWORD '" . addslashes($this->dbPassword) . "'");
163
-			$query->execute();
164
-			if ($this->databaseExists($connection)) {
165
-				$query = $connection->prepare('GRANT CONNECT ON DATABASE ' . addslashes($this->dbName) . ' TO '.addslashes($this->dbUser));
166
-				$query->execute();
167
-			}
168
-		} catch (DatabaseException $e) {
169
-			$this->logger->error('Error while trying to create database user', [
170
-				'exception' => $e,
171
-			]);
172
-		}
173
-	}
161
+            // create the user
162
+            $query = $connection->prepare("CREATE USER " . addslashes($this->dbUser) . " CREATEDB PASSWORD '" . addslashes($this->dbPassword) . "'");
163
+            $query->execute();
164
+            if ($this->databaseExists($connection)) {
165
+                $query = $connection->prepare('GRANT CONNECT ON DATABASE ' . addslashes($this->dbName) . ' TO '.addslashes($this->dbUser));
166
+                $query->execute();
167
+            }
168
+        } catch (DatabaseException $e) {
169
+            $this->logger->error('Error while trying to create database user', [
170
+                'exception' => $e,
171
+            ]);
172
+        }
173
+    }
174 174
 }
Please login to merge, or discard this patch.
Spacing   +6 added lines, -6 removed lines patch added patch discarded remove patch
@@ -65,7 +65,7 @@  discard block
 block discarded – undo
65 65
 				//use the admin login data for the new database user
66 66
 
67 67
 				//add prefix to the postgresql user name to prevent collisions
68
-				$this->dbUser = 'oc_' . strtolower($username);
68
+				$this->dbUser = 'oc_'.strtolower($username);
69 69
 				//create a new password so we don't need to store the admin config in the config file
70 70
 				$this->dbPassword = \OC::$server->getSecureRandom()->generate(30, ISecureRandom::CHAR_ALPHANUMERIC);
71 71
 
@@ -109,7 +109,7 @@  discard block
 block discarded – undo
109 109
 	private function createDatabase(Connection $connection) {
110 110
 		if (!$this->databaseExists($connection)) {
111 111
 			//The database does not exists... let's create it
112
-			$query = $connection->prepare("CREATE DATABASE " . addslashes($this->dbName) . " OWNER " . addslashes($this->dbUser));
112
+			$query = $connection->prepare("CREATE DATABASE ".addslashes($this->dbName)." OWNER ".addslashes($this->dbUser));
113 113
 			try {
114 114
 				$query->execute();
115 115
 			} catch (DatabaseException $e) {
@@ -118,7 +118,7 @@  discard block
 block discarded – undo
118 118
 				]);
119 119
 			}
120 120
 		} else {
121
-			$query = $connection->prepare("REVOKE ALL PRIVILEGES ON DATABASE " . addslashes($this->dbName) . " FROM PUBLIC");
121
+			$query = $connection->prepare("REVOKE ALL PRIVILEGES ON DATABASE ".addslashes($this->dbName)." FROM PUBLIC");
122 122
 			try {
123 123
 				$query->execute();
124 124
 			} catch (DatabaseException $e) {
@@ -155,14 +155,14 @@  discard block
 block discarded – undo
155 155
 			$i = 1;
156 156
 			while ($this->userExists($connection)) {
157 157
 				$i++;
158
-				$this->dbUser = $dbUser . $i;
158
+				$this->dbUser = $dbUser.$i;
159 159
 			}
160 160
 
161 161
 			// create the user
162
-			$query = $connection->prepare("CREATE USER " . addslashes($this->dbUser) . " CREATEDB PASSWORD '" . addslashes($this->dbPassword) . "'");
162
+			$query = $connection->prepare("CREATE USER ".addslashes($this->dbUser)." CREATEDB PASSWORD '".addslashes($this->dbPassword)."'");
163 163
 			$query->execute();
164 164
 			if ($this->databaseExists($connection)) {
165
-				$query = $connection->prepare('GRANT CONNECT ON DATABASE ' . addslashes($this->dbName) . ' TO '.addslashes($this->dbUser));
165
+				$query = $connection->prepare('GRANT CONNECT ON DATABASE '.addslashes($this->dbName).' TO '.addslashes($this->dbUser));
166 166
 				$query->execute();
167 167
 			}
168 168
 		} catch (DatabaseException $e) {
Please login to merge, or discard this patch.
lib/private/Setup/MySQL.php 2 patches
Indentation   +151 added lines, -151 removed lines patch added patch discarded remove patch
@@ -36,155 +36,155 @@
 block discarded – undo
36 36
 use OCP\Security\ISecureRandom;
37 37
 
38 38
 class MySQL extends AbstractDatabase {
39
-	public $dbprettyname = 'MySQL/MariaDB';
40
-
41
-	public function setupDatabase($username) {
42
-		//check if the database user has admin right
43
-		$connection = $this->connect(['dbname' => null]);
44
-
45
-		// detect mb4
46
-		$tools = new MySqlTools();
47
-		if ($tools->supports4ByteCharset(new ConnectionAdapter($connection))) {
48
-			$this->config->setValue('mysql.utf8mb4', true);
49
-			$connection = $this->connect(['dbname' => null]);
50
-		}
51
-
52
-		$this->createSpecificUser($username, new ConnectionAdapter($connection));
53
-
54
-		//create the database
55
-		$this->createDatabase($connection);
56
-
57
-		//fill the database if needed
58
-		$query = 'select count(*) from information_schema.tables where table_schema=? AND table_name = ?';
59
-		$connection->executeQuery($query, [$this->dbName, $this->tablePrefix.'users']);
60
-
61
-		$connection->close();
62
-		$connection = $this->connect();
63
-		try {
64
-			$connection->connect();
65
-		} catch (\Exception $e) {
66
-			$this->logger->error($e->getMessage(), [
67
-				'exception' => $e,
68
-			]);
69
-			throw new \OC\DatabaseSetupException($this->trans->t('MySQL username and/or password not valid'),
70
-				$this->trans->t('You need to enter details of an existing account.'), 0, $e);
71
-		}
72
-	}
73
-
74
-	/**
75
-	 * @param \OC\DB\Connection $connection
76
-	 */
77
-	private function createDatabase($connection) {
78
-		try {
79
-			$name = $this->dbName;
80
-			$user = $this->dbUser;
81
-			//we can't use OC_DB functions here because we need to connect as the administrative user.
82
-			$characterSet = $this->config->getValue('mysql.utf8mb4', false) ? 'utf8mb4' : 'utf8';
83
-			$query = "CREATE DATABASE IF NOT EXISTS `$name` CHARACTER SET $characterSet COLLATE ${characterSet}_bin;";
84
-			$connection->executeUpdate($query);
85
-		} catch (\Exception $ex) {
86
-			$this->logger->error('Database creation failed.', [
87
-				'exception' => $ex,
88
-				'app' => 'mysql.setup',
89
-			]);
90
-			return;
91
-		}
92
-
93
-		try {
94
-			//this query will fail if there aren't the right permissions, ignore the error
95
-			$query = "GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, EVENT, TRIGGER ON `$name` . * TO '$user'";
96
-			$connection->executeUpdate($query);
97
-		} catch (\Exception $ex) {
98
-			$this->logger->debug('Could not automatically grant privileges, this can be ignored if database user already had privileges.', [
99
-				'exception' => $ex,
100
-				'app' => 'mysql.setup',
101
-			]);
102
-		}
103
-	}
104
-
105
-	/**
106
-	 * @param IDBConnection $connection
107
-	 * @throws \OC\DatabaseSetupException
108
-	 */
109
-	private function createDBUser($connection) {
110
-		try {
111
-			$name = $this->dbUser;
112
-			$password = $this->dbPassword;
113
-			// we need to create 2 accounts, one for global use and one for local user. if we don't specify the local one,
114
-			// the anonymous user would take precedence when there is one.
115
-
116
-			if ($connection->getDatabasePlatform() instanceof Mysql80Platform) {
117
-				$query = "CREATE USER '$name'@'localhost' IDENTIFIED WITH mysql_native_password BY '$password'";
118
-				$connection->executeUpdate($query);
119
-				$query = "CREATE USER '$name'@'%' IDENTIFIED WITH mysql_native_password BY '$password'";
120
-				$connection->executeUpdate($query);
121
-			} else {
122
-				$query = "CREATE USER '$name'@'localhost' IDENTIFIED BY '$password'";
123
-				$connection->executeUpdate($query);
124
-				$query = "CREATE USER '$name'@'%' IDENTIFIED BY '$password'";
125
-				$connection->executeUpdate($query);
126
-			}
127
-		} catch (\Exception $ex) {
128
-			$this->logger->error('Database user creation failed.',[
129
-				'exception' => $ex,
130
-				'app' => 'mysql.setup',
131
-			]);
132
-		}
133
-	}
134
-
135
-	/**
136
-	 * @param $username
137
-	 * @param IDBConnection $connection
138
-	 * @return array
139
-	 */
140
-	private function createSpecificUser($username, $connection) {
141
-		try {
142
-			//user already specified in config
143
-			$oldUser = $this->config->getValue('dbuser', false);
144
-
145
-			//we don't have a dbuser specified in config
146
-			if ($this->dbUser !== $oldUser) {
147
-				//add prefix to the admin username to prevent collisions
148
-				$adminUser = substr('oc_' . $username, 0, 16);
149
-
150
-				$i = 1;
151
-				while (true) {
152
-					//this should be enough to check for admin rights in mysql
153
-					$query = 'SELECT user FROM mysql.user WHERE user=?';
154
-					$result = $connection->executeQuery($query, [$adminUser]);
155
-
156
-					//current dbuser has admin rights
157
-					$data = $result->fetchAll();
158
-					$result->closeCursor();
159
-					//new dbuser does not exist
160
-					if (count($data) === 0) {
161
-						//use the admin login data for the new database user
162
-						$this->dbUser = $adminUser;
163
-
164
-						//create a random password so we don't need to store the admin password in the config file
165
-						$this->dbPassword = $this->random->generate(30, ISecureRandom::CHAR_ALPHANUMERIC);
166
-
167
-						$this->createDBUser($connection);
168
-
169
-						break;
170
-					} else {
171
-						//repeat with different username
172
-						$length = strlen((string)$i);
173
-						$adminUser = substr('oc_' . $username, 0, 16 - $length) . $i;
174
-						$i++;
175
-					}
176
-				}
177
-			}
178
-		} catch (\Exception $ex) {
179
-			$this->logger->info('Can not create a new MySQL user, will continue with the provided user.', [
180
-				'exception' => $ex,
181
-				'app' => 'mysql.setup',
182
-			]);
183
-		}
184
-
185
-		$this->config->setValues([
186
-			'dbuser' => $this->dbUser,
187
-			'dbpassword' => $this->dbPassword,
188
-		]);
189
-	}
39
+    public $dbprettyname = 'MySQL/MariaDB';
40
+
41
+    public function setupDatabase($username) {
42
+        //check if the database user has admin right
43
+        $connection = $this->connect(['dbname' => null]);
44
+
45
+        // detect mb4
46
+        $tools = new MySqlTools();
47
+        if ($tools->supports4ByteCharset(new ConnectionAdapter($connection))) {
48
+            $this->config->setValue('mysql.utf8mb4', true);
49
+            $connection = $this->connect(['dbname' => null]);
50
+        }
51
+
52
+        $this->createSpecificUser($username, new ConnectionAdapter($connection));
53
+
54
+        //create the database
55
+        $this->createDatabase($connection);
56
+
57
+        //fill the database if needed
58
+        $query = 'select count(*) from information_schema.tables where table_schema=? AND table_name = ?';
59
+        $connection->executeQuery($query, [$this->dbName, $this->tablePrefix.'users']);
60
+
61
+        $connection->close();
62
+        $connection = $this->connect();
63
+        try {
64
+            $connection->connect();
65
+        } catch (\Exception $e) {
66
+            $this->logger->error($e->getMessage(), [
67
+                'exception' => $e,
68
+            ]);
69
+            throw new \OC\DatabaseSetupException($this->trans->t('MySQL username and/or password not valid'),
70
+                $this->trans->t('You need to enter details of an existing account.'), 0, $e);
71
+        }
72
+    }
73
+
74
+    /**
75
+     * @param \OC\DB\Connection $connection
76
+     */
77
+    private function createDatabase($connection) {
78
+        try {
79
+            $name = $this->dbName;
80
+            $user = $this->dbUser;
81
+            //we can't use OC_DB functions here because we need to connect as the administrative user.
82
+            $characterSet = $this->config->getValue('mysql.utf8mb4', false) ? 'utf8mb4' : 'utf8';
83
+            $query = "CREATE DATABASE IF NOT EXISTS `$name` CHARACTER SET $characterSet COLLATE ${characterSet}_bin;";
84
+            $connection->executeUpdate($query);
85
+        } catch (\Exception $ex) {
86
+            $this->logger->error('Database creation failed.', [
87
+                'exception' => $ex,
88
+                'app' => 'mysql.setup',
89
+            ]);
90
+            return;
91
+        }
92
+
93
+        try {
94
+            //this query will fail if there aren't the right permissions, ignore the error
95
+            $query = "GRANT SELECT, INSERT, UPDATE, DELETE, CREATE, DROP, REFERENCES, INDEX, ALTER, CREATE TEMPORARY TABLES, LOCK TABLES, EXECUTE, CREATE VIEW, SHOW VIEW, CREATE ROUTINE, ALTER ROUTINE, EVENT, TRIGGER ON `$name` . * TO '$user'";
96
+            $connection->executeUpdate($query);
97
+        } catch (\Exception $ex) {
98
+            $this->logger->debug('Could not automatically grant privileges, this can be ignored if database user already had privileges.', [
99
+                'exception' => $ex,
100
+                'app' => 'mysql.setup',
101
+            ]);
102
+        }
103
+    }
104
+
105
+    /**
106
+     * @param IDBConnection $connection
107
+     * @throws \OC\DatabaseSetupException
108
+     */
109
+    private function createDBUser($connection) {
110
+        try {
111
+            $name = $this->dbUser;
112
+            $password = $this->dbPassword;
113
+            // we need to create 2 accounts, one for global use and one for local user. if we don't specify the local one,
114
+            // the anonymous user would take precedence when there is one.
115
+
116
+            if ($connection->getDatabasePlatform() instanceof Mysql80Platform) {
117
+                $query = "CREATE USER '$name'@'localhost' IDENTIFIED WITH mysql_native_password BY '$password'";
118
+                $connection->executeUpdate($query);
119
+                $query = "CREATE USER '$name'@'%' IDENTIFIED WITH mysql_native_password BY '$password'";
120
+                $connection->executeUpdate($query);
121
+            } else {
122
+                $query = "CREATE USER '$name'@'localhost' IDENTIFIED BY '$password'";
123
+                $connection->executeUpdate($query);
124
+                $query = "CREATE USER '$name'@'%' IDENTIFIED BY '$password'";
125
+                $connection->executeUpdate($query);
126
+            }
127
+        } catch (\Exception $ex) {
128
+            $this->logger->error('Database user creation failed.',[
129
+                'exception' => $ex,
130
+                'app' => 'mysql.setup',
131
+            ]);
132
+        }
133
+    }
134
+
135
+    /**
136
+     * @param $username
137
+     * @param IDBConnection $connection
138
+     * @return array
139
+     */
140
+    private function createSpecificUser($username, $connection) {
141
+        try {
142
+            //user already specified in config
143
+            $oldUser = $this->config->getValue('dbuser', false);
144
+
145
+            //we don't have a dbuser specified in config
146
+            if ($this->dbUser !== $oldUser) {
147
+                //add prefix to the admin username to prevent collisions
148
+                $adminUser = substr('oc_' . $username, 0, 16);
149
+
150
+                $i = 1;
151
+                while (true) {
152
+                    //this should be enough to check for admin rights in mysql
153
+                    $query = 'SELECT user FROM mysql.user WHERE user=?';
154
+                    $result = $connection->executeQuery($query, [$adminUser]);
155
+
156
+                    //current dbuser has admin rights
157
+                    $data = $result->fetchAll();
158
+                    $result->closeCursor();
159
+                    //new dbuser does not exist
160
+                    if (count($data) === 0) {
161
+                        //use the admin login data for the new database user
162
+                        $this->dbUser = $adminUser;
163
+
164
+                        //create a random password so we don't need to store the admin password in the config file
165
+                        $this->dbPassword = $this->random->generate(30, ISecureRandom::CHAR_ALPHANUMERIC);
166
+
167
+                        $this->createDBUser($connection);
168
+
169
+                        break;
170
+                    } else {
171
+                        //repeat with different username
172
+                        $length = strlen((string)$i);
173
+                        $adminUser = substr('oc_' . $username, 0, 16 - $length) . $i;
174
+                        $i++;
175
+                    }
176
+                }
177
+            }
178
+        } catch (\Exception $ex) {
179
+            $this->logger->info('Can not create a new MySQL user, will continue with the provided user.', [
180
+                'exception' => $ex,
181
+                'app' => 'mysql.setup',
182
+            ]);
183
+        }
184
+
185
+        $this->config->setValues([
186
+            'dbuser' => $this->dbUser,
187
+            'dbpassword' => $this->dbPassword,
188
+        ]);
189
+    }
190 190
 }
Please login to merge, or discard this patch.
Spacing   +4 added lines, -4 removed lines patch added patch discarded remove patch
@@ -125,7 +125,7 @@  discard block
 block discarded – undo
125 125
 				$connection->executeUpdate($query);
126 126
 			}
127 127
 		} catch (\Exception $ex) {
128
-			$this->logger->error('Database user creation failed.',[
128
+			$this->logger->error('Database user creation failed.', [
129 129
 				'exception' => $ex,
130 130
 				'app' => 'mysql.setup',
131 131
 			]);
@@ -145,7 +145,7 @@  discard block
 block discarded – undo
145 145
 			//we don't have a dbuser specified in config
146 146
 			if ($this->dbUser !== $oldUser) {
147 147
 				//add prefix to the admin username to prevent collisions
148
-				$adminUser = substr('oc_' . $username, 0, 16);
148
+				$adminUser = substr('oc_'.$username, 0, 16);
149 149
 
150 150
 				$i = 1;
151 151
 				while (true) {
@@ -169,8 +169,8 @@  discard block
 block discarded – undo
169 169
 						break;
170 170
 					} else {
171 171
 						//repeat with different username
172
-						$length = strlen((string)$i);
173
-						$adminUser = substr('oc_' . $username, 0, 16 - $length) . $i;
172
+						$length = strlen((string) $i);
173
+						$adminUser = substr('oc_'.$username, 0, 16 - $length).$i;
174 174
 						$i++;
175 175
 					}
176 176
 				}
Please login to merge, or discard this patch.
lib/private/AppFramework/Http/Request.php 2 patches
Indentation   +858 added lines, -858 removed lines patch added patch discarded remove patch
@@ -63,862 +63,862 @@
 block discarded – undo
63 63
  * @property mixed[] server
64 64
  */
65 65
 class Request implements \ArrayAccess, \Countable, IRequest {
66
-	public const USER_AGENT_IE = '/(MSIE)|(Trident)/';
67
-	// Microsoft Edge User Agent from https://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
68
-	public const USER_AGENT_MS_EDGE = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Chrome\/[0-9.]+ (Mobile Safari|Safari)\/[0-9.]+ Edge\/[0-9.]+$/';
69
-	// Firefox User Agent from https://developer.mozilla.org/en-US/docs/Web/HTTP/Gecko_user_agent_string_reference
70
-	public const USER_AGENT_FIREFOX = '/^Mozilla\/5\.0 \([^)]+\) Gecko\/[0-9.]+ Firefox\/[0-9.]+$/';
71
-	// Chrome User Agent from https://developer.chrome.com/multidevice/user-agent
72
-	public const USER_AGENT_CHROME = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\)( Ubuntu Chromium\/[0-9.]+|) Chrome\/[0-9.]+ (Mobile Safari|Safari)\/[0-9.]+( (Vivaldi|Brave|OPR)\/[0-9.]+|)$/';
73
-	// Safari User Agent from http://www.useragentstring.com/pages/Safari/
74
-	public const USER_AGENT_SAFARI = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Version\/[0-9.]+ Safari\/[0-9.A-Z]+$/';
75
-	// Android Chrome user agent: https://developers.google.com/chrome/mobile/docs/user-agent
76
-	public const USER_AGENT_ANDROID_MOBILE_CHROME = '#Android.*Chrome/[.0-9]*#';
77
-	public const USER_AGENT_FREEBOX = '#^Mozilla/5\.0$#';
78
-	public const REGEX_LOCALHOST = '/^(127\.0\.0\.1|localhost|\[::1\])$/';
79
-
80
-	protected $inputStream;
81
-	protected $content;
82
-	protected $items = [];
83
-	protected $allowedKeys = [
84
-		'get',
85
-		'post',
86
-		'files',
87
-		'server',
88
-		'env',
89
-		'cookies',
90
-		'urlParams',
91
-		'parameters',
92
-		'method',
93
-		'requesttoken',
94
-	];
95
-	/** @var ISecureRandom */
96
-	protected $secureRandom;
97
-	/** @var IConfig */
98
-	protected $config;
99
-	/** @var string */
100
-	protected $requestId = '';
101
-	/** @var ICrypto */
102
-	protected $crypto;
103
-	/** @var CsrfTokenManager|null */
104
-	protected $csrfTokenManager;
105
-
106
-	/** @var bool */
107
-	protected $contentDecoded = false;
108
-
109
-	/**
110
-	 * @param array $vars An associative array with the following optional values:
111
-	 *        - array 'urlParams' the parameters which were matched from the URL
112
-	 *        - array 'get' the $_GET array
113
-	 *        - array|string 'post' the $_POST array or JSON string
114
-	 *        - array 'files' the $_FILES array
115
-	 *        - array 'server' the $_SERVER array
116
-	 *        - array 'env' the $_ENV array
117
-	 *        - array 'cookies' the $_COOKIE array
118
-	 *        - string 'method' the request method (GET, POST etc)
119
-	 *        - string|false 'requesttoken' the requesttoken or false when not available
120
-	 * @param ISecureRandom $secureRandom
121
-	 * @param IConfig $config
122
-	 * @param CsrfTokenManager|null $csrfTokenManager
123
-	 * @param string $stream
124
-	 * @see https://www.php.net/manual/en/reserved.variables.php
125
-	 */
126
-	public function __construct(array $vars,
127
-								ISecureRandom $secureRandom,
128
-								IConfig $config,
129
-								CsrfTokenManager $csrfTokenManager = null,
130
-								string $stream = 'php://input') {
131
-		$this->inputStream = $stream;
132
-		$this->items['params'] = [];
133
-		$this->secureRandom = $secureRandom;
134
-		$this->config = $config;
135
-		$this->csrfTokenManager = $csrfTokenManager;
136
-
137
-		if (!array_key_exists('method', $vars)) {
138
-			$vars['method'] = 'GET';
139
-		}
140
-
141
-		foreach ($this->allowedKeys as $name) {
142
-			$this->items[$name] = isset($vars[$name])
143
-				? $vars[$name]
144
-				: [];
145
-		}
146
-
147
-		$this->items['parameters'] = array_merge(
148
-			$this->items['get'],
149
-			$this->items['post'],
150
-			$this->items['urlParams'],
151
-			$this->items['params']
152
-		);
153
-	}
154
-	/**
155
-	 * @param array $parameters
156
-	 */
157
-	public function setUrlParameters(array $parameters) {
158
-		$this->items['urlParams'] = $parameters;
159
-		$this->items['parameters'] = array_merge(
160
-			$this->items['parameters'],
161
-			$this->items['urlParams']
162
-		);
163
-	}
164
-
165
-	/**
166
-	 * Countable method
167
-	 * @return int
168
-	 */
169
-	public function count(): int {
170
-		return \count($this->items['parameters']);
171
-	}
172
-
173
-	/**
174
-	 * ArrayAccess methods
175
-	 *
176
-	 * Gives access to the combined GET, POST and urlParams arrays
177
-	 *
178
-	 * Examples:
179
-	 *
180
-	 * $var = $request['myvar'];
181
-	 *
182
-	 * or
183
-	 *
184
-	 * if(!isset($request['myvar']) {
185
-	 * 	// Do something
186
-	 * }
187
-	 *
188
-	 * $request['myvar'] = 'something'; // This throws an exception.
189
-	 *
190
-	 * @param string $offset The key to lookup
191
-	 * @return boolean
192
-	 */
193
-	public function offsetExists($offset): bool {
194
-		return isset($this->items['parameters'][$offset]);
195
-	}
196
-
197
-	/**
198
-	 * @see offsetExists
199
-	 * @param string $offset
200
-	 * @return mixed
201
-	 */
202
-	public function offsetGet($offset) {
203
-		return isset($this->items['parameters'][$offset])
204
-			? $this->items['parameters'][$offset]
205
-			: null;
206
-	}
207
-
208
-	/**
209
-	 * @see offsetExists
210
-	 * @param string $offset
211
-	 * @param mixed $value
212
-	 */
213
-	public function offsetSet($offset, $value) {
214
-		throw new \RuntimeException('You cannot change the contents of the request object');
215
-	}
216
-
217
-	/**
218
-	 * @see offsetExists
219
-	 * @param string $offset
220
-	 */
221
-	public function offsetUnset($offset) {
222
-		throw new \RuntimeException('You cannot change the contents of the request object');
223
-	}
224
-
225
-	/**
226
-	 * Magic property accessors
227
-	 * @param string $name
228
-	 * @param mixed $value
229
-	 */
230
-	public function __set($name, $value) {
231
-		throw new \RuntimeException('You cannot change the contents of the request object');
232
-	}
233
-
234
-	/**
235
-	 * Access request variables by method and name.
236
-	 * Examples:
237
-	 *
238
-	 * $request->post['myvar']; // Only look for POST variables
239
-	 * $request->myvar; or $request->{'myvar'}; or $request->{$myvar}
240
-	 * Looks in the combined GET, POST and urlParams array.
241
-	 *
242
-	 * If you access e.g. ->post but the current HTTP request method
243
-	 * is GET a \LogicException will be thrown.
244
-	 *
245
-	 * @param string $name The key to look for.
246
-	 * @throws \LogicException
247
-	 * @return mixed|null
248
-	 */
249
-	public function __get($name) {
250
-		switch ($name) {
251
-			case 'put':
252
-			case 'patch':
253
-			case 'get':
254
-			case 'post':
255
-				if ($this->method !== strtoupper($name)) {
256
-					throw new \LogicException(sprintf('%s cannot be accessed in a %s request.', $name, $this->method));
257
-				}
258
-				return $this->getContent();
259
-			case 'files':
260
-			case 'server':
261
-			case 'env':
262
-			case 'cookies':
263
-			case 'urlParams':
264
-			case 'method':
265
-				return isset($this->items[$name])
266
-					? $this->items[$name]
267
-					: null;
268
-			case 'parameters':
269
-			case 'params':
270
-				return $this->getContent();
271
-			default:
272
-				return isset($this[$name])
273
-					? $this[$name]
274
-					: null;
275
-		}
276
-	}
277
-
278
-	/**
279
-	 * @param string $name
280
-	 * @return bool
281
-	 */
282
-	public function __isset($name) {
283
-		if (\in_array($name, $this->allowedKeys, true)) {
284
-			return true;
285
-		}
286
-		return isset($this->items['parameters'][$name]);
287
-	}
288
-
289
-	/**
290
-	 * @param string $id
291
-	 */
292
-	public function __unset($id) {
293
-		throw new \RuntimeException('You cannot change the contents of the request object');
294
-	}
295
-
296
-	/**
297
-	 * Returns the value for a specific http header.
298
-	 *
299
-	 * This method returns an empty string if the header did not exist.
300
-	 *
301
-	 * @param string $name
302
-	 * @return string
303
-	 */
304
-	public function getHeader(string $name): string {
305
-		$name = strtoupper(str_replace('-', '_',$name));
306
-		if (isset($this->server['HTTP_' . $name])) {
307
-			return $this->server['HTTP_' . $name];
308
-		}
309
-
310
-		// There's a few headers that seem to end up in the top-level
311
-		// server array.
312
-		switch ($name) {
313
-			case 'CONTENT_TYPE':
314
-			case 'CONTENT_LENGTH':
315
-			case 'REMOTE_ADDR':
316
-				if (isset($this->server[$name])) {
317
-					return $this->server[$name];
318
-				}
319
-				break;
320
-		}
321
-
322
-		return '';
323
-	}
324
-
325
-	/**
326
-	 * Lets you access post and get parameters by the index
327
-	 * In case of json requests the encoded json body is accessed
328
-	 *
329
-	 * @param string $key the key which you want to access in the URL Parameter
330
-	 *                     placeholder, $_POST or $_GET array.
331
-	 *                     The priority how they're returned is the following:
332
-	 *                     1. URL parameters
333
-	 *                     2. POST parameters
334
-	 *                     3. GET parameters
335
-	 * @param mixed $default If the key is not found, this value will be returned
336
-	 * @return mixed the content of the array
337
-	 */
338
-	public function getParam(string $key, $default = null) {
339
-		return isset($this->parameters[$key])
340
-			? $this->parameters[$key]
341
-			: $default;
342
-	}
343
-
344
-	/**
345
-	 * Returns all params that were received, be it from the request
346
-	 * (as GET or POST) or throuh the URL by the route
347
-	 * @return array the array with all parameters
348
-	 */
349
-	public function getParams(): array {
350
-		return is_array($this->parameters) ? $this->parameters : [];
351
-	}
352
-
353
-	/**
354
-	 * Returns the method of the request
355
-	 * @return string the method of the request (POST, GET, etc)
356
-	 */
357
-	public function getMethod(): string {
358
-		return $this->method;
359
-	}
360
-
361
-	/**
362
-	 * Shortcut for accessing an uploaded file through the $_FILES array
363
-	 * @param string $key the key that will be taken from the $_FILES array
364
-	 * @return array the file in the $_FILES element
365
-	 */
366
-	public function getUploadedFile(string $key) {
367
-		return isset($this->files[$key]) ? $this->files[$key] : null;
368
-	}
369
-
370
-	/**
371
-	 * Shortcut for getting env variables
372
-	 * @param string $key the key that will be taken from the $_ENV array
373
-	 * @return array the value in the $_ENV element
374
-	 */
375
-	public function getEnv(string $key) {
376
-		return isset($this->env[$key]) ? $this->env[$key] : null;
377
-	}
378
-
379
-	/**
380
-	 * Shortcut for getting cookie variables
381
-	 * @param string $key the key that will be taken from the $_COOKIE array
382
-	 * @return string the value in the $_COOKIE element
383
-	 */
384
-	public function getCookie(string $key) {
385
-		return isset($this->cookies[$key]) ? $this->cookies[$key] : null;
386
-	}
387
-
388
-	/**
389
-	 * Returns the request body content.
390
-	 *
391
-	 * If the HTTP request method is PUT and the body
392
-	 * not application/x-www-form-urlencoded or application/json a stream
393
-	 * resource is returned, otherwise an array.
394
-	 *
395
-	 * @return array|string|resource The request body content or a resource to read the body stream.
396
-	 *
397
-	 * @throws \LogicException
398
-	 */
399
-	protected function getContent() {
400
-		// If the content can't be parsed into an array then return a stream resource.
401
-		if ($this->method === 'PUT'
402
-			&& $this->getHeader('Content-Length') !== '0'
403
-			&& $this->getHeader('Content-Length') !== ''
404
-			&& strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') === false
405
-			&& strpos($this->getHeader('Content-Type'), 'application/json') === false
406
-		) {
407
-			if ($this->content === false) {
408
-				throw new \LogicException(
409
-					'"put" can only be accessed once if not '
410
-					. 'application/x-www-form-urlencoded or application/json.'
411
-				);
412
-			}
413
-			$this->content = false;
414
-			return fopen($this->inputStream, 'rb');
415
-		} else {
416
-			$this->decodeContent();
417
-			return $this->items['parameters'];
418
-		}
419
-	}
420
-
421
-	/**
422
-	 * Attempt to decode the content and populate parameters
423
-	 */
424
-	protected function decodeContent() {
425
-		if ($this->contentDecoded) {
426
-			return;
427
-		}
428
-		$params = [];
429
-
430
-		// 'application/json' must be decoded manually.
431
-		if (strpos($this->getHeader('Content-Type'), 'application/json') !== false) {
432
-			$params = json_decode(file_get_contents($this->inputStream), true);
433
-			if ($params !== null && \count($params) > 0) {
434
-				$this->items['params'] = $params;
435
-				if ($this->method === 'POST') {
436
-					$this->items['post'] = $params;
437
-				}
438
-			}
439
-
440
-			// Handle application/x-www-form-urlencoded for methods other than GET
441
-		// or post correctly
442
-		} elseif ($this->method !== 'GET'
443
-				&& $this->method !== 'POST'
444
-				&& strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') !== false) {
445
-			parse_str(file_get_contents($this->inputStream), $params);
446
-			if (\is_array($params)) {
447
-				$this->items['params'] = $params;
448
-			}
449
-		}
450
-
451
-		if (\is_array($params)) {
452
-			$this->items['parameters'] = array_merge($this->items['parameters'], $params);
453
-		}
454
-		$this->contentDecoded = true;
455
-	}
456
-
457
-
458
-	/**
459
-	 * Checks if the CSRF check was correct
460
-	 * @return bool true if CSRF check passed
461
-	 */
462
-	public function passesCSRFCheck(): bool {
463
-		if ($this->csrfTokenManager === null) {
464
-			return false;
465
-		}
466
-
467
-		if (!$this->passesStrictCookieCheck()) {
468
-			return false;
469
-		}
470
-
471
-		if (isset($this->items['get']['requesttoken'])) {
472
-			$token = $this->items['get']['requesttoken'];
473
-		} elseif (isset($this->items['post']['requesttoken'])) {
474
-			$token = $this->items['post']['requesttoken'];
475
-		} elseif (isset($this->items['server']['HTTP_REQUESTTOKEN'])) {
476
-			$token = $this->items['server']['HTTP_REQUESTTOKEN'];
477
-		} else {
478
-			//no token found.
479
-			return false;
480
-		}
481
-		$token = new CsrfToken($token);
482
-
483
-		return $this->csrfTokenManager->isTokenValid($token);
484
-	}
485
-
486
-	/**
487
-	 * Whether the cookie checks are required
488
-	 *
489
-	 * @return bool
490
-	 */
491
-	private function cookieCheckRequired(): bool {
492
-		if ($this->getHeader('OCS-APIREQUEST')) {
493
-			return false;
494
-		}
495
-		if ($this->getCookie(session_name()) === null && $this->getCookie('nc_token') === null) {
496
-			return false;
497
-		}
498
-
499
-		return true;
500
-	}
501
-
502
-	/**
503
-	 * Wrapper around session_get_cookie_params
504
-	 *
505
-	 * @return array
506
-	 */
507
-	public function getCookieParams(): array {
508
-		return session_get_cookie_params();
509
-	}
510
-
511
-	/**
512
-	 * Appends the __Host- prefix to the cookie if applicable
513
-	 *
514
-	 * @param string $name
515
-	 * @return string
516
-	 */
517
-	protected function getProtectedCookieName(string $name): string {
518
-		$cookieParams = $this->getCookieParams();
519
-		$prefix = '';
520
-		if ($cookieParams['secure'] === true && $cookieParams['path'] === '/') {
521
-			$prefix = '__Host-';
522
-		}
523
-
524
-		return $prefix.$name;
525
-	}
526
-
527
-	/**
528
-	 * Checks if the strict cookie has been sent with the request if the request
529
-	 * is including any cookies.
530
-	 *
531
-	 * @return bool
532
-	 * @since 9.1.0
533
-	 */
534
-	public function passesStrictCookieCheck(): bool {
535
-		if (!$this->cookieCheckRequired()) {
536
-			return true;
537
-		}
538
-
539
-		$cookieName = $this->getProtectedCookieName('nc_sameSiteCookiestrict');
540
-		if ($this->getCookie($cookieName) === 'true'
541
-			&& $this->passesLaxCookieCheck()) {
542
-			return true;
543
-		}
544
-		return false;
545
-	}
546
-
547
-	/**
548
-	 * Checks if the lax cookie has been sent with the request if the request
549
-	 * is including any cookies.
550
-	 *
551
-	 * @return bool
552
-	 * @since 9.1.0
553
-	 */
554
-	public function passesLaxCookieCheck(): bool {
555
-		if (!$this->cookieCheckRequired()) {
556
-			return true;
557
-		}
558
-
559
-		$cookieName = $this->getProtectedCookieName('nc_sameSiteCookielax');
560
-		if ($this->getCookie($cookieName) === 'true') {
561
-			return true;
562
-		}
563
-		return false;
564
-	}
565
-
566
-
567
-	/**
568
-	 * Returns an ID for the request, value is not guaranteed to be unique and is mostly meant for logging
569
-	 * If `mod_unique_id` is installed this value will be taken.
570
-	 * @return string
571
-	 */
572
-	public function getId(): string {
573
-		if (isset($this->server['UNIQUE_ID'])) {
574
-			return $this->server['UNIQUE_ID'];
575
-		}
576
-
577
-		if (empty($this->requestId)) {
578
-			$validChars = ISecureRandom::CHAR_ALPHANUMERIC;
579
-			$this->requestId = $this->secureRandom->generate(20, $validChars);
580
-		}
581
-
582
-		return $this->requestId;
583
-	}
584
-
585
-	/**
586
-	 * Checks if given $remoteAddress matches given $trustedProxy.
587
-	 * If $trustedProxy is an IPv4 IP range given in CIDR notation, true will be returned if
588
-	 * $remoteAddress is an IPv4 address within that IP range.
589
-	 * Otherwise $remoteAddress will be compared to $trustedProxy literally and the result
590
-	 * will be returned.
591
-	 * @return boolean true if $remoteAddress matches $trustedProxy, false otherwise
592
-	 */
593
-	protected function matchesTrustedProxy($trustedProxy, $remoteAddress) {
594
-		$cidrre = '/^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\/([0-9]{1,2})$/';
595
-
596
-		if (preg_match($cidrre, $trustedProxy, $match)) {
597
-			$net = $match[1];
598
-			$shiftbits = min(32, max(0, 32 - intval($match[2])));
599
-			$netnum = ip2long($net) >> $shiftbits;
600
-			$ipnum = ip2long($remoteAddress) >> $shiftbits;
601
-
602
-			return $ipnum === $netnum;
603
-		}
604
-
605
-		return $trustedProxy === $remoteAddress;
606
-	}
607
-
608
-	/**
609
-	 * Checks if given $remoteAddress matches any entry in the given array $trustedProxies.
610
-	 * For details regarding what "match" means, refer to `matchesTrustedProxy`.
611
-	 * @return boolean true if $remoteAddress matches any entry in $trustedProxies, false otherwise
612
-	 */
613
-	protected function isTrustedProxy($trustedProxies, $remoteAddress) {
614
-		foreach ($trustedProxies as $tp) {
615
-			if ($this->matchesTrustedProxy($tp, $remoteAddress)) {
616
-				return true;
617
-			}
618
-		}
619
-
620
-		return false;
621
-	}
622
-
623
-	/**
624
-	 * Returns the remote address, if the connection came from a trusted proxy
625
-	 * and `forwarded_for_headers` has been configured then the IP address
626
-	 * specified in this header will be returned instead.
627
-	 * Do always use this instead of $_SERVER['REMOTE_ADDR']
628
-	 * @return string IP address
629
-	 */
630
-	public function getRemoteAddress(): string {
631
-		$remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
632
-		$trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
633
-
634
-		if (\is_array($trustedProxies) && $this->isTrustedProxy($trustedProxies, $remoteAddress)) {
635
-			$forwardedForHeaders = $this->config->getSystemValue('forwarded_for_headers', [
636
-				'HTTP_X_FORWARDED_FOR'
637
-				// only have one default, so we cannot ship an insecure product out of the box
638
-			]);
639
-
640
-			foreach ($forwardedForHeaders as $header) {
641
-				if (isset($this->server[$header])) {
642
-					foreach (explode(',', $this->server[$header]) as $IP) {
643
-						$IP = trim($IP);
644
-
645
-						// remove brackets from IPv6 addresses
646
-						if (strpos($IP, '[') === 0 && substr($IP, -1) === ']') {
647
-							$IP = substr($IP, 1, -1);
648
-						}
649
-
650
-						if (filter_var($IP, FILTER_VALIDATE_IP) !== false) {
651
-							return $IP;
652
-						}
653
-					}
654
-				}
655
-			}
656
-		}
657
-
658
-		return $remoteAddress;
659
-	}
660
-
661
-	/**
662
-	 * Check overwrite condition
663
-	 * @param string $type
664
-	 * @return bool
665
-	 */
666
-	private function isOverwriteCondition(string $type = ''): bool {
667
-		$regex = '/' . $this->config->getSystemValue('overwritecondaddr', '')  . '/';
668
-		$remoteAddr = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
669
-		return $regex === '//' || preg_match($regex, $remoteAddr) === 1
670
-		|| $type !== 'protocol';
671
-	}
672
-
673
-	/**
674
-	 * Returns the server protocol. It respects one or more reverse proxies servers
675
-	 * and load balancers
676
-	 * @return string Server protocol (http or https)
677
-	 */
678
-	public function getServerProtocol(): string {
679
-		if ($this->config->getSystemValue('overwriteprotocol') !== ''
680
-			&& $this->isOverwriteCondition('protocol')) {
681
-			return $this->config->getSystemValue('overwriteprotocol');
682
-		}
683
-
684
-		if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_PROTO'])) {
685
-			if (strpos($this->server['HTTP_X_FORWARDED_PROTO'], ',') !== false) {
686
-				$parts = explode(',', $this->server['HTTP_X_FORWARDED_PROTO']);
687
-				$proto = strtolower(trim($parts[0]));
688
-			} else {
689
-				$proto = strtolower($this->server['HTTP_X_FORWARDED_PROTO']);
690
-			}
691
-
692
-			// Verify that the protocol is always HTTP or HTTPS
693
-			// default to http if an invalid value is provided
694
-			return $proto === 'https' ? 'https' : 'http';
695
-		}
696
-
697
-		if (isset($this->server['HTTPS'])
698
-			&& $this->server['HTTPS'] !== null
699
-			&& $this->server['HTTPS'] !== 'off'
700
-			&& $this->server['HTTPS'] !== '') {
701
-			return 'https';
702
-		}
703
-
704
-		return 'http';
705
-	}
706
-
707
-	/**
708
-	 * Returns the used HTTP protocol.
709
-	 *
710
-	 * @return string HTTP protocol. HTTP/2, HTTP/1.1 or HTTP/1.0.
711
-	 */
712
-	public function getHttpProtocol(): string {
713
-		$claimedProtocol = $this->server['SERVER_PROTOCOL'];
714
-
715
-		if (\is_string($claimedProtocol)) {
716
-			$claimedProtocol = strtoupper($claimedProtocol);
717
-		}
718
-
719
-		$validProtocols = [
720
-			'HTTP/1.0',
721
-			'HTTP/1.1',
722
-			'HTTP/2',
723
-		];
724
-
725
-		if (\in_array($claimedProtocol, $validProtocols, true)) {
726
-			return $claimedProtocol;
727
-		}
728
-
729
-		return 'HTTP/1.1';
730
-	}
731
-
732
-	/**
733
-	 * Returns the request uri, even if the website uses one or more
734
-	 * reverse proxies
735
-	 * @return string
736
-	 */
737
-	public function getRequestUri(): string {
738
-		$uri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
739
-		if ($this->config->getSystemValue('overwritewebroot') !== '' && $this->isOverwriteCondition()) {
740
-			$uri = $this->getScriptName() . substr($uri, \strlen($this->server['SCRIPT_NAME']));
741
-		}
742
-		return $uri;
743
-	}
744
-
745
-	/**
746
-	 * Get raw PathInfo from request (not urldecoded)
747
-	 * @throws \Exception
748
-	 * @return string Path info
749
-	 */
750
-	public function getRawPathInfo(): string {
751
-		$requestUri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
752
-		// remove too many slashes - can be caused by reverse proxy configuration
753
-		$requestUri = preg_replace('%/{2,}%', '/', $requestUri);
754
-
755
-		// Remove the query string from REQUEST_URI
756
-		if ($pos = strpos($requestUri, '?')) {
757
-			$requestUri = substr($requestUri, 0, $pos);
758
-		}
759
-
760
-		$scriptName = $this->server['SCRIPT_NAME'];
761
-		$pathInfo = $requestUri;
762
-
763
-		// strip off the script name's dir and file name
764
-		// FIXME: Sabre does not really belong here
765
-		[$path, $name] = \Sabre\Uri\split($scriptName);
766
-		if (!empty($path)) {
767
-			if ($path === $pathInfo || strpos($pathInfo, $path.'/') === 0) {
768
-				$pathInfo = substr($pathInfo, \strlen($path));
769
-			} else {
770
-				throw new \Exception("The requested uri($requestUri) cannot be processed by the script '$scriptName')");
771
-			}
772
-		}
773
-		if ($name === null) {
774
-			$name = '';
775
-		}
776
-
777
-		if (strpos($pathInfo, '/'.$name) === 0) {
778
-			$pathInfo = substr($pathInfo, \strlen($name) + 1);
779
-		}
780
-		if ($name !== '' && strpos($pathInfo, $name) === 0) {
781
-			$pathInfo = substr($pathInfo, \strlen($name));
782
-		}
783
-		if ($pathInfo === false || $pathInfo === '/') {
784
-			return '';
785
-		} else {
786
-			return $pathInfo;
787
-		}
788
-	}
789
-
790
-	/**
791
-	 * Get PathInfo from request
792
-	 * @throws \Exception
793
-	 * @return string|false Path info or false when not found
794
-	 */
795
-	public function getPathInfo() {
796
-		$pathInfo = $this->getRawPathInfo();
797
-		// following is taken from \Sabre\HTTP\URLUtil::decodePathSegment
798
-		$pathInfo = rawurldecode($pathInfo);
799
-		$encoding = mb_detect_encoding($pathInfo, ['UTF-8', 'ISO-8859-1']);
800
-
801
-		switch ($encoding) {
802
-			case 'ISO-8859-1':
803
-				$pathInfo = utf8_encode($pathInfo);
804
-		}
805
-		// end copy
806
-
807
-		return $pathInfo;
808
-	}
809
-
810
-	/**
811
-	 * Returns the script name, even if the website uses one or more
812
-	 * reverse proxies
813
-	 * @return string the script name
814
-	 */
815
-	public function getScriptName(): string {
816
-		$name = $this->server['SCRIPT_NAME'];
817
-		$overwriteWebRoot = $this->config->getSystemValue('overwritewebroot');
818
-		if ($overwriteWebRoot !== '' && $this->isOverwriteCondition()) {
819
-			// FIXME: This code is untestable due to __DIR__, also that hardcoded path is really dangerous
820
-			$serverRoot = str_replace('\\', '/', substr(__DIR__, 0, -\strlen('lib/private/appframework/http/')));
821
-			$suburi = str_replace('\\', '/', substr(realpath($this->server['SCRIPT_FILENAME']), \strlen($serverRoot)));
822
-			$name = '/' . ltrim($overwriteWebRoot . $suburi, '/');
823
-		}
824
-		return $name;
825
-	}
826
-
827
-	/**
828
-	 * Checks whether the user agent matches a given regex
829
-	 * @param array $agent array of agent names
830
-	 * @return bool true if at least one of the given agent matches, false otherwise
831
-	 */
832
-	public function isUserAgent(array $agent): bool {
833
-		if (!isset($this->server['HTTP_USER_AGENT'])) {
834
-			return false;
835
-		}
836
-		foreach ($agent as $regex) {
837
-			if (preg_match($regex, $this->server['HTTP_USER_AGENT'])) {
838
-				return true;
839
-			}
840
-		}
841
-		return false;
842
-	}
843
-
844
-	/**
845
-	 * Returns the unverified server host from the headers without checking
846
-	 * whether it is a trusted domain
847
-	 * @return string Server host
848
-	 */
849
-	public function getInsecureServerHost(): string {
850
-		if ($this->fromTrustedProxy() && $this->getOverwriteHost() !== null) {
851
-			return $this->getOverwriteHost();
852
-		}
853
-
854
-		$host = 'localhost';
855
-		if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_HOST'])) {
856
-			if (strpos($this->server['HTTP_X_FORWARDED_HOST'], ',') !== false) {
857
-				$parts = explode(',', $this->server['HTTP_X_FORWARDED_HOST']);
858
-				$host = trim(current($parts));
859
-			} else {
860
-				$host = $this->server['HTTP_X_FORWARDED_HOST'];
861
-			}
862
-		} else {
863
-			if (isset($this->server['HTTP_HOST'])) {
864
-				$host = $this->server['HTTP_HOST'];
865
-			} elseif (isset($this->server['SERVER_NAME'])) {
866
-				$host = $this->server['SERVER_NAME'];
867
-			}
868
-		}
869
-
870
-		return $host;
871
-	}
872
-
873
-
874
-	/**
875
-	 * Returns the server host from the headers, or the first configured
876
-	 * trusted domain if the host isn't in the trusted list
877
-	 * @return string Server host
878
-	 */
879
-	public function getServerHost(): string {
880
-		// overwritehost is always trusted
881
-		$host = $this->getOverwriteHost();
882
-		if ($host !== null) {
883
-			return $host;
884
-		}
885
-
886
-		// get the host from the headers
887
-		$host = $this->getInsecureServerHost();
888
-
889
-		// Verify that the host is a trusted domain if the trusted domains
890
-		// are defined
891
-		// If no trusted domain is provided the first trusted domain is returned
892
-		$trustedDomainHelper = new TrustedDomainHelper($this->config);
893
-		if ($trustedDomainHelper->isTrustedDomain($host)) {
894
-			return $host;
895
-		}
896
-
897
-		$trustedList = (array)$this->config->getSystemValue('trusted_domains', []);
898
-		if (count($trustedList) > 0) {
899
-			return reset($trustedList);
900
-		}
901
-
902
-		return '';
903
-	}
904
-
905
-	/**
906
-	 * Returns the overwritehost setting from the config if set and
907
-	 * if the overwrite condition is met
908
-	 * @return string|null overwritehost value or null if not defined or the defined condition
909
-	 * isn't met
910
-	 */
911
-	private function getOverwriteHost() {
912
-		if ($this->config->getSystemValue('overwritehost') !== '' && $this->isOverwriteCondition()) {
913
-			return $this->config->getSystemValue('overwritehost');
914
-		}
915
-		return null;
916
-	}
917
-
918
-	private function fromTrustedProxy(): bool {
919
-		$remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
920
-		$trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
921
-
922
-		return \is_array($trustedProxies) && $this->isTrustedProxy($trustedProxies, $remoteAddress);
923
-	}
66
+    public const USER_AGENT_IE = '/(MSIE)|(Trident)/';
67
+    // Microsoft Edge User Agent from https://msdn.microsoft.com/en-us/library/hh869301(v=vs.85).aspx
68
+    public const USER_AGENT_MS_EDGE = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Chrome\/[0-9.]+ (Mobile Safari|Safari)\/[0-9.]+ Edge\/[0-9.]+$/';
69
+    // Firefox User Agent from https://developer.mozilla.org/en-US/docs/Web/HTTP/Gecko_user_agent_string_reference
70
+    public const USER_AGENT_FIREFOX = '/^Mozilla\/5\.0 \([^)]+\) Gecko\/[0-9.]+ Firefox\/[0-9.]+$/';
71
+    // Chrome User Agent from https://developer.chrome.com/multidevice/user-agent
72
+    public const USER_AGENT_CHROME = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\)( Ubuntu Chromium\/[0-9.]+|) Chrome\/[0-9.]+ (Mobile Safari|Safari)\/[0-9.]+( (Vivaldi|Brave|OPR)\/[0-9.]+|)$/';
73
+    // Safari User Agent from http://www.useragentstring.com/pages/Safari/
74
+    public const USER_AGENT_SAFARI = '/^Mozilla\/5\.0 \([^)]+\) AppleWebKit\/[0-9.]+ \(KHTML, like Gecko\) Version\/[0-9.]+ Safari\/[0-9.A-Z]+$/';
75
+    // Android Chrome user agent: https://developers.google.com/chrome/mobile/docs/user-agent
76
+    public const USER_AGENT_ANDROID_MOBILE_CHROME = '#Android.*Chrome/[.0-9]*#';
77
+    public const USER_AGENT_FREEBOX = '#^Mozilla/5\.0$#';
78
+    public const REGEX_LOCALHOST = '/^(127\.0\.0\.1|localhost|\[::1\])$/';
79
+
80
+    protected $inputStream;
81
+    protected $content;
82
+    protected $items = [];
83
+    protected $allowedKeys = [
84
+        'get',
85
+        'post',
86
+        'files',
87
+        'server',
88
+        'env',
89
+        'cookies',
90
+        'urlParams',
91
+        'parameters',
92
+        'method',
93
+        'requesttoken',
94
+    ];
95
+    /** @var ISecureRandom */
96
+    protected $secureRandom;
97
+    /** @var IConfig */
98
+    protected $config;
99
+    /** @var string */
100
+    protected $requestId = '';
101
+    /** @var ICrypto */
102
+    protected $crypto;
103
+    /** @var CsrfTokenManager|null */
104
+    protected $csrfTokenManager;
105
+
106
+    /** @var bool */
107
+    protected $contentDecoded = false;
108
+
109
+    /**
110
+     * @param array $vars An associative array with the following optional values:
111
+     *        - array 'urlParams' the parameters which were matched from the URL
112
+     *        - array 'get' the $_GET array
113
+     *        - array|string 'post' the $_POST array or JSON string
114
+     *        - array 'files' the $_FILES array
115
+     *        - array 'server' the $_SERVER array
116
+     *        - array 'env' the $_ENV array
117
+     *        - array 'cookies' the $_COOKIE array
118
+     *        - string 'method' the request method (GET, POST etc)
119
+     *        - string|false 'requesttoken' the requesttoken or false when not available
120
+     * @param ISecureRandom $secureRandom
121
+     * @param IConfig $config
122
+     * @param CsrfTokenManager|null $csrfTokenManager
123
+     * @param string $stream
124
+     * @see https://www.php.net/manual/en/reserved.variables.php
125
+     */
126
+    public function __construct(array $vars,
127
+                                ISecureRandom $secureRandom,
128
+                                IConfig $config,
129
+                                CsrfTokenManager $csrfTokenManager = null,
130
+                                string $stream = 'php://input') {
131
+        $this->inputStream = $stream;
132
+        $this->items['params'] = [];
133
+        $this->secureRandom = $secureRandom;
134
+        $this->config = $config;
135
+        $this->csrfTokenManager = $csrfTokenManager;
136
+
137
+        if (!array_key_exists('method', $vars)) {
138
+            $vars['method'] = 'GET';
139
+        }
140
+
141
+        foreach ($this->allowedKeys as $name) {
142
+            $this->items[$name] = isset($vars[$name])
143
+                ? $vars[$name]
144
+                : [];
145
+        }
146
+
147
+        $this->items['parameters'] = array_merge(
148
+            $this->items['get'],
149
+            $this->items['post'],
150
+            $this->items['urlParams'],
151
+            $this->items['params']
152
+        );
153
+    }
154
+    /**
155
+     * @param array $parameters
156
+     */
157
+    public function setUrlParameters(array $parameters) {
158
+        $this->items['urlParams'] = $parameters;
159
+        $this->items['parameters'] = array_merge(
160
+            $this->items['parameters'],
161
+            $this->items['urlParams']
162
+        );
163
+    }
164
+
165
+    /**
166
+     * Countable method
167
+     * @return int
168
+     */
169
+    public function count(): int {
170
+        return \count($this->items['parameters']);
171
+    }
172
+
173
+    /**
174
+     * ArrayAccess methods
175
+     *
176
+     * Gives access to the combined GET, POST and urlParams arrays
177
+     *
178
+     * Examples:
179
+     *
180
+     * $var = $request['myvar'];
181
+     *
182
+     * or
183
+     *
184
+     * if(!isset($request['myvar']) {
185
+     * 	// Do something
186
+     * }
187
+     *
188
+     * $request['myvar'] = 'something'; // This throws an exception.
189
+     *
190
+     * @param string $offset The key to lookup
191
+     * @return boolean
192
+     */
193
+    public function offsetExists($offset): bool {
194
+        return isset($this->items['parameters'][$offset]);
195
+    }
196
+
197
+    /**
198
+     * @see offsetExists
199
+     * @param string $offset
200
+     * @return mixed
201
+     */
202
+    public function offsetGet($offset) {
203
+        return isset($this->items['parameters'][$offset])
204
+            ? $this->items['parameters'][$offset]
205
+            : null;
206
+    }
207
+
208
+    /**
209
+     * @see offsetExists
210
+     * @param string $offset
211
+     * @param mixed $value
212
+     */
213
+    public function offsetSet($offset, $value) {
214
+        throw new \RuntimeException('You cannot change the contents of the request object');
215
+    }
216
+
217
+    /**
218
+     * @see offsetExists
219
+     * @param string $offset
220
+     */
221
+    public function offsetUnset($offset) {
222
+        throw new \RuntimeException('You cannot change the contents of the request object');
223
+    }
224
+
225
+    /**
226
+     * Magic property accessors
227
+     * @param string $name
228
+     * @param mixed $value
229
+     */
230
+    public function __set($name, $value) {
231
+        throw new \RuntimeException('You cannot change the contents of the request object');
232
+    }
233
+
234
+    /**
235
+     * Access request variables by method and name.
236
+     * Examples:
237
+     *
238
+     * $request->post['myvar']; // Only look for POST variables
239
+     * $request->myvar; or $request->{'myvar'}; or $request->{$myvar}
240
+     * Looks in the combined GET, POST and urlParams array.
241
+     *
242
+     * If you access e.g. ->post but the current HTTP request method
243
+     * is GET a \LogicException will be thrown.
244
+     *
245
+     * @param string $name The key to look for.
246
+     * @throws \LogicException
247
+     * @return mixed|null
248
+     */
249
+    public function __get($name) {
250
+        switch ($name) {
251
+            case 'put':
252
+            case 'patch':
253
+            case 'get':
254
+            case 'post':
255
+                if ($this->method !== strtoupper($name)) {
256
+                    throw new \LogicException(sprintf('%s cannot be accessed in a %s request.', $name, $this->method));
257
+                }
258
+                return $this->getContent();
259
+            case 'files':
260
+            case 'server':
261
+            case 'env':
262
+            case 'cookies':
263
+            case 'urlParams':
264
+            case 'method':
265
+                return isset($this->items[$name])
266
+                    ? $this->items[$name]
267
+                    : null;
268
+            case 'parameters':
269
+            case 'params':
270
+                return $this->getContent();
271
+            default:
272
+                return isset($this[$name])
273
+                    ? $this[$name]
274
+                    : null;
275
+        }
276
+    }
277
+
278
+    /**
279
+     * @param string $name
280
+     * @return bool
281
+     */
282
+    public function __isset($name) {
283
+        if (\in_array($name, $this->allowedKeys, true)) {
284
+            return true;
285
+        }
286
+        return isset($this->items['parameters'][$name]);
287
+    }
288
+
289
+    /**
290
+     * @param string $id
291
+     */
292
+    public function __unset($id) {
293
+        throw new \RuntimeException('You cannot change the contents of the request object');
294
+    }
295
+
296
+    /**
297
+     * Returns the value for a specific http header.
298
+     *
299
+     * This method returns an empty string if the header did not exist.
300
+     *
301
+     * @param string $name
302
+     * @return string
303
+     */
304
+    public function getHeader(string $name): string {
305
+        $name = strtoupper(str_replace('-', '_',$name));
306
+        if (isset($this->server['HTTP_' . $name])) {
307
+            return $this->server['HTTP_' . $name];
308
+        }
309
+
310
+        // There's a few headers that seem to end up in the top-level
311
+        // server array.
312
+        switch ($name) {
313
+            case 'CONTENT_TYPE':
314
+            case 'CONTENT_LENGTH':
315
+            case 'REMOTE_ADDR':
316
+                if (isset($this->server[$name])) {
317
+                    return $this->server[$name];
318
+                }
319
+                break;
320
+        }
321
+
322
+        return '';
323
+    }
324
+
325
+    /**
326
+     * Lets you access post and get parameters by the index
327
+     * In case of json requests the encoded json body is accessed
328
+     *
329
+     * @param string $key the key which you want to access in the URL Parameter
330
+     *                     placeholder, $_POST or $_GET array.
331
+     *                     The priority how they're returned is the following:
332
+     *                     1. URL parameters
333
+     *                     2. POST parameters
334
+     *                     3. GET parameters
335
+     * @param mixed $default If the key is not found, this value will be returned
336
+     * @return mixed the content of the array
337
+     */
338
+    public function getParam(string $key, $default = null) {
339
+        return isset($this->parameters[$key])
340
+            ? $this->parameters[$key]
341
+            : $default;
342
+    }
343
+
344
+    /**
345
+     * Returns all params that were received, be it from the request
346
+     * (as GET or POST) or throuh the URL by the route
347
+     * @return array the array with all parameters
348
+     */
349
+    public function getParams(): array {
350
+        return is_array($this->parameters) ? $this->parameters : [];
351
+    }
352
+
353
+    /**
354
+     * Returns the method of the request
355
+     * @return string the method of the request (POST, GET, etc)
356
+     */
357
+    public function getMethod(): string {
358
+        return $this->method;
359
+    }
360
+
361
+    /**
362
+     * Shortcut for accessing an uploaded file through the $_FILES array
363
+     * @param string $key the key that will be taken from the $_FILES array
364
+     * @return array the file in the $_FILES element
365
+     */
366
+    public function getUploadedFile(string $key) {
367
+        return isset($this->files[$key]) ? $this->files[$key] : null;
368
+    }
369
+
370
+    /**
371
+     * Shortcut for getting env variables
372
+     * @param string $key the key that will be taken from the $_ENV array
373
+     * @return array the value in the $_ENV element
374
+     */
375
+    public function getEnv(string $key) {
376
+        return isset($this->env[$key]) ? $this->env[$key] : null;
377
+    }
378
+
379
+    /**
380
+     * Shortcut for getting cookie variables
381
+     * @param string $key the key that will be taken from the $_COOKIE array
382
+     * @return string the value in the $_COOKIE element
383
+     */
384
+    public function getCookie(string $key) {
385
+        return isset($this->cookies[$key]) ? $this->cookies[$key] : null;
386
+    }
387
+
388
+    /**
389
+     * Returns the request body content.
390
+     *
391
+     * If the HTTP request method is PUT and the body
392
+     * not application/x-www-form-urlencoded or application/json a stream
393
+     * resource is returned, otherwise an array.
394
+     *
395
+     * @return array|string|resource The request body content or a resource to read the body stream.
396
+     *
397
+     * @throws \LogicException
398
+     */
399
+    protected function getContent() {
400
+        // If the content can't be parsed into an array then return a stream resource.
401
+        if ($this->method === 'PUT'
402
+            && $this->getHeader('Content-Length') !== '0'
403
+            && $this->getHeader('Content-Length') !== ''
404
+            && strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') === false
405
+            && strpos($this->getHeader('Content-Type'), 'application/json') === false
406
+        ) {
407
+            if ($this->content === false) {
408
+                throw new \LogicException(
409
+                    '"put" can only be accessed once if not '
410
+                    . 'application/x-www-form-urlencoded or application/json.'
411
+                );
412
+            }
413
+            $this->content = false;
414
+            return fopen($this->inputStream, 'rb');
415
+        } else {
416
+            $this->decodeContent();
417
+            return $this->items['parameters'];
418
+        }
419
+    }
420
+
421
+    /**
422
+     * Attempt to decode the content and populate parameters
423
+     */
424
+    protected function decodeContent() {
425
+        if ($this->contentDecoded) {
426
+            return;
427
+        }
428
+        $params = [];
429
+
430
+        // 'application/json' must be decoded manually.
431
+        if (strpos($this->getHeader('Content-Type'), 'application/json') !== false) {
432
+            $params = json_decode(file_get_contents($this->inputStream), true);
433
+            if ($params !== null && \count($params) > 0) {
434
+                $this->items['params'] = $params;
435
+                if ($this->method === 'POST') {
436
+                    $this->items['post'] = $params;
437
+                }
438
+            }
439
+
440
+            // Handle application/x-www-form-urlencoded for methods other than GET
441
+        // or post correctly
442
+        } elseif ($this->method !== 'GET'
443
+                && $this->method !== 'POST'
444
+                && strpos($this->getHeader('Content-Type'), 'application/x-www-form-urlencoded') !== false) {
445
+            parse_str(file_get_contents($this->inputStream), $params);
446
+            if (\is_array($params)) {
447
+                $this->items['params'] = $params;
448
+            }
449
+        }
450
+
451
+        if (\is_array($params)) {
452
+            $this->items['parameters'] = array_merge($this->items['parameters'], $params);
453
+        }
454
+        $this->contentDecoded = true;
455
+    }
456
+
457
+
458
+    /**
459
+     * Checks if the CSRF check was correct
460
+     * @return bool true if CSRF check passed
461
+     */
462
+    public function passesCSRFCheck(): bool {
463
+        if ($this->csrfTokenManager === null) {
464
+            return false;
465
+        }
466
+
467
+        if (!$this->passesStrictCookieCheck()) {
468
+            return false;
469
+        }
470
+
471
+        if (isset($this->items['get']['requesttoken'])) {
472
+            $token = $this->items['get']['requesttoken'];
473
+        } elseif (isset($this->items['post']['requesttoken'])) {
474
+            $token = $this->items['post']['requesttoken'];
475
+        } elseif (isset($this->items['server']['HTTP_REQUESTTOKEN'])) {
476
+            $token = $this->items['server']['HTTP_REQUESTTOKEN'];
477
+        } else {
478
+            //no token found.
479
+            return false;
480
+        }
481
+        $token = new CsrfToken($token);
482
+
483
+        return $this->csrfTokenManager->isTokenValid($token);
484
+    }
485
+
486
+    /**
487
+     * Whether the cookie checks are required
488
+     *
489
+     * @return bool
490
+     */
491
+    private function cookieCheckRequired(): bool {
492
+        if ($this->getHeader('OCS-APIREQUEST')) {
493
+            return false;
494
+        }
495
+        if ($this->getCookie(session_name()) === null && $this->getCookie('nc_token') === null) {
496
+            return false;
497
+        }
498
+
499
+        return true;
500
+    }
501
+
502
+    /**
503
+     * Wrapper around session_get_cookie_params
504
+     *
505
+     * @return array
506
+     */
507
+    public function getCookieParams(): array {
508
+        return session_get_cookie_params();
509
+    }
510
+
511
+    /**
512
+     * Appends the __Host- prefix to the cookie if applicable
513
+     *
514
+     * @param string $name
515
+     * @return string
516
+     */
517
+    protected function getProtectedCookieName(string $name): string {
518
+        $cookieParams = $this->getCookieParams();
519
+        $prefix = '';
520
+        if ($cookieParams['secure'] === true && $cookieParams['path'] === '/') {
521
+            $prefix = '__Host-';
522
+        }
523
+
524
+        return $prefix.$name;
525
+    }
526
+
527
+    /**
528
+     * Checks if the strict cookie has been sent with the request if the request
529
+     * is including any cookies.
530
+     *
531
+     * @return bool
532
+     * @since 9.1.0
533
+     */
534
+    public function passesStrictCookieCheck(): bool {
535
+        if (!$this->cookieCheckRequired()) {
536
+            return true;
537
+        }
538
+
539
+        $cookieName = $this->getProtectedCookieName('nc_sameSiteCookiestrict');
540
+        if ($this->getCookie($cookieName) === 'true'
541
+            && $this->passesLaxCookieCheck()) {
542
+            return true;
543
+        }
544
+        return false;
545
+    }
546
+
547
+    /**
548
+     * Checks if the lax cookie has been sent with the request if the request
549
+     * is including any cookies.
550
+     *
551
+     * @return bool
552
+     * @since 9.1.0
553
+     */
554
+    public function passesLaxCookieCheck(): bool {
555
+        if (!$this->cookieCheckRequired()) {
556
+            return true;
557
+        }
558
+
559
+        $cookieName = $this->getProtectedCookieName('nc_sameSiteCookielax');
560
+        if ($this->getCookie($cookieName) === 'true') {
561
+            return true;
562
+        }
563
+        return false;
564
+    }
565
+
566
+
567
+    /**
568
+     * Returns an ID for the request, value is not guaranteed to be unique and is mostly meant for logging
569
+     * If `mod_unique_id` is installed this value will be taken.
570
+     * @return string
571
+     */
572
+    public function getId(): string {
573
+        if (isset($this->server['UNIQUE_ID'])) {
574
+            return $this->server['UNIQUE_ID'];
575
+        }
576
+
577
+        if (empty($this->requestId)) {
578
+            $validChars = ISecureRandom::CHAR_ALPHANUMERIC;
579
+            $this->requestId = $this->secureRandom->generate(20, $validChars);
580
+        }
581
+
582
+        return $this->requestId;
583
+    }
584
+
585
+    /**
586
+     * Checks if given $remoteAddress matches given $trustedProxy.
587
+     * If $trustedProxy is an IPv4 IP range given in CIDR notation, true will be returned if
588
+     * $remoteAddress is an IPv4 address within that IP range.
589
+     * Otherwise $remoteAddress will be compared to $trustedProxy literally and the result
590
+     * will be returned.
591
+     * @return boolean true if $remoteAddress matches $trustedProxy, false otherwise
592
+     */
593
+    protected function matchesTrustedProxy($trustedProxy, $remoteAddress) {
594
+        $cidrre = '/^([0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3})\/([0-9]{1,2})$/';
595
+
596
+        if (preg_match($cidrre, $trustedProxy, $match)) {
597
+            $net = $match[1];
598
+            $shiftbits = min(32, max(0, 32 - intval($match[2])));
599
+            $netnum = ip2long($net) >> $shiftbits;
600
+            $ipnum = ip2long($remoteAddress) >> $shiftbits;
601
+
602
+            return $ipnum === $netnum;
603
+        }
604
+
605
+        return $trustedProxy === $remoteAddress;
606
+    }
607
+
608
+    /**
609
+     * Checks if given $remoteAddress matches any entry in the given array $trustedProxies.
610
+     * For details regarding what "match" means, refer to `matchesTrustedProxy`.
611
+     * @return boolean true if $remoteAddress matches any entry in $trustedProxies, false otherwise
612
+     */
613
+    protected function isTrustedProxy($trustedProxies, $remoteAddress) {
614
+        foreach ($trustedProxies as $tp) {
615
+            if ($this->matchesTrustedProxy($tp, $remoteAddress)) {
616
+                return true;
617
+            }
618
+        }
619
+
620
+        return false;
621
+    }
622
+
623
+    /**
624
+     * Returns the remote address, if the connection came from a trusted proxy
625
+     * and `forwarded_for_headers` has been configured then the IP address
626
+     * specified in this header will be returned instead.
627
+     * Do always use this instead of $_SERVER['REMOTE_ADDR']
628
+     * @return string IP address
629
+     */
630
+    public function getRemoteAddress(): string {
631
+        $remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
632
+        $trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
633
+
634
+        if (\is_array($trustedProxies) && $this->isTrustedProxy($trustedProxies, $remoteAddress)) {
635
+            $forwardedForHeaders = $this->config->getSystemValue('forwarded_for_headers', [
636
+                'HTTP_X_FORWARDED_FOR'
637
+                // only have one default, so we cannot ship an insecure product out of the box
638
+            ]);
639
+
640
+            foreach ($forwardedForHeaders as $header) {
641
+                if (isset($this->server[$header])) {
642
+                    foreach (explode(',', $this->server[$header]) as $IP) {
643
+                        $IP = trim($IP);
644
+
645
+                        // remove brackets from IPv6 addresses
646
+                        if (strpos($IP, '[') === 0 && substr($IP, -1) === ']') {
647
+                            $IP = substr($IP, 1, -1);
648
+                        }
649
+
650
+                        if (filter_var($IP, FILTER_VALIDATE_IP) !== false) {
651
+                            return $IP;
652
+                        }
653
+                    }
654
+                }
655
+            }
656
+        }
657
+
658
+        return $remoteAddress;
659
+    }
660
+
661
+    /**
662
+     * Check overwrite condition
663
+     * @param string $type
664
+     * @return bool
665
+     */
666
+    private function isOverwriteCondition(string $type = ''): bool {
667
+        $regex = '/' . $this->config->getSystemValue('overwritecondaddr', '')  . '/';
668
+        $remoteAddr = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
669
+        return $regex === '//' || preg_match($regex, $remoteAddr) === 1
670
+        || $type !== 'protocol';
671
+    }
672
+
673
+    /**
674
+     * Returns the server protocol. It respects one or more reverse proxies servers
675
+     * and load balancers
676
+     * @return string Server protocol (http or https)
677
+     */
678
+    public function getServerProtocol(): string {
679
+        if ($this->config->getSystemValue('overwriteprotocol') !== ''
680
+            && $this->isOverwriteCondition('protocol')) {
681
+            return $this->config->getSystemValue('overwriteprotocol');
682
+        }
683
+
684
+        if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_PROTO'])) {
685
+            if (strpos($this->server['HTTP_X_FORWARDED_PROTO'], ',') !== false) {
686
+                $parts = explode(',', $this->server['HTTP_X_FORWARDED_PROTO']);
687
+                $proto = strtolower(trim($parts[0]));
688
+            } else {
689
+                $proto = strtolower($this->server['HTTP_X_FORWARDED_PROTO']);
690
+            }
691
+
692
+            // Verify that the protocol is always HTTP or HTTPS
693
+            // default to http if an invalid value is provided
694
+            return $proto === 'https' ? 'https' : 'http';
695
+        }
696
+
697
+        if (isset($this->server['HTTPS'])
698
+            && $this->server['HTTPS'] !== null
699
+            && $this->server['HTTPS'] !== 'off'
700
+            && $this->server['HTTPS'] !== '') {
701
+            return 'https';
702
+        }
703
+
704
+        return 'http';
705
+    }
706
+
707
+    /**
708
+     * Returns the used HTTP protocol.
709
+     *
710
+     * @return string HTTP protocol. HTTP/2, HTTP/1.1 or HTTP/1.0.
711
+     */
712
+    public function getHttpProtocol(): string {
713
+        $claimedProtocol = $this->server['SERVER_PROTOCOL'];
714
+
715
+        if (\is_string($claimedProtocol)) {
716
+            $claimedProtocol = strtoupper($claimedProtocol);
717
+        }
718
+
719
+        $validProtocols = [
720
+            'HTTP/1.0',
721
+            'HTTP/1.1',
722
+            'HTTP/2',
723
+        ];
724
+
725
+        if (\in_array($claimedProtocol, $validProtocols, true)) {
726
+            return $claimedProtocol;
727
+        }
728
+
729
+        return 'HTTP/1.1';
730
+    }
731
+
732
+    /**
733
+     * Returns the request uri, even if the website uses one or more
734
+     * reverse proxies
735
+     * @return string
736
+     */
737
+    public function getRequestUri(): string {
738
+        $uri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
739
+        if ($this->config->getSystemValue('overwritewebroot') !== '' && $this->isOverwriteCondition()) {
740
+            $uri = $this->getScriptName() . substr($uri, \strlen($this->server['SCRIPT_NAME']));
741
+        }
742
+        return $uri;
743
+    }
744
+
745
+    /**
746
+     * Get raw PathInfo from request (not urldecoded)
747
+     * @throws \Exception
748
+     * @return string Path info
749
+     */
750
+    public function getRawPathInfo(): string {
751
+        $requestUri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
752
+        // remove too many slashes - can be caused by reverse proxy configuration
753
+        $requestUri = preg_replace('%/{2,}%', '/', $requestUri);
754
+
755
+        // Remove the query string from REQUEST_URI
756
+        if ($pos = strpos($requestUri, '?')) {
757
+            $requestUri = substr($requestUri, 0, $pos);
758
+        }
759
+
760
+        $scriptName = $this->server['SCRIPT_NAME'];
761
+        $pathInfo = $requestUri;
762
+
763
+        // strip off the script name's dir and file name
764
+        // FIXME: Sabre does not really belong here
765
+        [$path, $name] = \Sabre\Uri\split($scriptName);
766
+        if (!empty($path)) {
767
+            if ($path === $pathInfo || strpos($pathInfo, $path.'/') === 0) {
768
+                $pathInfo = substr($pathInfo, \strlen($path));
769
+            } else {
770
+                throw new \Exception("The requested uri($requestUri) cannot be processed by the script '$scriptName')");
771
+            }
772
+        }
773
+        if ($name === null) {
774
+            $name = '';
775
+        }
776
+
777
+        if (strpos($pathInfo, '/'.$name) === 0) {
778
+            $pathInfo = substr($pathInfo, \strlen($name) + 1);
779
+        }
780
+        if ($name !== '' && strpos($pathInfo, $name) === 0) {
781
+            $pathInfo = substr($pathInfo, \strlen($name));
782
+        }
783
+        if ($pathInfo === false || $pathInfo === '/') {
784
+            return '';
785
+        } else {
786
+            return $pathInfo;
787
+        }
788
+    }
789
+
790
+    /**
791
+     * Get PathInfo from request
792
+     * @throws \Exception
793
+     * @return string|false Path info or false when not found
794
+     */
795
+    public function getPathInfo() {
796
+        $pathInfo = $this->getRawPathInfo();
797
+        // following is taken from \Sabre\HTTP\URLUtil::decodePathSegment
798
+        $pathInfo = rawurldecode($pathInfo);
799
+        $encoding = mb_detect_encoding($pathInfo, ['UTF-8', 'ISO-8859-1']);
800
+
801
+        switch ($encoding) {
802
+            case 'ISO-8859-1':
803
+                $pathInfo = utf8_encode($pathInfo);
804
+        }
805
+        // end copy
806
+
807
+        return $pathInfo;
808
+    }
809
+
810
+    /**
811
+     * Returns the script name, even if the website uses one or more
812
+     * reverse proxies
813
+     * @return string the script name
814
+     */
815
+    public function getScriptName(): string {
816
+        $name = $this->server['SCRIPT_NAME'];
817
+        $overwriteWebRoot = $this->config->getSystemValue('overwritewebroot');
818
+        if ($overwriteWebRoot !== '' && $this->isOverwriteCondition()) {
819
+            // FIXME: This code is untestable due to __DIR__, also that hardcoded path is really dangerous
820
+            $serverRoot = str_replace('\\', '/', substr(__DIR__, 0, -\strlen('lib/private/appframework/http/')));
821
+            $suburi = str_replace('\\', '/', substr(realpath($this->server['SCRIPT_FILENAME']), \strlen($serverRoot)));
822
+            $name = '/' . ltrim($overwriteWebRoot . $suburi, '/');
823
+        }
824
+        return $name;
825
+    }
826
+
827
+    /**
828
+     * Checks whether the user agent matches a given regex
829
+     * @param array $agent array of agent names
830
+     * @return bool true if at least one of the given agent matches, false otherwise
831
+     */
832
+    public function isUserAgent(array $agent): bool {
833
+        if (!isset($this->server['HTTP_USER_AGENT'])) {
834
+            return false;
835
+        }
836
+        foreach ($agent as $regex) {
837
+            if (preg_match($regex, $this->server['HTTP_USER_AGENT'])) {
838
+                return true;
839
+            }
840
+        }
841
+        return false;
842
+    }
843
+
844
+    /**
845
+     * Returns the unverified server host from the headers without checking
846
+     * whether it is a trusted domain
847
+     * @return string Server host
848
+     */
849
+    public function getInsecureServerHost(): string {
850
+        if ($this->fromTrustedProxy() && $this->getOverwriteHost() !== null) {
851
+            return $this->getOverwriteHost();
852
+        }
853
+
854
+        $host = 'localhost';
855
+        if ($this->fromTrustedProxy() && isset($this->server['HTTP_X_FORWARDED_HOST'])) {
856
+            if (strpos($this->server['HTTP_X_FORWARDED_HOST'], ',') !== false) {
857
+                $parts = explode(',', $this->server['HTTP_X_FORWARDED_HOST']);
858
+                $host = trim(current($parts));
859
+            } else {
860
+                $host = $this->server['HTTP_X_FORWARDED_HOST'];
861
+            }
862
+        } else {
863
+            if (isset($this->server['HTTP_HOST'])) {
864
+                $host = $this->server['HTTP_HOST'];
865
+            } elseif (isset($this->server['SERVER_NAME'])) {
866
+                $host = $this->server['SERVER_NAME'];
867
+            }
868
+        }
869
+
870
+        return $host;
871
+    }
872
+
873
+
874
+    /**
875
+     * Returns the server host from the headers, or the first configured
876
+     * trusted domain if the host isn't in the trusted list
877
+     * @return string Server host
878
+     */
879
+    public function getServerHost(): string {
880
+        // overwritehost is always trusted
881
+        $host = $this->getOverwriteHost();
882
+        if ($host !== null) {
883
+            return $host;
884
+        }
885
+
886
+        // get the host from the headers
887
+        $host = $this->getInsecureServerHost();
888
+
889
+        // Verify that the host is a trusted domain if the trusted domains
890
+        // are defined
891
+        // If no trusted domain is provided the first trusted domain is returned
892
+        $trustedDomainHelper = new TrustedDomainHelper($this->config);
893
+        if ($trustedDomainHelper->isTrustedDomain($host)) {
894
+            return $host;
895
+        }
896
+
897
+        $trustedList = (array)$this->config->getSystemValue('trusted_domains', []);
898
+        if (count($trustedList) > 0) {
899
+            return reset($trustedList);
900
+        }
901
+
902
+        return '';
903
+    }
904
+
905
+    /**
906
+     * Returns the overwritehost setting from the config if set and
907
+     * if the overwrite condition is met
908
+     * @return string|null overwritehost value or null if not defined or the defined condition
909
+     * isn't met
910
+     */
911
+    private function getOverwriteHost() {
912
+        if ($this->config->getSystemValue('overwritehost') !== '' && $this->isOverwriteCondition()) {
913
+            return $this->config->getSystemValue('overwritehost');
914
+        }
915
+        return null;
916
+    }
917
+
918
+    private function fromTrustedProxy(): bool {
919
+        $remoteAddress = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
920
+        $trustedProxies = $this->config->getSystemValue('trusted_proxies', []);
921
+
922
+        return \is_array($trustedProxies) && $this->isTrustedProxy($trustedProxies, $remoteAddress);
923
+    }
924 924
 }
Please login to merge, or discard this patch.
Spacing   +7 added lines, -7 removed lines patch added patch discarded remove patch
@@ -302,9 +302,9 @@  discard block
 block discarded – undo
302 302
 	 * @return string
303 303
 	 */
304 304
 	public function getHeader(string $name): string {
305
-		$name = strtoupper(str_replace('-', '_',$name));
306
-		if (isset($this->server['HTTP_' . $name])) {
307
-			return $this->server['HTTP_' . $name];
305
+		$name = strtoupper(str_replace('-', '_', $name));
306
+		if (isset($this->server['HTTP_'.$name])) {
307
+			return $this->server['HTTP_'.$name];
308 308
 		}
309 309
 
310 310
 		// There's a few headers that seem to end up in the top-level
@@ -664,7 +664,7 @@  discard block
 block discarded – undo
664 664
 	 * @return bool
665 665
 	 */
666 666
 	private function isOverwriteCondition(string $type = ''): bool {
667
-		$regex = '/' . $this->config->getSystemValue('overwritecondaddr', '')  . '/';
667
+		$regex = '/'.$this->config->getSystemValue('overwritecondaddr', '').'/';
668 668
 		$remoteAddr = isset($this->server['REMOTE_ADDR']) ? $this->server['REMOTE_ADDR'] : '';
669 669
 		return $regex === '//' || preg_match($regex, $remoteAddr) === 1
670 670
 		|| $type !== 'protocol';
@@ -737,7 +737,7 @@  discard block
 block discarded – undo
737 737
 	public function getRequestUri(): string {
738 738
 		$uri = isset($this->server['REQUEST_URI']) ? $this->server['REQUEST_URI'] : '';
739 739
 		if ($this->config->getSystemValue('overwritewebroot') !== '' && $this->isOverwriteCondition()) {
740
-			$uri = $this->getScriptName() . substr($uri, \strlen($this->server['SCRIPT_NAME']));
740
+			$uri = $this->getScriptName().substr($uri, \strlen($this->server['SCRIPT_NAME']));
741 741
 		}
742 742
 		return $uri;
743 743
 	}
@@ -819,7 +819,7 @@  discard block
 block discarded – undo
819 819
 			// FIXME: This code is untestable due to __DIR__, also that hardcoded path is really dangerous
820 820
 			$serverRoot = str_replace('\\', '/', substr(__DIR__, 0, -\strlen('lib/private/appframework/http/')));
821 821
 			$suburi = str_replace('\\', '/', substr(realpath($this->server['SCRIPT_FILENAME']), \strlen($serverRoot)));
822
-			$name = '/' . ltrim($overwriteWebRoot . $suburi, '/');
822
+			$name = '/'.ltrim($overwriteWebRoot.$suburi, '/');
823 823
 		}
824 824
 		return $name;
825 825
 	}
@@ -894,7 +894,7 @@  discard block
 block discarded – undo
894 894
 			return $host;
895 895
 		}
896 896
 
897
-		$trustedList = (array)$this->config->getSystemValue('trusted_domains', []);
897
+		$trustedList = (array) $this->config->getSystemValue('trusted_domains', []);
898 898
 		if (count($trustedList) > 0) {
899 899
 			return reset($trustedList);
900 900
 		}
Please login to merge, or discard this patch.
lib/private/Cache/File.php 2 patches
Indentation   +157 added lines, -157 removed lines patch added patch discarded remove patch
@@ -37,170 +37,170 @@
 block discarded – undo
37 37
 
38 38
 class File implements ICache {
39 39
 
40
-	/** @var View */
41
-	protected $storage;
40
+    /** @var View */
41
+    protected $storage;
42 42
 
43
-	/**
44
-	 * Returns the cache storage for the logged in user
45
-	 *
46
-	 * @return \OC\Files\View cache storage
47
-	 * @throws \OC\ForbiddenException
48
-	 * @throws \OC\User\NoUserException
49
-	 */
50
-	protected function getStorage() {
51
-		if ($this->storage !== null) {
52
-			return $this->storage;
53
-		}
54
-		if (\OC::$server->getUserSession()->isLoggedIn()) {
55
-			$rootView = new View();
56
-			$user = \OC::$server->getUserSession()->getUser();
57
-			Filesystem::initMountPoints($user->getUID());
58
-			if (!$rootView->file_exists('/' . $user->getUID() . '/cache')) {
59
-				$rootView->mkdir('/' . $user->getUID() . '/cache');
60
-			}
61
-			$this->storage = new View('/' . $user->getUID() . '/cache');
62
-			return $this->storage;
63
-		} else {
64
-			\OCP\Util::writeLog('core', 'Can\'t get cache storage, user not logged in', ILogger::ERROR);
65
-			throw new \OC\ForbiddenException('Can\t get cache storage, user not logged in');
66
-		}
67
-	}
43
+    /**
44
+     * Returns the cache storage for the logged in user
45
+     *
46
+     * @return \OC\Files\View cache storage
47
+     * @throws \OC\ForbiddenException
48
+     * @throws \OC\User\NoUserException
49
+     */
50
+    protected function getStorage() {
51
+        if ($this->storage !== null) {
52
+            return $this->storage;
53
+        }
54
+        if (\OC::$server->getUserSession()->isLoggedIn()) {
55
+            $rootView = new View();
56
+            $user = \OC::$server->getUserSession()->getUser();
57
+            Filesystem::initMountPoints($user->getUID());
58
+            if (!$rootView->file_exists('/' . $user->getUID() . '/cache')) {
59
+                $rootView->mkdir('/' . $user->getUID() . '/cache');
60
+            }
61
+            $this->storage = new View('/' . $user->getUID() . '/cache');
62
+            return $this->storage;
63
+        } else {
64
+            \OCP\Util::writeLog('core', 'Can\'t get cache storage, user not logged in', ILogger::ERROR);
65
+            throw new \OC\ForbiddenException('Can\t get cache storage, user not logged in');
66
+        }
67
+    }
68 68
 
69
-	/**
70
-	 * @param string $key
71
-	 * @return mixed|null
72
-	 * @throws \OC\ForbiddenException
73
-	 */
74
-	public function get($key) {
75
-		$result = null;
76
-		if ($this->hasKey($key)) {
77
-			$storage = $this->getStorage();
78
-			$result = $storage->file_get_contents($key);
79
-		}
80
-		return $result;
81
-	}
69
+    /**
70
+     * @param string $key
71
+     * @return mixed|null
72
+     * @throws \OC\ForbiddenException
73
+     */
74
+    public function get($key) {
75
+        $result = null;
76
+        if ($this->hasKey($key)) {
77
+            $storage = $this->getStorage();
78
+            $result = $storage->file_get_contents($key);
79
+        }
80
+        return $result;
81
+    }
82 82
 
83
-	/**
84
-	 * Returns the size of the stored/cached data
85
-	 *
86
-	 * @param string $key
87
-	 * @return int
88
-	 */
89
-	public function size($key) {
90
-		$result = 0;
91
-		if ($this->hasKey($key)) {
92
-			$storage = $this->getStorage();
93
-			$result = $storage->filesize($key);
94
-		}
95
-		return $result;
96
-	}
83
+    /**
84
+     * Returns the size of the stored/cached data
85
+     *
86
+     * @param string $key
87
+     * @return int
88
+     */
89
+    public function size($key) {
90
+        $result = 0;
91
+        if ($this->hasKey($key)) {
92
+            $storage = $this->getStorage();
93
+            $result = $storage->filesize($key);
94
+        }
95
+        return $result;
96
+    }
97 97
 
98
-	/**
99
-	 * @param string $key
100
-	 * @param mixed $value
101
-	 * @param int $ttl
102
-	 * @return bool|mixed
103
-	 * @throws \OC\ForbiddenException
104
-	 */
105
-	public function set($key, $value, $ttl = 0) {
106
-		$storage = $this->getStorage();
107
-		$result = false;
108
-		// unique id to avoid chunk collision, just in case
109
-		$uniqueId = \OC::$server->getSecureRandom()->generate(
110
-			16,
111
-			ISecureRandom::CHAR_ALPHANUMERIC
112
-		);
98
+    /**
99
+     * @param string $key
100
+     * @param mixed $value
101
+     * @param int $ttl
102
+     * @return bool|mixed
103
+     * @throws \OC\ForbiddenException
104
+     */
105
+    public function set($key, $value, $ttl = 0) {
106
+        $storage = $this->getStorage();
107
+        $result = false;
108
+        // unique id to avoid chunk collision, just in case
109
+        $uniqueId = \OC::$server->getSecureRandom()->generate(
110
+            16,
111
+            ISecureRandom::CHAR_ALPHANUMERIC
112
+        );
113 113
 
114
-		// use part file to prevent hasKey() to find the key
115
-		// while it is being written
116
-		$keyPart = $key . '.' . $uniqueId . '.part';
117
-		if ($storage and $storage->file_put_contents($keyPart, $value)) {
118
-			if ($ttl === 0) {
119
-				$ttl = 86400; // 60*60*24
120
-			}
121
-			$result = $storage->touch($keyPart, time() + $ttl);
122
-			$result &= $storage->rename($keyPart, $key);
123
-		}
124
-		return $result;
125
-	}
114
+        // use part file to prevent hasKey() to find the key
115
+        // while it is being written
116
+        $keyPart = $key . '.' . $uniqueId . '.part';
117
+        if ($storage and $storage->file_put_contents($keyPart, $value)) {
118
+            if ($ttl === 0) {
119
+                $ttl = 86400; // 60*60*24
120
+            }
121
+            $result = $storage->touch($keyPart, time() + $ttl);
122
+            $result &= $storage->rename($keyPart, $key);
123
+        }
124
+        return $result;
125
+    }
126 126
 
127
-	/**
128
-	 * @param string $key
129
-	 * @return bool
130
-	 * @throws \OC\ForbiddenException
131
-	 */
132
-	public function hasKey($key) {
133
-		$storage = $this->getStorage();
134
-		if ($storage && $storage->is_file($key) && $storage->isReadable($key)) {
135
-			return true;
136
-		}
137
-		return false;
138
-	}
127
+    /**
128
+     * @param string $key
129
+     * @return bool
130
+     * @throws \OC\ForbiddenException
131
+     */
132
+    public function hasKey($key) {
133
+        $storage = $this->getStorage();
134
+        if ($storage && $storage->is_file($key) && $storage->isReadable($key)) {
135
+            return true;
136
+        }
137
+        return false;
138
+    }
139 139
 
140
-	/**
141
-	 * @param string $key
142
-	 * @return bool|mixed
143
-	 * @throws \OC\ForbiddenException
144
-	 */
145
-	public function remove($key) {
146
-		$storage = $this->getStorage();
147
-		if (!$storage) {
148
-			return false;
149
-		}
150
-		return $storage->unlink($key);
151
-	}
140
+    /**
141
+     * @param string $key
142
+     * @return bool|mixed
143
+     * @throws \OC\ForbiddenException
144
+     */
145
+    public function remove($key) {
146
+        $storage = $this->getStorage();
147
+        if (!$storage) {
148
+            return false;
149
+        }
150
+        return $storage->unlink($key);
151
+    }
152 152
 
153
-	/**
154
-	 * @param string $prefix
155
-	 * @return bool
156
-	 * @throws \OC\ForbiddenException
157
-	 */
158
-	public function clear($prefix = '') {
159
-		$storage = $this->getStorage();
160
-		if ($storage and $storage->is_dir('/')) {
161
-			$dh = $storage->opendir('/');
162
-			if (is_resource($dh)) {
163
-				while (($file = readdir($dh)) !== false) {
164
-					if ($file != '.' and $file != '..' and ($prefix === '' || strpos($file, $prefix) === 0)) {
165
-						$storage->unlink('/' . $file);
166
-					}
167
-				}
168
-			}
169
-		}
170
-		return true;
171
-	}
153
+    /**
154
+     * @param string $prefix
155
+     * @return bool
156
+     * @throws \OC\ForbiddenException
157
+     */
158
+    public function clear($prefix = '') {
159
+        $storage = $this->getStorage();
160
+        if ($storage and $storage->is_dir('/')) {
161
+            $dh = $storage->opendir('/');
162
+            if (is_resource($dh)) {
163
+                while (($file = readdir($dh)) !== false) {
164
+                    if ($file != '.' and $file != '..' and ($prefix === '' || strpos($file, $prefix) === 0)) {
165
+                        $storage->unlink('/' . $file);
166
+                    }
167
+                }
168
+            }
169
+        }
170
+        return true;
171
+    }
172 172
 
173
-	/**
174
-	 * Runs GC
175
-	 * @throws \OC\ForbiddenException
176
-	 */
177
-	public function gc() {
178
-		$storage = $this->getStorage();
179
-		if ($storage) {
180
-			// extra hour safety, in case of stray part chunks that take longer to write,
181
-			// because touch() is only called after the chunk was finished
182
-			$now = time() - 3600;
183
-			$dh = $storage->opendir('/');
184
-			if (!is_resource($dh)) {
185
-				return null;
186
-			}
187
-			while (($file = readdir($dh)) !== false) {
188
-				if ($file != '.' and $file != '..') {
189
-					try {
190
-						$mtime = $storage->filemtime('/' . $file);
191
-						if ($mtime < $now) {
192
-							$storage->unlink('/' . $file);
193
-						}
194
-					} catch (\OCP\Lock\LockedException $e) {
195
-						// ignore locked chunks
196
-						\OC::$server->getLogger()->debug('Could not cleanup locked chunk "' . $file . '"', ['app' => 'core']);
197
-					} catch (\OCP\Files\ForbiddenException $e) {
198
-						\OC::$server->getLogger()->debug('Could not cleanup forbidden chunk "' . $file . '"', ['app' => 'core']);
199
-					} catch (\OCP\Files\LockNotAcquiredException $e) {
200
-						\OC::$server->getLogger()->debug('Could not cleanup locked chunk "' . $file . '"', ['app' => 'core']);
201
-					}
202
-				}
203
-			}
204
-		}
205
-	}
173
+    /**
174
+     * Runs GC
175
+     * @throws \OC\ForbiddenException
176
+     */
177
+    public function gc() {
178
+        $storage = $this->getStorage();
179
+        if ($storage) {
180
+            // extra hour safety, in case of stray part chunks that take longer to write,
181
+            // because touch() is only called after the chunk was finished
182
+            $now = time() - 3600;
183
+            $dh = $storage->opendir('/');
184
+            if (!is_resource($dh)) {
185
+                return null;
186
+            }
187
+            while (($file = readdir($dh)) !== false) {
188
+                if ($file != '.' and $file != '..') {
189
+                    try {
190
+                        $mtime = $storage->filemtime('/' . $file);
191
+                        if ($mtime < $now) {
192
+                            $storage->unlink('/' . $file);
193
+                        }
194
+                    } catch (\OCP\Lock\LockedException $e) {
195
+                        // ignore locked chunks
196
+                        \OC::$server->getLogger()->debug('Could not cleanup locked chunk "' . $file . '"', ['app' => 'core']);
197
+                    } catch (\OCP\Files\ForbiddenException $e) {
198
+                        \OC::$server->getLogger()->debug('Could not cleanup forbidden chunk "' . $file . '"', ['app' => 'core']);
199
+                    } catch (\OCP\Files\LockNotAcquiredException $e) {
200
+                        \OC::$server->getLogger()->debug('Could not cleanup locked chunk "' . $file . '"', ['app' => 'core']);
201
+                    }
202
+                }
203
+            }
204
+        }
205
+    }
206 206
 }
Please login to merge, or discard this patch.
Spacing   +10 added lines, -10 removed lines patch added patch discarded remove patch
@@ -55,10 +55,10 @@  discard block
 block discarded – undo
55 55
 			$rootView = new View();
56 56
 			$user = \OC::$server->getUserSession()->getUser();
57 57
 			Filesystem::initMountPoints($user->getUID());
58
-			if (!$rootView->file_exists('/' . $user->getUID() . '/cache')) {
59
-				$rootView->mkdir('/' . $user->getUID() . '/cache');
58
+			if (!$rootView->file_exists('/'.$user->getUID().'/cache')) {
59
+				$rootView->mkdir('/'.$user->getUID().'/cache');
60 60
 			}
61
-			$this->storage = new View('/' . $user->getUID() . '/cache');
61
+			$this->storage = new View('/'.$user->getUID().'/cache');
62 62
 			return $this->storage;
63 63
 		} else {
64 64
 			\OCP\Util::writeLog('core', 'Can\'t get cache storage, user not logged in', ILogger::ERROR);
@@ -113,7 +113,7 @@  discard block
 block discarded – undo
113 113
 
114 114
 		// use part file to prevent hasKey() to find the key
115 115
 		// while it is being written
116
-		$keyPart = $key . '.' . $uniqueId . '.part';
116
+		$keyPart = $key.'.'.$uniqueId.'.part';
117 117
 		if ($storage and $storage->file_put_contents($keyPart, $value)) {
118 118
 			if ($ttl === 0) {
119 119
 				$ttl = 86400; // 60*60*24
@@ -162,7 +162,7 @@  discard block
 block discarded – undo
162 162
 			if (is_resource($dh)) {
163 163
 				while (($file = readdir($dh)) !== false) {
164 164
 					if ($file != '.' and $file != '..' and ($prefix === '' || strpos($file, $prefix) === 0)) {
165
-						$storage->unlink('/' . $file);
165
+						$storage->unlink('/'.$file);
166 166
 					}
167 167
 				}
168 168
 			}
@@ -187,17 +187,17 @@  discard block
 block discarded – undo
187 187
 			while (($file = readdir($dh)) !== false) {
188 188
 				if ($file != '.' and $file != '..') {
189 189
 					try {
190
-						$mtime = $storage->filemtime('/' . $file);
190
+						$mtime = $storage->filemtime('/'.$file);
191 191
 						if ($mtime < $now) {
192
-							$storage->unlink('/' . $file);
192
+							$storage->unlink('/'.$file);
193 193
 						}
194 194
 					} catch (\OCP\Lock\LockedException $e) {
195 195
 						// ignore locked chunks
196
-						\OC::$server->getLogger()->debug('Could not cleanup locked chunk "' . $file . '"', ['app' => 'core']);
196
+						\OC::$server->getLogger()->debug('Could not cleanup locked chunk "'.$file.'"', ['app' => 'core']);
197 197
 					} catch (\OCP\Files\ForbiddenException $e) {
198
-						\OC::$server->getLogger()->debug('Could not cleanup forbidden chunk "' . $file . '"', ['app' => 'core']);
198
+						\OC::$server->getLogger()->debug('Could not cleanup forbidden chunk "'.$file.'"', ['app' => 'core']);
199 199
 					} catch (\OCP\Files\LockNotAcquiredException $e) {
200
-						\OC::$server->getLogger()->debug('Could not cleanup locked chunk "' . $file . '"', ['app' => 'core']);
200
+						\OC::$server->getLogger()->debug('Could not cleanup locked chunk "'.$file.'"', ['app' => 'core']);
201 201
 					}
202 202
 				}
203 203
 			}
Please login to merge, or discard this patch.
apps/settings/lib/Mailer/NewUserMailHelper.php 2 patches
Indentation   +131 added lines, -131 removed lines patch added patch discarded remove patch
@@ -41,146 +41,146 @@
 block discarded – undo
41 41
 use OCP\Security\ISecureRandom;
42 42
 
43 43
 class NewUserMailHelper {
44
-	/** @var Defaults */
45
-	private $themingDefaults;
46
-	/** @var IURLGenerator */
47
-	private $urlGenerator;
48
-	/** @var IFactory */
49
-	private $l10nFactory;
50
-	/** @var IMailer */
51
-	private $mailer;
52
-	/** @var ISecureRandom */
53
-	private $secureRandom;
54
-	/** @var ITimeFactory */
55
-	private $timeFactory;
56
-	/** @var IConfig */
57
-	private $config;
58
-	/** @var ICrypto */
59
-	private $crypto;
60
-	/** @var string */
61
-	private $fromAddress;
44
+    /** @var Defaults */
45
+    private $themingDefaults;
46
+    /** @var IURLGenerator */
47
+    private $urlGenerator;
48
+    /** @var IFactory */
49
+    private $l10nFactory;
50
+    /** @var IMailer */
51
+    private $mailer;
52
+    /** @var ISecureRandom */
53
+    private $secureRandom;
54
+    /** @var ITimeFactory */
55
+    private $timeFactory;
56
+    /** @var IConfig */
57
+    private $config;
58
+    /** @var ICrypto */
59
+    private $crypto;
60
+    /** @var string */
61
+    private $fromAddress;
62 62
 
63
-	/**
64
-	 * @param Defaults $themingDefaults
65
-	 * @param IURLGenerator $urlGenerator
66
-	 * @param IFactory $l10nFactory
67
-	 * @param IMailer $mailer
68
-	 * @param ISecureRandom $secureRandom
69
-	 * @param ITimeFactory $timeFactory
70
-	 * @param IConfig $config
71
-	 * @param ICrypto $crypto
72
-	 * @param string $fromAddress
73
-	 */
74
-	public function __construct(Defaults $themingDefaults,
75
-								IURLGenerator $urlGenerator,
76
-								IFactory $l10nFactory,
77
-								IMailer $mailer,
78
-								ISecureRandom $secureRandom,
79
-								ITimeFactory $timeFactory,
80
-								IConfig $config,
81
-								ICrypto $crypto,
82
-								$fromAddress) {
83
-		$this->themingDefaults = $themingDefaults;
84
-		$this->urlGenerator = $urlGenerator;
85
-		$this->l10nFactory = $l10nFactory;
86
-		$this->mailer = $mailer;
87
-		$this->secureRandom = $secureRandom;
88
-		$this->timeFactory = $timeFactory;
89
-		$this->config = $config;
90
-		$this->crypto = $crypto;
91
-		$this->fromAddress = $fromAddress;
92
-	}
63
+    /**
64
+     * @param Defaults $themingDefaults
65
+     * @param IURLGenerator $urlGenerator
66
+     * @param IFactory $l10nFactory
67
+     * @param IMailer $mailer
68
+     * @param ISecureRandom $secureRandom
69
+     * @param ITimeFactory $timeFactory
70
+     * @param IConfig $config
71
+     * @param ICrypto $crypto
72
+     * @param string $fromAddress
73
+     */
74
+    public function __construct(Defaults $themingDefaults,
75
+                                IURLGenerator $urlGenerator,
76
+                                IFactory $l10nFactory,
77
+                                IMailer $mailer,
78
+                                ISecureRandom $secureRandom,
79
+                                ITimeFactory $timeFactory,
80
+                                IConfig $config,
81
+                                ICrypto $crypto,
82
+                                $fromAddress) {
83
+        $this->themingDefaults = $themingDefaults;
84
+        $this->urlGenerator = $urlGenerator;
85
+        $this->l10nFactory = $l10nFactory;
86
+        $this->mailer = $mailer;
87
+        $this->secureRandom = $secureRandom;
88
+        $this->timeFactory = $timeFactory;
89
+        $this->config = $config;
90
+        $this->crypto = $crypto;
91
+        $this->fromAddress = $fromAddress;
92
+    }
93 93
 
94
-	/**
95
-	 * @param IUser $user
96
-	 * @param bool $generatePasswordResetToken
97
-	 * @return IEMailTemplate
98
-	 */
99
-	public function generateTemplate(IUser $user, $generatePasswordResetToken = false) {
100
-		$userId = $user->getUID();
101
-		$lang = $this->l10nFactory->getUserLanguage($user);
102
-		$l10n = $this->l10nFactory->get('settings', $lang);
94
+    /**
95
+     * @param IUser $user
96
+     * @param bool $generatePasswordResetToken
97
+     * @return IEMailTemplate
98
+     */
99
+    public function generateTemplate(IUser $user, $generatePasswordResetToken = false) {
100
+        $userId = $user->getUID();
101
+        $lang = $this->l10nFactory->getUserLanguage($user);
102
+        $l10n = $this->l10nFactory->get('settings', $lang);
103 103
 
104
-		if ($generatePasswordResetToken) {
105
-			$token = $this->secureRandom->generate(
106
-				21,
107
-				ISecureRandom::CHAR_ALPHANUMERIC
108
-			);
109
-			$tokenValue = $this->timeFactory->getTime() . ':' . $token;
110
-			$mailAddress = (null !== $user->getEMailAddress()) ? $user->getEMailAddress() : '';
111
-			$encryptedValue = $this->crypto->encrypt($tokenValue, $mailAddress . $this->config->getSystemValue('secret'));
112
-			$this->config->setUserValue($user->getUID(), 'core', 'lostpassword', $encryptedValue);
113
-			$link = $this->urlGenerator->linkToRouteAbsolute('core.lost.resetform', ['userId' => $user->getUID(), 'token' => $token]);
114
-		} else {
115
-			$link = $this->urlGenerator->getAbsoluteURL('/');
116
-		}
117
-		$displayName = $user->getDisplayName();
104
+        if ($generatePasswordResetToken) {
105
+            $token = $this->secureRandom->generate(
106
+                21,
107
+                ISecureRandom::CHAR_ALPHANUMERIC
108
+            );
109
+            $tokenValue = $this->timeFactory->getTime() . ':' . $token;
110
+            $mailAddress = (null !== $user->getEMailAddress()) ? $user->getEMailAddress() : '';
111
+            $encryptedValue = $this->crypto->encrypt($tokenValue, $mailAddress . $this->config->getSystemValue('secret'));
112
+            $this->config->setUserValue($user->getUID(), 'core', 'lostpassword', $encryptedValue);
113
+            $link = $this->urlGenerator->linkToRouteAbsolute('core.lost.resetform', ['userId' => $user->getUID(), 'token' => $token]);
114
+        } else {
115
+            $link = $this->urlGenerator->getAbsoluteURL('/');
116
+        }
117
+        $displayName = $user->getDisplayName();
118 118
 
119
-		$emailTemplate = $this->mailer->createEMailTemplate('settings.Welcome', [
120
-			'link' => $link,
121
-			'displayname' => $displayName,
122
-			'userid' => $userId,
123
-			'instancename' => $this->themingDefaults->getName(),
124
-			'resetTokenGenerated' => $generatePasswordResetToken,
125
-		]);
119
+        $emailTemplate = $this->mailer->createEMailTemplate('settings.Welcome', [
120
+            'link' => $link,
121
+            'displayname' => $displayName,
122
+            'userid' => $userId,
123
+            'instancename' => $this->themingDefaults->getName(),
124
+            'resetTokenGenerated' => $generatePasswordResetToken,
125
+        ]);
126 126
 
127
-		$emailTemplate->setSubject($l10n->t('Your %s account was created', [$this->themingDefaults->getName()]));
128
-		$emailTemplate->addHeader();
129
-		if ($displayName === $userId) {
130
-			$emailTemplate->addHeading($l10n->t('Welcome aboard'));
131
-		} else {
132
-			$emailTemplate->addHeading($l10n->t('Welcome aboard %s', [$displayName]));
133
-		}
134
-		$emailTemplate->addBodyText($l10n->t('Welcome to your %s account, you can add, protect, and share your data.', [$this->themingDefaults->getName()]));
135
-		if ($user->getBackendClassName() !== 'LDAP') {
136
-			$emailTemplate->addBodyText($l10n->t('Your username is: %s', [$userId]));
137
-		}
138
-		if ($generatePasswordResetToken) {
139
-			$leftButtonText = $l10n->t('Set your password');
140
-		} else {
141
-			$leftButtonText = $l10n->t('Go to %s', [$this->themingDefaults->getName()]);
142
-		}
127
+        $emailTemplate->setSubject($l10n->t('Your %s account was created', [$this->themingDefaults->getName()]));
128
+        $emailTemplate->addHeader();
129
+        if ($displayName === $userId) {
130
+            $emailTemplate->addHeading($l10n->t('Welcome aboard'));
131
+        } else {
132
+            $emailTemplate->addHeading($l10n->t('Welcome aboard %s', [$displayName]));
133
+        }
134
+        $emailTemplate->addBodyText($l10n->t('Welcome to your %s account, you can add, protect, and share your data.', [$this->themingDefaults->getName()]));
135
+        if ($user->getBackendClassName() !== 'LDAP') {
136
+            $emailTemplate->addBodyText($l10n->t('Your username is: %s', [$userId]));
137
+        }
138
+        if ($generatePasswordResetToken) {
139
+            $leftButtonText = $l10n->t('Set your password');
140
+        } else {
141
+            $leftButtonText = $l10n->t('Go to %s', [$this->themingDefaults->getName()]);
142
+        }
143 143
 
144
-		$clientDownload = $this->config->getSystemValue('customclient_desktop', 'https://nextcloud.com/install/#install-clients');
145
-		if ($clientDownload === '') {
146
-			$emailTemplate->addBodyButton(
147
-				$leftButtonText,
148
-				$link
149
-			);
150
-		} else {
151
-			$emailTemplate->addBodyButtonGroup(
152
-				$leftButtonText,
153
-				$link,
154
-				$l10n->t('Install Client'),
155
-				$clientDownload
156
-			);
157
-		}
144
+        $clientDownload = $this->config->getSystemValue('customclient_desktop', 'https://nextcloud.com/install/#install-clients');
145
+        if ($clientDownload === '') {
146
+            $emailTemplate->addBodyButton(
147
+                $leftButtonText,
148
+                $link
149
+            );
150
+        } else {
151
+            $emailTemplate->addBodyButtonGroup(
152
+                $leftButtonText,
153
+                $link,
154
+                $l10n->t('Install Client'),
155
+                $clientDownload
156
+            );
157
+        }
158 158
 
159
-		$emailTemplate->addFooter('', $lang);
159
+        $emailTemplate->addFooter('', $lang);
160 160
 
161
-		return $emailTemplate;
162
-	}
161
+        return $emailTemplate;
162
+    }
163 163
 
164
-	/**
165
-	 * Sends a welcome mail to $user
166
-	 *
167
-	 * @param IUser $user
168
-	 * @param IEmailTemplate $emailTemplate
169
-	 * @throws \Exception If mail could not be sent
170
-	 */
171
-	public function sendMail(IUser $user,
172
-							 IEMailTemplate $emailTemplate): void {
164
+    /**
165
+     * Sends a welcome mail to $user
166
+     *
167
+     * @param IUser $user
168
+     * @param IEmailTemplate $emailTemplate
169
+     * @throws \Exception If mail could not be sent
170
+     */
171
+    public function sendMail(IUser $user,
172
+                                IEMailTemplate $emailTemplate): void {
173 173
 
174
-		// Be sure to never try to send to an empty e-mail
175
-		$email = $user->getEMailAddress();
176
-		if ($email === null) {
177
-			return;
178
-		}
174
+        // Be sure to never try to send to an empty e-mail
175
+        $email = $user->getEMailAddress();
176
+        if ($email === null) {
177
+            return;
178
+        }
179 179
 
180
-		$message = $this->mailer->createMessage();
181
-		$message->setTo([$email => $user->getDisplayName()]);
182
-		$message->setFrom([$this->fromAddress => $this->themingDefaults->getName()]);
183
-		$message->useTemplate($emailTemplate);
184
-		$this->mailer->send($message);
185
-	}
180
+        $message = $this->mailer->createMessage();
181
+        $message->setTo([$email => $user->getDisplayName()]);
182
+        $message->setFrom([$this->fromAddress => $this->themingDefaults->getName()]);
183
+        $message->useTemplate($emailTemplate);
184
+        $this->mailer->send($message);
185
+    }
186 186
 }
Please login to merge, or discard this patch.
Spacing   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -106,9 +106,9 @@
 block discarded – undo
106 106
 				21,
107 107
 				ISecureRandom::CHAR_ALPHANUMERIC
108 108
 			);
109
-			$tokenValue = $this->timeFactory->getTime() . ':' . $token;
109
+			$tokenValue = $this->timeFactory->getTime().':'.$token;
110 110
 			$mailAddress = (null !== $user->getEMailAddress()) ? $user->getEMailAddress() : '';
111
-			$encryptedValue = $this->crypto->encrypt($tokenValue, $mailAddress . $this->config->getSystemValue('secret'));
111
+			$encryptedValue = $this->crypto->encrypt($tokenValue, $mailAddress.$this->config->getSystemValue('secret'));
112 112
 			$this->config->setUserValue($user->getUID(), 'core', 'lostpassword', $encryptedValue);
113 113
 			$link = $this->urlGenerator->linkToRouteAbsolute('core.lost.resetform', ['userId' => $user->getUID(), 'token' => $token]);
114 114
 		} else {
Please login to merge, or discard this patch.
apps/dav/lib/Controller/DirectController.php 2 patches
Indentation   +57 added lines, -57 removed lines patch added patch discarded remove patch
@@ -41,79 +41,79 @@
 block discarded – undo
41 41
 
42 42
 class DirectController extends OCSController {
43 43
 
44
-	/** @var IRootFolder */
45
-	private $rootFolder;
44
+    /** @var IRootFolder */
45
+    private $rootFolder;
46 46
 
47
-	/** @var string */
48
-	private $userId;
47
+    /** @var string */
48
+    private $userId;
49 49
 
50
-	/** @var DirectMapper */
51
-	private $mapper;
50
+    /** @var DirectMapper */
51
+    private $mapper;
52 52
 
53
-	/** @var ISecureRandom */
54
-	private $random;
53
+    /** @var ISecureRandom */
54
+    private $random;
55 55
 
56
-	/** @var ITimeFactory */
57
-	private $timeFactory;
56
+    /** @var ITimeFactory */
57
+    private $timeFactory;
58 58
 
59
-	/** @var IURLGenerator */
60
-	private $urlGenerator;
59
+    /** @var IURLGenerator */
60
+    private $urlGenerator;
61 61
 
62 62
 
63
-	public function __construct(string $appName,
64
-								IRequest $request,
65
-								IRootFolder $rootFolder,
66
-								string $userId,
67
-								DirectMapper $mapper,
68
-								ISecureRandom $random,
69
-								ITimeFactory $timeFactory,
70
-								IURLGenerator $urlGenerator) {
71
-		parent::__construct($appName, $request);
63
+    public function __construct(string $appName,
64
+                                IRequest $request,
65
+                                IRootFolder $rootFolder,
66
+                                string $userId,
67
+                                DirectMapper $mapper,
68
+                                ISecureRandom $random,
69
+                                ITimeFactory $timeFactory,
70
+                                IURLGenerator $urlGenerator) {
71
+        parent::__construct($appName, $request);
72 72
 
73
-		$this->rootFolder = $rootFolder;
74
-		$this->userId = $userId;
75
-		$this->mapper = $mapper;
76
-		$this->random = $random;
77
-		$this->timeFactory = $timeFactory;
78
-		$this->urlGenerator = $urlGenerator;
79
-	}
73
+        $this->rootFolder = $rootFolder;
74
+        $this->userId = $userId;
75
+        $this->mapper = $mapper;
76
+        $this->random = $random;
77
+        $this->timeFactory = $timeFactory;
78
+        $this->urlGenerator = $urlGenerator;
79
+    }
80 80
 
81
-	/**
82
-	 * @NoAdminRequired
83
-	 */
84
-	public function getUrl(int $fileId, int $expirationTime = 60 * 60 * 8): DataResponse {
85
-		$userFolder = $this->rootFolder->getUserFolder($this->userId);
81
+    /**
82
+     * @NoAdminRequired
83
+     */
84
+    public function getUrl(int $fileId, int $expirationTime = 60 * 60 * 8): DataResponse {
85
+        $userFolder = $this->rootFolder->getUserFolder($this->userId);
86 86
 
87
-		$files = $userFolder->getById($fileId);
87
+        $files = $userFolder->getById($fileId);
88 88
 
89
-		if ($files === []) {
90
-			throw new OCSNotFoundException();
91
-		}
89
+        if ($files === []) {
90
+            throw new OCSNotFoundException();
91
+        }
92 92
 
93
-		if ($expirationTime <= 0 || $expirationTime > (60 * 60 * 24)) {
94
-			throw new OCSBadRequestException('Expiration time should be greater than 0 and less than or equal to ' . (60 * 60 * 24));
95
-		}
93
+        if ($expirationTime <= 0 || $expirationTime > (60 * 60 * 24)) {
94
+            throw new OCSBadRequestException('Expiration time should be greater than 0 and less than or equal to ' . (60 * 60 * 24));
95
+        }
96 96
 
97
-		$file = array_shift($files);
98
-		if (!($file instanceof File)) {
99
-			throw new OCSBadRequestException('Direct download only works for files');
100
-		}
97
+        $file = array_shift($files);
98
+        if (!($file instanceof File)) {
99
+            throw new OCSBadRequestException('Direct download only works for files');
100
+        }
101 101
 
102
-		//TODO: at some point we should use the directdownlaod function of storages
103
-		$direct = new Direct();
104
-		$direct->setUserId($this->userId);
105
-		$direct->setFileId($fileId);
102
+        //TODO: at some point we should use the directdownlaod function of storages
103
+        $direct = new Direct();
104
+        $direct->setUserId($this->userId);
105
+        $direct->setFileId($fileId);
106 106
 
107
-		$token = $this->random->generate(60, ISecureRandom::CHAR_ALPHANUMERIC);
108
-		$direct->setToken($token);
109
-		$direct->setExpiration($this->timeFactory->getTime() + $expirationTime);
107
+        $token = $this->random->generate(60, ISecureRandom::CHAR_ALPHANUMERIC);
108
+        $direct->setToken($token);
109
+        $direct->setExpiration($this->timeFactory->getTime() + $expirationTime);
110 110
 
111
-		$this->mapper->insert($direct);
111
+        $this->mapper->insert($direct);
112 112
 
113
-		$url = $this->urlGenerator->getAbsoluteURL('remote.php/direct/'.$token);
113
+        $url = $this->urlGenerator->getAbsoluteURL('remote.php/direct/'.$token);
114 114
 
115
-		return new DataResponse([
116
-			'url' => $url,
117
-		]);
118
-	}
115
+        return new DataResponse([
116
+            'url' => $url,
117
+        ]);
118
+    }
119 119
 }
Please login to merge, or discard this patch.
Spacing   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -91,7 +91,7 @@
 block discarded – undo
91 91
 		}
92 92
 
93 93
 		if ($expirationTime <= 0 || $expirationTime > (60 * 60 * 24)) {
94
-			throw new OCSBadRequestException('Expiration time should be greater than 0 and less than or equal to ' . (60 * 60 * 24));
94
+			throw new OCSBadRequestException('Expiration time should be greater than 0 and less than or equal to '.(60 * 60 * 24));
95 95
 		}
96 96
 
97 97
 		$file = array_shift($files);
Please login to merge, or discard this patch.
apps/dav/lib/CalDAV/Schedule/IMipPlugin.php 2 patches
Indentation   +627 added lines, -627 removed lines patch added patch discarded remove patch
@@ -72,194 +72,194 @@  discard block
 block discarded – undo
72 72
  */
73 73
 class IMipPlugin extends SabreIMipPlugin {
74 74
 
75
-	/** @var string */
76
-	private $userId;
77
-
78
-	/** @var IConfig */
79
-	private $config;
80
-
81
-	/** @var IMailer */
82
-	private $mailer;
83
-
84
-	/** @var ILogger */
85
-	private $logger;
86
-
87
-	/** @var ITimeFactory */
88
-	private $timeFactory;
89
-
90
-	/** @var L10NFactory */
91
-	private $l10nFactory;
92
-
93
-	/** @var IURLGenerator */
94
-	private $urlGenerator;
95
-
96
-	/** @var ISecureRandom */
97
-	private $random;
98
-
99
-	/** @var IDBConnection */
100
-	private $db;
101
-
102
-	/** @var Defaults */
103
-	private $defaults;
104
-
105
-	/** @var IUserManager */
106
-	private $userManager;
107
-
108
-	public const MAX_DATE = '2038-01-01';
109
-
110
-	public const METHOD_REQUEST = 'request';
111
-	public const METHOD_REPLY = 'reply';
112
-	public const METHOD_CANCEL = 'cancel';
113
-	public const IMIP_INDENT = 15; // Enough for the length of all body bullet items, in all languages
114
-
115
-	/**
116
-	 * @param IConfig $config
117
-	 * @param IMailer $mailer
118
-	 * @param ILogger $logger
119
-	 * @param ITimeFactory $timeFactory
120
-	 * @param L10NFactory $l10nFactory
121
-	 * @param IUrlGenerator $urlGenerator
122
-	 * @param Defaults $defaults
123
-	 * @param ISecureRandom $random
124
-	 * @param IDBConnection $db
125
-	 * @param string $userId
126
-	 */
127
-	public function __construct(IConfig $config, IMailer $mailer, ILogger $logger,
128
-								ITimeFactory $timeFactory, L10NFactory $l10nFactory,
129
-								IURLGenerator $urlGenerator, Defaults $defaults,
130
-								ISecureRandom $random, IDBConnection $db, IUserManager $userManager,
131
-								$userId) {
132
-		parent::__construct('');
133
-		$this->userId = $userId;
134
-		$this->config = $config;
135
-		$this->mailer = $mailer;
136
-		$this->logger = $logger;
137
-		$this->timeFactory = $timeFactory;
138
-		$this->l10nFactory = $l10nFactory;
139
-		$this->urlGenerator = $urlGenerator;
140
-		$this->random = $random;
141
-		$this->db = $db;
142
-		$this->defaults = $defaults;
143
-		$this->userManager = $userManager;
144
-	}
145
-
146
-	/**
147
-	 * Event handler for the 'schedule' event.
148
-	 *
149
-	 * @param Message $iTipMessage
150
-	 * @return void
151
-	 */
152
-	public function schedule(Message $iTipMessage) {
153
-
154
-		// Not sending any emails if the system considers the update
155
-		// insignificant.
156
-		if (!$iTipMessage->significantChange) {
157
-			if (!$iTipMessage->scheduleStatus) {
158
-				$iTipMessage->scheduleStatus = '1.0;We got the message, but it\'s not significant enough to warrant an email';
159
-			}
160
-			return;
161
-		}
162
-
163
-		$summary = $iTipMessage->message->VEVENT->SUMMARY;
164
-
165
-		if (parse_url($iTipMessage->sender, PHP_URL_SCHEME) !== 'mailto') {
166
-			return;
167
-		}
168
-
169
-		if (parse_url($iTipMessage->recipient, PHP_URL_SCHEME) !== 'mailto') {
170
-			return;
171
-		}
172
-
173
-		// don't send out mails for events that already took place
174
-		$lastOccurrence = $this->getLastOccurrence($iTipMessage->message);
175
-		$currentTime = $this->timeFactory->getTime();
176
-		if ($lastOccurrence < $currentTime) {
177
-			return;
178
-		}
179
-
180
-		// Strip off mailto:
181
-		$sender = substr($iTipMessage->sender, 7);
182
-		$recipient = substr($iTipMessage->recipient, 7);
183
-		if ($recipient === false || !$this->mailer->validateMailAddress($recipient)) {
184
-			// Nothing to send if the recipient doesn't have a valid email address
185
-			$iTipMessage->scheduleStatus = '5.0; EMail delivery failed';
186
-			return;
187
-		}
188
-
189
-		$senderName = $iTipMessage->senderName ?: null;
190
-		$recipientName = $iTipMessage->recipientName ?: null;
191
-
192
-		if ($senderName === null || empty(trim($senderName))) {
193
-			$user = $this->userManager->get($this->userId);
194
-			if ($user) {
195
-				// getDisplayName automatically uses the uid
196
-				// if no display-name is set
197
-				$senderName = $user->getDisplayName();
198
-			}
199
-		}
200
-
201
-		/** @var VEvent $vevent */
202
-		$vevent = $iTipMessage->message->VEVENT;
203
-
204
-		$attendee = $this->getCurrentAttendee($iTipMessage);
205
-		$defaultLang = $this->l10nFactory->findLanguage();
206
-		$lang = $this->getAttendeeLangOrDefault($defaultLang, $attendee);
207
-		$l10n = $this->l10nFactory->get('dav', $lang);
208
-
209
-		$meetingAttendeeName = $recipientName ?: $recipient;
210
-		$meetingInviteeName = $senderName ?: $sender;
211
-
212
-		$meetingTitle = $vevent->SUMMARY;
213
-		$meetingDescription = $vevent->DESCRIPTION;
214
-
215
-
216
-		$meetingUrl = $vevent->URL;
217
-		$meetingLocation = $vevent->LOCATION;
218
-
219
-		$defaultVal = '--';
220
-
221
-		$method = self::METHOD_REQUEST;
222
-		switch (strtolower($iTipMessage->method)) {
223
-			case self::METHOD_REPLY:
224
-				$method = self::METHOD_REPLY;
225
-				break;
226
-			case self::METHOD_CANCEL:
227
-				$method = self::METHOD_CANCEL;
228
-				break;
229
-		}
230
-
231
-		$data = [
232
-			'attendee_name' => (string)$meetingAttendeeName ?: $defaultVal,
233
-			'invitee_name' => (string)$meetingInviteeName ?: $defaultVal,
234
-			'meeting_title' => (string)$meetingTitle ?: $defaultVal,
235
-			'meeting_description' => (string)$meetingDescription ?: $defaultVal,
236
-			'meeting_url' => (string)$meetingUrl ?: $defaultVal,
237
-		];
238
-
239
-		$fromEMail = Util::getDefaultEmailAddress('invitations-noreply');
240
-		$fromName = $l10n->t('%1$s via %2$s', [$senderName, $this->defaults->getName()]);
241
-
242
-		$message = $this->mailer->createMessage()
243
-			->setFrom([$fromEMail => $fromName])
244
-			->setTo([$recipient => $recipientName]);
245
-
246
-		if ($sender !== false) {
247
-			$message->setReplyTo([$sender => $senderName]);
248
-		}
249
-
250
-		$template = $this->mailer->createEMailTemplate('dav.calendarInvite.' . $method, $data);
251
-		$template->addHeader();
252
-
253
-		$summary = ((string) $summary !== '') ? (string) $summary : $l10n->t('Untitled event');
254
-
255
-		$this->addSubjectAndHeading($template, $l10n, $method, $summary);
256
-		$this->addBulletList($template, $l10n, $vevent);
257
-
258
-
259
-		// Only add response buttons to invitation requests: Fix Issue #11230
260
-		if (($method == self::METHOD_REQUEST) && $this->getAttendeeRSVP($attendee)) {
75
+    /** @var string */
76
+    private $userId;
77
+
78
+    /** @var IConfig */
79
+    private $config;
80
+
81
+    /** @var IMailer */
82
+    private $mailer;
83
+
84
+    /** @var ILogger */
85
+    private $logger;
86
+
87
+    /** @var ITimeFactory */
88
+    private $timeFactory;
89
+
90
+    /** @var L10NFactory */
91
+    private $l10nFactory;
92
+
93
+    /** @var IURLGenerator */
94
+    private $urlGenerator;
95
+
96
+    /** @var ISecureRandom */
97
+    private $random;
98
+
99
+    /** @var IDBConnection */
100
+    private $db;
101
+
102
+    /** @var Defaults */
103
+    private $defaults;
104
+
105
+    /** @var IUserManager */
106
+    private $userManager;
107
+
108
+    public const MAX_DATE = '2038-01-01';
109
+
110
+    public const METHOD_REQUEST = 'request';
111
+    public const METHOD_REPLY = 'reply';
112
+    public const METHOD_CANCEL = 'cancel';
113
+    public const IMIP_INDENT = 15; // Enough for the length of all body bullet items, in all languages
114
+
115
+    /**
116
+     * @param IConfig $config
117
+     * @param IMailer $mailer
118
+     * @param ILogger $logger
119
+     * @param ITimeFactory $timeFactory
120
+     * @param L10NFactory $l10nFactory
121
+     * @param IUrlGenerator $urlGenerator
122
+     * @param Defaults $defaults
123
+     * @param ISecureRandom $random
124
+     * @param IDBConnection $db
125
+     * @param string $userId
126
+     */
127
+    public function __construct(IConfig $config, IMailer $mailer, ILogger $logger,
128
+                                ITimeFactory $timeFactory, L10NFactory $l10nFactory,
129
+                                IURLGenerator $urlGenerator, Defaults $defaults,
130
+                                ISecureRandom $random, IDBConnection $db, IUserManager $userManager,
131
+                                $userId) {
132
+        parent::__construct('');
133
+        $this->userId = $userId;
134
+        $this->config = $config;
135
+        $this->mailer = $mailer;
136
+        $this->logger = $logger;
137
+        $this->timeFactory = $timeFactory;
138
+        $this->l10nFactory = $l10nFactory;
139
+        $this->urlGenerator = $urlGenerator;
140
+        $this->random = $random;
141
+        $this->db = $db;
142
+        $this->defaults = $defaults;
143
+        $this->userManager = $userManager;
144
+    }
145
+
146
+    /**
147
+     * Event handler for the 'schedule' event.
148
+     *
149
+     * @param Message $iTipMessage
150
+     * @return void
151
+     */
152
+    public function schedule(Message $iTipMessage) {
153
+
154
+        // Not sending any emails if the system considers the update
155
+        // insignificant.
156
+        if (!$iTipMessage->significantChange) {
157
+            if (!$iTipMessage->scheduleStatus) {
158
+                $iTipMessage->scheduleStatus = '1.0;We got the message, but it\'s not significant enough to warrant an email';
159
+            }
160
+            return;
161
+        }
162
+
163
+        $summary = $iTipMessage->message->VEVENT->SUMMARY;
164
+
165
+        if (parse_url($iTipMessage->sender, PHP_URL_SCHEME) !== 'mailto') {
166
+            return;
167
+        }
168
+
169
+        if (parse_url($iTipMessage->recipient, PHP_URL_SCHEME) !== 'mailto') {
170
+            return;
171
+        }
172
+
173
+        // don't send out mails for events that already took place
174
+        $lastOccurrence = $this->getLastOccurrence($iTipMessage->message);
175
+        $currentTime = $this->timeFactory->getTime();
176
+        if ($lastOccurrence < $currentTime) {
177
+            return;
178
+        }
179
+
180
+        // Strip off mailto:
181
+        $sender = substr($iTipMessage->sender, 7);
182
+        $recipient = substr($iTipMessage->recipient, 7);
183
+        if ($recipient === false || !$this->mailer->validateMailAddress($recipient)) {
184
+            // Nothing to send if the recipient doesn't have a valid email address
185
+            $iTipMessage->scheduleStatus = '5.0; EMail delivery failed';
186
+            return;
187
+        }
188
+
189
+        $senderName = $iTipMessage->senderName ?: null;
190
+        $recipientName = $iTipMessage->recipientName ?: null;
191
+
192
+        if ($senderName === null || empty(trim($senderName))) {
193
+            $user = $this->userManager->get($this->userId);
194
+            if ($user) {
195
+                // getDisplayName automatically uses the uid
196
+                // if no display-name is set
197
+                $senderName = $user->getDisplayName();
198
+            }
199
+        }
200
+
201
+        /** @var VEvent $vevent */
202
+        $vevent = $iTipMessage->message->VEVENT;
203
+
204
+        $attendee = $this->getCurrentAttendee($iTipMessage);
205
+        $defaultLang = $this->l10nFactory->findLanguage();
206
+        $lang = $this->getAttendeeLangOrDefault($defaultLang, $attendee);
207
+        $l10n = $this->l10nFactory->get('dav', $lang);
208
+
209
+        $meetingAttendeeName = $recipientName ?: $recipient;
210
+        $meetingInviteeName = $senderName ?: $sender;
211
+
212
+        $meetingTitle = $vevent->SUMMARY;
213
+        $meetingDescription = $vevent->DESCRIPTION;
214
+
215
+
216
+        $meetingUrl = $vevent->URL;
217
+        $meetingLocation = $vevent->LOCATION;
218
+
219
+        $defaultVal = '--';
220
+
221
+        $method = self::METHOD_REQUEST;
222
+        switch (strtolower($iTipMessage->method)) {
223
+            case self::METHOD_REPLY:
224
+                $method = self::METHOD_REPLY;
225
+                break;
226
+            case self::METHOD_CANCEL:
227
+                $method = self::METHOD_CANCEL;
228
+                break;
229
+        }
230
+
231
+        $data = [
232
+            'attendee_name' => (string)$meetingAttendeeName ?: $defaultVal,
233
+            'invitee_name' => (string)$meetingInviteeName ?: $defaultVal,
234
+            'meeting_title' => (string)$meetingTitle ?: $defaultVal,
235
+            'meeting_description' => (string)$meetingDescription ?: $defaultVal,
236
+            'meeting_url' => (string)$meetingUrl ?: $defaultVal,
237
+        ];
238
+
239
+        $fromEMail = Util::getDefaultEmailAddress('invitations-noreply');
240
+        $fromName = $l10n->t('%1$s via %2$s', [$senderName, $this->defaults->getName()]);
241
+
242
+        $message = $this->mailer->createMessage()
243
+            ->setFrom([$fromEMail => $fromName])
244
+            ->setTo([$recipient => $recipientName]);
245
+
246
+        if ($sender !== false) {
247
+            $message->setReplyTo([$sender => $senderName]);
248
+        }
249
+
250
+        $template = $this->mailer->createEMailTemplate('dav.calendarInvite.' . $method, $data);
251
+        $template->addHeader();
252
+
253
+        $summary = ((string) $summary !== '') ? (string) $summary : $l10n->t('Untitled event');
254
+
255
+        $this->addSubjectAndHeading($template, $l10n, $method, $summary);
256
+        $this->addBulletList($template, $l10n, $vevent);
257
+
258
+
259
+        // Only add response buttons to invitation requests: Fix Issue #11230
260
+        if (($method == self::METHOD_REQUEST) && $this->getAttendeeRSVP($attendee)) {
261 261
 
262
-			/*
262
+            /*
263 263
 			** Only offer invitation accept/reject buttons, which link back to the
264 264
 			** nextcloud server, to recipients who can access the nextcloud server via
265 265
 			** their internet/intranet.  Issue #12156
@@ -278,444 +278,444 @@  discard block
 block discarded – undo
278 278
 			** To suppress URLs entirely, set invitation_link_recipients to boolean "no".
279 279
 			*/
280 280
 
281
-			$recipientDomain = substr(strrchr($recipient, "@"), 1);
282
-			$invitationLinkRecipients = explode(',', preg_replace('/\s+/', '', strtolower($this->config->getAppValue('dav', 'invitation_link_recipients', 'yes'))));
283
-
284
-			if (strcmp('yes', $invitationLinkRecipients[0]) === 0
285
-				 || in_array(strtolower($recipient), $invitationLinkRecipients)
286
-				 || in_array(strtolower($recipientDomain), $invitationLinkRecipients)) {
287
-				$this->addResponseButtons($template, $l10n, $iTipMessage, $lastOccurrence);
288
-			}
289
-		}
290
-
291
-		$template->addFooter();
292
-
293
-		$message->useTemplate($template);
294
-
295
-		$attachment = $this->mailer->createAttachment(
296
-			$iTipMessage->message->serialize(),
297
-			'event.ics',// TODO(leon): Make file name unique, e.g. add event id
298
-			'text/calendar; method=' . $iTipMessage->method
299
-		);
300
-		$message->attach($attachment);
301
-
302
-		try {
303
-			$failed = $this->mailer->send($message);
304
-			$iTipMessage->scheduleStatus = '1.1; Scheduling message is sent via iMip';
305
-			if ($failed) {
306
-				$this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => implode(', ', $failed)]);
307
-				$iTipMessage->scheduleStatus = '5.0; EMail delivery failed';
308
-			}
309
-		} catch (\Exception $ex) {
310
-			$this->logger->logException($ex, ['app' => 'dav']);
311
-			$iTipMessage->scheduleStatus = '5.0; EMail delivery failed';
312
-		}
313
-	}
314
-
315
-	/**
316
-	 * check if event took place in the past already
317
-	 * @param VCalendar $vObject
318
-	 * @return int
319
-	 */
320
-	private function getLastOccurrence(VCalendar $vObject) {
321
-		/** @var VEvent $component */
322
-		$component = $vObject->VEVENT;
323
-
324
-		$firstOccurrence = $component->DTSTART->getDateTime()->getTimeStamp();
325
-		// Finding the last occurrence is a bit harder
326
-		if (!isset($component->RRULE)) {
327
-			if (isset($component->DTEND)) {
328
-				$lastOccurrence = $component->DTEND->getDateTime()->getTimeStamp();
329
-			} elseif (isset($component->DURATION)) {
330
-				/** @var \DateTime $endDate */
331
-				$endDate = clone $component->DTSTART->getDateTime();
332
-				// $component->DTEND->getDateTime() returns DateTimeImmutable
333
-				$endDate = $endDate->add(DateTimeParser::parse($component->DURATION->getValue()));
334
-				$lastOccurrence = $endDate->getTimestamp();
335
-			} elseif (!$component->DTSTART->hasTime()) {
336
-				/** @var \DateTime $endDate */
337
-				$endDate = clone $component->DTSTART->getDateTime();
338
-				// $component->DTSTART->getDateTime() returns DateTimeImmutable
339
-				$endDate = $endDate->modify('+1 day');
340
-				$lastOccurrence = $endDate->getTimestamp();
341
-			} else {
342
-				$lastOccurrence = $firstOccurrence;
343
-			}
344
-		} else {
345
-			$it = new EventIterator($vObject, (string)$component->UID);
346
-			$maxDate = new \DateTime(self::MAX_DATE);
347
-			if ($it->isInfinite()) {
348
-				$lastOccurrence = $maxDate->getTimestamp();
349
-			} else {
350
-				$end = $it->getDtEnd();
351
-				while ($it->valid() && $end < $maxDate) {
352
-					$end = $it->getDtEnd();
353
-					$it->next();
354
-				}
355
-				$lastOccurrence = $end->getTimestamp();
356
-			}
357
-		}
358
-
359
-		return $lastOccurrence;
360
-	}
361
-
362
-	/**
363
-	 * @param Message $iTipMessage
364
-	 * @return null|Property
365
-	 */
366
-	private function getCurrentAttendee(Message $iTipMessage) {
367
-		/** @var VEvent $vevent */
368
-		$vevent = $iTipMessage->message->VEVENT;
369
-		$attendees = $vevent->select('ATTENDEE');
370
-		foreach ($attendees as $attendee) {
371
-			/** @var Property $attendee */
372
-			if (strcasecmp($attendee->getValue(), $iTipMessage->recipient) === 0) {
373
-				return $attendee;
374
-			}
375
-		}
376
-		return null;
377
-	}
378
-
379
-	/**
380
-	 * @param string $default
381
-	 * @param Property|null $attendee
382
-	 * @return string
383
-	 */
384
-	private function getAttendeeLangOrDefault($default, Property $attendee = null) {
385
-		if ($attendee !== null) {
386
-			$lang = $attendee->offsetGet('LANGUAGE');
387
-			if ($lang instanceof Parameter) {
388
-				return $lang->getValue();
389
-			}
390
-		}
391
-		return $default;
392
-	}
393
-
394
-	/**
395
-	 * @param Property|null $attendee
396
-	 * @return bool
397
-	 */
398
-	private function getAttendeeRSVP(Property $attendee = null) {
399
-		if ($attendee !== null) {
400
-			$rsvp = $attendee->offsetGet('RSVP');
401
-			if (($rsvp instanceof Parameter) && (strcasecmp($rsvp->getValue(), 'TRUE') === 0)) {
402
-				return true;
403
-			}
404
-		}
405
-		// RFC 5545 3.2.17: default RSVP is false
406
-		return false;
407
-	}
408
-
409
-	/**
410
-	 * @param IL10N $l10n
411
-	 * @param VEvent $vevent
412
-	 */
413
-	private function generateWhenString(IL10N $l10n, VEvent $vevent) {
414
-		$dtstart = $vevent->DTSTART;
415
-		if (isset($vevent->DTEND)) {
416
-			$dtend = $vevent->DTEND;
417
-		} elseif (isset($vevent->DURATION)) {
418
-			$isFloating = $vevent->DTSTART->isFloating();
419
-			$dtend = clone $vevent->DTSTART;
420
-			$endDateTime = $dtend->getDateTime();
421
-			$endDateTime = $endDateTime->add(DateTimeParser::parse($vevent->DURATION->getValue()));
422
-			$dtend->setDateTime($endDateTime, $isFloating);
423
-		} elseif (!$vevent->DTSTART->hasTime()) {
424
-			$isFloating = $vevent->DTSTART->isFloating();
425
-			$dtend = clone $vevent->DTSTART;
426
-			$endDateTime = $dtend->getDateTime();
427
-			$endDateTime = $endDateTime->modify('+1 day');
428
-			$dtend->setDateTime($endDateTime, $isFloating);
429
-		} else {
430
-			$dtend = clone $vevent->DTSTART;
431
-		}
432
-
433
-		$isAllDay = $dtstart instanceof Property\ICalendar\Date;
434
-
435
-		/** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtstart */
436
-		/** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtend */
437
-		/** @var \DateTimeImmutable $dtstartDt */
438
-		$dtstartDt = $dtstart->getDateTime();
439
-		/** @var \DateTimeImmutable $dtendDt */
440
-		$dtendDt = $dtend->getDateTime();
441
-
442
-		$diff = $dtstartDt->diff($dtendDt);
443
-
444
-		$dtstartDt = new \DateTime($dtstartDt->format(\DateTimeInterface::ATOM));
445
-		$dtendDt = new \DateTime($dtendDt->format(\DateTimeInterface::ATOM));
446
-
447
-		if ($isAllDay) {
448
-			// One day event
449
-			if ($diff->days === 1) {
450
-				return $l10n->l('date', $dtstartDt, ['width' => 'medium']);
451
-			}
452
-
453
-			// DTEND is exclusive, so if the ics data says 2020-01-01 to 2020-01-05,
454
-			// the email should show 2020-01-01 to 2020-01-04.
455
-			$dtendDt->modify('-1 day');
456
-
457
-			//event that spans over multiple days
458
-			$localeStart = $l10n->l('date', $dtstartDt, ['width' => 'medium']);
459
-			$localeEnd = $l10n->l('date', $dtendDt, ['width' => 'medium']);
460
-
461
-			return $localeStart . ' - ' . $localeEnd;
462
-		}
463
-
464
-		/** @var Property\ICalendar\DateTime $dtstart */
465
-		/** @var Property\ICalendar\DateTime $dtend */
466
-		$isFloating = $dtstart->isFloating();
467
-		$startTimezone = $endTimezone = null;
468
-		if (!$isFloating) {
469
-			$prop = $dtstart->offsetGet('TZID');
470
-			if ($prop instanceof Parameter) {
471
-				$startTimezone = $prop->getValue();
472
-			}
473
-
474
-			$prop = $dtend->offsetGet('TZID');
475
-			if ($prop instanceof Parameter) {
476
-				$endTimezone = $prop->getValue();
477
-			}
478
-		}
479
-
480
-		$localeStart = $l10n->l('weekdayName', $dtstartDt, ['width' => 'abbreviated']) . ', ' .
481
-			$l10n->l('datetime', $dtstartDt, ['width' => 'medium|short']);
482
-
483
-		// always show full date with timezone if timezones are different
484
-		if ($startTimezone !== $endTimezone) {
485
-			$localeEnd = $l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
486
-
487
-			return $localeStart . ' (' . $startTimezone . ') - ' .
488
-				$localeEnd . ' (' . $endTimezone . ')';
489
-		}
490
-
491
-		// show only end time if date is the same
492
-		if ($this->isDayEqual($dtstartDt, $dtendDt)) {
493
-			$localeEnd = $l10n->l('time', $dtendDt, ['width' => 'short']);
494
-		} else {
495
-			$localeEnd = $l10n->l('weekdayName', $dtendDt, ['width' => 'abbreviated']) . ', ' .
496
-				$l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
497
-		}
498
-
499
-		return  $localeStart . ' - ' . $localeEnd . ' (' . $startTimezone . ')';
500
-	}
501
-
502
-	/**
503
-	 * @param \DateTime $dtStart
504
-	 * @param \DateTime $dtEnd
505
-	 * @return bool
506
-	 */
507
-	private function isDayEqual(\DateTime $dtStart, \DateTime $dtEnd) {
508
-		return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d');
509
-	}
510
-
511
-	/**
512
-	 * @param IEMailTemplate $template
513
-	 * @param IL10N $l10n
514
-	 * @param string $method
515
-	 * @param string $summary
516
-	 */
517
-	private function addSubjectAndHeading(IEMailTemplate $template, IL10N $l10n,
518
-										  $method, $summary) {
519
-		if ($method === self::METHOD_CANCEL) {
520
-			// TRANSLATORS Subject for email, when an invitation is cancelled. Ex: "Cancelled: {{Event Name}}"
521
-			$template->setSubject($l10n->t('Cancelled: %1$s', [$summary]));
522
-			$template->addHeading($l10n->t('Invitation canceled'));
523
-		} elseif ($method === self::METHOD_REPLY) {
524
-			// TRANSLATORS Subject for email, when an invitation is updated. Ex: "Re: {{Event Name}}"
525
-			$template->setSubject($l10n->t('Re: %1$s', [$summary]));
526
-			$template->addHeading($l10n->t('Invitation updated'));
527
-		} else {
528
-			// TRANSLATORS Subject for email, when an invitation is sent. Ex: "Invitation: {{Event Name}}"
529
-			$template->setSubject($l10n->t('Invitation: %1$s', [$summary]));
530
-			$template->addHeading($l10n->t('Invitation'));
531
-		}
532
-	}
533
-
534
-	/**
535
-	 * @param IEMailTemplate $template
536
-	 * @param IL10N $l10n
537
-	 * @param VEVENT $vevent
538
-	 */
539
-	private function addBulletList(IEMailTemplate $template, IL10N $l10n, $vevent) {
540
-		if ($vevent->SUMMARY) {
541
-			$template->addBodyListItem($vevent->SUMMARY, $l10n->t('Title:'),
542
-				$this->getAbsoluteImagePath('caldav/title.png'),'','',self::IMIP_INDENT);
543
-		}
544
-		$meetingWhen = $this->generateWhenString($l10n, $vevent);
545
-		if ($meetingWhen) {
546
-			$template->addBodyListItem($meetingWhen, $l10n->t('Time:'),
547
-				$this->getAbsoluteImagePath('caldav/time.png'),'','',self::IMIP_INDENT);
548
-		}
549
-		if ($vevent->LOCATION) {
550
-			$template->addBodyListItem($vevent->LOCATION, $l10n->t('Location:'),
551
-				$this->getAbsoluteImagePath('caldav/location.png'),'','',self::IMIP_INDENT);
552
-		}
553
-		if ($vevent->URL) {
554
-			$url = $vevent->URL->getValue();
555
-			$template->addBodyListItem(sprintf('<a href="%s">%s</a>',
556
-					htmlspecialchars($url),
557
-					htmlspecialchars($url)),
558
-				$l10n->t('Link:'),
559
-				$this->getAbsoluteImagePath('caldav/link.png'),
560
-				$url,'',self::IMIP_INDENT);
561
-		}
562
-
563
-		$this->addAttendees($template, $l10n, $vevent);
564
-
565
-		/* Put description last, like an email body, since it can be arbitrarily long */
566
-		if ($vevent->DESCRIPTION) {
567
-			$template->addBodyListItem($vevent->DESCRIPTION->getValue(), $l10n->t('Description:'),
568
-				$this->getAbsoluteImagePath('caldav/description.png'),'','',self::IMIP_INDENT);
569
-		}
570
-	}
571
-
572
-	/**
573
-	 * addAttendees: add organizer and attendee names/emails to iMip mail.
574
-	 *
575
-	 * Enable with DAV setting: invitation_list_attendees (default: no)
576
-	 *
577
-	 * The default is 'no', which matches old behavior, and is privacy preserving.
578
-	 *
579
-	 * To enable including attendees in invitation emails:
580
-	 *   % php occ config:app:set dav invitation_list_attendees --value yes
581
-	 *
582
-	 * @param IEMailTemplate $template
583
-	 * @param IL10N $l10n
584
-	 * @param Message $iTipMessage
585
-	 * @param int $lastOccurrence
586
-	 * @author brad2014 on github.com
587
-	 */
588
-
589
-	private function addAttendees(IEMailTemplate $template, IL10N $l10n, VEvent $vevent) {
590
-		if ($this->config->getAppValue('dav', 'invitation_list_attendees', 'no') === 'no') {
591
-			return;
592
-		}
593
-
594
-		if (isset($vevent->ORGANIZER)) {
595
-			/** @var Property\ICalendar\CalAddress $organizer */
596
-			$organizer = $vevent->ORGANIZER;
597
-			$organizerURI = $organizer->getNormalizedValue();
598
-			[$scheme,$organizerEmail] = explode(':',$organizerURI,2); # strip off scheme mailto:
599
-			/** @var string|null $organizerName */
600
-			$organizerName = isset($organizer['CN']) ? $organizer['CN'] : null;
601
-			$organizerHTML = sprintf('<a href="%s">%s</a>',
602
-				htmlspecialchars($organizerURI),
603
-				htmlspecialchars($organizerName ?: $organizerEmail));
604
-			$organizerText = sprintf('%s <%s>', $organizerName, $organizerEmail);
605
-			if (isset($organizer['PARTSTAT'])) {
606
-				/** @var Parameter $partstat */
607
-				$partstat = $organizer['PARTSTAT'];
608
-				if (strcasecmp($partstat->getValue(), 'ACCEPTED') === 0) {
609
-					$organizerHTML .= ' ✔︎';
610
-					$organizerText .= ' ✔︎';
611
-				}
612
-			}
613
-			$template->addBodyListItem($organizerHTML, $l10n->t('Organizer:'),
614
-				$this->getAbsoluteImagePath('caldav/organizer.png'),
615
-				$organizerText,'',self::IMIP_INDENT);
616
-		}
617
-
618
-		$attendees = $vevent->select('ATTENDEE');
619
-		if (count($attendees) === 0) {
620
-			return;
621
-		}
622
-
623
-		$attendeesHTML = [];
624
-		$attendeesText = [];
625
-		foreach ($attendees as $attendee) {
626
-			$attendeeURI = $attendee->getNormalizedValue();
627
-			[$scheme,$attendeeEmail] = explode(':',$attendeeURI,2); # strip off scheme mailto:
628
-			$attendeeName = isset($attendee['CN']) ? $attendee['CN'] : null;
629
-			$attendeeHTML = sprintf('<a href="%s">%s</a>',
630
-				htmlspecialchars($attendeeURI),
631
-				htmlspecialchars($attendeeName ?: $attendeeEmail));
632
-			$attendeeText = sprintf('%s <%s>', $attendeeName, $attendeeEmail);
633
-			if (isset($attendee['PARTSTAT'])
634
-				&& strcasecmp($attendee['PARTSTAT'], 'ACCEPTED') === 0) {
635
-				$attendeeHTML .= ' ✔︎';
636
-				$attendeeText .= ' ✔︎';
637
-			}
638
-			array_push($attendeesHTML, $attendeeHTML);
639
-			array_push($attendeesText, $attendeeText);
640
-		}
641
-
642
-		$template->addBodyListItem(implode('<br/>',$attendeesHTML), $l10n->t('Attendees:'),
643
-			$this->getAbsoluteImagePath('caldav/attendees.png'),
644
-			implode("\n",$attendeesText),'',self::IMIP_INDENT);
645
-	}
646
-
647
-	/**
648
-	 * @param IEMailTemplate $template
649
-	 * @param IL10N $l10n
650
-	 * @param Message $iTipMessage
651
-	 * @param int $lastOccurrence
652
-	 */
653
-	private function addResponseButtons(IEMailTemplate $template, IL10N $l10n,
654
-										Message $iTipMessage, $lastOccurrence) {
655
-		$token = $this->createInvitationToken($iTipMessage, $lastOccurrence);
656
-
657
-		$template->addBodyButtonGroup(
658
-			$l10n->t('Accept'),
659
-			$this->urlGenerator->linkToRouteAbsolute('dav.invitation_response.accept', [
660
-				'token' => $token,
661
-			]),
662
-			$l10n->t('Decline'),
663
-			$this->urlGenerator->linkToRouteAbsolute('dav.invitation_response.decline', [
664
-				'token' => $token,
665
-			])
666
-		);
667
-
668
-		$moreOptionsURL = $this->urlGenerator->linkToRouteAbsolute('dav.invitation_response.options', [
669
-			'token' => $token,
670
-		]);
671
-		$html = vsprintf('<small><a href="%s">%s</a></small>', [
672
-			$moreOptionsURL, $l10n->t('More options …')
673
-		]);
674
-		$text = $l10n->t('More options at %s', [$moreOptionsURL]);
675
-
676
-		$template->addBodyText($html, $text);
677
-	}
678
-
679
-	/**
680
-	 * @param string $path
681
-	 * @return string
682
-	 */
683
-	private function getAbsoluteImagePath($path) {
684
-		return $this->urlGenerator->getAbsoluteURL(
685
-			$this->urlGenerator->imagePath('core', $path)
686
-		);
687
-	}
688
-
689
-	/**
690
-	 * @param Message $iTipMessage
691
-	 * @param int $lastOccurrence
692
-	 * @return string
693
-	 */
694
-	private function createInvitationToken(Message $iTipMessage, $lastOccurrence):string {
695
-		$token = $this->random->generate(60, ISecureRandom::CHAR_ALPHANUMERIC);
696
-
697
-		/** @var VEvent $vevent */
698
-		$vevent = $iTipMessage->message->VEVENT;
699
-		$attendee = $iTipMessage->recipient;
700
-		$organizer = $iTipMessage->sender;
701
-		$sequence = $iTipMessage->sequence;
702
-		$recurrenceId = isset($vevent->{'RECURRENCE-ID'}) ?
703
-			$vevent->{'RECURRENCE-ID'}->serialize() : null;
704
-		$uid = $vevent->{'UID'};
705
-
706
-		$query = $this->db->getQueryBuilder();
707
-		$query->insert('calendar_invitations')
708
-			->values([
709
-				'token' => $query->createNamedParameter($token),
710
-				'attendee' => $query->createNamedParameter($attendee),
711
-				'organizer' => $query->createNamedParameter($organizer),
712
-				'sequence' => $query->createNamedParameter($sequence),
713
-				'recurrenceid' => $query->createNamedParameter($recurrenceId),
714
-				'expiration' => $query->createNamedParameter($lastOccurrence),
715
-				'uid' => $query->createNamedParameter($uid)
716
-			])
717
-			->execute();
718
-
719
-		return $token;
720
-	}
281
+            $recipientDomain = substr(strrchr($recipient, "@"), 1);
282
+            $invitationLinkRecipients = explode(',', preg_replace('/\s+/', '', strtolower($this->config->getAppValue('dav', 'invitation_link_recipients', 'yes'))));
283
+
284
+            if (strcmp('yes', $invitationLinkRecipients[0]) === 0
285
+                 || in_array(strtolower($recipient), $invitationLinkRecipients)
286
+                 || in_array(strtolower($recipientDomain), $invitationLinkRecipients)) {
287
+                $this->addResponseButtons($template, $l10n, $iTipMessage, $lastOccurrence);
288
+            }
289
+        }
290
+
291
+        $template->addFooter();
292
+
293
+        $message->useTemplate($template);
294
+
295
+        $attachment = $this->mailer->createAttachment(
296
+            $iTipMessage->message->serialize(),
297
+            'event.ics',// TODO(leon): Make file name unique, e.g. add event id
298
+            'text/calendar; method=' . $iTipMessage->method
299
+        );
300
+        $message->attach($attachment);
301
+
302
+        try {
303
+            $failed = $this->mailer->send($message);
304
+            $iTipMessage->scheduleStatus = '1.1; Scheduling message is sent via iMip';
305
+            if ($failed) {
306
+                $this->logger->error('Unable to deliver message to {failed}', ['app' => 'dav', 'failed' => implode(', ', $failed)]);
307
+                $iTipMessage->scheduleStatus = '5.0; EMail delivery failed';
308
+            }
309
+        } catch (\Exception $ex) {
310
+            $this->logger->logException($ex, ['app' => 'dav']);
311
+            $iTipMessage->scheduleStatus = '5.0; EMail delivery failed';
312
+        }
313
+    }
314
+
315
+    /**
316
+     * check if event took place in the past already
317
+     * @param VCalendar $vObject
318
+     * @return int
319
+     */
320
+    private function getLastOccurrence(VCalendar $vObject) {
321
+        /** @var VEvent $component */
322
+        $component = $vObject->VEVENT;
323
+
324
+        $firstOccurrence = $component->DTSTART->getDateTime()->getTimeStamp();
325
+        // Finding the last occurrence is a bit harder
326
+        if (!isset($component->RRULE)) {
327
+            if (isset($component->DTEND)) {
328
+                $lastOccurrence = $component->DTEND->getDateTime()->getTimeStamp();
329
+            } elseif (isset($component->DURATION)) {
330
+                /** @var \DateTime $endDate */
331
+                $endDate = clone $component->DTSTART->getDateTime();
332
+                // $component->DTEND->getDateTime() returns DateTimeImmutable
333
+                $endDate = $endDate->add(DateTimeParser::parse($component->DURATION->getValue()));
334
+                $lastOccurrence = $endDate->getTimestamp();
335
+            } elseif (!$component->DTSTART->hasTime()) {
336
+                /** @var \DateTime $endDate */
337
+                $endDate = clone $component->DTSTART->getDateTime();
338
+                // $component->DTSTART->getDateTime() returns DateTimeImmutable
339
+                $endDate = $endDate->modify('+1 day');
340
+                $lastOccurrence = $endDate->getTimestamp();
341
+            } else {
342
+                $lastOccurrence = $firstOccurrence;
343
+            }
344
+        } else {
345
+            $it = new EventIterator($vObject, (string)$component->UID);
346
+            $maxDate = new \DateTime(self::MAX_DATE);
347
+            if ($it->isInfinite()) {
348
+                $lastOccurrence = $maxDate->getTimestamp();
349
+            } else {
350
+                $end = $it->getDtEnd();
351
+                while ($it->valid() && $end < $maxDate) {
352
+                    $end = $it->getDtEnd();
353
+                    $it->next();
354
+                }
355
+                $lastOccurrence = $end->getTimestamp();
356
+            }
357
+        }
358
+
359
+        return $lastOccurrence;
360
+    }
361
+
362
+    /**
363
+     * @param Message $iTipMessage
364
+     * @return null|Property
365
+     */
366
+    private function getCurrentAttendee(Message $iTipMessage) {
367
+        /** @var VEvent $vevent */
368
+        $vevent = $iTipMessage->message->VEVENT;
369
+        $attendees = $vevent->select('ATTENDEE');
370
+        foreach ($attendees as $attendee) {
371
+            /** @var Property $attendee */
372
+            if (strcasecmp($attendee->getValue(), $iTipMessage->recipient) === 0) {
373
+                return $attendee;
374
+            }
375
+        }
376
+        return null;
377
+    }
378
+
379
+    /**
380
+     * @param string $default
381
+     * @param Property|null $attendee
382
+     * @return string
383
+     */
384
+    private function getAttendeeLangOrDefault($default, Property $attendee = null) {
385
+        if ($attendee !== null) {
386
+            $lang = $attendee->offsetGet('LANGUAGE');
387
+            if ($lang instanceof Parameter) {
388
+                return $lang->getValue();
389
+            }
390
+        }
391
+        return $default;
392
+    }
393
+
394
+    /**
395
+     * @param Property|null $attendee
396
+     * @return bool
397
+     */
398
+    private function getAttendeeRSVP(Property $attendee = null) {
399
+        if ($attendee !== null) {
400
+            $rsvp = $attendee->offsetGet('RSVP');
401
+            if (($rsvp instanceof Parameter) && (strcasecmp($rsvp->getValue(), 'TRUE') === 0)) {
402
+                return true;
403
+            }
404
+        }
405
+        // RFC 5545 3.2.17: default RSVP is false
406
+        return false;
407
+    }
408
+
409
+    /**
410
+     * @param IL10N $l10n
411
+     * @param VEvent $vevent
412
+     */
413
+    private function generateWhenString(IL10N $l10n, VEvent $vevent) {
414
+        $dtstart = $vevent->DTSTART;
415
+        if (isset($vevent->DTEND)) {
416
+            $dtend = $vevent->DTEND;
417
+        } elseif (isset($vevent->DURATION)) {
418
+            $isFloating = $vevent->DTSTART->isFloating();
419
+            $dtend = clone $vevent->DTSTART;
420
+            $endDateTime = $dtend->getDateTime();
421
+            $endDateTime = $endDateTime->add(DateTimeParser::parse($vevent->DURATION->getValue()));
422
+            $dtend->setDateTime($endDateTime, $isFloating);
423
+        } elseif (!$vevent->DTSTART->hasTime()) {
424
+            $isFloating = $vevent->DTSTART->isFloating();
425
+            $dtend = clone $vevent->DTSTART;
426
+            $endDateTime = $dtend->getDateTime();
427
+            $endDateTime = $endDateTime->modify('+1 day');
428
+            $dtend->setDateTime($endDateTime, $isFloating);
429
+        } else {
430
+            $dtend = clone $vevent->DTSTART;
431
+        }
432
+
433
+        $isAllDay = $dtstart instanceof Property\ICalendar\Date;
434
+
435
+        /** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtstart */
436
+        /** @var Property\ICalendar\Date | Property\ICalendar\DateTime $dtend */
437
+        /** @var \DateTimeImmutable $dtstartDt */
438
+        $dtstartDt = $dtstart->getDateTime();
439
+        /** @var \DateTimeImmutable $dtendDt */
440
+        $dtendDt = $dtend->getDateTime();
441
+
442
+        $diff = $dtstartDt->diff($dtendDt);
443
+
444
+        $dtstartDt = new \DateTime($dtstartDt->format(\DateTimeInterface::ATOM));
445
+        $dtendDt = new \DateTime($dtendDt->format(\DateTimeInterface::ATOM));
446
+
447
+        if ($isAllDay) {
448
+            // One day event
449
+            if ($diff->days === 1) {
450
+                return $l10n->l('date', $dtstartDt, ['width' => 'medium']);
451
+            }
452
+
453
+            // DTEND is exclusive, so if the ics data says 2020-01-01 to 2020-01-05,
454
+            // the email should show 2020-01-01 to 2020-01-04.
455
+            $dtendDt->modify('-1 day');
456
+
457
+            //event that spans over multiple days
458
+            $localeStart = $l10n->l('date', $dtstartDt, ['width' => 'medium']);
459
+            $localeEnd = $l10n->l('date', $dtendDt, ['width' => 'medium']);
460
+
461
+            return $localeStart . ' - ' . $localeEnd;
462
+        }
463
+
464
+        /** @var Property\ICalendar\DateTime $dtstart */
465
+        /** @var Property\ICalendar\DateTime $dtend */
466
+        $isFloating = $dtstart->isFloating();
467
+        $startTimezone = $endTimezone = null;
468
+        if (!$isFloating) {
469
+            $prop = $dtstart->offsetGet('TZID');
470
+            if ($prop instanceof Parameter) {
471
+                $startTimezone = $prop->getValue();
472
+            }
473
+
474
+            $prop = $dtend->offsetGet('TZID');
475
+            if ($prop instanceof Parameter) {
476
+                $endTimezone = $prop->getValue();
477
+            }
478
+        }
479
+
480
+        $localeStart = $l10n->l('weekdayName', $dtstartDt, ['width' => 'abbreviated']) . ', ' .
481
+            $l10n->l('datetime', $dtstartDt, ['width' => 'medium|short']);
482
+
483
+        // always show full date with timezone if timezones are different
484
+        if ($startTimezone !== $endTimezone) {
485
+            $localeEnd = $l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
486
+
487
+            return $localeStart . ' (' . $startTimezone . ') - ' .
488
+                $localeEnd . ' (' . $endTimezone . ')';
489
+        }
490
+
491
+        // show only end time if date is the same
492
+        if ($this->isDayEqual($dtstartDt, $dtendDt)) {
493
+            $localeEnd = $l10n->l('time', $dtendDt, ['width' => 'short']);
494
+        } else {
495
+            $localeEnd = $l10n->l('weekdayName', $dtendDt, ['width' => 'abbreviated']) . ', ' .
496
+                $l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
497
+        }
498
+
499
+        return  $localeStart . ' - ' . $localeEnd . ' (' . $startTimezone . ')';
500
+    }
501
+
502
+    /**
503
+     * @param \DateTime $dtStart
504
+     * @param \DateTime $dtEnd
505
+     * @return bool
506
+     */
507
+    private function isDayEqual(\DateTime $dtStart, \DateTime $dtEnd) {
508
+        return $dtStart->format('Y-m-d') === $dtEnd->format('Y-m-d');
509
+    }
510
+
511
+    /**
512
+     * @param IEMailTemplate $template
513
+     * @param IL10N $l10n
514
+     * @param string $method
515
+     * @param string $summary
516
+     */
517
+    private function addSubjectAndHeading(IEMailTemplate $template, IL10N $l10n,
518
+                                            $method, $summary) {
519
+        if ($method === self::METHOD_CANCEL) {
520
+            // TRANSLATORS Subject for email, when an invitation is cancelled. Ex: "Cancelled: {{Event Name}}"
521
+            $template->setSubject($l10n->t('Cancelled: %1$s', [$summary]));
522
+            $template->addHeading($l10n->t('Invitation canceled'));
523
+        } elseif ($method === self::METHOD_REPLY) {
524
+            // TRANSLATORS Subject for email, when an invitation is updated. Ex: "Re: {{Event Name}}"
525
+            $template->setSubject($l10n->t('Re: %1$s', [$summary]));
526
+            $template->addHeading($l10n->t('Invitation updated'));
527
+        } else {
528
+            // TRANSLATORS Subject for email, when an invitation is sent. Ex: "Invitation: {{Event Name}}"
529
+            $template->setSubject($l10n->t('Invitation: %1$s', [$summary]));
530
+            $template->addHeading($l10n->t('Invitation'));
531
+        }
532
+    }
533
+
534
+    /**
535
+     * @param IEMailTemplate $template
536
+     * @param IL10N $l10n
537
+     * @param VEVENT $vevent
538
+     */
539
+    private function addBulletList(IEMailTemplate $template, IL10N $l10n, $vevent) {
540
+        if ($vevent->SUMMARY) {
541
+            $template->addBodyListItem($vevent->SUMMARY, $l10n->t('Title:'),
542
+                $this->getAbsoluteImagePath('caldav/title.png'),'','',self::IMIP_INDENT);
543
+        }
544
+        $meetingWhen = $this->generateWhenString($l10n, $vevent);
545
+        if ($meetingWhen) {
546
+            $template->addBodyListItem($meetingWhen, $l10n->t('Time:'),
547
+                $this->getAbsoluteImagePath('caldav/time.png'),'','',self::IMIP_INDENT);
548
+        }
549
+        if ($vevent->LOCATION) {
550
+            $template->addBodyListItem($vevent->LOCATION, $l10n->t('Location:'),
551
+                $this->getAbsoluteImagePath('caldav/location.png'),'','',self::IMIP_INDENT);
552
+        }
553
+        if ($vevent->URL) {
554
+            $url = $vevent->URL->getValue();
555
+            $template->addBodyListItem(sprintf('<a href="%s">%s</a>',
556
+                    htmlspecialchars($url),
557
+                    htmlspecialchars($url)),
558
+                $l10n->t('Link:'),
559
+                $this->getAbsoluteImagePath('caldav/link.png'),
560
+                $url,'',self::IMIP_INDENT);
561
+        }
562
+
563
+        $this->addAttendees($template, $l10n, $vevent);
564
+
565
+        /* Put description last, like an email body, since it can be arbitrarily long */
566
+        if ($vevent->DESCRIPTION) {
567
+            $template->addBodyListItem($vevent->DESCRIPTION->getValue(), $l10n->t('Description:'),
568
+                $this->getAbsoluteImagePath('caldav/description.png'),'','',self::IMIP_INDENT);
569
+        }
570
+    }
571
+
572
+    /**
573
+     * addAttendees: add organizer and attendee names/emails to iMip mail.
574
+     *
575
+     * Enable with DAV setting: invitation_list_attendees (default: no)
576
+     *
577
+     * The default is 'no', which matches old behavior, and is privacy preserving.
578
+     *
579
+     * To enable including attendees in invitation emails:
580
+     *   % php occ config:app:set dav invitation_list_attendees --value yes
581
+     *
582
+     * @param IEMailTemplate $template
583
+     * @param IL10N $l10n
584
+     * @param Message $iTipMessage
585
+     * @param int $lastOccurrence
586
+     * @author brad2014 on github.com
587
+     */
588
+
589
+    private function addAttendees(IEMailTemplate $template, IL10N $l10n, VEvent $vevent) {
590
+        if ($this->config->getAppValue('dav', 'invitation_list_attendees', 'no') === 'no') {
591
+            return;
592
+        }
593
+
594
+        if (isset($vevent->ORGANIZER)) {
595
+            /** @var Property\ICalendar\CalAddress $organizer */
596
+            $organizer = $vevent->ORGANIZER;
597
+            $organizerURI = $organizer->getNormalizedValue();
598
+            [$scheme,$organizerEmail] = explode(':',$organizerURI,2); # strip off scheme mailto:
599
+            /** @var string|null $organizerName */
600
+            $organizerName = isset($organizer['CN']) ? $organizer['CN'] : null;
601
+            $organizerHTML = sprintf('<a href="%s">%s</a>',
602
+                htmlspecialchars($organizerURI),
603
+                htmlspecialchars($organizerName ?: $organizerEmail));
604
+            $organizerText = sprintf('%s <%s>', $organizerName, $organizerEmail);
605
+            if (isset($organizer['PARTSTAT'])) {
606
+                /** @var Parameter $partstat */
607
+                $partstat = $organizer['PARTSTAT'];
608
+                if (strcasecmp($partstat->getValue(), 'ACCEPTED') === 0) {
609
+                    $organizerHTML .= ' ✔︎';
610
+                    $organizerText .= ' ✔︎';
611
+                }
612
+            }
613
+            $template->addBodyListItem($organizerHTML, $l10n->t('Organizer:'),
614
+                $this->getAbsoluteImagePath('caldav/organizer.png'),
615
+                $organizerText,'',self::IMIP_INDENT);
616
+        }
617
+
618
+        $attendees = $vevent->select('ATTENDEE');
619
+        if (count($attendees) === 0) {
620
+            return;
621
+        }
622
+
623
+        $attendeesHTML = [];
624
+        $attendeesText = [];
625
+        foreach ($attendees as $attendee) {
626
+            $attendeeURI = $attendee->getNormalizedValue();
627
+            [$scheme,$attendeeEmail] = explode(':',$attendeeURI,2); # strip off scheme mailto:
628
+            $attendeeName = isset($attendee['CN']) ? $attendee['CN'] : null;
629
+            $attendeeHTML = sprintf('<a href="%s">%s</a>',
630
+                htmlspecialchars($attendeeURI),
631
+                htmlspecialchars($attendeeName ?: $attendeeEmail));
632
+            $attendeeText = sprintf('%s <%s>', $attendeeName, $attendeeEmail);
633
+            if (isset($attendee['PARTSTAT'])
634
+                && strcasecmp($attendee['PARTSTAT'], 'ACCEPTED') === 0) {
635
+                $attendeeHTML .= ' ✔︎';
636
+                $attendeeText .= ' ✔︎';
637
+            }
638
+            array_push($attendeesHTML, $attendeeHTML);
639
+            array_push($attendeesText, $attendeeText);
640
+        }
641
+
642
+        $template->addBodyListItem(implode('<br/>',$attendeesHTML), $l10n->t('Attendees:'),
643
+            $this->getAbsoluteImagePath('caldav/attendees.png'),
644
+            implode("\n",$attendeesText),'',self::IMIP_INDENT);
645
+    }
646
+
647
+    /**
648
+     * @param IEMailTemplate $template
649
+     * @param IL10N $l10n
650
+     * @param Message $iTipMessage
651
+     * @param int $lastOccurrence
652
+     */
653
+    private function addResponseButtons(IEMailTemplate $template, IL10N $l10n,
654
+                                        Message $iTipMessage, $lastOccurrence) {
655
+        $token = $this->createInvitationToken($iTipMessage, $lastOccurrence);
656
+
657
+        $template->addBodyButtonGroup(
658
+            $l10n->t('Accept'),
659
+            $this->urlGenerator->linkToRouteAbsolute('dav.invitation_response.accept', [
660
+                'token' => $token,
661
+            ]),
662
+            $l10n->t('Decline'),
663
+            $this->urlGenerator->linkToRouteAbsolute('dav.invitation_response.decline', [
664
+                'token' => $token,
665
+            ])
666
+        );
667
+
668
+        $moreOptionsURL = $this->urlGenerator->linkToRouteAbsolute('dav.invitation_response.options', [
669
+            'token' => $token,
670
+        ]);
671
+        $html = vsprintf('<small><a href="%s">%s</a></small>', [
672
+            $moreOptionsURL, $l10n->t('More options …')
673
+        ]);
674
+        $text = $l10n->t('More options at %s', [$moreOptionsURL]);
675
+
676
+        $template->addBodyText($html, $text);
677
+    }
678
+
679
+    /**
680
+     * @param string $path
681
+     * @return string
682
+     */
683
+    private function getAbsoluteImagePath($path) {
684
+        return $this->urlGenerator->getAbsoluteURL(
685
+            $this->urlGenerator->imagePath('core', $path)
686
+        );
687
+    }
688
+
689
+    /**
690
+     * @param Message $iTipMessage
691
+     * @param int $lastOccurrence
692
+     * @return string
693
+     */
694
+    private function createInvitationToken(Message $iTipMessage, $lastOccurrence):string {
695
+        $token = $this->random->generate(60, ISecureRandom::CHAR_ALPHANUMERIC);
696
+
697
+        /** @var VEvent $vevent */
698
+        $vevent = $iTipMessage->message->VEVENT;
699
+        $attendee = $iTipMessage->recipient;
700
+        $organizer = $iTipMessage->sender;
701
+        $sequence = $iTipMessage->sequence;
702
+        $recurrenceId = isset($vevent->{'RECURRENCE-ID'}) ?
703
+            $vevent->{'RECURRENCE-ID'}->serialize() : null;
704
+        $uid = $vevent->{'UID'};
705
+
706
+        $query = $this->db->getQueryBuilder();
707
+        $query->insert('calendar_invitations')
708
+            ->values([
709
+                'token' => $query->createNamedParameter($token),
710
+                'attendee' => $query->createNamedParameter($attendee),
711
+                'organizer' => $query->createNamedParameter($organizer),
712
+                'sequence' => $query->createNamedParameter($sequence),
713
+                'recurrenceid' => $query->createNamedParameter($recurrenceId),
714
+                'expiration' => $query->createNamedParameter($lastOccurrence),
715
+                'uid' => $query->createNamedParameter($uid)
716
+            ])
717
+            ->execute();
718
+
719
+        return $token;
720
+    }
721 721
 }
Please login to merge, or discard this patch.
Spacing   +25 added lines, -25 removed lines patch added patch discarded remove patch
@@ -229,11 +229,11 @@  discard block
 block discarded – undo
229 229
 		}
230 230
 
231 231
 		$data = [
232
-			'attendee_name' => (string)$meetingAttendeeName ?: $defaultVal,
233
-			'invitee_name' => (string)$meetingInviteeName ?: $defaultVal,
234
-			'meeting_title' => (string)$meetingTitle ?: $defaultVal,
235
-			'meeting_description' => (string)$meetingDescription ?: $defaultVal,
236
-			'meeting_url' => (string)$meetingUrl ?: $defaultVal,
232
+			'attendee_name' => (string) $meetingAttendeeName ?: $defaultVal,
233
+			'invitee_name' => (string) $meetingInviteeName ?: $defaultVal,
234
+			'meeting_title' => (string) $meetingTitle ?: $defaultVal,
235
+			'meeting_description' => (string) $meetingDescription ?: $defaultVal,
236
+			'meeting_url' => (string) $meetingUrl ?: $defaultVal,
237 237
 		];
238 238
 
239 239
 		$fromEMail = Util::getDefaultEmailAddress('invitations-noreply');
@@ -247,7 +247,7 @@  discard block
 block discarded – undo
247 247
 			$message->setReplyTo([$sender => $senderName]);
248 248
 		}
249 249
 
250
-		$template = $this->mailer->createEMailTemplate('dav.calendarInvite.' . $method, $data);
250
+		$template = $this->mailer->createEMailTemplate('dav.calendarInvite.'.$method, $data);
251 251
 		$template->addHeader();
252 252
 
253 253
 		$summary = ((string) $summary !== '') ? (string) $summary : $l10n->t('Untitled event');
@@ -294,8 +294,8 @@  discard block
 block discarded – undo
294 294
 
295 295
 		$attachment = $this->mailer->createAttachment(
296 296
 			$iTipMessage->message->serialize(),
297
-			'event.ics',// TODO(leon): Make file name unique, e.g. add event id
298
-			'text/calendar; method=' . $iTipMessage->method
297
+			'event.ics', // TODO(leon): Make file name unique, e.g. add event id
298
+			'text/calendar; method='.$iTipMessage->method
299 299
 		);
300 300
 		$message->attach($attachment);
301 301
 
@@ -342,7 +342,7 @@  discard block
 block discarded – undo
342 342
 				$lastOccurrence = $firstOccurrence;
343 343
 			}
344 344
 		} else {
345
-			$it = new EventIterator($vObject, (string)$component->UID);
345
+			$it = new EventIterator($vObject, (string) $component->UID);
346 346
 			$maxDate = new \DateTime(self::MAX_DATE);
347 347
 			if ($it->isInfinite()) {
348 348
 				$lastOccurrence = $maxDate->getTimestamp();
@@ -458,7 +458,7 @@  discard block
 block discarded – undo
458 458
 			$localeStart = $l10n->l('date', $dtstartDt, ['width' => 'medium']);
459 459
 			$localeEnd = $l10n->l('date', $dtendDt, ['width' => 'medium']);
460 460
 
461
-			return $localeStart . ' - ' . $localeEnd;
461
+			return $localeStart.' - '.$localeEnd;
462 462
 		}
463 463
 
464 464
 		/** @var Property\ICalendar\DateTime $dtstart */
@@ -477,26 +477,26 @@  discard block
 block discarded – undo
477 477
 			}
478 478
 		}
479 479
 
480
-		$localeStart = $l10n->l('weekdayName', $dtstartDt, ['width' => 'abbreviated']) . ', ' .
480
+		$localeStart = $l10n->l('weekdayName', $dtstartDt, ['width' => 'abbreviated']).', '.
481 481
 			$l10n->l('datetime', $dtstartDt, ['width' => 'medium|short']);
482 482
 
483 483
 		// always show full date with timezone if timezones are different
484 484
 		if ($startTimezone !== $endTimezone) {
485 485
 			$localeEnd = $l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
486 486
 
487
-			return $localeStart . ' (' . $startTimezone . ') - ' .
488
-				$localeEnd . ' (' . $endTimezone . ')';
487
+			return $localeStart.' ('.$startTimezone.') - '.
488
+				$localeEnd.' ('.$endTimezone.')';
489 489
 		}
490 490
 
491 491
 		// show only end time if date is the same
492 492
 		if ($this->isDayEqual($dtstartDt, $dtendDt)) {
493 493
 			$localeEnd = $l10n->l('time', $dtendDt, ['width' => 'short']);
494 494
 		} else {
495
-			$localeEnd = $l10n->l('weekdayName', $dtendDt, ['width' => 'abbreviated']) . ', ' .
495
+			$localeEnd = $l10n->l('weekdayName', $dtendDt, ['width' => 'abbreviated']).', '.
496 496
 				$l10n->l('datetime', $dtendDt, ['width' => 'medium|short']);
497 497
 		}
498 498
 
499
-		return  $localeStart . ' - ' . $localeEnd . ' (' . $startTimezone . ')';
499
+		return  $localeStart.' - '.$localeEnd.' ('.$startTimezone.')';
500 500
 	}
501 501
 
502 502
 	/**
@@ -539,16 +539,16 @@  discard block
 block discarded – undo
539 539
 	private function addBulletList(IEMailTemplate $template, IL10N $l10n, $vevent) {
540 540
 		if ($vevent->SUMMARY) {
541 541
 			$template->addBodyListItem($vevent->SUMMARY, $l10n->t('Title:'),
542
-				$this->getAbsoluteImagePath('caldav/title.png'),'','',self::IMIP_INDENT);
542
+				$this->getAbsoluteImagePath('caldav/title.png'), '', '', self::IMIP_INDENT);
543 543
 		}
544 544
 		$meetingWhen = $this->generateWhenString($l10n, $vevent);
545 545
 		if ($meetingWhen) {
546 546
 			$template->addBodyListItem($meetingWhen, $l10n->t('Time:'),
547
-				$this->getAbsoluteImagePath('caldav/time.png'),'','',self::IMIP_INDENT);
547
+				$this->getAbsoluteImagePath('caldav/time.png'), '', '', self::IMIP_INDENT);
548 548
 		}
549 549
 		if ($vevent->LOCATION) {
550 550
 			$template->addBodyListItem($vevent->LOCATION, $l10n->t('Location:'),
551
-				$this->getAbsoluteImagePath('caldav/location.png'),'','',self::IMIP_INDENT);
551
+				$this->getAbsoluteImagePath('caldav/location.png'), '', '', self::IMIP_INDENT);
552 552
 		}
553 553
 		if ($vevent->URL) {
554 554
 			$url = $vevent->URL->getValue();
@@ -557,7 +557,7 @@  discard block
 block discarded – undo
557 557
 					htmlspecialchars($url)),
558 558
 				$l10n->t('Link:'),
559 559
 				$this->getAbsoluteImagePath('caldav/link.png'),
560
-				$url,'',self::IMIP_INDENT);
560
+				$url, '', self::IMIP_INDENT);
561 561
 		}
562 562
 
563 563
 		$this->addAttendees($template, $l10n, $vevent);
@@ -565,7 +565,7 @@  discard block
 block discarded – undo
565 565
 		/* Put description last, like an email body, since it can be arbitrarily long */
566 566
 		if ($vevent->DESCRIPTION) {
567 567
 			$template->addBodyListItem($vevent->DESCRIPTION->getValue(), $l10n->t('Description:'),
568
-				$this->getAbsoluteImagePath('caldav/description.png'),'','',self::IMIP_INDENT);
568
+				$this->getAbsoluteImagePath('caldav/description.png'), '', '', self::IMIP_INDENT);
569 569
 		}
570 570
 	}
571 571
 
@@ -595,7 +595,7 @@  discard block
 block discarded – undo
595 595
 			/** @var Property\ICalendar\CalAddress $organizer */
596 596
 			$organizer = $vevent->ORGANIZER;
597 597
 			$organizerURI = $organizer->getNormalizedValue();
598
-			[$scheme,$organizerEmail] = explode(':',$organizerURI,2); # strip off scheme mailto:
598
+			[$scheme, $organizerEmail] = explode(':', $organizerURI, 2); # strip off scheme mailto:
599 599
 			/** @var string|null $organizerName */
600 600
 			$organizerName = isset($organizer['CN']) ? $organizer['CN'] : null;
601 601
 			$organizerHTML = sprintf('<a href="%s">%s</a>',
@@ -612,7 +612,7 @@  discard block
 block discarded – undo
612 612
 			}
613 613
 			$template->addBodyListItem($organizerHTML, $l10n->t('Organizer:'),
614 614
 				$this->getAbsoluteImagePath('caldav/organizer.png'),
615
-				$organizerText,'',self::IMIP_INDENT);
615
+				$organizerText, '', self::IMIP_INDENT);
616 616
 		}
617 617
 
618 618
 		$attendees = $vevent->select('ATTENDEE');
@@ -624,7 +624,7 @@  discard block
 block discarded – undo
624 624
 		$attendeesText = [];
625 625
 		foreach ($attendees as $attendee) {
626 626
 			$attendeeURI = $attendee->getNormalizedValue();
627
-			[$scheme,$attendeeEmail] = explode(':',$attendeeURI,2); # strip off scheme mailto:
627
+			[$scheme, $attendeeEmail] = explode(':', $attendeeURI, 2); # strip off scheme mailto:
628 628
 			$attendeeName = isset($attendee['CN']) ? $attendee['CN'] : null;
629 629
 			$attendeeHTML = sprintf('<a href="%s">%s</a>',
630 630
 				htmlspecialchars($attendeeURI),
@@ -639,9 +639,9 @@  discard block
 block discarded – undo
639 639
 			array_push($attendeesText, $attendeeText);
640 640
 		}
641 641
 
642
-		$template->addBodyListItem(implode('<br/>',$attendeesHTML), $l10n->t('Attendees:'),
642
+		$template->addBodyListItem(implode('<br/>', $attendeesHTML), $l10n->t('Attendees:'),
643 643
 			$this->getAbsoluteImagePath('caldav/attendees.png'),
644
-			implode("\n",$attendeesText),'',self::IMIP_INDENT);
644
+			implode("\n", $attendeesText), '', self::IMIP_INDENT);
645 645
 	}
646 646
 
647 647
 	/**
Please login to merge, or discard this patch.
apps/encryption/lib/Crypto/EncryptAll.php 1 patch
Indentation   +430 added lines, -430 removed lines patch added patch discarded remove patch
@@ -46,434 +46,434 @@
 block discarded – undo
46 46
 
47 47
 class EncryptAll {
48 48
 
49
-	/** @var Setup */
50
-	protected $userSetup;
51
-
52
-	/** @var IUserManager */
53
-	protected $userManager;
54
-
55
-	/** @var View */
56
-	protected $rootView;
57
-
58
-	/** @var KeyManager */
59
-	protected $keyManager;
60
-
61
-	/** @var Util */
62
-	protected $util;
63
-
64
-	/** @var array  */
65
-	protected $userPasswords;
66
-
67
-	/** @var  IConfig */
68
-	protected $config;
69
-
70
-	/** @var IMailer */
71
-	protected $mailer;
72
-
73
-	/** @var  IL10N */
74
-	protected $l;
75
-
76
-	/** @var  QuestionHelper */
77
-	protected $questionHelper;
78
-
79
-	/** @var  OutputInterface */
80
-	protected $output;
81
-
82
-	/** @var  InputInterface */
83
-	protected $input;
84
-
85
-	/** @var ISecureRandom */
86
-	protected $secureRandom;
87
-
88
-	/**
89
-	 * @param Setup $userSetup
90
-	 * @param IUserManager $userManager
91
-	 * @param View $rootView
92
-	 * @param KeyManager $keyManager
93
-	 * @param Util $util
94
-	 * @param IConfig $config
95
-	 * @param IMailer $mailer
96
-	 * @param IL10N $l
97
-	 * @param QuestionHelper $questionHelper
98
-	 * @param ISecureRandom $secureRandom
99
-	 */
100
-	public function __construct(
101
-		Setup $userSetup,
102
-		IUserManager $userManager,
103
-		View $rootView,
104
-		KeyManager $keyManager,
105
-		Util $util,
106
-		IConfig $config,
107
-		IMailer $mailer,
108
-		IL10N $l,
109
-		QuestionHelper $questionHelper,
110
-		ISecureRandom $secureRandom
111
-	) {
112
-		$this->userSetup = $userSetup;
113
-		$this->userManager = $userManager;
114
-		$this->rootView = $rootView;
115
-		$this->keyManager = $keyManager;
116
-		$this->util = $util;
117
-		$this->config = $config;
118
-		$this->mailer = $mailer;
119
-		$this->l = $l;
120
-		$this->questionHelper = $questionHelper;
121
-		$this->secureRandom = $secureRandom;
122
-		// store one time passwords for the users
123
-		$this->userPasswords = [];
124
-	}
125
-
126
-	/**
127
-	 * start to encrypt all files
128
-	 *
129
-	 * @param InputInterface $input
130
-	 * @param OutputInterface $output
131
-	 */
132
-	public function encryptAll(InputInterface $input, OutputInterface $output) {
133
-		$this->input = $input;
134
-		$this->output = $output;
135
-
136
-		$headline = 'Encrypt all files with the ' . Encryption::DISPLAY_NAME;
137
-		$this->output->writeln("\n");
138
-		$this->output->writeln($headline);
139
-		$this->output->writeln(str_pad('', strlen($headline), '='));
140
-		$this->output->writeln("\n");
141
-
142
-		if ($this->util->isMasterKeyEnabled()) {
143
-			$this->output->writeln('Use master key to encrypt all files.');
144
-			$this->keyManager->validateMasterKey();
145
-		} else {
146
-			//create private/public keys for each user and store the private key password
147
-			$this->output->writeln('Create key-pair for every user');
148
-			$this->output->writeln('------------------------------');
149
-			$this->output->writeln('');
150
-			$this->output->writeln('This module will encrypt all files in the users files folder initially.');
151
-			$this->output->writeln('Already existing versions and files in the trash bin will not be encrypted.');
152
-			$this->output->writeln('');
153
-			$this->createKeyPairs();
154
-		}
155
-
156
-
157
-		// output generated encryption key passwords
158
-		if ($this->util->isMasterKeyEnabled() === false) {
159
-			//send-out or display password list and write it to a file
160
-			$this->output->writeln("\n");
161
-			$this->output->writeln('Generated encryption key passwords');
162
-			$this->output->writeln('----------------------------------');
163
-			$this->output->writeln('');
164
-			$this->outputPasswords();
165
-		}
166
-
167
-		//setup users file system and encrypt all files one by one (take should encrypt setting of storage into account)
168
-		$this->output->writeln("\n");
169
-		$this->output->writeln('Start to encrypt users files');
170
-		$this->output->writeln('----------------------------');
171
-		$this->output->writeln('');
172
-		$this->encryptAllUsersFiles();
173
-		$this->output->writeln("\n");
174
-	}
175
-
176
-	/**
177
-	 * create key-pair for every user
178
-	 */
179
-	protected function createKeyPairs() {
180
-		$this->output->writeln("\n");
181
-		$progress = new ProgressBar($this->output);
182
-		$progress->setFormat(" %message% \n [%bar%]");
183
-		$progress->start();
184
-
185
-		foreach ($this->userManager->getBackends() as $backend) {
186
-			$limit = 500;
187
-			$offset = 0;
188
-			do {
189
-				$users = $backend->getUsers('', $limit, $offset);
190
-				foreach ($users as $user) {
191
-					if ($this->keyManager->userHasKeys($user) === false) {
192
-						$progress->setMessage('Create key-pair for ' . $user);
193
-						$progress->advance();
194
-						$this->setupUserFS($user);
195
-						$password = $this->generateOneTimePassword($user);
196
-						$this->userSetup->setupUser($user, $password);
197
-					} else {
198
-						// users which already have a key-pair will be stored with a
199
-						// empty password and filtered out later
200
-						$this->userPasswords[$user] = '';
201
-					}
202
-				}
203
-				$offset += $limit;
204
-			} while (count($users) >= $limit);
205
-		}
206
-
207
-		$progress->setMessage('Key-pair created for all users');
208
-		$progress->finish();
209
-	}
210
-
211
-	/**
212
-	 * iterate over all user and encrypt their files
213
-	 */
214
-	protected function encryptAllUsersFiles() {
215
-		$this->output->writeln("\n");
216
-		$progress = new ProgressBar($this->output);
217
-		$progress->setFormat(" %message% \n [%bar%]");
218
-		$progress->start();
219
-		$numberOfUsers = count($this->userPasswords);
220
-		$userNo = 1;
221
-		if ($this->util->isMasterKeyEnabled()) {
222
-			$this->encryptAllUserFilesWithMasterKey($progress);
223
-		} else {
224
-			foreach ($this->userPasswords as $uid => $password) {
225
-				$userCount = "$uid ($userNo of $numberOfUsers)";
226
-				$this->encryptUsersFiles($uid, $progress, $userCount);
227
-				$userNo++;
228
-			}
229
-		}
230
-		$progress->setMessage("all files encrypted");
231
-		$progress->finish();
232
-	}
233
-
234
-	/**
235
-	 * encrypt all user files with the master key
236
-	 *
237
-	 * @param ProgressBar $progress
238
-	 */
239
-	protected function encryptAllUserFilesWithMasterKey(ProgressBar $progress) {
240
-		$userNo = 1;
241
-		foreach ($this->userManager->getBackends() as $backend) {
242
-			$limit = 500;
243
-			$offset = 0;
244
-			do {
245
-				$users = $backend->getUsers('', $limit, $offset);
246
-				foreach ($users as $user) {
247
-					$userCount = "$user ($userNo)";
248
-					$this->encryptUsersFiles($user, $progress, $userCount);
249
-					$userNo++;
250
-				}
251
-				$offset += $limit;
252
-			} while (count($users) >= $limit);
253
-		}
254
-	}
255
-
256
-	/**
257
-	 * encrypt files from the given user
258
-	 *
259
-	 * @param string $uid
260
-	 * @param ProgressBar $progress
261
-	 * @param string $userCount
262
-	 */
263
-	protected function encryptUsersFiles($uid, ProgressBar $progress, $userCount) {
264
-		$this->setupUserFS($uid);
265
-		$directories = [];
266
-		$directories[] = '/' . $uid . '/files';
267
-
268
-		while ($root = array_pop($directories)) {
269
-			$content = $this->rootView->getDirectoryContent($root);
270
-			foreach ($content as $file) {
271
-				$path = $root . '/' . $file['name'];
272
-				if ($this->rootView->is_dir($path)) {
273
-					$directories[] = $path;
274
-					continue;
275
-				} else {
276
-					$progress->setMessage("encrypt files for user $userCount: $path");
277
-					$progress->advance();
278
-					if ($this->encryptFile($path) === false) {
279
-						$progress->setMessage("encrypt files for user $userCount: $path (already encrypted)");
280
-						$progress->advance();
281
-					}
282
-				}
283
-			}
284
-		}
285
-	}
286
-
287
-	/**
288
-	 * encrypt file
289
-	 *
290
-	 * @param string $path
291
-	 * @return bool
292
-	 */
293
-	protected function encryptFile($path) {
294
-
295
-		// skip already encrypted files
296
-		$fileInfo = $this->rootView->getFileInfo($path);
297
-		if ($fileInfo !== false && $fileInfo->isEncrypted()) {
298
-			return true;
299
-		}
300
-
301
-		$source = $path;
302
-		$target = $path . '.encrypted.' . time();
303
-
304
-		try {
305
-			$this->rootView->copy($source, $target);
306
-			$this->rootView->rename($target, $source);
307
-		} catch (DecryptionFailedException $e) {
308
-			if ($this->rootView->file_exists($target)) {
309
-				$this->rootView->unlink($target);
310
-			}
311
-			return false;
312
-		}
313
-
314
-		return true;
315
-	}
316
-
317
-	/**
318
-	 * output one-time encryption passwords
319
-	 */
320
-	protected function outputPasswords() {
321
-		$table = new Table($this->output);
322
-		$table->setHeaders(['Username', 'Private key password']);
323
-
324
-		//create rows
325
-		$newPasswords = [];
326
-		$unchangedPasswords = [];
327
-		foreach ($this->userPasswords as $uid => $password) {
328
-			if (empty($password)) {
329
-				$unchangedPasswords[] = $uid;
330
-			} else {
331
-				$newPasswords[] = [$uid, $password];
332
-			}
333
-		}
334
-
335
-		if (empty($newPasswords)) {
336
-			$this->output->writeln("\nAll users already had a key-pair, no further action needed.\n");
337
-			return;
338
-		}
339
-
340
-		$table->setRows($newPasswords);
341
-		$table->render();
342
-
343
-		if (!empty($unchangedPasswords)) {
344
-			$this->output->writeln("\nThe following users already had a key-pair which was reused without setting a new password:\n");
345
-			foreach ($unchangedPasswords as $uid) {
346
-				$this->output->writeln("    $uid");
347
-			}
348
-		}
349
-
350
-		$this->writePasswordsToFile($newPasswords);
351
-
352
-		$this->output->writeln('');
353
-		$question = new ConfirmationQuestion('Do you want to send the passwords directly to the users by mail? (y/n) ', false);
354
-		if ($this->questionHelper->ask($this->input, $this->output, $question)) {
355
-			$this->sendPasswordsByMail();
356
-		}
357
-	}
358
-
359
-	/**
360
-	 * write one-time encryption passwords to a csv file
361
-	 *
362
-	 * @param array $passwords
363
-	 */
364
-	protected function writePasswordsToFile(array $passwords) {
365
-		$fp = $this->rootView->fopen('oneTimeEncryptionPasswords.csv', 'w');
366
-		foreach ($passwords as $pwd) {
367
-			fputcsv($fp, $pwd);
368
-		}
369
-		fclose($fp);
370
-		$this->output->writeln("\n");
371
-		$this->output->writeln('A list of all newly created passwords was written to data/oneTimeEncryptionPasswords.csv');
372
-		$this->output->writeln('');
373
-		$this->output->writeln('Each of these users need to login to the web interface, go to the');
374
-		$this->output->writeln('personal settings section "basic encryption module" and');
375
-		$this->output->writeln('update the private key password to match the login password again by');
376
-		$this->output->writeln('entering the one-time password into the "old log-in password" field');
377
-		$this->output->writeln('and their current login password');
378
-	}
379
-
380
-	/**
381
-	 * setup user file system
382
-	 *
383
-	 * @param string $uid
384
-	 */
385
-	protected function setupUserFS($uid) {
386
-		\OC_Util::tearDownFS();
387
-		\OC_Util::setupFS($uid);
388
-	}
389
-
390
-	/**
391
-	 * generate one time password for the user and store it in a array
392
-	 *
393
-	 * @param string $uid
394
-	 * @return string password
395
-	 */
396
-	protected function generateOneTimePassword($uid) {
397
-		$password = $this->secureRandom->generate(16, ISecureRandom::CHAR_HUMAN_READABLE);
398
-		$this->userPasswords[$uid] = $password;
399
-		return $password;
400
-	}
401
-
402
-	/**
403
-	 * send encryption key passwords to the users by mail
404
-	 */
405
-	protected function sendPasswordsByMail() {
406
-		$noMail = [];
407
-
408
-		$this->output->writeln('');
409
-		$progress = new ProgressBar($this->output, count($this->userPasswords));
410
-		$progress->start();
411
-
412
-		foreach ($this->userPasswords as $uid => $password) {
413
-			$progress->advance();
414
-			if (!empty($password)) {
415
-				$recipient = $this->userManager->get($uid);
416
-				$recipientDisplayName = $recipient->getDisplayName();
417
-				$to = $recipient->getEMailAddress();
418
-
419
-				if ($to === '' || $to === null) {
420
-					$noMail[] = $uid;
421
-					continue;
422
-				}
423
-
424
-				$subject = $this->l->t('one-time password for server-side-encryption');
425
-				[$htmlBody, $textBody] = $this->createMailBody($password);
426
-
427
-				// send it out now
428
-				try {
429
-					$message = $this->mailer->createMessage();
430
-					$message->setSubject($subject);
431
-					$message->setTo([$to => $recipientDisplayName]);
432
-					$message->setHtmlBody($htmlBody);
433
-					$message->setPlainBody($textBody);
434
-					$message->setFrom([
435
-						\OCP\Util::getDefaultEmailAddress('admin-noreply')
436
-					]);
437
-
438
-					$this->mailer->send($message);
439
-				} catch (\Exception $e) {
440
-					$noMail[] = $uid;
441
-				}
442
-			}
443
-		}
444
-
445
-		$progress->finish();
446
-
447
-		if (empty($noMail)) {
448
-			$this->output->writeln("\n\nPassword successfully send to all users");
449
-		} else {
450
-			$table = new Table($this->output);
451
-			$table->setHeaders(['Username', 'Private key password']);
452
-			$this->output->writeln("\n\nCould not send password to following users:\n");
453
-			$rows = [];
454
-			foreach ($noMail as $uid) {
455
-				$rows[] = [$uid, $this->userPasswords[$uid]];
456
-			}
457
-			$table->setRows($rows);
458
-			$table->render();
459
-		}
460
-	}
461
-
462
-	/**
463
-	 * create mail body for plain text and html mail
464
-	 *
465
-	 * @param string $password one-time encryption password
466
-	 * @return array an array of the html mail body and the plain text mail body
467
-	 */
468
-	protected function createMailBody($password) {
469
-		$html = new \OC_Template("encryption", "mail", "");
470
-		$html->assign('password', $password);
471
-		$htmlMail = $html->fetchPage();
472
-
473
-		$plainText = new \OC_Template("encryption", "altmail", "");
474
-		$plainText->assign('password', $password);
475
-		$plainTextMail = $plainText->fetchPage();
476
-
477
-		return [$htmlMail, $plainTextMail];
478
-	}
49
+    /** @var Setup */
50
+    protected $userSetup;
51
+
52
+    /** @var IUserManager */
53
+    protected $userManager;
54
+
55
+    /** @var View */
56
+    protected $rootView;
57
+
58
+    /** @var KeyManager */
59
+    protected $keyManager;
60
+
61
+    /** @var Util */
62
+    protected $util;
63
+
64
+    /** @var array  */
65
+    protected $userPasswords;
66
+
67
+    /** @var  IConfig */
68
+    protected $config;
69
+
70
+    /** @var IMailer */
71
+    protected $mailer;
72
+
73
+    /** @var  IL10N */
74
+    protected $l;
75
+
76
+    /** @var  QuestionHelper */
77
+    protected $questionHelper;
78
+
79
+    /** @var  OutputInterface */
80
+    protected $output;
81
+
82
+    /** @var  InputInterface */
83
+    protected $input;
84
+
85
+    /** @var ISecureRandom */
86
+    protected $secureRandom;
87
+
88
+    /**
89
+     * @param Setup $userSetup
90
+     * @param IUserManager $userManager
91
+     * @param View $rootView
92
+     * @param KeyManager $keyManager
93
+     * @param Util $util
94
+     * @param IConfig $config
95
+     * @param IMailer $mailer
96
+     * @param IL10N $l
97
+     * @param QuestionHelper $questionHelper
98
+     * @param ISecureRandom $secureRandom
99
+     */
100
+    public function __construct(
101
+        Setup $userSetup,
102
+        IUserManager $userManager,
103
+        View $rootView,
104
+        KeyManager $keyManager,
105
+        Util $util,
106
+        IConfig $config,
107
+        IMailer $mailer,
108
+        IL10N $l,
109
+        QuestionHelper $questionHelper,
110
+        ISecureRandom $secureRandom
111
+    ) {
112
+        $this->userSetup = $userSetup;
113
+        $this->userManager = $userManager;
114
+        $this->rootView = $rootView;
115
+        $this->keyManager = $keyManager;
116
+        $this->util = $util;
117
+        $this->config = $config;
118
+        $this->mailer = $mailer;
119
+        $this->l = $l;
120
+        $this->questionHelper = $questionHelper;
121
+        $this->secureRandom = $secureRandom;
122
+        // store one time passwords for the users
123
+        $this->userPasswords = [];
124
+    }
125
+
126
+    /**
127
+     * start to encrypt all files
128
+     *
129
+     * @param InputInterface $input
130
+     * @param OutputInterface $output
131
+     */
132
+    public function encryptAll(InputInterface $input, OutputInterface $output) {
133
+        $this->input = $input;
134
+        $this->output = $output;
135
+
136
+        $headline = 'Encrypt all files with the ' . Encryption::DISPLAY_NAME;
137
+        $this->output->writeln("\n");
138
+        $this->output->writeln($headline);
139
+        $this->output->writeln(str_pad('', strlen($headline), '='));
140
+        $this->output->writeln("\n");
141
+
142
+        if ($this->util->isMasterKeyEnabled()) {
143
+            $this->output->writeln('Use master key to encrypt all files.');
144
+            $this->keyManager->validateMasterKey();
145
+        } else {
146
+            //create private/public keys for each user and store the private key password
147
+            $this->output->writeln('Create key-pair for every user');
148
+            $this->output->writeln('------------------------------');
149
+            $this->output->writeln('');
150
+            $this->output->writeln('This module will encrypt all files in the users files folder initially.');
151
+            $this->output->writeln('Already existing versions and files in the trash bin will not be encrypted.');
152
+            $this->output->writeln('');
153
+            $this->createKeyPairs();
154
+        }
155
+
156
+
157
+        // output generated encryption key passwords
158
+        if ($this->util->isMasterKeyEnabled() === false) {
159
+            //send-out or display password list and write it to a file
160
+            $this->output->writeln("\n");
161
+            $this->output->writeln('Generated encryption key passwords');
162
+            $this->output->writeln('----------------------------------');
163
+            $this->output->writeln('');
164
+            $this->outputPasswords();
165
+        }
166
+
167
+        //setup users file system and encrypt all files one by one (take should encrypt setting of storage into account)
168
+        $this->output->writeln("\n");
169
+        $this->output->writeln('Start to encrypt users files');
170
+        $this->output->writeln('----------------------------');
171
+        $this->output->writeln('');
172
+        $this->encryptAllUsersFiles();
173
+        $this->output->writeln("\n");
174
+    }
175
+
176
+    /**
177
+     * create key-pair for every user
178
+     */
179
+    protected function createKeyPairs() {
180
+        $this->output->writeln("\n");
181
+        $progress = new ProgressBar($this->output);
182
+        $progress->setFormat(" %message% \n [%bar%]");
183
+        $progress->start();
184
+
185
+        foreach ($this->userManager->getBackends() as $backend) {
186
+            $limit = 500;
187
+            $offset = 0;
188
+            do {
189
+                $users = $backend->getUsers('', $limit, $offset);
190
+                foreach ($users as $user) {
191
+                    if ($this->keyManager->userHasKeys($user) === false) {
192
+                        $progress->setMessage('Create key-pair for ' . $user);
193
+                        $progress->advance();
194
+                        $this->setupUserFS($user);
195
+                        $password = $this->generateOneTimePassword($user);
196
+                        $this->userSetup->setupUser($user, $password);
197
+                    } else {
198
+                        // users which already have a key-pair will be stored with a
199
+                        // empty password and filtered out later
200
+                        $this->userPasswords[$user] = '';
201
+                    }
202
+                }
203
+                $offset += $limit;
204
+            } while (count($users) >= $limit);
205
+        }
206
+
207
+        $progress->setMessage('Key-pair created for all users');
208
+        $progress->finish();
209
+    }
210
+
211
+    /**
212
+     * iterate over all user and encrypt their files
213
+     */
214
+    protected function encryptAllUsersFiles() {
215
+        $this->output->writeln("\n");
216
+        $progress = new ProgressBar($this->output);
217
+        $progress->setFormat(" %message% \n [%bar%]");
218
+        $progress->start();
219
+        $numberOfUsers = count($this->userPasswords);
220
+        $userNo = 1;
221
+        if ($this->util->isMasterKeyEnabled()) {
222
+            $this->encryptAllUserFilesWithMasterKey($progress);
223
+        } else {
224
+            foreach ($this->userPasswords as $uid => $password) {
225
+                $userCount = "$uid ($userNo of $numberOfUsers)";
226
+                $this->encryptUsersFiles($uid, $progress, $userCount);
227
+                $userNo++;
228
+            }
229
+        }
230
+        $progress->setMessage("all files encrypted");
231
+        $progress->finish();
232
+    }
233
+
234
+    /**
235
+     * encrypt all user files with the master key
236
+     *
237
+     * @param ProgressBar $progress
238
+     */
239
+    protected function encryptAllUserFilesWithMasterKey(ProgressBar $progress) {
240
+        $userNo = 1;
241
+        foreach ($this->userManager->getBackends() as $backend) {
242
+            $limit = 500;
243
+            $offset = 0;
244
+            do {
245
+                $users = $backend->getUsers('', $limit, $offset);
246
+                foreach ($users as $user) {
247
+                    $userCount = "$user ($userNo)";
248
+                    $this->encryptUsersFiles($user, $progress, $userCount);
249
+                    $userNo++;
250
+                }
251
+                $offset += $limit;
252
+            } while (count($users) >= $limit);
253
+        }
254
+    }
255
+
256
+    /**
257
+     * encrypt files from the given user
258
+     *
259
+     * @param string $uid
260
+     * @param ProgressBar $progress
261
+     * @param string $userCount
262
+     */
263
+    protected function encryptUsersFiles($uid, ProgressBar $progress, $userCount) {
264
+        $this->setupUserFS($uid);
265
+        $directories = [];
266
+        $directories[] = '/' . $uid . '/files';
267
+
268
+        while ($root = array_pop($directories)) {
269
+            $content = $this->rootView->getDirectoryContent($root);
270
+            foreach ($content as $file) {
271
+                $path = $root . '/' . $file['name'];
272
+                if ($this->rootView->is_dir($path)) {
273
+                    $directories[] = $path;
274
+                    continue;
275
+                } else {
276
+                    $progress->setMessage("encrypt files for user $userCount: $path");
277
+                    $progress->advance();
278
+                    if ($this->encryptFile($path) === false) {
279
+                        $progress->setMessage("encrypt files for user $userCount: $path (already encrypted)");
280
+                        $progress->advance();
281
+                    }
282
+                }
283
+            }
284
+        }
285
+    }
286
+
287
+    /**
288
+     * encrypt file
289
+     *
290
+     * @param string $path
291
+     * @return bool
292
+     */
293
+    protected function encryptFile($path) {
294
+
295
+        // skip already encrypted files
296
+        $fileInfo = $this->rootView->getFileInfo($path);
297
+        if ($fileInfo !== false && $fileInfo->isEncrypted()) {
298
+            return true;
299
+        }
300
+
301
+        $source = $path;
302
+        $target = $path . '.encrypted.' . time();
303
+
304
+        try {
305
+            $this->rootView->copy($source, $target);
306
+            $this->rootView->rename($target, $source);
307
+        } catch (DecryptionFailedException $e) {
308
+            if ($this->rootView->file_exists($target)) {
309
+                $this->rootView->unlink($target);
310
+            }
311
+            return false;
312
+        }
313
+
314
+        return true;
315
+    }
316
+
317
+    /**
318
+     * output one-time encryption passwords
319
+     */
320
+    protected function outputPasswords() {
321
+        $table = new Table($this->output);
322
+        $table->setHeaders(['Username', 'Private key password']);
323
+
324
+        //create rows
325
+        $newPasswords = [];
326
+        $unchangedPasswords = [];
327
+        foreach ($this->userPasswords as $uid => $password) {
328
+            if (empty($password)) {
329
+                $unchangedPasswords[] = $uid;
330
+            } else {
331
+                $newPasswords[] = [$uid, $password];
332
+            }
333
+        }
334
+
335
+        if (empty($newPasswords)) {
336
+            $this->output->writeln("\nAll users already had a key-pair, no further action needed.\n");
337
+            return;
338
+        }
339
+
340
+        $table->setRows($newPasswords);
341
+        $table->render();
342
+
343
+        if (!empty($unchangedPasswords)) {
344
+            $this->output->writeln("\nThe following users already had a key-pair which was reused without setting a new password:\n");
345
+            foreach ($unchangedPasswords as $uid) {
346
+                $this->output->writeln("    $uid");
347
+            }
348
+        }
349
+
350
+        $this->writePasswordsToFile($newPasswords);
351
+
352
+        $this->output->writeln('');
353
+        $question = new ConfirmationQuestion('Do you want to send the passwords directly to the users by mail? (y/n) ', false);
354
+        if ($this->questionHelper->ask($this->input, $this->output, $question)) {
355
+            $this->sendPasswordsByMail();
356
+        }
357
+    }
358
+
359
+    /**
360
+     * write one-time encryption passwords to a csv file
361
+     *
362
+     * @param array $passwords
363
+     */
364
+    protected function writePasswordsToFile(array $passwords) {
365
+        $fp = $this->rootView->fopen('oneTimeEncryptionPasswords.csv', 'w');
366
+        foreach ($passwords as $pwd) {
367
+            fputcsv($fp, $pwd);
368
+        }
369
+        fclose($fp);
370
+        $this->output->writeln("\n");
371
+        $this->output->writeln('A list of all newly created passwords was written to data/oneTimeEncryptionPasswords.csv');
372
+        $this->output->writeln('');
373
+        $this->output->writeln('Each of these users need to login to the web interface, go to the');
374
+        $this->output->writeln('personal settings section "basic encryption module" and');
375
+        $this->output->writeln('update the private key password to match the login password again by');
376
+        $this->output->writeln('entering the one-time password into the "old log-in password" field');
377
+        $this->output->writeln('and their current login password');
378
+    }
379
+
380
+    /**
381
+     * setup user file system
382
+     *
383
+     * @param string $uid
384
+     */
385
+    protected function setupUserFS($uid) {
386
+        \OC_Util::tearDownFS();
387
+        \OC_Util::setupFS($uid);
388
+    }
389
+
390
+    /**
391
+     * generate one time password for the user and store it in a array
392
+     *
393
+     * @param string $uid
394
+     * @return string password
395
+     */
396
+    protected function generateOneTimePassword($uid) {
397
+        $password = $this->secureRandom->generate(16, ISecureRandom::CHAR_HUMAN_READABLE);
398
+        $this->userPasswords[$uid] = $password;
399
+        return $password;
400
+    }
401
+
402
+    /**
403
+     * send encryption key passwords to the users by mail
404
+     */
405
+    protected function sendPasswordsByMail() {
406
+        $noMail = [];
407
+
408
+        $this->output->writeln('');
409
+        $progress = new ProgressBar($this->output, count($this->userPasswords));
410
+        $progress->start();
411
+
412
+        foreach ($this->userPasswords as $uid => $password) {
413
+            $progress->advance();
414
+            if (!empty($password)) {
415
+                $recipient = $this->userManager->get($uid);
416
+                $recipientDisplayName = $recipient->getDisplayName();
417
+                $to = $recipient->getEMailAddress();
418
+
419
+                if ($to === '' || $to === null) {
420
+                    $noMail[] = $uid;
421
+                    continue;
422
+                }
423
+
424
+                $subject = $this->l->t('one-time password for server-side-encryption');
425
+                [$htmlBody, $textBody] = $this->createMailBody($password);
426
+
427
+                // send it out now
428
+                try {
429
+                    $message = $this->mailer->createMessage();
430
+                    $message->setSubject($subject);
431
+                    $message->setTo([$to => $recipientDisplayName]);
432
+                    $message->setHtmlBody($htmlBody);
433
+                    $message->setPlainBody($textBody);
434
+                    $message->setFrom([
435
+                        \OCP\Util::getDefaultEmailAddress('admin-noreply')
436
+                    ]);
437
+
438
+                    $this->mailer->send($message);
439
+                } catch (\Exception $e) {
440
+                    $noMail[] = $uid;
441
+                }
442
+            }
443
+        }
444
+
445
+        $progress->finish();
446
+
447
+        if (empty($noMail)) {
448
+            $this->output->writeln("\n\nPassword successfully send to all users");
449
+        } else {
450
+            $table = new Table($this->output);
451
+            $table->setHeaders(['Username', 'Private key password']);
452
+            $this->output->writeln("\n\nCould not send password to following users:\n");
453
+            $rows = [];
454
+            foreach ($noMail as $uid) {
455
+                $rows[] = [$uid, $this->userPasswords[$uid]];
456
+            }
457
+            $table->setRows($rows);
458
+            $table->render();
459
+        }
460
+    }
461
+
462
+    /**
463
+     * create mail body for plain text and html mail
464
+     *
465
+     * @param string $password one-time encryption password
466
+     * @return array an array of the html mail body and the plain text mail body
467
+     */
468
+    protected function createMailBody($password) {
469
+        $html = new \OC_Template("encryption", "mail", "");
470
+        $html->assign('password', $password);
471
+        $htmlMail = $html->fetchPage();
472
+
473
+        $plainText = new \OC_Template("encryption", "altmail", "");
474
+        $plainText->assign('password', $password);
475
+        $plainTextMail = $plainText->fetchPage();
476
+
477
+        return [$htmlMail, $plainTextMail];
478
+    }
479 479
 }
Please login to merge, or discard this patch.