Passed
Push — master ( 62403d...0c3e2f )
by Joas
14:50 queued 14s
created
lib/private/Search.php 1 patch
Indentation   +76 added lines, -76 removed lines patch added patch discarded remove patch
@@ -35,86 +35,86 @@
 block discarded – undo
35 35
  */
36 36
 class Search implements ISearch {
37 37
 
38
-	private $providers = [];
39
-	private $registeredProviders = [];
38
+    private $providers = [];
39
+    private $registeredProviders = [];
40 40
 
41
-	/**
42
-	 * Search all providers for $query
43
-	 * @param string $query
44
-	 * @param string[] $inApps optionally limit results to the given apps
45
-	 * @param int $page pages start at page 1
46
-	 * @param int $size, 0 = all
47
-	 * @return array An array of OC\Search\Result's
48
-	 */
49
-	public function searchPaged($query, array $inApps = [], $page = 1, $size = 30) {
50
-		$this->initProviders();
51
-		$results = [];
52
-		foreach($this->providers as $provider) {
53
-			/** @var $provider Provider */
54
-			if ( ! $provider->providesResultsFor($inApps) ) {
55
-				continue;
56
-			}
57
-			if ($provider instanceof PagedProvider) {
58
-				$results = array_merge($results, $provider->searchPaged($query, $page, $size));
59
-			} else if ($provider instanceof Provider) {
60
-				$providerResults = $provider->search($query);
61
-				if ($size > 0) {
62
-					$slicedResults = array_slice($providerResults, ($page - 1) * $size, $size);
63
-					$results = array_merge($results, $slicedResults);
64
-				} else {
65
-					$results = array_merge($results, $providerResults);
66
-				}
67
-			} else {
68
-				\OC::$server->getLogger()->warning('Ignoring Unknown search provider', ['provider' => $provider]);
69
-			}
70
-		}
71
-		return $results;
72
-	}
41
+    /**
42
+     * Search all providers for $query
43
+     * @param string $query
44
+     * @param string[] $inApps optionally limit results to the given apps
45
+     * @param int $page pages start at page 1
46
+     * @param int $size, 0 = all
47
+     * @return array An array of OC\Search\Result's
48
+     */
49
+    public function searchPaged($query, array $inApps = [], $page = 1, $size = 30) {
50
+        $this->initProviders();
51
+        $results = [];
52
+        foreach($this->providers as $provider) {
53
+            /** @var $provider Provider */
54
+            if ( ! $provider->providesResultsFor($inApps) ) {
55
+                continue;
56
+            }
57
+            if ($provider instanceof PagedProvider) {
58
+                $results = array_merge($results, $provider->searchPaged($query, $page, $size));
59
+            } else if ($provider instanceof Provider) {
60
+                $providerResults = $provider->search($query);
61
+                if ($size > 0) {
62
+                    $slicedResults = array_slice($providerResults, ($page - 1) * $size, $size);
63
+                    $results = array_merge($results, $slicedResults);
64
+                } else {
65
+                    $results = array_merge($results, $providerResults);
66
+                }
67
+            } else {
68
+                \OC::$server->getLogger()->warning('Ignoring Unknown search provider', ['provider' => $provider]);
69
+            }
70
+        }
71
+        return $results;
72
+    }
73 73
 
74
-	/**
75
-	 * Remove all registered search providers
76
-	 */
77
-	public function clearProviders() {
78
-		$this->providers = [];
79
-		$this->registeredProviders = [];
80
-	}
74
+    /**
75
+     * Remove all registered search providers
76
+     */
77
+    public function clearProviders() {
78
+        $this->providers = [];
79
+        $this->registeredProviders = [];
80
+    }
81 81
 
82
-	/**
83
-	 * Remove one existing search provider
84
-	 * @param string $provider class name of a OC\Search\Provider
85
-	 */
86
-	public function removeProvider($provider) {
87
-		$this->registeredProviders = array_filter(
88
-			$this->registeredProviders,
89
-			function ($element) use ($provider) {
90
-				return ($element['class'] != $provider);
91
-			}
92
-		);
93
-		// force regeneration of providers on next search
94
-		$this->providers = [];
95
-	}
82
+    /**
83
+     * Remove one existing search provider
84
+     * @param string $provider class name of a OC\Search\Provider
85
+     */
86
+    public function removeProvider($provider) {
87
+        $this->registeredProviders = array_filter(
88
+            $this->registeredProviders,
89
+            function ($element) use ($provider) {
90
+                return ($element['class'] != $provider);
91
+            }
92
+        );
93
+        // force regeneration of providers on next search
94
+        $this->providers = [];
95
+    }
96 96
 
97
-	/**
98
-	 * Register a new search provider to search with
99
-	 * @param string $class class name of a OC\Search\Provider
100
-	 * @param array $options optional
101
-	 */
102
-	public function registerProvider($class, array $options = []) {
103
-		$this->registeredProviders[] = ['class' => $class, 'options' => $options];
104
-	}
97
+    /**
98
+     * Register a new search provider to search with
99
+     * @param string $class class name of a OC\Search\Provider
100
+     * @param array $options optional
101
+     */
102
+    public function registerProvider($class, array $options = []) {
103
+        $this->registeredProviders[] = ['class' => $class, 'options' => $options];
104
+    }
105 105
 
106
-	/**
107
-	 * Create instances of all the registered search providers
108
-	 */
109
-	private function initProviders() {
110
-		if( ! empty($this->providers) ) {
111
-			return;
112
-		}
113
-		foreach($this->registeredProviders as $provider) {
114
-			$class = $provider['class'];
115
-			$options = $provider['options'];
116
-			$this->providers[] = new $class($options);
117
-		}
118
-	}
106
+    /**
107
+     * Create instances of all the registered search providers
108
+     */
109
+    private function initProviders() {
110
+        if( ! empty($this->providers) ) {
111
+            return;
112
+        }
113
+        foreach($this->registeredProviders as $provider) {
114
+            $class = $provider['class'];
115
+            $options = $provider['options'];
116
+            $this->providers[] = new $class($options);
117
+        }
118
+    }
119 119
 
120 120
 }
Please login to merge, or discard this patch.
lib/private/OCS/Result.php 1 patch
Indentation   +124 added lines, -124 removed lines patch added patch discarded remove patch
@@ -32,129 +32,129 @@
 block discarded – undo
32 32
 
33 33
 class Result {
34 34
 
35
-	/** @var array  */
36
-	protected $data;
37
-
38
-	/** @var null|string */
39
-	protected $message;
40
-
41
-	/** @var int */
42
-	protected $statusCode;
43
-
44
-	/** @var integer */
45
-	protected $items;
46
-
47
-	/** @var integer */
48
-	protected $perPage;
49
-
50
-	/** @var array */
51
-	private $headers = [];
52
-
53
-	/**
54
-	 * create the OCS_Result object
55
-	 * @param mixed $data the data to return
56
-	 * @param int $code
57
-	 * @param null|string $message
58
-	 * @param array $headers
59
-	 */
60
-	public function __construct($data = null, $code = 100, $message = null, $headers = []) {
61
-		if ($data === null) {
62
-			$this->data = [];
63
-		} elseif (!is_array($data)) {
64
-			$this->data = [$this->data];
65
-		} else {
66
-			$this->data = $data;
67
-		}
68
-		$this->statusCode = $code;
69
-		$this->message = $message;
70
-		$this->headers = $headers;
71
-	}
72
-
73
-	/**
74
-	 * optionally set the total number of items available
75
-	 * @param int $items
76
-	 */
77
-	public function setTotalItems($items) {
78
-		$this->items = $items;
79
-	}
80
-
81
-	/**
82
-	 * optionally set the the number of items per page
83
-	 * @param int $items
84
-	 */
85
-	public function setItemsPerPage($items) {
86
-		$this->perPage = $items;
87
-	}
88
-
89
-	/**
90
-	 * get the status code
91
-	 * @return int
92
-	 */
93
-	public function getStatusCode() {
94
-		return $this->statusCode;
95
-	}
96
-
97
-	/**
98
-	 * get the meta data for the result
99
-	 * @return array
100
-	 */
101
-	public function getMeta() {
102
-		$meta = [];
103
-		$meta['status'] = $this->succeeded() ? 'ok' : 'failure';
104
-		$meta['statuscode'] = $this->statusCode;
105
-		$meta['message'] = $this->message;
106
-		if(isset($this->items)) {
107
-			$meta['totalitems'] = $this->items;
108
-		}
109
-		if(isset($this->perPage)) {
110
-			$meta['itemsperpage'] = $this->perPage;
111
-		}
112
-		return $meta;
113
-
114
-	}
115
-
116
-	/**
117
-	 * get the result data
118
-	 * @return array
119
-	 */
120
-	public function getData() {
121
-		return $this->data;
122
-	}
123
-
124
-	/**
125
-	 * return bool Whether the method succeeded
126
-	 * @return bool
127
-	 */
128
-	public function succeeded() {
129
-		return ($this->statusCode == 100);
130
-	}
131
-
132
-	/**
133
-	 * Adds a new header to the response
134
-	 * @param string $name The name of the HTTP header
135
-	 * @param string $value The value, null will delete it
136
-	 * @return $this
137
-	 */
138
-	public function addHeader($name, $value) {
139
-		$name = trim($name);  // always remove leading and trailing whitespace
140
-		// to be able to reliably check for security
141
-		// headers
142
-
143
-		if(is_null($value)) {
144
-			unset($this->headers[$name]);
145
-		} else {
146
-			$this->headers[$name] = $value;
147
-		}
148
-
149
-		return $this;
150
-	}
151
-
152
-	/**
153
-	 * Returns the set headers
154
-	 * @return array the headers
155
-	 */
156
-	public function getHeaders() {
157
-		return $this->headers;
158
-	}
35
+    /** @var array  */
36
+    protected $data;
37
+
38
+    /** @var null|string */
39
+    protected $message;
40
+
41
+    /** @var int */
42
+    protected $statusCode;
43
+
44
+    /** @var integer */
45
+    protected $items;
46
+
47
+    /** @var integer */
48
+    protected $perPage;
49
+
50
+    /** @var array */
51
+    private $headers = [];
52
+
53
+    /**
54
+     * create the OCS_Result object
55
+     * @param mixed $data the data to return
56
+     * @param int $code
57
+     * @param null|string $message
58
+     * @param array $headers
59
+     */
60
+    public function __construct($data = null, $code = 100, $message = null, $headers = []) {
61
+        if ($data === null) {
62
+            $this->data = [];
63
+        } elseif (!is_array($data)) {
64
+            $this->data = [$this->data];
65
+        } else {
66
+            $this->data = $data;
67
+        }
68
+        $this->statusCode = $code;
69
+        $this->message = $message;
70
+        $this->headers = $headers;
71
+    }
72
+
73
+    /**
74
+     * optionally set the total number of items available
75
+     * @param int $items
76
+     */
77
+    public function setTotalItems($items) {
78
+        $this->items = $items;
79
+    }
80
+
81
+    /**
82
+     * optionally set the the number of items per page
83
+     * @param int $items
84
+     */
85
+    public function setItemsPerPage($items) {
86
+        $this->perPage = $items;
87
+    }
88
+
89
+    /**
90
+     * get the status code
91
+     * @return int
92
+     */
93
+    public function getStatusCode() {
94
+        return $this->statusCode;
95
+    }
96
+
97
+    /**
98
+     * get the meta data for the result
99
+     * @return array
100
+     */
101
+    public function getMeta() {
102
+        $meta = [];
103
+        $meta['status'] = $this->succeeded() ? 'ok' : 'failure';
104
+        $meta['statuscode'] = $this->statusCode;
105
+        $meta['message'] = $this->message;
106
+        if(isset($this->items)) {
107
+            $meta['totalitems'] = $this->items;
108
+        }
109
+        if(isset($this->perPage)) {
110
+            $meta['itemsperpage'] = $this->perPage;
111
+        }
112
+        return $meta;
113
+
114
+    }
115
+
116
+    /**
117
+     * get the result data
118
+     * @return array
119
+     */
120
+    public function getData() {
121
+        return $this->data;
122
+    }
123
+
124
+    /**
125
+     * return bool Whether the method succeeded
126
+     * @return bool
127
+     */
128
+    public function succeeded() {
129
+        return ($this->statusCode == 100);
130
+    }
131
+
132
+    /**
133
+     * Adds a new header to the response
134
+     * @param string $name The name of the HTTP header
135
+     * @param string $value The value, null will delete it
136
+     * @return $this
137
+     */
138
+    public function addHeader($name, $value) {
139
+        $name = trim($name);  // always remove leading and trailing whitespace
140
+        // to be able to reliably check for security
141
+        // headers
142
+
143
+        if(is_null($value)) {
144
+            unset($this->headers[$name]);
145
+        } else {
146
+            $this->headers[$name] = $value;
147
+        }
148
+
149
+        return $this;
150
+    }
151
+
152
+    /**
153
+     * Returns the set headers
154
+     * @return array the headers
155
+     */
156
+    public function getHeaders() {
157
+        return $this->headers;
158
+    }
159 159
 
160 160
 }
Please login to merge, or discard this patch.
lib/private/NaturalSort.php 1 patch
Indentation   +102 added lines, -102 removed lines patch added patch discarded remove patch
@@ -30,113 +30,113 @@
 block discarded – undo
30 30
 use OCP\ILogger;
31 31
 
32 32
 class NaturalSort {
33
-	private static $instance;
34
-	private $collator;
35
-	private $cache = [];
33
+    private static $instance;
34
+    private $collator;
35
+    private $cache = [];
36 36
 
37
-	/**
38
-	 * Instantiate a new \OC\NaturalSort instance.
39
-	 * @param object $injectedCollator
40
-	 */
41
-	public function __construct($injectedCollator = null) {
42
-		// inject an instance of \Collator('en_US') to force using the php5-intl Collator
43
-		// or inject an instance of \OC\NaturalSort_DefaultCollator to force using Owncloud's default collator
44
-		if (isset($injectedCollator)) {
45
-			$this->collator = $injectedCollator;
46
-			\OCP\Util::writeLog('core', 'forced use of '.get_class($injectedCollator), ILogger::DEBUG);
47
-		}
48
-	}
37
+    /**
38
+     * Instantiate a new \OC\NaturalSort instance.
39
+     * @param object $injectedCollator
40
+     */
41
+    public function __construct($injectedCollator = null) {
42
+        // inject an instance of \Collator('en_US') to force using the php5-intl Collator
43
+        // or inject an instance of \OC\NaturalSort_DefaultCollator to force using Owncloud's default collator
44
+        if (isset($injectedCollator)) {
45
+            $this->collator = $injectedCollator;
46
+            \OCP\Util::writeLog('core', 'forced use of '.get_class($injectedCollator), ILogger::DEBUG);
47
+        }
48
+    }
49 49
 
50
-	/**
51
-	 * Split the given string in chunks of numbers and strings
52
-	 * @param string $t string
53
-	 * @return array of strings and number chunks
54
-	 */
55
-	private function naturalSortChunkify($t) {
56
-		// Adapted and ported to PHP from
57
-		// http://my.opera.com/GreyWyvern/blog/show.dml/1671288
58
-		if (isset($this->cache[$t])) {
59
-			return $this->cache[$t];
60
-		}
61
-		$tz = [];
62
-		$x = 0;
63
-		$y = -1;
64
-		$n = null;
50
+    /**
51
+     * Split the given string in chunks of numbers and strings
52
+     * @param string $t string
53
+     * @return array of strings and number chunks
54
+     */
55
+    private function naturalSortChunkify($t) {
56
+        // Adapted and ported to PHP from
57
+        // http://my.opera.com/GreyWyvern/blog/show.dml/1671288
58
+        if (isset($this->cache[$t])) {
59
+            return $this->cache[$t];
60
+        }
61
+        $tz = [];
62
+        $x = 0;
63
+        $y = -1;
64
+        $n = null;
65 65
 
66
-		while (isset($t[$x])) {
67
-			$c = $t[$x];
68
-			// only include the dot in strings
69
-			$m = ((!$n && $c === '.') || ($c >= '0' && $c <= '9'));
70
-			if ($m !== $n) {
71
-				// next chunk
72
-				$y++;
73
-				$tz[$y] = '';
74
-				$n = $m;
75
-			}
76
-			$tz[$y] .= $c;
77
-			$x++;
78
-		}
79
-		$this->cache[$t] = $tz;
80
-		return $tz;
81
-	}
66
+        while (isset($t[$x])) {
67
+            $c = $t[$x];
68
+            // only include the dot in strings
69
+            $m = ((!$n && $c === '.') || ($c >= '0' && $c <= '9'));
70
+            if ($m !== $n) {
71
+                // next chunk
72
+                $y++;
73
+                $tz[$y] = '';
74
+                $n = $m;
75
+            }
76
+            $tz[$y] .= $c;
77
+            $x++;
78
+        }
79
+        $this->cache[$t] = $tz;
80
+        return $tz;
81
+    }
82 82
 
83
-	/**
84
-	 * Returns the string collator
85
-	 * @return \Collator string collator
86
-	 */
87
-	private function getCollator() {
88
-		if (!isset($this->collator)) {
89
-			// looks like the default is en_US_POSIX which yields wrong sorting with
90
-			// German umlauts, so using en_US instead
91
-			if (class_exists('Collator')) {
92
-				$this->collator = new \Collator('en_US');
93
-			}
94
-			else {
95
-				$this->collator = new \OC\NaturalSort_DefaultCollator();
96
-			}
97
-		}
98
-		return $this->collator;
99
-	}
83
+    /**
84
+     * Returns the string collator
85
+     * @return \Collator string collator
86
+     */
87
+    private function getCollator() {
88
+        if (!isset($this->collator)) {
89
+            // looks like the default is en_US_POSIX which yields wrong sorting with
90
+            // German umlauts, so using en_US instead
91
+            if (class_exists('Collator')) {
92
+                $this->collator = new \Collator('en_US');
93
+            }
94
+            else {
95
+                $this->collator = new \OC\NaturalSort_DefaultCollator();
96
+            }
97
+        }
98
+        return $this->collator;
99
+    }
100 100
 
101
-	/**
102
-	 * Compare two strings to provide a natural sort
103
-	 * @param string $a first string to compare
104
-	 * @param string $b second string to compare
105
-	 * @return int -1 if $b comes before $a, 1 if $a comes before $b
106
-	 * or 0 if the strings are identical
107
-	 */
108
-	public function compare($a, $b) {
109
-		// Needed because PHP doesn't sort correctly when numbers are enclosed in
110
-		// parenthesis, even with NUMERIC_COLLATION enabled.
111
-		// For example it gave ["test (2).txt", "test.txt"]
112
-		// instead of ["test.txt", "test (2).txt"]
113
-		$aa = self::naturalSortChunkify($a);
114
-		$bb = self::naturalSortChunkify($b);
101
+    /**
102
+     * Compare two strings to provide a natural sort
103
+     * @param string $a first string to compare
104
+     * @param string $b second string to compare
105
+     * @return int -1 if $b comes before $a, 1 if $a comes before $b
106
+     * or 0 if the strings are identical
107
+     */
108
+    public function compare($a, $b) {
109
+        // Needed because PHP doesn't sort correctly when numbers are enclosed in
110
+        // parenthesis, even with NUMERIC_COLLATION enabled.
111
+        // For example it gave ["test (2).txt", "test.txt"]
112
+        // instead of ["test.txt", "test (2).txt"]
113
+        $aa = self::naturalSortChunkify($a);
114
+        $bb = self::naturalSortChunkify($b);
115 115
 
116
-		for ($x = 0; isset($aa[$x]) && isset($bb[$x]); $x++) {
117
-			$aChunk = $aa[$x];
118
-			$bChunk = $bb[$x];
119
-			if ($aChunk !== $bChunk) {
120
-				// test first character (character comparison, not number comparison)
121
-				if ($aChunk[0] >= '0' && $aChunk[0] <= '9' && $bChunk[0] >= '0' && $bChunk[0] <= '9') {
122
-					$aNum = (int)$aChunk;
123
-					$bNum = (int)$bChunk;
124
-					return $aNum - $bNum;
125
-				}
126
-				return self::getCollator()->compare($aChunk, $bChunk);
127
-			}
128
-		}
129
-		return count($aa) - count($bb);
130
-	}
116
+        for ($x = 0; isset($aa[$x]) && isset($bb[$x]); $x++) {
117
+            $aChunk = $aa[$x];
118
+            $bChunk = $bb[$x];
119
+            if ($aChunk !== $bChunk) {
120
+                // test first character (character comparison, not number comparison)
121
+                if ($aChunk[0] >= '0' && $aChunk[0] <= '9' && $bChunk[0] >= '0' && $bChunk[0] <= '9') {
122
+                    $aNum = (int)$aChunk;
123
+                    $bNum = (int)$bChunk;
124
+                    return $aNum - $bNum;
125
+                }
126
+                return self::getCollator()->compare($aChunk, $bChunk);
127
+            }
128
+        }
129
+        return count($aa) - count($bb);
130
+    }
131 131
 
132
-	/**
133
-	 * Returns a singleton
134
-	 * @return \OC\NaturalSort instance
135
-	 */
136
-	public static function getInstance() {
137
-		if (!isset(self::$instance)) {
138
-			self::$instance = new \OC\NaturalSort();
139
-		}
140
-		return self::$instance;
141
-	}
132
+    /**
133
+     * Returns a singleton
134
+     * @return \OC\NaturalSort instance
135
+     */
136
+    public static function getInstance() {
137
+        if (!isset(self::$instance)) {
138
+            self::$instance = new \OC\NaturalSort();
139
+        }
140
+        return self::$instance;
141
+    }
142 142
 }
Please login to merge, or discard this patch.
lib/private/Security/CertificateManager.php 1 patch
Indentation   +248 added lines, -248 removed lines patch added patch discarded remove patch
@@ -38,253 +38,253 @@
 block discarded – undo
38 38
  * Manage trusted certificates for users
39 39
  */
40 40
 class CertificateManager implements ICertificateManager {
41
-	/**
42
-	 * @var string
43
-	 */
44
-	protected $uid;
45
-
46
-	/**
47
-	 * @var \OC\Files\View
48
-	 */
49
-	protected $view;
50
-
51
-	/**
52
-	 * @var IConfig
53
-	 */
54
-	protected $config;
55
-
56
-	/**
57
-	 * @var ILogger
58
-	 */
59
-	protected $logger;
60
-
61
-	/** @var ISecureRandom */
62
-	protected $random;
63
-
64
-	/**
65
-	 * @param string $uid
66
-	 * @param \OC\Files\View $view relative to data/
67
-	 * @param IConfig $config
68
-	 * @param ILogger $logger
69
-	 * @param ISecureRandom $random
70
-	 */
71
-	public function __construct($uid,
72
-								\OC\Files\View $view,
73
-								IConfig $config,
74
-								ILogger $logger,
75
-								ISecureRandom $random) {
76
-		$this->uid = $uid;
77
-		$this->view = $view;
78
-		$this->config = $config;
79
-		$this->logger = $logger;
80
-		$this->random = $random;
81
-	}
82
-
83
-	/**
84
-	 * Returns all certificates trusted by the user
85
-	 *
86
-	 * @return \OCP\ICertificate[]
87
-	 */
88
-	public function listCertificates() {
89
-
90
-		if (!$this->config->getSystemValue('installed', false)) {
91
-			return [];
92
-		}
93
-
94
-		$path = $this->getPathToCertificates() . 'uploads/';
95
-		if (!$this->view->is_dir($path)) {
96
-			return [];
97
-		}
98
-		$result = [];
99
-		$handle = $this->view->opendir($path);
100
-		if (!is_resource($handle)) {
101
-			return [];
102
-		}
103
-		while (false !== ($file = readdir($handle))) {
104
-			if ($file != '.' && $file != '..') {
105
-				try {
106
-					$result[] = new Certificate($this->view->file_get_contents($path . $file), $file);
107
-				} catch (\Exception $e) {
108
-				}
109
-			}
110
-		}
111
-		closedir($handle);
112
-		return $result;
113
-	}
114
-
115
-	/**
116
-	 * create the certificate bundle of all trusted certificated
117
-	 */
118
-	public function createCertificateBundle() {
119
-		$path = $this->getPathToCertificates();
120
-		$certs = $this->listCertificates();
121
-
122
-		if (!$this->view->file_exists($path)) {
123
-			$this->view->mkdir($path);
124
-		}
125
-
126
-		$defaultCertificates = file_get_contents(\OC::$SERVERROOT . '/resources/config/ca-bundle.crt');
127
-		if (strlen($defaultCertificates) < 1024) { // sanity check to verify that we have some content for our bundle
128
-			// log as exception so we have a stacktrace
129
-			$this->logger->logException(new \Exception('Shipped ca-bundle is empty, refusing to create certificate bundle'));
130
-			return;
131
-		}
132
-
133
-		$certPath = $path . 'rootcerts.crt';
134
-		$tmpPath = $certPath . '.tmp' . $this->random->generate(10, ISecureRandom::CHAR_DIGITS);
135
-		$fhCerts = $this->view->fopen($tmpPath, 'w');
136
-
137
-		// Write user certificates
138
-		foreach ($certs as $cert) {
139
-			$file = $path . '/uploads/' . $cert->getName();
140
-			$data = $this->view->file_get_contents($file);
141
-			if (strpos($data, 'BEGIN CERTIFICATE')) {
142
-				fwrite($fhCerts, $data);
143
-				fwrite($fhCerts, "\r\n");
144
-			}
145
-		}
146
-
147
-		// Append the default certificates
148
-		fwrite($fhCerts, $defaultCertificates);
149
-
150
-		// Append the system certificate bundle
151
-		$systemBundle = $this->getCertificateBundle(null);
152
-		if ($systemBundle !== $certPath && $this->view->file_exists($systemBundle)) {
153
-			$systemCertificates = $this->view->file_get_contents($systemBundle);
154
-			fwrite($fhCerts, $systemCertificates);
155
-		}
156
-
157
-		fclose($fhCerts);
158
-
159
-		$this->view->rename($tmpPath, $certPath);
160
-	}
161
-
162
-	/**
163
-	 * Save the certificate and re-generate the certificate bundle
164
-	 *
165
-	 * @param string $certificate the certificate data
166
-	 * @param string $name the filename for the certificate
167
-	 * @return \OCP\ICertificate
168
-	 * @throws \Exception If the certificate could not get added
169
-	 */
170
-	public function addCertificate($certificate, $name) {
171
-		if (!Filesystem::isValidPath($name) or Filesystem::isFileBlacklisted($name)) {
172
-			throw new \Exception('Filename is not valid');
173
-		}
174
-
175
-		$dir = $this->getPathToCertificates() . 'uploads/';
176
-		if (!$this->view->file_exists($dir)) {
177
-			$this->view->mkdir($dir);
178
-		}
179
-
180
-		try {
181
-			$file = $dir . $name;
182
-			$certificateObject = new Certificate($certificate, $name);
183
-			$this->view->file_put_contents($file, $certificate);
184
-			$this->createCertificateBundle();
185
-			return $certificateObject;
186
-		} catch (\Exception $e) {
187
-			throw $e;
188
-		}
189
-
190
-	}
191
-
192
-	/**
193
-	 * Remove the certificate and re-generate the certificate bundle
194
-	 *
195
-	 * @param string $name
196
-	 * @return bool
197
-	 */
198
-	public function removeCertificate($name) {
199
-		if (!Filesystem::isValidPath($name)) {
200
-			return false;
201
-		}
202
-		$path = $this->getPathToCertificates() . 'uploads/';
203
-		if ($this->view->file_exists($path . $name)) {
204
-			$this->view->unlink($path . $name);
205
-			$this->createCertificateBundle();
206
-		}
207
-		return true;
208
-	}
209
-
210
-	/**
211
-	 * Get the path to the certificate bundle for this user
212
-	 *
213
-	 * @param string|null $uid (optional) user to get the certificate bundle for, use `null` to get the system bundle
214
-	 * @return string
215
-	 */
216
-	public function getCertificateBundle($uid = '') {
217
-		if ($uid === '') {
218
-			$uid = $this->uid;
219
-		}
220
-		return $this->getPathToCertificates($uid) . 'rootcerts.crt';
221
-	}
222
-
223
-	/**
224
-	 * Get the full local path to the certificate bundle for this user
225
-	 *
226
-	 * @param string $uid (optional) user to get the certificate bundle for, use `null` to get the system bundle
227
-	 * @return string
228
-	 */
229
-	public function getAbsoluteBundlePath($uid = '') {
230
-		if ($uid === '') {
231
-			$uid = $this->uid;
232
-		}
233
-		if ($this->needsRebundling($uid)) {
234
-			if (is_null($uid)) {
235
-				$manager = new CertificateManager(null, $this->view, $this->config, $this->logger, $this->random);
236
-				$manager->createCertificateBundle();
237
-			} else {
238
-				$this->createCertificateBundle();
239
-			}
240
-		}
241
-		return $this->view->getLocalFile($this->getCertificateBundle($uid));
242
-	}
243
-
244
-	/**
245
-	 * @param string|null $uid (optional) user to get the certificate path for, use `null` to get the system path
246
-	 * @return string
247
-	 */
248
-	private function getPathToCertificates($uid = '') {
249
-		if ($uid === '') {
250
-			$uid = $this->uid;
251
-		}
252
-		return is_null($uid) ? '/files_external/' : '/' . $uid . '/files_external/';
253
-	}
254
-
255
-	/**
256
-	 * Check if we need to re-bundle the certificates because one of the sources has updated
257
-	 *
258
-	 * @param string $uid (optional) user to get the certificate path for, use `null` to get the system path
259
-	 * @return bool
260
-	 */
261
-	private function needsRebundling($uid = '') {
262
-		if ($uid === '') {
263
-			$uid = $this->uid;
264
-		}
265
-		$sourceMTimes = [$this->getFilemtimeOfCaBundle()];
266
-		$targetBundle = $this->getCertificateBundle($uid);
267
-		if (!$this->view->file_exists($targetBundle)) {
268
-			return true;
269
-		}
270
-
271
-		if (!is_null($uid)) { // also depend on the system bundle
272
-			$sourceMTimes[] = $this->view->filemtime($this->getCertificateBundle(null));
273
-		}
274
-
275
-		$sourceMTime = array_reduce($sourceMTimes, function ($max, $mtime) {
276
-			return max($max, $mtime);
277
-		}, 0);
278
-		return $sourceMTime > $this->view->filemtime($targetBundle);
279
-	}
280
-
281
-	/**
282
-	 * get mtime of ca-bundle shipped by Nextcloud
283
-	 *
284
-	 * @return int
285
-	 */
286
-	protected function getFilemtimeOfCaBundle() {
287
-		return filemtime(\OC::$SERVERROOT . '/resources/config/ca-bundle.crt');
288
-	}
41
+    /**
42
+     * @var string
43
+     */
44
+    protected $uid;
45
+
46
+    /**
47
+     * @var \OC\Files\View
48
+     */
49
+    protected $view;
50
+
51
+    /**
52
+     * @var IConfig
53
+     */
54
+    protected $config;
55
+
56
+    /**
57
+     * @var ILogger
58
+     */
59
+    protected $logger;
60
+
61
+    /** @var ISecureRandom */
62
+    protected $random;
63
+
64
+    /**
65
+     * @param string $uid
66
+     * @param \OC\Files\View $view relative to data/
67
+     * @param IConfig $config
68
+     * @param ILogger $logger
69
+     * @param ISecureRandom $random
70
+     */
71
+    public function __construct($uid,
72
+                                \OC\Files\View $view,
73
+                                IConfig $config,
74
+                                ILogger $logger,
75
+                                ISecureRandom $random) {
76
+        $this->uid = $uid;
77
+        $this->view = $view;
78
+        $this->config = $config;
79
+        $this->logger = $logger;
80
+        $this->random = $random;
81
+    }
82
+
83
+    /**
84
+     * Returns all certificates trusted by the user
85
+     *
86
+     * @return \OCP\ICertificate[]
87
+     */
88
+    public function listCertificates() {
89
+
90
+        if (!$this->config->getSystemValue('installed', false)) {
91
+            return [];
92
+        }
93
+
94
+        $path = $this->getPathToCertificates() . 'uploads/';
95
+        if (!$this->view->is_dir($path)) {
96
+            return [];
97
+        }
98
+        $result = [];
99
+        $handle = $this->view->opendir($path);
100
+        if (!is_resource($handle)) {
101
+            return [];
102
+        }
103
+        while (false !== ($file = readdir($handle))) {
104
+            if ($file != '.' && $file != '..') {
105
+                try {
106
+                    $result[] = new Certificate($this->view->file_get_contents($path . $file), $file);
107
+                } catch (\Exception $e) {
108
+                }
109
+            }
110
+        }
111
+        closedir($handle);
112
+        return $result;
113
+    }
114
+
115
+    /**
116
+     * create the certificate bundle of all trusted certificated
117
+     */
118
+    public function createCertificateBundle() {
119
+        $path = $this->getPathToCertificates();
120
+        $certs = $this->listCertificates();
121
+
122
+        if (!$this->view->file_exists($path)) {
123
+            $this->view->mkdir($path);
124
+        }
125
+
126
+        $defaultCertificates = file_get_contents(\OC::$SERVERROOT . '/resources/config/ca-bundle.crt');
127
+        if (strlen($defaultCertificates) < 1024) { // sanity check to verify that we have some content for our bundle
128
+            // log as exception so we have a stacktrace
129
+            $this->logger->logException(new \Exception('Shipped ca-bundle is empty, refusing to create certificate bundle'));
130
+            return;
131
+        }
132
+
133
+        $certPath = $path . 'rootcerts.crt';
134
+        $tmpPath = $certPath . '.tmp' . $this->random->generate(10, ISecureRandom::CHAR_DIGITS);
135
+        $fhCerts = $this->view->fopen($tmpPath, 'w');
136
+
137
+        // Write user certificates
138
+        foreach ($certs as $cert) {
139
+            $file = $path . '/uploads/' . $cert->getName();
140
+            $data = $this->view->file_get_contents($file);
141
+            if (strpos($data, 'BEGIN CERTIFICATE')) {
142
+                fwrite($fhCerts, $data);
143
+                fwrite($fhCerts, "\r\n");
144
+            }
145
+        }
146
+
147
+        // Append the default certificates
148
+        fwrite($fhCerts, $defaultCertificates);
149
+
150
+        // Append the system certificate bundle
151
+        $systemBundle = $this->getCertificateBundle(null);
152
+        if ($systemBundle !== $certPath && $this->view->file_exists($systemBundle)) {
153
+            $systemCertificates = $this->view->file_get_contents($systemBundle);
154
+            fwrite($fhCerts, $systemCertificates);
155
+        }
156
+
157
+        fclose($fhCerts);
158
+
159
+        $this->view->rename($tmpPath, $certPath);
160
+    }
161
+
162
+    /**
163
+     * Save the certificate and re-generate the certificate bundle
164
+     *
165
+     * @param string $certificate the certificate data
166
+     * @param string $name the filename for the certificate
167
+     * @return \OCP\ICertificate
168
+     * @throws \Exception If the certificate could not get added
169
+     */
170
+    public function addCertificate($certificate, $name) {
171
+        if (!Filesystem::isValidPath($name) or Filesystem::isFileBlacklisted($name)) {
172
+            throw new \Exception('Filename is not valid');
173
+        }
174
+
175
+        $dir = $this->getPathToCertificates() . 'uploads/';
176
+        if (!$this->view->file_exists($dir)) {
177
+            $this->view->mkdir($dir);
178
+        }
179
+
180
+        try {
181
+            $file = $dir . $name;
182
+            $certificateObject = new Certificate($certificate, $name);
183
+            $this->view->file_put_contents($file, $certificate);
184
+            $this->createCertificateBundle();
185
+            return $certificateObject;
186
+        } catch (\Exception $e) {
187
+            throw $e;
188
+        }
189
+
190
+    }
191
+
192
+    /**
193
+     * Remove the certificate and re-generate the certificate bundle
194
+     *
195
+     * @param string $name
196
+     * @return bool
197
+     */
198
+    public function removeCertificate($name) {
199
+        if (!Filesystem::isValidPath($name)) {
200
+            return false;
201
+        }
202
+        $path = $this->getPathToCertificates() . 'uploads/';
203
+        if ($this->view->file_exists($path . $name)) {
204
+            $this->view->unlink($path . $name);
205
+            $this->createCertificateBundle();
206
+        }
207
+        return true;
208
+    }
209
+
210
+    /**
211
+     * Get the path to the certificate bundle for this user
212
+     *
213
+     * @param string|null $uid (optional) user to get the certificate bundle for, use `null` to get the system bundle
214
+     * @return string
215
+     */
216
+    public function getCertificateBundle($uid = '') {
217
+        if ($uid === '') {
218
+            $uid = $this->uid;
219
+        }
220
+        return $this->getPathToCertificates($uid) . 'rootcerts.crt';
221
+    }
222
+
223
+    /**
224
+     * Get the full local path to the certificate bundle for this user
225
+     *
226
+     * @param string $uid (optional) user to get the certificate bundle for, use `null` to get the system bundle
227
+     * @return string
228
+     */
229
+    public function getAbsoluteBundlePath($uid = '') {
230
+        if ($uid === '') {
231
+            $uid = $this->uid;
232
+        }
233
+        if ($this->needsRebundling($uid)) {
234
+            if (is_null($uid)) {
235
+                $manager = new CertificateManager(null, $this->view, $this->config, $this->logger, $this->random);
236
+                $manager->createCertificateBundle();
237
+            } else {
238
+                $this->createCertificateBundle();
239
+            }
240
+        }
241
+        return $this->view->getLocalFile($this->getCertificateBundle($uid));
242
+    }
243
+
244
+    /**
245
+     * @param string|null $uid (optional) user to get the certificate path for, use `null` to get the system path
246
+     * @return string
247
+     */
248
+    private function getPathToCertificates($uid = '') {
249
+        if ($uid === '') {
250
+            $uid = $this->uid;
251
+        }
252
+        return is_null($uid) ? '/files_external/' : '/' . $uid . '/files_external/';
253
+    }
254
+
255
+    /**
256
+     * Check if we need to re-bundle the certificates because one of the sources has updated
257
+     *
258
+     * @param string $uid (optional) user to get the certificate path for, use `null` to get the system path
259
+     * @return bool
260
+     */
261
+    private function needsRebundling($uid = '') {
262
+        if ($uid === '') {
263
+            $uid = $this->uid;
264
+        }
265
+        $sourceMTimes = [$this->getFilemtimeOfCaBundle()];
266
+        $targetBundle = $this->getCertificateBundle($uid);
267
+        if (!$this->view->file_exists($targetBundle)) {
268
+            return true;
269
+        }
270
+
271
+        if (!is_null($uid)) { // also depend on the system bundle
272
+            $sourceMTimes[] = $this->view->filemtime($this->getCertificateBundle(null));
273
+        }
274
+
275
+        $sourceMTime = array_reduce($sourceMTimes, function ($max, $mtime) {
276
+            return max($max, $mtime);
277
+        }, 0);
278
+        return $sourceMTime > $this->view->filemtime($targetBundle);
279
+    }
280
+
281
+    /**
282
+     * get mtime of ca-bundle shipped by Nextcloud
283
+     *
284
+     * @return int
285
+     */
286
+    protected function getFilemtimeOfCaBundle() {
287
+        return filemtime(\OC::$SERVERROOT . '/resources/config/ca-bundle.crt');
288
+    }
289 289
 
290 290
 }
Please login to merge, or discard this patch.
lib/private/ContactsManager.php 1 patch
Indentation   +177 added lines, -177 removed lines patch added patch discarded remove patch
@@ -29,181 +29,181 @@
 block discarded – undo
29 29
 
30 30
 namespace OC {
31 31
 
32
-	class ContactsManager implements \OCP\Contacts\IManager {
33
-
34
-		/**
35
-		 * This function is used to search and find contacts within the users address books.
36
-		 * In case $pattern is empty all contacts will be returned.
37
-		 *
38
-		 * @param string $pattern which should match within the $searchProperties
39
-		 * @param array $searchProperties defines the properties within the query pattern should match
40
-		 * @param array $options = array() to define the search behavior
41
-		 * 	- 'escape_like_param' - If set to false wildcards _ and % are not escaped
42
-		 * @return array an array of contacts which are arrays of key-value-pairs
43
-		 */
44
-		public function search($pattern, $searchProperties = [], $options = []) {
45
-			$this->loadAddressBooks();
46
-			$result = [];
47
-			foreach($this->addressBooks as $addressBook) {
48
-				$r = $addressBook->search($pattern, $searchProperties, $options);
49
-				$contacts = [];
50
-				foreach($r as $c){
51
-					$c['addressbook-key'] = $addressBook->getKey();
52
-					$contacts[] = $c;
53
-				}
54
-				$result = array_merge($result, $contacts);
55
-			}
56
-
57
-			return $result;
58
-		}
59
-
60
-		/**
61
-		 * This function can be used to delete the contact identified by the given id
62
-		 *
63
-		 * @param object $id the unique identifier to a contact
64
-		 * @param string $addressBookKey identifier of the address book in which the contact shall be deleted
65
-		 * @return bool successful or not
66
-		 */
67
-		public function delete($id, $addressBookKey) {
68
-			$addressBook = $this->getAddressBook($addressBookKey);
69
-			if (!$addressBook) {
70
-				return null;
71
-			}
72
-
73
-			if ($addressBook->getPermissions() & \OCP\Constants::PERMISSION_DELETE) {
74
-				return $addressBook->delete($id);
75
-			}
76
-
77
-			return null;
78
-		}
79
-
80
-		/**
81
-		 * This function is used to create a new contact if 'id' is not given or not present.
82
-		 * Otherwise the contact will be updated by replacing the entire data set.
83
-		 *
84
-		 * @param array $properties this array if key-value-pairs defines a contact
85
-		 * @param string $addressBookKey identifier of the address book in which the contact shall be created or updated
86
-		 * @return array representing the contact just created or updated
87
-		 */
88
-		public function createOrUpdate($properties, $addressBookKey) {
89
-			$addressBook = $this->getAddressBook($addressBookKey);
90
-			if (!$addressBook) {
91
-				return null;
92
-			}
93
-
94
-			if ($addressBook->getPermissions() & \OCP\Constants::PERMISSION_CREATE) {
95
-				return $addressBook->createOrUpdate($properties);
96
-			}
97
-
98
-			return null;
99
-		}
100
-
101
-		/**
102
-		 * Check if contacts are available (e.g. contacts app enabled)
103
-		 *
104
-		 * @return bool true if enabled, false if not
105
-		 */
106
-		public function isEnabled() {
107
-			return !empty($this->addressBooks) || !empty($this->addressBookLoaders);
108
-		}
109
-
110
-		/**
111
-		 * @param \OCP\IAddressBook $addressBook
112
-		 */
113
-		public function registerAddressBook(\OCP\IAddressBook $addressBook) {
114
-			$this->addressBooks[$addressBook->getKey()] = $addressBook;
115
-		}
116
-
117
-		/**
118
-		 * @param \OCP\IAddressBook $addressBook
119
-		 */
120
-		public function unregisterAddressBook(\OCP\IAddressBook $addressBook) {
121
-			unset($this->addressBooks[$addressBook->getKey()]);
122
-		}
123
-
124
-		/**
125
-		 * Return a list of the user's addressbooks display names
126
-		 * ! The addressBook displayName are not unique, please use getUserAddressBooks
127
-		 * 
128
-		 * @return array
129
-		 * @since 6.0.0
130
-		 * @deprecated 16.0.0 - Use `$this->getUserAddressBooks()` instead
131
-		 */
132
-		public function getAddressBooks() {
133
-			$this->loadAddressBooks();
134
-			$result = [];
135
-			foreach($this->addressBooks as $addressBook) {
136
-				$result[$addressBook->getKey()] = $addressBook->getDisplayName();
137
-			}
138
-
139
-			return $result;
140
-		}
141
-
142
-		/**
143
-		 * Return a list of the user's addressbooks
144
-		 * 
145
-		 * @return IAddressBook[]
146
-		 * @since 16.0.0
147
-		 */
148
-		public function getUserAddressBooks(): Array {
149
-			$this->loadAddressBooks();
150
-			return $this->addressBooks;
151
-		}
152
-
153
-		/**
154
-		 * removes all registered address book instances
155
-		 */
156
-		public function clear() {
157
-			$this->addressBooks = [];
158
-			$this->addressBookLoaders = [];
159
-		}
160
-
161
-		/**
162
-		 * @var \OCP\IAddressBook[] which holds all registered address books
163
-		 */
164
-		private $addressBooks = [];
165
-
166
-		/**
167
-		 * @var \Closure[] to call to load/register address books
168
-		 */
169
-		private $addressBookLoaders = [];
170
-
171
-		/**
172
-		 * In order to improve lazy loading a closure can be registered which will be called in case
173
-		 * address books are actually requested
174
-		 *
175
-		 * @param \Closure $callable
176
-		 */
177
-		public function register(\Closure $callable)
178
-		{
179
-			$this->addressBookLoaders[] = $callable;
180
-		}
181
-
182
-		/**
183
-		 * Get (and load when needed) the address book for $key
184
-		 *
185
-		 * @param string $addressBookKey
186
-		 * @return \OCP\IAddressBook
187
-		 */
188
-		protected function getAddressBook($addressBookKey)
189
-		{
190
-			$this->loadAddressBooks();
191
-			if (!array_key_exists($addressBookKey, $this->addressBooks)) {
192
-				return null;
193
-			}
194
-
195
-			return $this->addressBooks[$addressBookKey];
196
-		}
197
-
198
-		/**
199
-		 * Load all address books registered with 'register'
200
-		 */
201
-		protected function loadAddressBooks()
202
-		{
203
-			foreach($this->addressBookLoaders as $callable) {
204
-				$callable($this);
205
-			}
206
-			$this->addressBookLoaders = [];
207
-		}
208
-	}
32
+    class ContactsManager implements \OCP\Contacts\IManager {
33
+
34
+        /**
35
+         * This function is used to search and find contacts within the users address books.
36
+         * In case $pattern is empty all contacts will be returned.
37
+         *
38
+         * @param string $pattern which should match within the $searchProperties
39
+         * @param array $searchProperties defines the properties within the query pattern should match
40
+         * @param array $options = array() to define the search behavior
41
+         * 	- 'escape_like_param' - If set to false wildcards _ and % are not escaped
42
+         * @return array an array of contacts which are arrays of key-value-pairs
43
+         */
44
+        public function search($pattern, $searchProperties = [], $options = []) {
45
+            $this->loadAddressBooks();
46
+            $result = [];
47
+            foreach($this->addressBooks as $addressBook) {
48
+                $r = $addressBook->search($pattern, $searchProperties, $options);
49
+                $contacts = [];
50
+                foreach($r as $c){
51
+                    $c['addressbook-key'] = $addressBook->getKey();
52
+                    $contacts[] = $c;
53
+                }
54
+                $result = array_merge($result, $contacts);
55
+            }
56
+
57
+            return $result;
58
+        }
59
+
60
+        /**
61
+         * This function can be used to delete the contact identified by the given id
62
+         *
63
+         * @param object $id the unique identifier to a contact
64
+         * @param string $addressBookKey identifier of the address book in which the contact shall be deleted
65
+         * @return bool successful or not
66
+         */
67
+        public function delete($id, $addressBookKey) {
68
+            $addressBook = $this->getAddressBook($addressBookKey);
69
+            if (!$addressBook) {
70
+                return null;
71
+            }
72
+
73
+            if ($addressBook->getPermissions() & \OCP\Constants::PERMISSION_DELETE) {
74
+                return $addressBook->delete($id);
75
+            }
76
+
77
+            return null;
78
+        }
79
+
80
+        /**
81
+         * This function is used to create a new contact if 'id' is not given or not present.
82
+         * Otherwise the contact will be updated by replacing the entire data set.
83
+         *
84
+         * @param array $properties this array if key-value-pairs defines a contact
85
+         * @param string $addressBookKey identifier of the address book in which the contact shall be created or updated
86
+         * @return array representing the contact just created or updated
87
+         */
88
+        public function createOrUpdate($properties, $addressBookKey) {
89
+            $addressBook = $this->getAddressBook($addressBookKey);
90
+            if (!$addressBook) {
91
+                return null;
92
+            }
93
+
94
+            if ($addressBook->getPermissions() & \OCP\Constants::PERMISSION_CREATE) {
95
+                return $addressBook->createOrUpdate($properties);
96
+            }
97
+
98
+            return null;
99
+        }
100
+
101
+        /**
102
+         * Check if contacts are available (e.g. contacts app enabled)
103
+         *
104
+         * @return bool true if enabled, false if not
105
+         */
106
+        public function isEnabled() {
107
+            return !empty($this->addressBooks) || !empty($this->addressBookLoaders);
108
+        }
109
+
110
+        /**
111
+         * @param \OCP\IAddressBook $addressBook
112
+         */
113
+        public function registerAddressBook(\OCP\IAddressBook $addressBook) {
114
+            $this->addressBooks[$addressBook->getKey()] = $addressBook;
115
+        }
116
+
117
+        /**
118
+         * @param \OCP\IAddressBook $addressBook
119
+         */
120
+        public function unregisterAddressBook(\OCP\IAddressBook $addressBook) {
121
+            unset($this->addressBooks[$addressBook->getKey()]);
122
+        }
123
+
124
+        /**
125
+         * Return a list of the user's addressbooks display names
126
+         * ! The addressBook displayName are not unique, please use getUserAddressBooks
127
+         * 
128
+         * @return array
129
+         * @since 6.0.0
130
+         * @deprecated 16.0.0 - Use `$this->getUserAddressBooks()` instead
131
+         */
132
+        public function getAddressBooks() {
133
+            $this->loadAddressBooks();
134
+            $result = [];
135
+            foreach($this->addressBooks as $addressBook) {
136
+                $result[$addressBook->getKey()] = $addressBook->getDisplayName();
137
+            }
138
+
139
+            return $result;
140
+        }
141
+
142
+        /**
143
+         * Return a list of the user's addressbooks
144
+         * 
145
+         * @return IAddressBook[]
146
+         * @since 16.0.0
147
+         */
148
+        public function getUserAddressBooks(): Array {
149
+            $this->loadAddressBooks();
150
+            return $this->addressBooks;
151
+        }
152
+
153
+        /**
154
+         * removes all registered address book instances
155
+         */
156
+        public function clear() {
157
+            $this->addressBooks = [];
158
+            $this->addressBookLoaders = [];
159
+        }
160
+
161
+        /**
162
+         * @var \OCP\IAddressBook[] which holds all registered address books
163
+         */
164
+        private $addressBooks = [];
165
+
166
+        /**
167
+         * @var \Closure[] to call to load/register address books
168
+         */
169
+        private $addressBookLoaders = [];
170
+
171
+        /**
172
+         * In order to improve lazy loading a closure can be registered which will be called in case
173
+         * address books are actually requested
174
+         *
175
+         * @param \Closure $callable
176
+         */
177
+        public function register(\Closure $callable)
178
+        {
179
+            $this->addressBookLoaders[] = $callable;
180
+        }
181
+
182
+        /**
183
+         * Get (and load when needed) the address book for $key
184
+         *
185
+         * @param string $addressBookKey
186
+         * @return \OCP\IAddressBook
187
+         */
188
+        protected function getAddressBook($addressBookKey)
189
+        {
190
+            $this->loadAddressBooks();
191
+            if (!array_key_exists($addressBookKey, $this->addressBooks)) {
192
+                return null;
193
+            }
194
+
195
+            return $this->addressBooks[$addressBookKey];
196
+        }
197
+
198
+        /**
199
+         * Load all address books registered with 'register'
200
+         */
201
+        protected function loadAddressBooks()
202
+        {
203
+            foreach($this->addressBookLoaders as $callable) {
204
+                $callable($this);
205
+            }
206
+            $this->addressBookLoaders = [];
207
+        }
208
+    }
209 209
 }
Please login to merge, or discard this patch.
lib/private/Tags.php 1 patch
Indentation   +805 added lines, -805 removed lines patch added patch discarded remove patch
@@ -52,809 +52,809 @@
 block discarded – undo
52 52
 
53 53
 class Tags implements ITags {
54 54
 
55
-	/**
56
-	 * Tags
57
-	 *
58
-	 * @var array
59
-	 */
60
-	private $tags = [];
61
-
62
-	/**
63
-	 * Used for storing objectid/categoryname pairs while rescanning.
64
-	 *
65
-	 * @var array
66
-	 */
67
-	private static $relations = [];
68
-
69
-	/**
70
-	 * Type
71
-	 *
72
-	 * @var string
73
-	 */
74
-	private $type;
75
-
76
-	/**
77
-	 * User
78
-	 *
79
-	 * @var string
80
-	 */
81
-	private $user;
82
-
83
-	/**
84
-	 * Are we including tags for shared items?
85
-	 *
86
-	 * @var bool
87
-	 */
88
-	private $includeShared = false;
89
-
90
-	/**
91
-	 * The current user, plus any owners of the items shared with the current
92
-	 * user, if $this->includeShared === true.
93
-	 *
94
-	 * @var array
95
-	 */
96
-	private $owners = [];
97
-
98
-	/**
99
-	 * The Mapper we're using to communicate our Tag objects to the database.
100
-	 *
101
-	 * @var TagMapper
102
-	 */
103
-	private $mapper;
104
-
105
-	/**
106
-	 * The sharing backend for objects of $this->type. Required if
107
-	 * $this->includeShared === true to determine ownership of items.
108
-	 *
109
-	 * @var \OCP\Share_Backend
110
-	 */
111
-	private $backend;
112
-
113
-	const TAG_TABLE = '*PREFIX*vcategory';
114
-	const RELATION_TABLE = '*PREFIX*vcategory_to_object';
115
-
116
-	/**
117
-	* Constructor.
118
-	*
119
-	* @param TagMapper $mapper Instance of the TagMapper abstraction layer.
120
-	* @param string $user The user whose data the object will operate on.
121
-	* @param string $type The type of items for which tags will be loaded.
122
-	* @param array $defaultTags Tags that should be created at construction.
123
-	* @param boolean $includeShared Whether to include tags for items shared with this user by others.
124
-	*/
125
-	public function __construct(TagMapper $mapper, $user, $type, $defaultTags = [], $includeShared = false) {
126
-		$this->mapper = $mapper;
127
-		$this->user = $user;
128
-		$this->type = $type;
129
-		$this->includeShared = $includeShared;
130
-		$this->owners = [$this->user];
131
-		if ($this->includeShared) {
132
-			$this->owners = array_merge($this->owners, \OC\Share\Share::getSharedItemsOwners($this->user, $this->type, true));
133
-			$this->backend = \OC\Share\Share::getBackend($this->type);
134
-		}
135
-		$this->tags = $this->mapper->loadTags($this->owners, $this->type);
136
-
137
-		if(count($defaultTags) > 0 && count($this->tags) === 0) {
138
-			$this->addMultiple($defaultTags, true);
139
-		}
140
-	}
141
-
142
-	/**
143
-	* Check if any tags are saved for this type and user.
144
-	*
145
-	* @return boolean
146
-	*/
147
-	public function isEmpty() {
148
-		return count($this->tags) === 0;
149
-	}
150
-
151
-	/**
152
-	* Returns an array mapping a given tag's properties to its values:
153
-	* ['id' => 0, 'name' = 'Tag', 'owner' = 'User', 'type' => 'tagtype']
154
-	*
155
-	* @param string $id The ID of the tag that is going to be mapped
156
-	* @return array|false
157
-	*/
158
-	public function getTag($id) {
159
-		$key = $this->getTagById($id);
160
-		if ($key !== false) {
161
-			return $this->tagMap($this->tags[$key]);
162
-		}
163
-		return false;
164
-	}
165
-
166
-	/**
167
-	* Get the tags for a specific user.
168
-	*
169
-	* This returns an array with maps containing each tag's properties:
170
-	* [
171
-	* 	['id' => 0, 'name' = 'First tag', 'owner' = 'User', 'type' => 'tagtype'],
172
-	* 	['id' => 1, 'name' = 'Shared tag', 'owner' = 'Other user', 'type' => 'tagtype'],
173
-	* ]
174
-	*
175
-	* @return array
176
-	*/
177
-	public function getTags() {
178
-		if(!count($this->tags)) {
179
-			return [];
180
-		}
181
-
182
-		usort($this->tags, function($a, $b) {
183
-			return strnatcasecmp($a->getName(), $b->getName());
184
-		});
185
-		$tagMap = [];
186
-
187
-		foreach($this->tags as $tag) {
188
-			if($tag->getName() !== ITags::TAG_FAVORITE) {
189
-				$tagMap[] = $this->tagMap($tag);
190
-			}
191
-		}
192
-		return $tagMap;
193
-
194
-	}
195
-
196
-	/**
197
-	* Return only the tags owned by the given user, omitting any tags shared
198
-	* by other users.
199
-	*
200
-	* @param string $user The user whose tags are to be checked.
201
-	* @return array An array of Tag objects.
202
-	*/
203
-	public function getTagsForUser($user) {
204
-		return array_filter($this->tags,
205
-			function($tag) use($user) {
206
-				return $tag->getOwner() === $user;
207
-			}
208
-		);
209
-	}
210
-
211
-	/**
212
-	 * Get the list of tags for the given ids.
213
-	 *
214
-	 * @param array $objIds array of object ids
215
-	 * @return array|boolean of tags id as key to array of tag names
216
-	 * or false if an error occurred
217
-	 */
218
-	public function getTagsForObjects(array $objIds) {
219
-		$entries = [];
220
-
221
-		try {
222
-			$conn = \OC::$server->getDatabaseConnection();
223
-			$chunks = array_chunk($objIds, 900, false);
224
-			foreach ($chunks as $chunk) {
225
-				$result = $conn->executeQuery(
226
-					'SELECT `category`, `categoryid`, `objid` ' .
227
-					'FROM `' . self::RELATION_TABLE . '` r, `' . self::TAG_TABLE . '` ' .
228
-					'WHERE `categoryid` = `id` AND `uid` = ? AND r.`type` = ? AND `objid` IN (?)',
229
-					[$this->user, $this->type, $chunk],
230
-					[null, null, IQueryBuilder::PARAM_INT_ARRAY]
231
-				);
232
-				while ($row = $result->fetch()) {
233
-					$objId = (int)$row['objid'];
234
-					if (!isset($entries[$objId])) {
235
-						$entries[$objId] = [];
236
-					}
237
-					$entries[$objId][] = $row['category'];
238
-				}
239
-				if ($result === null) {
240
-					\OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
241
-					return false;
242
-				}
243
-			}
244
-		} catch(\Exception $e) {
245
-			\OC::$server->getLogger()->logException($e, [
246
-				'message' => __METHOD__,
247
-				'level' => ILogger::ERROR,
248
-				'app' => 'core',
249
-			]);
250
-			return false;
251
-		}
252
-
253
-		return $entries;
254
-	}
255
-
256
-	/**
257
-	* Get the a list if items tagged with $tag.
258
-	*
259
-	* Throws an exception if the tag could not be found.
260
-	*
261
-	* @param string $tag Tag id or name.
262
-	* @return array|false An array of object ids or false on error.
263
-	* @throws \Exception
264
-	*/
265
-	public function getIdsForTag($tag) {
266
-		$result = null;
267
-		$tagId = false;
268
-		if(is_numeric($tag)) {
269
-			$tagId = $tag;
270
-		} elseif(is_string($tag)) {
271
-			$tag = trim($tag);
272
-			if($tag === '') {
273
-				\OCP\Util::writeLog('core', __METHOD__.', Cannot use empty tag names', ILogger::DEBUG);
274
-				return false;
275
-			}
276
-			$tagId = $this->getTagId($tag);
277
-		}
278
-
279
-		if($tagId === false) {
280
-			$l10n = \OC::$server->getL10N('core');
281
-			throw new \Exception(
282
-				$l10n->t('Could not find category "%s"', [$tag])
283
-			);
284
-		}
285
-
286
-		$ids = [];
287
-		$sql = 'SELECT `objid` FROM `' . self::RELATION_TABLE
288
-			. '` WHERE `categoryid` = ?';
289
-
290
-		try {
291
-			$stmt = \OC_DB::prepare($sql);
292
-			$result = $stmt->execute([$tagId]);
293
-			if ($result === null) {
294
-				\OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
295
-				return false;
296
-			}
297
-		} catch(\Exception $e) {
298
-			\OC::$server->getLogger()->logException($e, [
299
-				'message' => __METHOD__,
300
-				'level' => ILogger::ERROR,
301
-				'app' => 'core',
302
-			]);
303
-			return false;
304
-		}
305
-
306
-		if(!is_null($result)) {
307
-			while( $row = $result->fetchRow()) {
308
-				$id = (int)$row['objid'];
309
-
310
-				if ($this->includeShared) {
311
-					// We have to check if we are really allowed to access the
312
-					// items that are tagged with $tag. To that end, we ask the
313
-					// corresponding sharing backend if the item identified by $id
314
-					// is owned by any of $this->owners.
315
-					foreach ($this->owners as $owner) {
316
-						if ($this->backend->isValidSource($id, $owner)) {
317
-							$ids[] = $id;
318
-							break;
319
-						}
320
-					}
321
-				} else {
322
-					$ids[] = $id;
323
-				}
324
-			}
325
-		}
326
-
327
-		return $ids;
328
-	}
329
-
330
-	/**
331
-	* Checks whether a tag is saved for the given user,
332
-	* disregarding the ones shared with him or her.
333
-	*
334
-	* @param string $name The tag name to check for.
335
-	* @param string $user The user whose tags are to be checked.
336
-	* @return bool
337
-	*/
338
-	public function userHasTag($name, $user) {
339
-		$key = $this->array_searchi($name, $this->getTagsForUser($user));
340
-		return ($key !== false) ? $this->tags[$key]->getId() : false;
341
-	}
342
-
343
-	/**
344
-	* Checks whether a tag is saved for or shared with the current user.
345
-	*
346
-	* @param string $name The tag name to check for.
347
-	* @return bool
348
-	*/
349
-	public function hasTag($name) {
350
-		return $this->getTagId($name) !== false;
351
-	}
352
-
353
-	/**
354
-	* Add a new tag.
355
-	*
356
-	* @param string $name A string with a name of the tag
357
-	* @return false|int the id of the added tag or false on error.
358
-	*/
359
-	public function add($name) {
360
-		$name = trim($name);
361
-
362
-		if($name === '') {
363
-			\OCP\Util::writeLog('core', __METHOD__.', Cannot add an empty tag', ILogger::DEBUG);
364
-			return false;
365
-		}
366
-		if($this->userHasTag($name, $this->user)) {
367
-			\OCP\Util::writeLog('core', __METHOD__.', name: ' . $name. ' exists already', ILogger::DEBUG);
368
-			return false;
369
-		}
370
-		try {
371
-			$tag = new Tag($this->user, $this->type, $name);
372
-			$tag = $this->mapper->insert($tag);
373
-			$this->tags[] = $tag;
374
-		} catch(\Exception $e) {
375
-			\OC::$server->getLogger()->logException($e, [
376
-				'message' => __METHOD__,
377
-				'level' => ILogger::ERROR,
378
-				'app' => 'core',
379
-			]);
380
-			return false;
381
-		}
382
-		\OCP\Util::writeLog('core', __METHOD__.', id: ' . $tag->getId(), ILogger::DEBUG);
383
-		return $tag->getId();
384
-	}
385
-
386
-	/**
387
-	* Rename tag.
388
-	*
389
-	* @param string|integer $from The name or ID of the existing tag
390
-	* @param string $to The new name of the tag.
391
-	* @return bool
392
-	*/
393
-	public function rename($from, $to) {
394
-		$from = trim($from);
395
-		$to = trim($to);
396
-
397
-		if($to === '' || $from === '') {
398
-			\OCP\Util::writeLog('core', __METHOD__.', Cannot use empty tag names', ILogger::DEBUG);
399
-			return false;
400
-		}
401
-
402
-		if (is_numeric($from)) {
403
-			$key = $this->getTagById($from);
404
-		} else {
405
-			$key = $this->getTagByName($from);
406
-		}
407
-		if($key === false) {
408
-			\OCP\Util::writeLog('core', __METHOD__.', tag: ' . $from. ' does not exist', ILogger::DEBUG);
409
-			return false;
410
-		}
411
-		$tag = $this->tags[$key];
412
-
413
-		if($this->userHasTag($to, $tag->getOwner())) {
414
-			\OCP\Util::writeLog('core', __METHOD__.', A tag named ' . $to. ' already exists for user ' . $tag->getOwner() . '.', ILogger::DEBUG);
415
-			return false;
416
-		}
417
-
418
-		try {
419
-			$tag->setName($to);
420
-			$this->tags[$key] = $this->mapper->update($tag);
421
-		} catch(\Exception $e) {
422
-			\OC::$server->getLogger()->logException($e, [
423
-				'message' => __METHOD__,
424
-				'level' => ILogger::ERROR,
425
-				'app' => 'core',
426
-			]);
427
-			return false;
428
-		}
429
-		return true;
430
-	}
431
-
432
-	/**
433
-	* Add a list of new tags.
434
-	*
435
-	* @param string[] $names A string with a name or an array of strings containing
436
-	* the name(s) of the tag(s) to add.
437
-	* @param bool $sync When true, save the tags
438
-	* @param int|null $id int Optional object id to add to this|these tag(s)
439
-	* @return bool Returns false on error.
440
-	*/
441
-	public function addMultiple($names, $sync=false, $id = null) {
442
-		if(!is_array($names)) {
443
-			$names = [$names];
444
-		}
445
-		$names = array_map('trim', $names);
446
-		array_filter($names);
447
-
448
-		$newones = [];
449
-		foreach($names as $name) {
450
-			if(!$this->hasTag($name) && $name !== '') {
451
-				$newones[] = new Tag($this->user, $this->type, $name);
452
-			}
453
-			if(!is_null($id) ) {
454
-				// Insert $objectid, $categoryid  pairs if not exist.
455
-				self::$relations[] = ['objid' => $id, 'tag' => $name];
456
-			}
457
-		}
458
-		$this->tags = array_merge($this->tags, $newones);
459
-		if($sync === true) {
460
-			$this->save();
461
-		}
462
-
463
-		return true;
464
-	}
465
-
466
-	/**
467
-	 * Save the list of tags and their object relations
468
-	 */
469
-	protected function save() {
470
-		if(is_array($this->tags)) {
471
-			foreach($this->tags as $tag) {
472
-				try {
473
-					if (!$this->mapper->tagExists($tag)) {
474
-						$this->mapper->insert($tag);
475
-					}
476
-				} catch(\Exception $e) {
477
-					\OC::$server->getLogger()->logException($e, [
478
-						'message' => __METHOD__,
479
-						'level' => ILogger::ERROR,
480
-						'app' => 'core',
481
-					]);
482
-				}
483
-			}
484
-
485
-			// reload tags to get the proper ids.
486
-			$this->tags = $this->mapper->loadTags($this->owners, $this->type);
487
-			\OCP\Util::writeLog('core', __METHOD__.', tags: ' . print_r($this->tags, true),
488
-				ILogger::DEBUG);
489
-			// Loop through temporarily cached objectid/tagname pairs
490
-			// and save relations.
491
-			$tags = $this->tags;
492
-			// For some reason this is needed or array_search(i) will return 0..?
493
-			ksort($tags);
494
-			$dbConnection = \OC::$server->getDatabaseConnection();
495
-			foreach(self::$relations as $relation) {
496
-				$tagId = $this->getTagId($relation['tag']);
497
-				\OCP\Util::writeLog('core', __METHOD__ . 'catid, ' . $relation['tag'] . ' ' . $tagId, ILogger::DEBUG);
498
-				if($tagId) {
499
-					try {
500
-						$dbConnection->insertIfNotExist(self::RELATION_TABLE,
501
-							[
502
-								'objid' => $relation['objid'],
503
-								'categoryid' => $tagId,
504
-								'type' => $this->type,
505
-								]);
506
-					} catch(\Exception $e) {
507
-						\OC::$server->getLogger()->logException($e, [
508
-							'message' => __METHOD__,
509
-							'level' => ILogger::ERROR,
510
-							'app' => 'core',
511
-						]);
512
-					}
513
-				}
514
-			}
515
-			self::$relations = []; // reset
516
-		} else {
517
-			\OCP\Util::writeLog('core', __METHOD__.', $this->tags is not an array! '
518
-				. print_r($this->tags, true), ILogger::ERROR);
519
-		}
520
-	}
521
-
522
-	/**
523
-	* Delete tags and tag/object relations for a user.
524
-	*
525
-	* For hooking up on post_deleteUser
526
-	*
527
-	* @param array $arguments
528
-	*/
529
-	public static function post_deleteUser($arguments) {
530
-		// Find all objectid/tagId pairs.
531
-		$result = null;
532
-		try {
533
-			$stmt = \OC_DB::prepare('SELECT `id` FROM `' . self::TAG_TABLE . '` '
534
-				. 'WHERE `uid` = ?');
535
-			$result = $stmt->execute([$arguments['uid']]);
536
-			if ($result === null) {
537
-				\OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
538
-			}
539
-		} catch(\Exception $e) {
540
-			\OC::$server->getLogger()->logException($e, [
541
-				'message' => __METHOD__,
542
-				'level' => ILogger::ERROR,
543
-				'app' => 'core',
544
-			]);
545
-		}
546
-
547
-		if(!is_null($result)) {
548
-			try {
549
-				$stmt = \OC_DB::prepare('DELETE FROM `' . self::RELATION_TABLE . '` '
550
-					. 'WHERE `categoryid` = ?');
551
-				while( $row = $result->fetchRow()) {
552
-					try {
553
-						$stmt->execute([$row['id']]);
554
-					} catch(\Exception $e) {
555
-						\OC::$server->getLogger()->logException($e, [
556
-							'message' => __METHOD__,
557
-							'level' => ILogger::ERROR,
558
-							'app' => 'core',
559
-						]);
560
-					}
561
-				}
562
-			} catch(\Exception $e) {
563
-				\OC::$server->getLogger()->logException($e, [
564
-					'message' => __METHOD__,
565
-					'level' => ILogger::ERROR,
566
-					'app' => 'core',
567
-				]);
568
-			}
569
-		}
570
-		try {
571
-			$stmt = \OC_DB::prepare('DELETE FROM `' . self::TAG_TABLE . '` '
572
-				. 'WHERE `uid` = ?');
573
-			$result = $stmt->execute([$arguments['uid']]);
574
-			if ($result === null) {
575
-				\OCP\Util::writeLog('core', __METHOD__. ', DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
576
-			}
577
-		} catch(\Exception $e) {
578
-			\OC::$server->getLogger()->logException($e, [
579
-				'message' => __METHOD__,
580
-				'level' => ILogger::ERROR,
581
-				'app' => 'core',
582
-			]);
583
-		}
584
-	}
585
-
586
-	/**
587
-	* Delete tag/object relations from the db
588
-	*
589
-	* @param array $ids The ids of the objects
590
-	* @return boolean Returns false on error.
591
-	*/
592
-	public function purgeObjects(array $ids) {
593
-		if(count($ids) === 0) {
594
-			// job done ;)
595
-			return true;
596
-		}
597
-		$updates = $ids;
598
-		try {
599
-			$query = 'DELETE FROM `' . self::RELATION_TABLE . '` ';
600
-			$query .= 'WHERE `objid` IN (' . str_repeat('?,', count($ids)-1) . '?) ';
601
-			$query .= 'AND `type`= ?';
602
-			$updates[] = $this->type;
603
-			$stmt = \OC_DB::prepare($query);
604
-			$result = $stmt->execute($updates);
605
-			if ($result === null) {
606
-				\OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
607
-				return false;
608
-			}
609
-		} catch(\Exception $e) {
610
-			\OC::$server->getLogger()->logException($e, [
611
-				'message' => __METHOD__,
612
-				'level' => ILogger::ERROR,
613
-				'app' => 'core',
614
-			]);
615
-			return false;
616
-		}
617
-		return true;
618
-	}
619
-
620
-	/**
621
-	* Get favorites for an object type
622
-	*
623
-	* @return array|false An array of object ids.
624
-	*/
625
-	public function getFavorites() {
626
-		if(!$this->userHasTag(ITags::TAG_FAVORITE, $this->user)) {
627
-			return [];
628
-		}
629
-
630
-		try {
631
-			return $this->getIdsForTag(ITags::TAG_FAVORITE);
632
-		} catch(\Exception $e) {
633
-			\OC::$server->getLogger()->logException($e, [
634
-				'message' => __METHOD__,
635
-				'level' => ILogger::ERROR,
636
-				'app' => 'core',
637
-			]);
638
-			return [];
639
-		}
640
-	}
641
-
642
-	/**
643
-	* Add an object to favorites
644
-	*
645
-	* @param int $objid The id of the object
646
-	* @return boolean
647
-	*/
648
-	public function addToFavorites($objid) {
649
-		if(!$this->userHasTag(ITags::TAG_FAVORITE, $this->user)) {
650
-			$this->add(ITags::TAG_FAVORITE);
651
-		}
652
-		return $this->tagAs($objid, ITags::TAG_FAVORITE);
653
-	}
654
-
655
-	/**
656
-	* Remove an object from favorites
657
-	*
658
-	* @param int $objid The id of the object
659
-	* @return boolean
660
-	*/
661
-	public function removeFromFavorites($objid) {
662
-		return $this->unTag($objid, ITags::TAG_FAVORITE);
663
-	}
664
-
665
-	/**
666
-	* Creates a tag/object relation.
667
-	*
668
-	* @param int $objid The id of the object
669
-	* @param string $tag The id or name of the tag
670
-	* @return boolean Returns false on error.
671
-	*/
672
-	public function tagAs($objid, $tag) {
673
-		if(is_string($tag) && !is_numeric($tag)) {
674
-			$tag = trim($tag);
675
-			if($tag === '') {
676
-				\OCP\Util::writeLog('core', __METHOD__.', Cannot add an empty tag', ILogger::DEBUG);
677
-				return false;
678
-			}
679
-			if(!$this->hasTag($tag)) {
680
-				$this->add($tag);
681
-			}
682
-			$tagId =  $this->getTagId($tag);
683
-		} else {
684
-			$tagId = $tag;
685
-		}
686
-		try {
687
-			\OC::$server->getDatabaseConnection()->insertIfNotExist(self::RELATION_TABLE,
688
-				[
689
-					'objid' => $objid,
690
-					'categoryid' => $tagId,
691
-					'type' => $this->type,
692
-				]);
693
-		} catch(\Exception $e) {
694
-			\OC::$server->getLogger()->logException($e, [
695
-				'message' => __METHOD__,
696
-				'level' => ILogger::ERROR,
697
-				'app' => 'core',
698
-			]);
699
-			return false;
700
-		}
701
-		return true;
702
-	}
703
-
704
-	/**
705
-	* Delete single tag/object relation from the db
706
-	*
707
-	* @param int $objid The id of the object
708
-	* @param string $tag The id or name of the tag
709
-	* @return boolean
710
-	*/
711
-	public function unTag($objid, $tag) {
712
-		if(is_string($tag) && !is_numeric($tag)) {
713
-			$tag = trim($tag);
714
-			if($tag === '') {
715
-				\OCP\Util::writeLog('core', __METHOD__.', Tag name is empty', ILogger::DEBUG);
716
-				return false;
717
-			}
718
-			$tagId =  $this->getTagId($tag);
719
-		} else {
720
-			$tagId = $tag;
721
-		}
722
-
723
-		try {
724
-			$sql = 'DELETE FROM `' . self::RELATION_TABLE . '` '
725
-					. 'WHERE `objid` = ? AND `categoryid` = ? AND `type` = ?';
726
-			$stmt = \OC_DB::prepare($sql);
727
-			$stmt->execute([$objid, $tagId, $this->type]);
728
-		} catch(\Exception $e) {
729
-			\OC::$server->getLogger()->logException($e, [
730
-				'message' => __METHOD__,
731
-				'level' => ILogger::ERROR,
732
-				'app' => 'core',
733
-			]);
734
-			return false;
735
-		}
736
-		return true;
737
-	}
738
-
739
-	/**
740
-	* Delete tags from the database.
741
-	*
742
-	* @param string[]|integer[] $names An array of tags (names or IDs) to delete
743
-	* @return bool Returns false on error
744
-	*/
745
-	public function delete($names) {
746
-		if(!is_array($names)) {
747
-			$names = [$names];
748
-		}
749
-
750
-		$names = array_map('trim', $names);
751
-		array_filter($names);
752
-
753
-		\OCP\Util::writeLog('core', __METHOD__ . ', before: '
754
-			. print_r($this->tags, true), ILogger::DEBUG);
755
-		foreach($names as $name) {
756
-			$id = null;
757
-
758
-			if (is_numeric($name)) {
759
-				$key = $this->getTagById($name);
760
-			} else {
761
-				$key = $this->getTagByName($name);
762
-			}
763
-			if ($key !== false) {
764
-				$tag = $this->tags[$key];
765
-				$id = $tag->getId();
766
-				unset($this->tags[$key]);
767
-				$this->mapper->delete($tag);
768
-			} else {
769
-				\OCP\Util::writeLog('core', __METHOD__ . 'Cannot delete tag ' . $name
770
-					. ': not found.', ILogger::ERROR);
771
-			}
772
-			if(!is_null($id) && $id !== false) {
773
-				try {
774
-					$sql = 'DELETE FROM `' . self::RELATION_TABLE . '` '
775
-							. 'WHERE `categoryid` = ?';
776
-					$stmt = \OC_DB::prepare($sql);
777
-					$result = $stmt->execute([$id]);
778
-					if ($result === null) {
779
-						\OCP\Util::writeLog('core',
780
-							__METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(),
781
-							ILogger::ERROR);
782
-						return false;
783
-					}
784
-				} catch(\Exception $e) {
785
-					\OC::$server->getLogger()->logException($e, [
786
-						'message' => __METHOD__,
787
-						'level' => ILogger::ERROR,
788
-						'app' => 'core',
789
-					]);
790
-					return false;
791
-				}
792
-			}
793
-		}
794
-		return true;
795
-	}
796
-
797
-	// case-insensitive array_search
798
-	protected function array_searchi($needle, $haystack, $mem='getName') {
799
-		if(!is_array($haystack)) {
800
-			return false;
801
-		}
802
-		return array_search(strtolower($needle), array_map(
803
-			function($tag) use($mem) {
804
-				return strtolower(call_user_func([$tag, $mem]));
805
-			}, $haystack)
806
-		);
807
-	}
808
-
809
-	/**
810
-	* Get a tag's ID.
811
-	*
812
-	* @param string $name The tag name to look for.
813
-	* @return string|bool The tag's id or false if no matching tag is found.
814
-	*/
815
-	private function getTagId($name) {
816
-		$key = $this->array_searchi($name, $this->tags);
817
-		if ($key !== false) {
818
-			return $this->tags[$key]->getId();
819
-		}
820
-		return false;
821
-	}
822
-
823
-	/**
824
-	* Get a tag by its name.
825
-	*
826
-	* @param string $name The tag name.
827
-	* @return integer|bool The tag object's offset within the $this->tags
828
-	*                      array or false if it doesn't exist.
829
-	*/
830
-	private function getTagByName($name) {
831
-		return $this->array_searchi($name, $this->tags, 'getName');
832
-	}
833
-
834
-	/**
835
-	* Get a tag by its ID.
836
-	*
837
-	* @param string $id The tag ID to look for.
838
-	* @return integer|bool The tag object's offset within the $this->tags
839
-	*                      array or false if it doesn't exist.
840
-	*/
841
-	private function getTagById($id) {
842
-		return $this->array_searchi($id, $this->tags, 'getId');
843
-	}
844
-
845
-	/**
846
-	* Returns an array mapping a given tag's properties to its values:
847
-	* ['id' => 0, 'name' = 'Tag', 'owner' = 'User', 'type' => 'tagtype']
848
-	*
849
-	* @param Tag $tag The tag that is going to be mapped
850
-	* @return array
851
-	*/
852
-	private function tagMap(Tag $tag) {
853
-		return [
854
-			'id'    => $tag->getId(),
855
-			'name'  => $tag->getName(),
856
-			'owner' => $tag->getOwner(),
857
-			'type'  => $tag->getType()
858
-		];
859
-	}
55
+    /**
56
+     * Tags
57
+     *
58
+     * @var array
59
+     */
60
+    private $tags = [];
61
+
62
+    /**
63
+     * Used for storing objectid/categoryname pairs while rescanning.
64
+     *
65
+     * @var array
66
+     */
67
+    private static $relations = [];
68
+
69
+    /**
70
+     * Type
71
+     *
72
+     * @var string
73
+     */
74
+    private $type;
75
+
76
+    /**
77
+     * User
78
+     *
79
+     * @var string
80
+     */
81
+    private $user;
82
+
83
+    /**
84
+     * Are we including tags for shared items?
85
+     *
86
+     * @var bool
87
+     */
88
+    private $includeShared = false;
89
+
90
+    /**
91
+     * The current user, plus any owners of the items shared with the current
92
+     * user, if $this->includeShared === true.
93
+     *
94
+     * @var array
95
+     */
96
+    private $owners = [];
97
+
98
+    /**
99
+     * The Mapper we're using to communicate our Tag objects to the database.
100
+     *
101
+     * @var TagMapper
102
+     */
103
+    private $mapper;
104
+
105
+    /**
106
+     * The sharing backend for objects of $this->type. Required if
107
+     * $this->includeShared === true to determine ownership of items.
108
+     *
109
+     * @var \OCP\Share_Backend
110
+     */
111
+    private $backend;
112
+
113
+    const TAG_TABLE = '*PREFIX*vcategory';
114
+    const RELATION_TABLE = '*PREFIX*vcategory_to_object';
115
+
116
+    /**
117
+     * Constructor.
118
+     *
119
+     * @param TagMapper $mapper Instance of the TagMapper abstraction layer.
120
+     * @param string $user The user whose data the object will operate on.
121
+     * @param string $type The type of items for which tags will be loaded.
122
+     * @param array $defaultTags Tags that should be created at construction.
123
+     * @param boolean $includeShared Whether to include tags for items shared with this user by others.
124
+     */
125
+    public function __construct(TagMapper $mapper, $user, $type, $defaultTags = [], $includeShared = false) {
126
+        $this->mapper = $mapper;
127
+        $this->user = $user;
128
+        $this->type = $type;
129
+        $this->includeShared = $includeShared;
130
+        $this->owners = [$this->user];
131
+        if ($this->includeShared) {
132
+            $this->owners = array_merge($this->owners, \OC\Share\Share::getSharedItemsOwners($this->user, $this->type, true));
133
+            $this->backend = \OC\Share\Share::getBackend($this->type);
134
+        }
135
+        $this->tags = $this->mapper->loadTags($this->owners, $this->type);
136
+
137
+        if(count($defaultTags) > 0 && count($this->tags) === 0) {
138
+            $this->addMultiple($defaultTags, true);
139
+        }
140
+    }
141
+
142
+    /**
143
+     * Check if any tags are saved for this type and user.
144
+     *
145
+     * @return boolean
146
+     */
147
+    public function isEmpty() {
148
+        return count($this->tags) === 0;
149
+    }
150
+
151
+    /**
152
+     * Returns an array mapping a given tag's properties to its values:
153
+     * ['id' => 0, 'name' = 'Tag', 'owner' = 'User', 'type' => 'tagtype']
154
+     *
155
+     * @param string $id The ID of the tag that is going to be mapped
156
+     * @return array|false
157
+     */
158
+    public function getTag($id) {
159
+        $key = $this->getTagById($id);
160
+        if ($key !== false) {
161
+            return $this->tagMap($this->tags[$key]);
162
+        }
163
+        return false;
164
+    }
165
+
166
+    /**
167
+     * Get the tags for a specific user.
168
+     *
169
+     * This returns an array with maps containing each tag's properties:
170
+     * [
171
+     * 	['id' => 0, 'name' = 'First tag', 'owner' = 'User', 'type' => 'tagtype'],
172
+     * 	['id' => 1, 'name' = 'Shared tag', 'owner' = 'Other user', 'type' => 'tagtype'],
173
+     * ]
174
+     *
175
+     * @return array
176
+     */
177
+    public function getTags() {
178
+        if(!count($this->tags)) {
179
+            return [];
180
+        }
181
+
182
+        usort($this->tags, function($a, $b) {
183
+            return strnatcasecmp($a->getName(), $b->getName());
184
+        });
185
+        $tagMap = [];
186
+
187
+        foreach($this->tags as $tag) {
188
+            if($tag->getName() !== ITags::TAG_FAVORITE) {
189
+                $tagMap[] = $this->tagMap($tag);
190
+            }
191
+        }
192
+        return $tagMap;
193
+
194
+    }
195
+
196
+    /**
197
+     * Return only the tags owned by the given user, omitting any tags shared
198
+     * by other users.
199
+     *
200
+     * @param string $user The user whose tags are to be checked.
201
+     * @return array An array of Tag objects.
202
+     */
203
+    public function getTagsForUser($user) {
204
+        return array_filter($this->tags,
205
+            function($tag) use($user) {
206
+                return $tag->getOwner() === $user;
207
+            }
208
+        );
209
+    }
210
+
211
+    /**
212
+     * Get the list of tags for the given ids.
213
+     *
214
+     * @param array $objIds array of object ids
215
+     * @return array|boolean of tags id as key to array of tag names
216
+     * or false if an error occurred
217
+     */
218
+    public function getTagsForObjects(array $objIds) {
219
+        $entries = [];
220
+
221
+        try {
222
+            $conn = \OC::$server->getDatabaseConnection();
223
+            $chunks = array_chunk($objIds, 900, false);
224
+            foreach ($chunks as $chunk) {
225
+                $result = $conn->executeQuery(
226
+                    'SELECT `category`, `categoryid`, `objid` ' .
227
+                    'FROM `' . self::RELATION_TABLE . '` r, `' . self::TAG_TABLE . '` ' .
228
+                    'WHERE `categoryid` = `id` AND `uid` = ? AND r.`type` = ? AND `objid` IN (?)',
229
+                    [$this->user, $this->type, $chunk],
230
+                    [null, null, IQueryBuilder::PARAM_INT_ARRAY]
231
+                );
232
+                while ($row = $result->fetch()) {
233
+                    $objId = (int)$row['objid'];
234
+                    if (!isset($entries[$objId])) {
235
+                        $entries[$objId] = [];
236
+                    }
237
+                    $entries[$objId][] = $row['category'];
238
+                }
239
+                if ($result === null) {
240
+                    \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
241
+                    return false;
242
+                }
243
+            }
244
+        } catch(\Exception $e) {
245
+            \OC::$server->getLogger()->logException($e, [
246
+                'message' => __METHOD__,
247
+                'level' => ILogger::ERROR,
248
+                'app' => 'core',
249
+            ]);
250
+            return false;
251
+        }
252
+
253
+        return $entries;
254
+    }
255
+
256
+    /**
257
+     * Get the a list if items tagged with $tag.
258
+     *
259
+     * Throws an exception if the tag could not be found.
260
+     *
261
+     * @param string $tag Tag id or name.
262
+     * @return array|false An array of object ids or false on error.
263
+     * @throws \Exception
264
+     */
265
+    public function getIdsForTag($tag) {
266
+        $result = null;
267
+        $tagId = false;
268
+        if(is_numeric($tag)) {
269
+            $tagId = $tag;
270
+        } elseif(is_string($tag)) {
271
+            $tag = trim($tag);
272
+            if($tag === '') {
273
+                \OCP\Util::writeLog('core', __METHOD__.', Cannot use empty tag names', ILogger::DEBUG);
274
+                return false;
275
+            }
276
+            $tagId = $this->getTagId($tag);
277
+        }
278
+
279
+        if($tagId === false) {
280
+            $l10n = \OC::$server->getL10N('core');
281
+            throw new \Exception(
282
+                $l10n->t('Could not find category "%s"', [$tag])
283
+            );
284
+        }
285
+
286
+        $ids = [];
287
+        $sql = 'SELECT `objid` FROM `' . self::RELATION_TABLE
288
+            . '` WHERE `categoryid` = ?';
289
+
290
+        try {
291
+            $stmt = \OC_DB::prepare($sql);
292
+            $result = $stmt->execute([$tagId]);
293
+            if ($result === null) {
294
+                \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
295
+                return false;
296
+            }
297
+        } catch(\Exception $e) {
298
+            \OC::$server->getLogger()->logException($e, [
299
+                'message' => __METHOD__,
300
+                'level' => ILogger::ERROR,
301
+                'app' => 'core',
302
+            ]);
303
+            return false;
304
+        }
305
+
306
+        if(!is_null($result)) {
307
+            while( $row = $result->fetchRow()) {
308
+                $id = (int)$row['objid'];
309
+
310
+                if ($this->includeShared) {
311
+                    // We have to check if we are really allowed to access the
312
+                    // items that are tagged with $tag. To that end, we ask the
313
+                    // corresponding sharing backend if the item identified by $id
314
+                    // is owned by any of $this->owners.
315
+                    foreach ($this->owners as $owner) {
316
+                        if ($this->backend->isValidSource($id, $owner)) {
317
+                            $ids[] = $id;
318
+                            break;
319
+                        }
320
+                    }
321
+                } else {
322
+                    $ids[] = $id;
323
+                }
324
+            }
325
+        }
326
+
327
+        return $ids;
328
+    }
329
+
330
+    /**
331
+     * Checks whether a tag is saved for the given user,
332
+     * disregarding the ones shared with him or her.
333
+     *
334
+     * @param string $name The tag name to check for.
335
+     * @param string $user The user whose tags are to be checked.
336
+     * @return bool
337
+     */
338
+    public function userHasTag($name, $user) {
339
+        $key = $this->array_searchi($name, $this->getTagsForUser($user));
340
+        return ($key !== false) ? $this->tags[$key]->getId() : false;
341
+    }
342
+
343
+    /**
344
+     * Checks whether a tag is saved for or shared with the current user.
345
+     *
346
+     * @param string $name The tag name to check for.
347
+     * @return bool
348
+     */
349
+    public function hasTag($name) {
350
+        return $this->getTagId($name) !== false;
351
+    }
352
+
353
+    /**
354
+     * Add a new tag.
355
+     *
356
+     * @param string $name A string with a name of the tag
357
+     * @return false|int the id of the added tag or false on error.
358
+     */
359
+    public function add($name) {
360
+        $name = trim($name);
361
+
362
+        if($name === '') {
363
+            \OCP\Util::writeLog('core', __METHOD__.', Cannot add an empty tag', ILogger::DEBUG);
364
+            return false;
365
+        }
366
+        if($this->userHasTag($name, $this->user)) {
367
+            \OCP\Util::writeLog('core', __METHOD__.', name: ' . $name. ' exists already', ILogger::DEBUG);
368
+            return false;
369
+        }
370
+        try {
371
+            $tag = new Tag($this->user, $this->type, $name);
372
+            $tag = $this->mapper->insert($tag);
373
+            $this->tags[] = $tag;
374
+        } catch(\Exception $e) {
375
+            \OC::$server->getLogger()->logException($e, [
376
+                'message' => __METHOD__,
377
+                'level' => ILogger::ERROR,
378
+                'app' => 'core',
379
+            ]);
380
+            return false;
381
+        }
382
+        \OCP\Util::writeLog('core', __METHOD__.', id: ' . $tag->getId(), ILogger::DEBUG);
383
+        return $tag->getId();
384
+    }
385
+
386
+    /**
387
+     * Rename tag.
388
+     *
389
+     * @param string|integer $from The name or ID of the existing tag
390
+     * @param string $to The new name of the tag.
391
+     * @return bool
392
+     */
393
+    public function rename($from, $to) {
394
+        $from = trim($from);
395
+        $to = trim($to);
396
+
397
+        if($to === '' || $from === '') {
398
+            \OCP\Util::writeLog('core', __METHOD__.', Cannot use empty tag names', ILogger::DEBUG);
399
+            return false;
400
+        }
401
+
402
+        if (is_numeric($from)) {
403
+            $key = $this->getTagById($from);
404
+        } else {
405
+            $key = $this->getTagByName($from);
406
+        }
407
+        if($key === false) {
408
+            \OCP\Util::writeLog('core', __METHOD__.', tag: ' . $from. ' does not exist', ILogger::DEBUG);
409
+            return false;
410
+        }
411
+        $tag = $this->tags[$key];
412
+
413
+        if($this->userHasTag($to, $tag->getOwner())) {
414
+            \OCP\Util::writeLog('core', __METHOD__.', A tag named ' . $to. ' already exists for user ' . $tag->getOwner() . '.', ILogger::DEBUG);
415
+            return false;
416
+        }
417
+
418
+        try {
419
+            $tag->setName($to);
420
+            $this->tags[$key] = $this->mapper->update($tag);
421
+        } catch(\Exception $e) {
422
+            \OC::$server->getLogger()->logException($e, [
423
+                'message' => __METHOD__,
424
+                'level' => ILogger::ERROR,
425
+                'app' => 'core',
426
+            ]);
427
+            return false;
428
+        }
429
+        return true;
430
+    }
431
+
432
+    /**
433
+     * Add a list of new tags.
434
+     *
435
+     * @param string[] $names A string with a name or an array of strings containing
436
+     * the name(s) of the tag(s) to add.
437
+     * @param bool $sync When true, save the tags
438
+     * @param int|null $id int Optional object id to add to this|these tag(s)
439
+     * @return bool Returns false on error.
440
+     */
441
+    public function addMultiple($names, $sync=false, $id = null) {
442
+        if(!is_array($names)) {
443
+            $names = [$names];
444
+        }
445
+        $names = array_map('trim', $names);
446
+        array_filter($names);
447
+
448
+        $newones = [];
449
+        foreach($names as $name) {
450
+            if(!$this->hasTag($name) && $name !== '') {
451
+                $newones[] = new Tag($this->user, $this->type, $name);
452
+            }
453
+            if(!is_null($id) ) {
454
+                // Insert $objectid, $categoryid  pairs if not exist.
455
+                self::$relations[] = ['objid' => $id, 'tag' => $name];
456
+            }
457
+        }
458
+        $this->tags = array_merge($this->tags, $newones);
459
+        if($sync === true) {
460
+            $this->save();
461
+        }
462
+
463
+        return true;
464
+    }
465
+
466
+    /**
467
+     * Save the list of tags and their object relations
468
+     */
469
+    protected function save() {
470
+        if(is_array($this->tags)) {
471
+            foreach($this->tags as $tag) {
472
+                try {
473
+                    if (!$this->mapper->tagExists($tag)) {
474
+                        $this->mapper->insert($tag);
475
+                    }
476
+                } catch(\Exception $e) {
477
+                    \OC::$server->getLogger()->logException($e, [
478
+                        'message' => __METHOD__,
479
+                        'level' => ILogger::ERROR,
480
+                        'app' => 'core',
481
+                    ]);
482
+                }
483
+            }
484
+
485
+            // reload tags to get the proper ids.
486
+            $this->tags = $this->mapper->loadTags($this->owners, $this->type);
487
+            \OCP\Util::writeLog('core', __METHOD__.', tags: ' . print_r($this->tags, true),
488
+                ILogger::DEBUG);
489
+            // Loop through temporarily cached objectid/tagname pairs
490
+            // and save relations.
491
+            $tags = $this->tags;
492
+            // For some reason this is needed or array_search(i) will return 0..?
493
+            ksort($tags);
494
+            $dbConnection = \OC::$server->getDatabaseConnection();
495
+            foreach(self::$relations as $relation) {
496
+                $tagId = $this->getTagId($relation['tag']);
497
+                \OCP\Util::writeLog('core', __METHOD__ . 'catid, ' . $relation['tag'] . ' ' . $tagId, ILogger::DEBUG);
498
+                if($tagId) {
499
+                    try {
500
+                        $dbConnection->insertIfNotExist(self::RELATION_TABLE,
501
+                            [
502
+                                'objid' => $relation['objid'],
503
+                                'categoryid' => $tagId,
504
+                                'type' => $this->type,
505
+                                ]);
506
+                    } catch(\Exception $e) {
507
+                        \OC::$server->getLogger()->logException($e, [
508
+                            'message' => __METHOD__,
509
+                            'level' => ILogger::ERROR,
510
+                            'app' => 'core',
511
+                        ]);
512
+                    }
513
+                }
514
+            }
515
+            self::$relations = []; // reset
516
+        } else {
517
+            \OCP\Util::writeLog('core', __METHOD__.', $this->tags is not an array! '
518
+                . print_r($this->tags, true), ILogger::ERROR);
519
+        }
520
+    }
521
+
522
+    /**
523
+     * Delete tags and tag/object relations for a user.
524
+     *
525
+     * For hooking up on post_deleteUser
526
+     *
527
+     * @param array $arguments
528
+     */
529
+    public static function post_deleteUser($arguments) {
530
+        // Find all objectid/tagId pairs.
531
+        $result = null;
532
+        try {
533
+            $stmt = \OC_DB::prepare('SELECT `id` FROM `' . self::TAG_TABLE . '` '
534
+                . 'WHERE `uid` = ?');
535
+            $result = $stmt->execute([$arguments['uid']]);
536
+            if ($result === null) {
537
+                \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
538
+            }
539
+        } catch(\Exception $e) {
540
+            \OC::$server->getLogger()->logException($e, [
541
+                'message' => __METHOD__,
542
+                'level' => ILogger::ERROR,
543
+                'app' => 'core',
544
+            ]);
545
+        }
546
+
547
+        if(!is_null($result)) {
548
+            try {
549
+                $stmt = \OC_DB::prepare('DELETE FROM `' . self::RELATION_TABLE . '` '
550
+                    . 'WHERE `categoryid` = ?');
551
+                while( $row = $result->fetchRow()) {
552
+                    try {
553
+                        $stmt->execute([$row['id']]);
554
+                    } catch(\Exception $e) {
555
+                        \OC::$server->getLogger()->logException($e, [
556
+                            'message' => __METHOD__,
557
+                            'level' => ILogger::ERROR,
558
+                            'app' => 'core',
559
+                        ]);
560
+                    }
561
+                }
562
+            } catch(\Exception $e) {
563
+                \OC::$server->getLogger()->logException($e, [
564
+                    'message' => __METHOD__,
565
+                    'level' => ILogger::ERROR,
566
+                    'app' => 'core',
567
+                ]);
568
+            }
569
+        }
570
+        try {
571
+            $stmt = \OC_DB::prepare('DELETE FROM `' . self::TAG_TABLE . '` '
572
+                . 'WHERE `uid` = ?');
573
+            $result = $stmt->execute([$arguments['uid']]);
574
+            if ($result === null) {
575
+                \OCP\Util::writeLog('core', __METHOD__. ', DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
576
+            }
577
+        } catch(\Exception $e) {
578
+            \OC::$server->getLogger()->logException($e, [
579
+                'message' => __METHOD__,
580
+                'level' => ILogger::ERROR,
581
+                'app' => 'core',
582
+            ]);
583
+        }
584
+    }
585
+
586
+    /**
587
+     * Delete tag/object relations from the db
588
+     *
589
+     * @param array $ids The ids of the objects
590
+     * @return boolean Returns false on error.
591
+     */
592
+    public function purgeObjects(array $ids) {
593
+        if(count($ids) === 0) {
594
+            // job done ;)
595
+            return true;
596
+        }
597
+        $updates = $ids;
598
+        try {
599
+            $query = 'DELETE FROM `' . self::RELATION_TABLE . '` ';
600
+            $query .= 'WHERE `objid` IN (' . str_repeat('?,', count($ids)-1) . '?) ';
601
+            $query .= 'AND `type`= ?';
602
+            $updates[] = $this->type;
603
+            $stmt = \OC_DB::prepare($query);
604
+            $result = $stmt->execute($updates);
605
+            if ($result === null) {
606
+                \OCP\Util::writeLog('core', __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(), ILogger::ERROR);
607
+                return false;
608
+            }
609
+        } catch(\Exception $e) {
610
+            \OC::$server->getLogger()->logException($e, [
611
+                'message' => __METHOD__,
612
+                'level' => ILogger::ERROR,
613
+                'app' => 'core',
614
+            ]);
615
+            return false;
616
+        }
617
+        return true;
618
+    }
619
+
620
+    /**
621
+     * Get favorites for an object type
622
+     *
623
+     * @return array|false An array of object ids.
624
+     */
625
+    public function getFavorites() {
626
+        if(!$this->userHasTag(ITags::TAG_FAVORITE, $this->user)) {
627
+            return [];
628
+        }
629
+
630
+        try {
631
+            return $this->getIdsForTag(ITags::TAG_FAVORITE);
632
+        } catch(\Exception $e) {
633
+            \OC::$server->getLogger()->logException($e, [
634
+                'message' => __METHOD__,
635
+                'level' => ILogger::ERROR,
636
+                'app' => 'core',
637
+            ]);
638
+            return [];
639
+        }
640
+    }
641
+
642
+    /**
643
+     * Add an object to favorites
644
+     *
645
+     * @param int $objid The id of the object
646
+     * @return boolean
647
+     */
648
+    public function addToFavorites($objid) {
649
+        if(!$this->userHasTag(ITags::TAG_FAVORITE, $this->user)) {
650
+            $this->add(ITags::TAG_FAVORITE);
651
+        }
652
+        return $this->tagAs($objid, ITags::TAG_FAVORITE);
653
+    }
654
+
655
+    /**
656
+     * Remove an object from favorites
657
+     *
658
+     * @param int $objid The id of the object
659
+     * @return boolean
660
+     */
661
+    public function removeFromFavorites($objid) {
662
+        return $this->unTag($objid, ITags::TAG_FAVORITE);
663
+    }
664
+
665
+    /**
666
+     * Creates a tag/object relation.
667
+     *
668
+     * @param int $objid The id of the object
669
+     * @param string $tag The id or name of the tag
670
+     * @return boolean Returns false on error.
671
+     */
672
+    public function tagAs($objid, $tag) {
673
+        if(is_string($tag) && !is_numeric($tag)) {
674
+            $tag = trim($tag);
675
+            if($tag === '') {
676
+                \OCP\Util::writeLog('core', __METHOD__.', Cannot add an empty tag', ILogger::DEBUG);
677
+                return false;
678
+            }
679
+            if(!$this->hasTag($tag)) {
680
+                $this->add($tag);
681
+            }
682
+            $tagId =  $this->getTagId($tag);
683
+        } else {
684
+            $tagId = $tag;
685
+        }
686
+        try {
687
+            \OC::$server->getDatabaseConnection()->insertIfNotExist(self::RELATION_TABLE,
688
+                [
689
+                    'objid' => $objid,
690
+                    'categoryid' => $tagId,
691
+                    'type' => $this->type,
692
+                ]);
693
+        } catch(\Exception $e) {
694
+            \OC::$server->getLogger()->logException($e, [
695
+                'message' => __METHOD__,
696
+                'level' => ILogger::ERROR,
697
+                'app' => 'core',
698
+            ]);
699
+            return false;
700
+        }
701
+        return true;
702
+    }
703
+
704
+    /**
705
+     * Delete single tag/object relation from the db
706
+     *
707
+     * @param int $objid The id of the object
708
+     * @param string $tag The id or name of the tag
709
+     * @return boolean
710
+     */
711
+    public function unTag($objid, $tag) {
712
+        if(is_string($tag) && !is_numeric($tag)) {
713
+            $tag = trim($tag);
714
+            if($tag === '') {
715
+                \OCP\Util::writeLog('core', __METHOD__.', Tag name is empty', ILogger::DEBUG);
716
+                return false;
717
+            }
718
+            $tagId =  $this->getTagId($tag);
719
+        } else {
720
+            $tagId = $tag;
721
+        }
722
+
723
+        try {
724
+            $sql = 'DELETE FROM `' . self::RELATION_TABLE . '` '
725
+                    . 'WHERE `objid` = ? AND `categoryid` = ? AND `type` = ?';
726
+            $stmt = \OC_DB::prepare($sql);
727
+            $stmt->execute([$objid, $tagId, $this->type]);
728
+        } catch(\Exception $e) {
729
+            \OC::$server->getLogger()->logException($e, [
730
+                'message' => __METHOD__,
731
+                'level' => ILogger::ERROR,
732
+                'app' => 'core',
733
+            ]);
734
+            return false;
735
+        }
736
+        return true;
737
+    }
738
+
739
+    /**
740
+     * Delete tags from the database.
741
+     *
742
+     * @param string[]|integer[] $names An array of tags (names or IDs) to delete
743
+     * @return bool Returns false on error
744
+     */
745
+    public function delete($names) {
746
+        if(!is_array($names)) {
747
+            $names = [$names];
748
+        }
749
+
750
+        $names = array_map('trim', $names);
751
+        array_filter($names);
752
+
753
+        \OCP\Util::writeLog('core', __METHOD__ . ', before: '
754
+            . print_r($this->tags, true), ILogger::DEBUG);
755
+        foreach($names as $name) {
756
+            $id = null;
757
+
758
+            if (is_numeric($name)) {
759
+                $key = $this->getTagById($name);
760
+            } else {
761
+                $key = $this->getTagByName($name);
762
+            }
763
+            if ($key !== false) {
764
+                $tag = $this->tags[$key];
765
+                $id = $tag->getId();
766
+                unset($this->tags[$key]);
767
+                $this->mapper->delete($tag);
768
+            } else {
769
+                \OCP\Util::writeLog('core', __METHOD__ . 'Cannot delete tag ' . $name
770
+                    . ': not found.', ILogger::ERROR);
771
+            }
772
+            if(!is_null($id) && $id !== false) {
773
+                try {
774
+                    $sql = 'DELETE FROM `' . self::RELATION_TABLE . '` '
775
+                            . 'WHERE `categoryid` = ?';
776
+                    $stmt = \OC_DB::prepare($sql);
777
+                    $result = $stmt->execute([$id]);
778
+                    if ($result === null) {
779
+                        \OCP\Util::writeLog('core',
780
+                            __METHOD__. 'DB error: ' . \OC::$server->getDatabaseConnection()->getError(),
781
+                            ILogger::ERROR);
782
+                        return false;
783
+                    }
784
+                } catch(\Exception $e) {
785
+                    \OC::$server->getLogger()->logException($e, [
786
+                        'message' => __METHOD__,
787
+                        'level' => ILogger::ERROR,
788
+                        'app' => 'core',
789
+                    ]);
790
+                    return false;
791
+                }
792
+            }
793
+        }
794
+        return true;
795
+    }
796
+
797
+    // case-insensitive array_search
798
+    protected function array_searchi($needle, $haystack, $mem='getName') {
799
+        if(!is_array($haystack)) {
800
+            return false;
801
+        }
802
+        return array_search(strtolower($needle), array_map(
803
+            function($tag) use($mem) {
804
+                return strtolower(call_user_func([$tag, $mem]));
805
+            }, $haystack)
806
+        );
807
+    }
808
+
809
+    /**
810
+     * Get a tag's ID.
811
+     *
812
+     * @param string $name The tag name to look for.
813
+     * @return string|bool The tag's id or false if no matching tag is found.
814
+     */
815
+    private function getTagId($name) {
816
+        $key = $this->array_searchi($name, $this->tags);
817
+        if ($key !== false) {
818
+            return $this->tags[$key]->getId();
819
+        }
820
+        return false;
821
+    }
822
+
823
+    /**
824
+     * Get a tag by its name.
825
+     *
826
+     * @param string $name The tag name.
827
+     * @return integer|bool The tag object's offset within the $this->tags
828
+     *                      array or false if it doesn't exist.
829
+     */
830
+    private function getTagByName($name) {
831
+        return $this->array_searchi($name, $this->tags, 'getName');
832
+    }
833
+
834
+    /**
835
+     * Get a tag by its ID.
836
+     *
837
+     * @param string $id The tag ID to look for.
838
+     * @return integer|bool The tag object's offset within the $this->tags
839
+     *                      array or false if it doesn't exist.
840
+     */
841
+    private function getTagById($id) {
842
+        return $this->array_searchi($id, $this->tags, 'getId');
843
+    }
844
+
845
+    /**
846
+     * Returns an array mapping a given tag's properties to its values:
847
+     * ['id' => 0, 'name' = 'Tag', 'owner' = 'User', 'type' => 'tagtype']
848
+     *
849
+     * @param Tag $tag The tag that is going to be mapped
850
+     * @return array
851
+     */
852
+    private function tagMap(Tag $tag) {
853
+        return [
854
+            'id'    => $tag->getId(),
855
+            'name'  => $tag->getName(),
856
+            'owner' => $tag->getOwner(),
857
+            'type'  => $tag->getType()
858
+        ];
859
+    }
860 860
 }
Please login to merge, or discard this patch.
lib/private/Setup.php 1 patch
Indentation   +545 added lines, -545 removed lines patch added patch discarded remove patch
@@ -63,549 +63,549 @@
 block discarded – undo
63 63
 use OCP\Security\ISecureRandom;
64 64
 
65 65
 class Setup {
66
-	/** @var SystemConfig */
67
-	protected $config;
68
-	/** @var IniGetWrapper */
69
-	protected $iniWrapper;
70
-	/** @var IL10N */
71
-	protected $l10n;
72
-	/** @var Defaults */
73
-	protected $defaults;
74
-	/** @var ILogger */
75
-	protected $logger;
76
-	/** @var ISecureRandom */
77
-	protected $random;
78
-	/** @var Installer */
79
-	protected $installer;
80
-
81
-	/**
82
-	 * @param SystemConfig $config
83
-	 * @param IniGetWrapper $iniWrapper
84
-	 * @param IL10N $l10n
85
-	 * @param Defaults $defaults
86
-	 * @param ILogger $logger
87
-	 * @param ISecureRandom $random
88
-	 * @param Installer $installer
89
-	 */
90
-	public function __construct(
91
-		SystemConfig $config,
92
-		IniGetWrapper $iniWrapper,
93
-		IL10N $l10n,
94
-		Defaults $defaults,
95
-		ILogger $logger,
96
-		ISecureRandom $random,
97
-		Installer $installer
98
-	) {
99
-		$this->config = $config;
100
-		$this->iniWrapper = $iniWrapper;
101
-		$this->l10n = $l10n;
102
-		$this->defaults = $defaults;
103
-		$this->logger = $logger;
104
-		$this->random = $random;
105
-		$this->installer = $installer;
106
-	}
107
-
108
-	static protected $dbSetupClasses = [
109
-		'mysql' => \OC\Setup\MySQL::class,
110
-		'pgsql' => \OC\Setup\PostgreSQL::class,
111
-		'oci' => \OC\Setup\OCI::class,
112
-		'sqlite' => \OC\Setup\Sqlite::class,
113
-		'sqlite3' => \OC\Setup\Sqlite::class,
114
-	];
115
-
116
-	/**
117
-	 * Wrapper around the "class_exists" PHP function to be able to mock it
118
-	 *
119
-	 * @param string $name
120
-	 * @return bool
121
-	 */
122
-	protected function class_exists($name) {
123
-		return class_exists($name);
124
-	}
125
-
126
-	/**
127
-	 * Wrapper around the "is_callable" PHP function to be able to mock it
128
-	 *
129
-	 * @param string $name
130
-	 * @return bool
131
-	 */
132
-	protected function is_callable($name) {
133
-		return is_callable($name);
134
-	}
135
-
136
-	/**
137
-	 * Wrapper around \PDO::getAvailableDrivers
138
-	 *
139
-	 * @return array
140
-	 */
141
-	protected function getAvailableDbDriversForPdo() {
142
-		return \PDO::getAvailableDrivers();
143
-	}
144
-
145
-	/**
146
-	 * Get the available and supported databases of this instance
147
-	 *
148
-	 * @param bool $allowAllDatabases
149
-	 * @return array
150
-	 * @throws Exception
151
-	 */
152
-	public function getSupportedDatabases($allowAllDatabases = false) {
153
-		$availableDatabases = [
154
-			'sqlite' => [
155
-				'type' => 'pdo',
156
-				'call' => 'sqlite',
157
-				'name' => 'SQLite',
158
-			],
159
-			'mysql' => [
160
-				'type' => 'pdo',
161
-				'call' => 'mysql',
162
-				'name' => 'MySQL/MariaDB',
163
-			],
164
-			'pgsql' => [
165
-				'type' => 'pdo',
166
-				'call' => 'pgsql',
167
-				'name' => 'PostgreSQL',
168
-			],
169
-			'oci' => [
170
-				'type' => 'function',
171
-				'call' => 'oci_connect',
172
-				'name' => 'Oracle',
173
-			],
174
-		];
175
-		if ($allowAllDatabases) {
176
-			$configuredDatabases = array_keys($availableDatabases);
177
-		} else {
178
-			$configuredDatabases = $this->config->getValue('supportedDatabases',
179
-				['sqlite', 'mysql', 'pgsql']);
180
-		}
181
-		if (!is_array($configuredDatabases)) {
182
-			throw new Exception('Supported databases are not properly configured.');
183
-		}
184
-
185
-		$supportedDatabases = [];
186
-
187
-		foreach ($configuredDatabases as $database) {
188
-			if (array_key_exists($database, $availableDatabases)) {
189
-				$working = false;
190
-				$type = $availableDatabases[$database]['type'];
191
-				$call = $availableDatabases[$database]['call'];
192
-
193
-				if ($type === 'function') {
194
-					$working = $this->is_callable($call);
195
-				} elseif ($type === 'pdo') {
196
-					$working = in_array($call, $this->getAvailableDbDriversForPdo(), true);
197
-				}
198
-				if ($working) {
199
-					$supportedDatabases[$database] = $availableDatabases[$database]['name'];
200
-				}
201
-			}
202
-		}
203
-
204
-		return $supportedDatabases;
205
-	}
206
-
207
-	/**
208
-	 * Gathers system information like database type and does
209
-	 * a few system checks.
210
-	 *
211
-	 * @return array of system info, including an "errors" value
212
-	 * in case of errors/warnings
213
-	 */
214
-	public function getSystemInfo($allowAllDatabases = false) {
215
-		$databases = $this->getSupportedDatabases($allowAllDatabases);
216
-
217
-		$dataDir = $this->config->getValue('datadirectory', \OC::$SERVERROOT . '/data');
218
-
219
-		$errors = [];
220
-
221
-		// Create data directory to test whether the .htaccess works
222
-		// Notice that this is not necessarily the same data directory as the one
223
-		// that will effectively be used.
224
-		if (!file_exists($dataDir)) {
225
-			@mkdir($dataDir);
226
-		}
227
-		$htAccessWorking = true;
228
-		if (is_dir($dataDir) && is_writable($dataDir)) {
229
-			// Protect data directory here, so we can test if the protection is working
230
-			self::protectDataDirectory();
231
-
232
-			try {
233
-				$util = new \OC_Util();
234
-				$htAccessWorking = $util->isHtaccessWorking(\OC::$server->getConfig());
235
-			} catch (\OC\HintException $e) {
236
-				$errors[] = [
237
-					'error' => $e->getMessage(),
238
-					'hint' => $e->getHint(),
239
-				];
240
-				$htAccessWorking = false;
241
-			}
242
-		}
243
-
244
-		if (\OC_Util::runningOnMac()) {
245
-			$errors[] = [
246
-				'error' => $this->l10n->t(
247
-					'Mac OS X is not supported and %s will not work properly on this platform. ' .
248
-					'Use it at your own risk! ',
249
-					[$this->defaults->getName()]
250
-				),
251
-				'hint' => $this->l10n->t('For the best results, please consider using a GNU/Linux server instead.'),
252
-			];
253
-		}
254
-
255
-		if ($this->iniWrapper->getString('open_basedir') !== '' && PHP_INT_SIZE === 4) {
256
-			$errors[] = [
257
-				'error' => $this->l10n->t(
258
-					'It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. ' .
259
-					'This will lead to problems with files over 4 GB and is highly discouraged.',
260
-					[$this->defaults->getName()]
261
-				),
262
-				'hint' => $this->l10n->t('Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP.'),
263
-			];
264
-		}
265
-
266
-		return [
267
-			'hasSQLite' => isset($databases['sqlite']),
268
-			'hasMySQL' => isset($databases['mysql']),
269
-			'hasPostgreSQL' => isset($databases['pgsql']),
270
-			'hasOracle' => isset($databases['oci']),
271
-			'databases' => $databases,
272
-			'directory' => $dataDir,
273
-			'htaccessWorking' => $htAccessWorking,
274
-			'errors' => $errors,
275
-		];
276
-	}
277
-
278
-	/**
279
-	 * @param $options
280
-	 * @return array
281
-	 */
282
-	public function install($options) {
283
-		$l = $this->l10n;
284
-
285
-		$error = [];
286
-		$dbType = $options['dbtype'];
287
-
288
-		if (empty($options['adminlogin'])) {
289
-			$error[] = $l->t('Set an admin username.');
290
-		}
291
-		if (empty($options['adminpass'])) {
292
-			$error[] = $l->t('Set an admin password.');
293
-		}
294
-		if (empty($options['directory'])) {
295
-			$options['directory'] = \OC::$SERVERROOT . "/data";
296
-		}
297
-
298
-		if (!isset(self::$dbSetupClasses[$dbType])) {
299
-			$dbType = 'sqlite';
300
-		}
301
-
302
-		$username = htmlspecialchars_decode($options['adminlogin']);
303
-		$password = htmlspecialchars_decode($options['adminpass']);
304
-		$dataDir = htmlspecialchars_decode($options['directory']);
305
-
306
-		$class = self::$dbSetupClasses[$dbType];
307
-		/** @var \OC\Setup\AbstractDatabase $dbSetup */
308
-		$dbSetup = new $class($l, $this->config, $this->logger, $this->random);
309
-		$error = array_merge($error, $dbSetup->validate($options));
310
-
311
-		// validate the data directory
312
-		if ((!is_dir($dataDir) && !mkdir($dataDir)) || !is_writable($dataDir)) {
313
-			$error[] = $l->t("Can't create or write into the data directory %s", [$dataDir]);
314
-		}
315
-
316
-		if (!empty($error)) {
317
-			return $error;
318
-		}
319
-
320
-		$request = \OC::$server->getRequest();
321
-
322
-		//no errors, good
323
-		if (isset($options['trusted_domains'])
324
-			&& is_array($options['trusted_domains'])) {
325
-			$trustedDomains = $options['trusted_domains'];
326
-		} else {
327
-			$trustedDomains = [$request->getInsecureServerHost()];
328
-		}
329
-
330
-		//use sqlite3 when available, otherwise sqlite2 will be used.
331
-		if ($dbType === 'sqlite' && class_exists('SQLite3')) {
332
-			$dbType = 'sqlite3';
333
-		}
334
-
335
-		//generate a random salt that is used to salt the local user passwords
336
-		$salt = $this->random->generate(30);
337
-		// generate a secret
338
-		$secret = $this->random->generate(48);
339
-
340
-		//write the config file
341
-		$newConfigValues = [
342
-			'passwordsalt' => $salt,
343
-			'secret' => $secret,
344
-			'trusted_domains' => $trustedDomains,
345
-			'datadirectory' => $dataDir,
346
-			'dbtype' => $dbType,
347
-			'version' => implode('.', \OCP\Util::getVersion()),
348
-		];
349
-
350
-		if ($this->config->getValue('overwrite.cli.url', null) === null) {
351
-			$newConfigValues['overwrite.cli.url'] = $request->getServerProtocol() . '://' . $request->getInsecureServerHost() . \OC::$WEBROOT;
352
-		}
353
-
354
-		$this->config->setValues($newConfigValues);
355
-
356
-		$dbSetup->initialize($options);
357
-		try {
358
-			$dbSetup->setupDatabase($username);
359
-		} catch (\OC\DatabaseSetupException $e) {
360
-			$error[] = [
361
-				'error' => $e->getMessage(),
362
-				'hint' => $e->getHint(),
363
-			];
364
-			return $error;
365
-		} catch (Exception $e) {
366
-			$error[] = [
367
-				'error' => 'Error while trying to create admin user: ' . $e->getMessage(),
368
-				'hint' => '',
369
-			];
370
-			return $error;
371
-		}
372
-		try {
373
-			// apply necessary migrations
374
-			$dbSetup->runMigrations();
375
-		} catch (Exception $e) {
376
-			$error[] = [
377
-				'error' => 'Error while trying to initialise the database: ' . $e->getMessage(),
378
-				'hint' => '',
379
-			];
380
-			return $error;
381
-		}
382
-
383
-		//create the user and group
384
-		$user = null;
385
-		try {
386
-			$user = \OC::$server->getUserManager()->createUser($username, $password);
387
-			if (!$user) {
388
-				$error[] = "User <$username> could not be created.";
389
-			}
390
-		} catch (Exception $exception) {
391
-			$error[] = $exception->getMessage();
392
-		}
393
-
394
-		if (empty($error)) {
395
-			$config = \OC::$server->getConfig();
396
-			$config->setAppValue('core', 'installedat', microtime(true));
397
-			$config->setAppValue('core', 'lastupdatedat', microtime(true));
398
-			$config->setAppValue('core', 'vendor', $this->getVendor());
399
-
400
-			$group = \OC::$server->getGroupManager()->createGroup('admin');
401
-			if ($group instanceof IGroup) {
402
-				$group->addUser($user);
403
-			}
404
-
405
-			// Install shipped apps and specified app bundles
406
-			Installer::installShippedApps();
407
-			$bundleFetcher = new BundleFetcher(\OC::$server->getL10N('lib'));
408
-			$defaultInstallationBundles = $bundleFetcher->getDefaultInstallationBundle();
409
-			foreach ($defaultInstallationBundles as $bundle) {
410
-				try {
411
-					$this->installer->installAppBundle($bundle);
412
-				} catch (Exception $e) {
413
-				}
414
-			}
415
-
416
-			// create empty file in data dir, so we can later find
417
-			// out that this is indeed an ownCloud data directory
418
-			file_put_contents($config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/.ocdata', '');
419
-
420
-			// Update .htaccess files
421
-			self::updateHtaccess();
422
-			self::protectDataDirectory();
423
-
424
-			self::installBackgroundJobs();
425
-
426
-			//and we are done
427
-			$config->setSystemValue('installed', true);
428
-
429
-			// Create a session token for the newly created user
430
-			// The token provider requires a working db, so it's not injected on setup
431
-			/* @var $userSession User\Session */
432
-			$userSession = \OC::$server->getUserSession();
433
-			$defaultTokenProvider = \OC::$server->query(DefaultTokenProvider::class);
434
-			$userSession->setTokenProvider($defaultTokenProvider);
435
-			$userSession->login($username, $password);
436
-			$userSession->createSessionToken($request, $userSession->getUser()->getUID(), $username, $password);
437
-
438
-			$session = $userSession->getSession();
439
-			$session->set('last-password-confirm', \OC::$server->query(ITimeFactory::class)->getTime());
440
-
441
-			// Set email for admin
442
-			if (!empty($options['adminemail'])) {
443
-				$config->setUserValue($user->getUID(), 'settings', 'email', $options['adminemail']);
444
-			}
445
-		}
446
-
447
-		return $error;
448
-	}
449
-
450
-	public static function installBackgroundJobs() {
451
-		$jobList = \OC::$server->getJobList();
452
-		$jobList->add(DefaultTokenCleanupJob::class);
453
-		$jobList->add(Rotate::class);
454
-		$jobList->add(BackgroundCleanupJob::class);
455
-	}
456
-
457
-	/**
458
-	 * @return string Absolute path to htaccess
459
-	 */
460
-	private function pathToHtaccess() {
461
-		return \OC::$SERVERROOT . '/.htaccess';
462
-	}
463
-
464
-	/**
465
-	 * Find webroot from config
466
-	 *
467
-	 * @param SystemConfig $config
468
-	 * @return string
469
-	 * @throws InvalidArgumentException when invalid value for overwrite.cli.url
470
-	 */
471
-	private static function findWebRoot(SystemConfig $config): string {
472
-		// For CLI read the value from overwrite.cli.url
473
-		if (\OC::$CLI) {
474
-			$webRoot = $config->getValue('overwrite.cli.url', '');
475
-			if ($webRoot === '') {
476
-				throw new InvalidArgumentException('overwrite.cli.url is empty');
477
-			}
478
-			if (!filter_var($webRoot, FILTER_VALIDATE_URL)) {
479
-				throw new InvalidArgumentException('invalid value for overwrite.cli.url');
480
-			}
481
-			$webRoot = rtrim(parse_url($webRoot, PHP_URL_PATH), '/');
482
-		} else {
483
-			$webRoot = !empty(\OC::$WEBROOT) ? \OC::$WEBROOT : '/';
484
-		}
485
-
486
-		return $webRoot;
487
-	}
488
-
489
-	/**
490
-	 * Append the correct ErrorDocument path for Apache hosts
491
-	 *
492
-	 * @return bool True when success, False otherwise
493
-	 * @throws \OCP\AppFramework\QueryException
494
-	 */
495
-	public static function updateHtaccess() {
496
-		$config = \OC::$server->getSystemConfig();
497
-
498
-		try {
499
-			$webRoot = self::findWebRoot($config);
500
-		} catch (InvalidArgumentException $e) {
501
-			return false;
502
-		}
503
-
504
-		$setupHelper = new \OC\Setup(
505
-			$config,
506
-			\OC::$server->getIniWrapper(),
507
-			\OC::$server->getL10N('lib'),
508
-			\OC::$server->query(Defaults::class),
509
-			\OC::$server->getLogger(),
510
-			\OC::$server->getSecureRandom(),
511
-			\OC::$server->query(Installer::class)
512
-		);
513
-
514
-		$htaccessContent = file_get_contents($setupHelper->pathToHtaccess());
515
-		$content = "#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####\n";
516
-		$htaccessContent = explode($content, $htaccessContent, 2)[0];
517
-
518
-		//custom 403 error page
519
-		$content .= "\nErrorDocument 403 " . $webRoot . '/';
520
-
521
-		//custom 404 error page
522
-		$content .= "\nErrorDocument 404 " . $webRoot . '/';
523
-
524
-		// Add rewrite rules if the RewriteBase is configured
525
-		$rewriteBase = $config->getValue('htaccess.RewriteBase', '');
526
-		if ($rewriteBase !== '') {
527
-			$content .= "\n<IfModule mod_rewrite.c>";
528
-			$content .= "\n  Options -MultiViews";
529
-			$content .= "\n  RewriteRule ^core/js/oc.js$ index.php [PT,E=PATH_INFO:$1]";
530
-			$content .= "\n  RewriteRule ^core/preview.png$ index.php [PT,E=PATH_INFO:$1]";
531
-			$content .= "\n  RewriteCond %{REQUEST_FILENAME} !\\.(css|js|svg|gif|png|html|ttf|woff2?|ico|jpg|jpeg|map|webm|mp4)$";
532
-			$content .= "\n  RewriteCond %{REQUEST_FILENAME} !core/img/favicon.ico$";
533
-			$content .= "\n  RewriteCond %{REQUEST_FILENAME} !core/img/manifest.json$";
534
-			$content .= "\n  RewriteCond %{REQUEST_FILENAME} !/remote.php";
535
-			$content .= "\n  RewriteCond %{REQUEST_FILENAME} !/public.php";
536
-			$content .= "\n  RewriteCond %{REQUEST_FILENAME} !/cron.php";
537
-			$content .= "\n  RewriteCond %{REQUEST_FILENAME} !/core/ajax/update.php";
538
-			$content .= "\n  RewriteCond %{REQUEST_FILENAME} !/status.php";
539
-			$content .= "\n  RewriteCond %{REQUEST_FILENAME} !/ocs/v1.php";
540
-			$content .= "\n  RewriteCond %{REQUEST_FILENAME} !/ocs/v2.php";
541
-			$content .= "\n  RewriteCond %{REQUEST_FILENAME} !/robots.txt";
542
-			$content .= "\n  RewriteCond %{REQUEST_FILENAME} !/updater/";
543
-			$content .= "\n  RewriteCond %{REQUEST_FILENAME} !/ocs-provider/";
544
-			$content .= "\n  RewriteCond %{REQUEST_FILENAME} !/ocm-provider/";
545
-			$content .= "\n  RewriteCond %{REQUEST_URI} !^/\\.well-known/(acme-challenge|pki-validation)/.*";
546
-			$content .= "\n  RewriteRule . index.php [PT,E=PATH_INFO:$1]";
547
-			$content .= "\n  RewriteBase " . $rewriteBase;
548
-			$content .= "\n  <IfModule mod_env.c>";
549
-			$content .= "\n    SetEnv front_controller_active true";
550
-			$content .= "\n    <IfModule mod_dir.c>";
551
-			$content .= "\n      DirectorySlash off";
552
-			$content .= "\n    </IfModule>";
553
-			$content .= "\n  </IfModule>";
554
-			$content .= "\n</IfModule>";
555
-		}
556
-
557
-		if ($content !== '') {
558
-			//suppress errors in case we don't have permissions for it
559
-			return (bool)@file_put_contents($setupHelper->pathToHtaccess(), $htaccessContent . $content . "\n");
560
-		}
561
-
562
-		return false;
563
-	}
564
-
565
-	public static function protectDataDirectory() {
566
-		//Require all denied
567
-		$now = date('Y-m-d H:i:s');
568
-		$content = "# Generated by Nextcloud on $now\n";
569
-		$content .= "# Section for Apache 2.4 to 2.6\n";
570
-		$content .= "<IfModule mod_authz_core.c>\n";
571
-		$content .= "  Require all denied\n";
572
-		$content .= "</IfModule>\n";
573
-		$content .= "<IfModule mod_access_compat.c>\n";
574
-		$content .= "  Order Allow,Deny\n";
575
-		$content .= "  Deny from all\n";
576
-		$content .= "  Satisfy All\n";
577
-		$content .= "</IfModule>\n\n";
578
-		$content .= "# Section for Apache 2.2\n";
579
-		$content .= "<IfModule !mod_authz_core.c>\n";
580
-		$content .= "  <IfModule !mod_access_compat.c>\n";
581
-		$content .= "    <IfModule mod_authz_host.c>\n";
582
-		$content .= "      Order Allow,Deny\n";
583
-		$content .= "      Deny from all\n";
584
-		$content .= "    </IfModule>\n";
585
-		$content .= "    Satisfy All\n";
586
-		$content .= "  </IfModule>\n";
587
-		$content .= "</IfModule>\n\n";
588
-		$content .= "# Section for Apache 2.2 to 2.6\n";
589
-		$content .= "<IfModule mod_autoindex.c>\n";
590
-		$content .= "  IndexIgnore *\n";
591
-		$content .= "</IfModule>";
592
-
593
-		$baseDir = \OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data');
594
-		file_put_contents($baseDir . '/.htaccess', $content);
595
-		file_put_contents($baseDir . '/index.html', '');
596
-	}
597
-
598
-	/**
599
-	 * Return vendor from which this version was published
600
-	 *
601
-	 * @return string Get the vendor
602
-	 *
603
-	 * Copy of \OC\Updater::getVendor()
604
-	 */
605
-	private function getVendor() {
606
-		// this should really be a JSON file
607
-		require \OC::$SERVERROOT . '/version.php';
608
-		/** @var string $vendor */
609
-		return (string)$vendor;
610
-	}
66
+    /** @var SystemConfig */
67
+    protected $config;
68
+    /** @var IniGetWrapper */
69
+    protected $iniWrapper;
70
+    /** @var IL10N */
71
+    protected $l10n;
72
+    /** @var Defaults */
73
+    protected $defaults;
74
+    /** @var ILogger */
75
+    protected $logger;
76
+    /** @var ISecureRandom */
77
+    protected $random;
78
+    /** @var Installer */
79
+    protected $installer;
80
+
81
+    /**
82
+     * @param SystemConfig $config
83
+     * @param IniGetWrapper $iniWrapper
84
+     * @param IL10N $l10n
85
+     * @param Defaults $defaults
86
+     * @param ILogger $logger
87
+     * @param ISecureRandom $random
88
+     * @param Installer $installer
89
+     */
90
+    public function __construct(
91
+        SystemConfig $config,
92
+        IniGetWrapper $iniWrapper,
93
+        IL10N $l10n,
94
+        Defaults $defaults,
95
+        ILogger $logger,
96
+        ISecureRandom $random,
97
+        Installer $installer
98
+    ) {
99
+        $this->config = $config;
100
+        $this->iniWrapper = $iniWrapper;
101
+        $this->l10n = $l10n;
102
+        $this->defaults = $defaults;
103
+        $this->logger = $logger;
104
+        $this->random = $random;
105
+        $this->installer = $installer;
106
+    }
107
+
108
+    static protected $dbSetupClasses = [
109
+        'mysql' => \OC\Setup\MySQL::class,
110
+        'pgsql' => \OC\Setup\PostgreSQL::class,
111
+        'oci' => \OC\Setup\OCI::class,
112
+        'sqlite' => \OC\Setup\Sqlite::class,
113
+        'sqlite3' => \OC\Setup\Sqlite::class,
114
+    ];
115
+
116
+    /**
117
+     * Wrapper around the "class_exists" PHP function to be able to mock it
118
+     *
119
+     * @param string $name
120
+     * @return bool
121
+     */
122
+    protected function class_exists($name) {
123
+        return class_exists($name);
124
+    }
125
+
126
+    /**
127
+     * Wrapper around the "is_callable" PHP function to be able to mock it
128
+     *
129
+     * @param string $name
130
+     * @return bool
131
+     */
132
+    protected function is_callable($name) {
133
+        return is_callable($name);
134
+    }
135
+
136
+    /**
137
+     * Wrapper around \PDO::getAvailableDrivers
138
+     *
139
+     * @return array
140
+     */
141
+    protected function getAvailableDbDriversForPdo() {
142
+        return \PDO::getAvailableDrivers();
143
+    }
144
+
145
+    /**
146
+     * Get the available and supported databases of this instance
147
+     *
148
+     * @param bool $allowAllDatabases
149
+     * @return array
150
+     * @throws Exception
151
+     */
152
+    public function getSupportedDatabases($allowAllDatabases = false) {
153
+        $availableDatabases = [
154
+            'sqlite' => [
155
+                'type' => 'pdo',
156
+                'call' => 'sqlite',
157
+                'name' => 'SQLite',
158
+            ],
159
+            'mysql' => [
160
+                'type' => 'pdo',
161
+                'call' => 'mysql',
162
+                'name' => 'MySQL/MariaDB',
163
+            ],
164
+            'pgsql' => [
165
+                'type' => 'pdo',
166
+                'call' => 'pgsql',
167
+                'name' => 'PostgreSQL',
168
+            ],
169
+            'oci' => [
170
+                'type' => 'function',
171
+                'call' => 'oci_connect',
172
+                'name' => 'Oracle',
173
+            ],
174
+        ];
175
+        if ($allowAllDatabases) {
176
+            $configuredDatabases = array_keys($availableDatabases);
177
+        } else {
178
+            $configuredDatabases = $this->config->getValue('supportedDatabases',
179
+                ['sqlite', 'mysql', 'pgsql']);
180
+        }
181
+        if (!is_array($configuredDatabases)) {
182
+            throw new Exception('Supported databases are not properly configured.');
183
+        }
184
+
185
+        $supportedDatabases = [];
186
+
187
+        foreach ($configuredDatabases as $database) {
188
+            if (array_key_exists($database, $availableDatabases)) {
189
+                $working = false;
190
+                $type = $availableDatabases[$database]['type'];
191
+                $call = $availableDatabases[$database]['call'];
192
+
193
+                if ($type === 'function') {
194
+                    $working = $this->is_callable($call);
195
+                } elseif ($type === 'pdo') {
196
+                    $working = in_array($call, $this->getAvailableDbDriversForPdo(), true);
197
+                }
198
+                if ($working) {
199
+                    $supportedDatabases[$database] = $availableDatabases[$database]['name'];
200
+                }
201
+            }
202
+        }
203
+
204
+        return $supportedDatabases;
205
+    }
206
+
207
+    /**
208
+     * Gathers system information like database type and does
209
+     * a few system checks.
210
+     *
211
+     * @return array of system info, including an "errors" value
212
+     * in case of errors/warnings
213
+     */
214
+    public function getSystemInfo($allowAllDatabases = false) {
215
+        $databases = $this->getSupportedDatabases($allowAllDatabases);
216
+
217
+        $dataDir = $this->config->getValue('datadirectory', \OC::$SERVERROOT . '/data');
218
+
219
+        $errors = [];
220
+
221
+        // Create data directory to test whether the .htaccess works
222
+        // Notice that this is not necessarily the same data directory as the one
223
+        // that will effectively be used.
224
+        if (!file_exists($dataDir)) {
225
+            @mkdir($dataDir);
226
+        }
227
+        $htAccessWorking = true;
228
+        if (is_dir($dataDir) && is_writable($dataDir)) {
229
+            // Protect data directory here, so we can test if the protection is working
230
+            self::protectDataDirectory();
231
+
232
+            try {
233
+                $util = new \OC_Util();
234
+                $htAccessWorking = $util->isHtaccessWorking(\OC::$server->getConfig());
235
+            } catch (\OC\HintException $e) {
236
+                $errors[] = [
237
+                    'error' => $e->getMessage(),
238
+                    'hint' => $e->getHint(),
239
+                ];
240
+                $htAccessWorking = false;
241
+            }
242
+        }
243
+
244
+        if (\OC_Util::runningOnMac()) {
245
+            $errors[] = [
246
+                'error' => $this->l10n->t(
247
+                    'Mac OS X is not supported and %s will not work properly on this platform. ' .
248
+                    'Use it at your own risk! ',
249
+                    [$this->defaults->getName()]
250
+                ),
251
+                'hint' => $this->l10n->t('For the best results, please consider using a GNU/Linux server instead.'),
252
+            ];
253
+        }
254
+
255
+        if ($this->iniWrapper->getString('open_basedir') !== '' && PHP_INT_SIZE === 4) {
256
+            $errors[] = [
257
+                'error' => $this->l10n->t(
258
+                    'It seems that this %s instance is running on a 32-bit PHP environment and the open_basedir has been configured in php.ini. ' .
259
+                    'This will lead to problems with files over 4 GB and is highly discouraged.',
260
+                    [$this->defaults->getName()]
261
+                ),
262
+                'hint' => $this->l10n->t('Please remove the open_basedir setting within your php.ini or switch to 64-bit PHP.'),
263
+            ];
264
+        }
265
+
266
+        return [
267
+            'hasSQLite' => isset($databases['sqlite']),
268
+            'hasMySQL' => isset($databases['mysql']),
269
+            'hasPostgreSQL' => isset($databases['pgsql']),
270
+            'hasOracle' => isset($databases['oci']),
271
+            'databases' => $databases,
272
+            'directory' => $dataDir,
273
+            'htaccessWorking' => $htAccessWorking,
274
+            'errors' => $errors,
275
+        ];
276
+    }
277
+
278
+    /**
279
+     * @param $options
280
+     * @return array
281
+     */
282
+    public function install($options) {
283
+        $l = $this->l10n;
284
+
285
+        $error = [];
286
+        $dbType = $options['dbtype'];
287
+
288
+        if (empty($options['adminlogin'])) {
289
+            $error[] = $l->t('Set an admin username.');
290
+        }
291
+        if (empty($options['adminpass'])) {
292
+            $error[] = $l->t('Set an admin password.');
293
+        }
294
+        if (empty($options['directory'])) {
295
+            $options['directory'] = \OC::$SERVERROOT . "/data";
296
+        }
297
+
298
+        if (!isset(self::$dbSetupClasses[$dbType])) {
299
+            $dbType = 'sqlite';
300
+        }
301
+
302
+        $username = htmlspecialchars_decode($options['adminlogin']);
303
+        $password = htmlspecialchars_decode($options['adminpass']);
304
+        $dataDir = htmlspecialchars_decode($options['directory']);
305
+
306
+        $class = self::$dbSetupClasses[$dbType];
307
+        /** @var \OC\Setup\AbstractDatabase $dbSetup */
308
+        $dbSetup = new $class($l, $this->config, $this->logger, $this->random);
309
+        $error = array_merge($error, $dbSetup->validate($options));
310
+
311
+        // validate the data directory
312
+        if ((!is_dir($dataDir) && !mkdir($dataDir)) || !is_writable($dataDir)) {
313
+            $error[] = $l->t("Can't create or write into the data directory %s", [$dataDir]);
314
+        }
315
+
316
+        if (!empty($error)) {
317
+            return $error;
318
+        }
319
+
320
+        $request = \OC::$server->getRequest();
321
+
322
+        //no errors, good
323
+        if (isset($options['trusted_domains'])
324
+            && is_array($options['trusted_domains'])) {
325
+            $trustedDomains = $options['trusted_domains'];
326
+        } else {
327
+            $trustedDomains = [$request->getInsecureServerHost()];
328
+        }
329
+
330
+        //use sqlite3 when available, otherwise sqlite2 will be used.
331
+        if ($dbType === 'sqlite' && class_exists('SQLite3')) {
332
+            $dbType = 'sqlite3';
333
+        }
334
+
335
+        //generate a random salt that is used to salt the local user passwords
336
+        $salt = $this->random->generate(30);
337
+        // generate a secret
338
+        $secret = $this->random->generate(48);
339
+
340
+        //write the config file
341
+        $newConfigValues = [
342
+            'passwordsalt' => $salt,
343
+            'secret' => $secret,
344
+            'trusted_domains' => $trustedDomains,
345
+            'datadirectory' => $dataDir,
346
+            'dbtype' => $dbType,
347
+            'version' => implode('.', \OCP\Util::getVersion()),
348
+        ];
349
+
350
+        if ($this->config->getValue('overwrite.cli.url', null) === null) {
351
+            $newConfigValues['overwrite.cli.url'] = $request->getServerProtocol() . '://' . $request->getInsecureServerHost() . \OC::$WEBROOT;
352
+        }
353
+
354
+        $this->config->setValues($newConfigValues);
355
+
356
+        $dbSetup->initialize($options);
357
+        try {
358
+            $dbSetup->setupDatabase($username);
359
+        } catch (\OC\DatabaseSetupException $e) {
360
+            $error[] = [
361
+                'error' => $e->getMessage(),
362
+                'hint' => $e->getHint(),
363
+            ];
364
+            return $error;
365
+        } catch (Exception $e) {
366
+            $error[] = [
367
+                'error' => 'Error while trying to create admin user: ' . $e->getMessage(),
368
+                'hint' => '',
369
+            ];
370
+            return $error;
371
+        }
372
+        try {
373
+            // apply necessary migrations
374
+            $dbSetup->runMigrations();
375
+        } catch (Exception $e) {
376
+            $error[] = [
377
+                'error' => 'Error while trying to initialise the database: ' . $e->getMessage(),
378
+                'hint' => '',
379
+            ];
380
+            return $error;
381
+        }
382
+
383
+        //create the user and group
384
+        $user = null;
385
+        try {
386
+            $user = \OC::$server->getUserManager()->createUser($username, $password);
387
+            if (!$user) {
388
+                $error[] = "User <$username> could not be created.";
389
+            }
390
+        } catch (Exception $exception) {
391
+            $error[] = $exception->getMessage();
392
+        }
393
+
394
+        if (empty($error)) {
395
+            $config = \OC::$server->getConfig();
396
+            $config->setAppValue('core', 'installedat', microtime(true));
397
+            $config->setAppValue('core', 'lastupdatedat', microtime(true));
398
+            $config->setAppValue('core', 'vendor', $this->getVendor());
399
+
400
+            $group = \OC::$server->getGroupManager()->createGroup('admin');
401
+            if ($group instanceof IGroup) {
402
+                $group->addUser($user);
403
+            }
404
+
405
+            // Install shipped apps and specified app bundles
406
+            Installer::installShippedApps();
407
+            $bundleFetcher = new BundleFetcher(\OC::$server->getL10N('lib'));
408
+            $defaultInstallationBundles = $bundleFetcher->getDefaultInstallationBundle();
409
+            foreach ($defaultInstallationBundles as $bundle) {
410
+                try {
411
+                    $this->installer->installAppBundle($bundle);
412
+                } catch (Exception $e) {
413
+                }
414
+            }
415
+
416
+            // create empty file in data dir, so we can later find
417
+            // out that this is indeed an ownCloud data directory
418
+            file_put_contents($config->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data') . '/.ocdata', '');
419
+
420
+            // Update .htaccess files
421
+            self::updateHtaccess();
422
+            self::protectDataDirectory();
423
+
424
+            self::installBackgroundJobs();
425
+
426
+            //and we are done
427
+            $config->setSystemValue('installed', true);
428
+
429
+            // Create a session token for the newly created user
430
+            // The token provider requires a working db, so it's not injected on setup
431
+            /* @var $userSession User\Session */
432
+            $userSession = \OC::$server->getUserSession();
433
+            $defaultTokenProvider = \OC::$server->query(DefaultTokenProvider::class);
434
+            $userSession->setTokenProvider($defaultTokenProvider);
435
+            $userSession->login($username, $password);
436
+            $userSession->createSessionToken($request, $userSession->getUser()->getUID(), $username, $password);
437
+
438
+            $session = $userSession->getSession();
439
+            $session->set('last-password-confirm', \OC::$server->query(ITimeFactory::class)->getTime());
440
+
441
+            // Set email for admin
442
+            if (!empty($options['adminemail'])) {
443
+                $config->setUserValue($user->getUID(), 'settings', 'email', $options['adminemail']);
444
+            }
445
+        }
446
+
447
+        return $error;
448
+    }
449
+
450
+    public static function installBackgroundJobs() {
451
+        $jobList = \OC::$server->getJobList();
452
+        $jobList->add(DefaultTokenCleanupJob::class);
453
+        $jobList->add(Rotate::class);
454
+        $jobList->add(BackgroundCleanupJob::class);
455
+    }
456
+
457
+    /**
458
+     * @return string Absolute path to htaccess
459
+     */
460
+    private function pathToHtaccess() {
461
+        return \OC::$SERVERROOT . '/.htaccess';
462
+    }
463
+
464
+    /**
465
+     * Find webroot from config
466
+     *
467
+     * @param SystemConfig $config
468
+     * @return string
469
+     * @throws InvalidArgumentException when invalid value for overwrite.cli.url
470
+     */
471
+    private static function findWebRoot(SystemConfig $config): string {
472
+        // For CLI read the value from overwrite.cli.url
473
+        if (\OC::$CLI) {
474
+            $webRoot = $config->getValue('overwrite.cli.url', '');
475
+            if ($webRoot === '') {
476
+                throw new InvalidArgumentException('overwrite.cli.url is empty');
477
+            }
478
+            if (!filter_var($webRoot, FILTER_VALIDATE_URL)) {
479
+                throw new InvalidArgumentException('invalid value for overwrite.cli.url');
480
+            }
481
+            $webRoot = rtrim(parse_url($webRoot, PHP_URL_PATH), '/');
482
+        } else {
483
+            $webRoot = !empty(\OC::$WEBROOT) ? \OC::$WEBROOT : '/';
484
+        }
485
+
486
+        return $webRoot;
487
+    }
488
+
489
+    /**
490
+     * Append the correct ErrorDocument path for Apache hosts
491
+     *
492
+     * @return bool True when success, False otherwise
493
+     * @throws \OCP\AppFramework\QueryException
494
+     */
495
+    public static function updateHtaccess() {
496
+        $config = \OC::$server->getSystemConfig();
497
+
498
+        try {
499
+            $webRoot = self::findWebRoot($config);
500
+        } catch (InvalidArgumentException $e) {
501
+            return false;
502
+        }
503
+
504
+        $setupHelper = new \OC\Setup(
505
+            $config,
506
+            \OC::$server->getIniWrapper(),
507
+            \OC::$server->getL10N('lib'),
508
+            \OC::$server->query(Defaults::class),
509
+            \OC::$server->getLogger(),
510
+            \OC::$server->getSecureRandom(),
511
+            \OC::$server->query(Installer::class)
512
+        );
513
+
514
+        $htaccessContent = file_get_contents($setupHelper->pathToHtaccess());
515
+        $content = "#### DO NOT CHANGE ANYTHING ABOVE THIS LINE ####\n";
516
+        $htaccessContent = explode($content, $htaccessContent, 2)[0];
517
+
518
+        //custom 403 error page
519
+        $content .= "\nErrorDocument 403 " . $webRoot . '/';
520
+
521
+        //custom 404 error page
522
+        $content .= "\nErrorDocument 404 " . $webRoot . '/';
523
+
524
+        // Add rewrite rules if the RewriteBase is configured
525
+        $rewriteBase = $config->getValue('htaccess.RewriteBase', '');
526
+        if ($rewriteBase !== '') {
527
+            $content .= "\n<IfModule mod_rewrite.c>";
528
+            $content .= "\n  Options -MultiViews";
529
+            $content .= "\n  RewriteRule ^core/js/oc.js$ index.php [PT,E=PATH_INFO:$1]";
530
+            $content .= "\n  RewriteRule ^core/preview.png$ index.php [PT,E=PATH_INFO:$1]";
531
+            $content .= "\n  RewriteCond %{REQUEST_FILENAME} !\\.(css|js|svg|gif|png|html|ttf|woff2?|ico|jpg|jpeg|map|webm|mp4)$";
532
+            $content .= "\n  RewriteCond %{REQUEST_FILENAME} !core/img/favicon.ico$";
533
+            $content .= "\n  RewriteCond %{REQUEST_FILENAME} !core/img/manifest.json$";
534
+            $content .= "\n  RewriteCond %{REQUEST_FILENAME} !/remote.php";
535
+            $content .= "\n  RewriteCond %{REQUEST_FILENAME} !/public.php";
536
+            $content .= "\n  RewriteCond %{REQUEST_FILENAME} !/cron.php";
537
+            $content .= "\n  RewriteCond %{REQUEST_FILENAME} !/core/ajax/update.php";
538
+            $content .= "\n  RewriteCond %{REQUEST_FILENAME} !/status.php";
539
+            $content .= "\n  RewriteCond %{REQUEST_FILENAME} !/ocs/v1.php";
540
+            $content .= "\n  RewriteCond %{REQUEST_FILENAME} !/ocs/v2.php";
541
+            $content .= "\n  RewriteCond %{REQUEST_FILENAME} !/robots.txt";
542
+            $content .= "\n  RewriteCond %{REQUEST_FILENAME} !/updater/";
543
+            $content .= "\n  RewriteCond %{REQUEST_FILENAME} !/ocs-provider/";
544
+            $content .= "\n  RewriteCond %{REQUEST_FILENAME} !/ocm-provider/";
545
+            $content .= "\n  RewriteCond %{REQUEST_URI} !^/\\.well-known/(acme-challenge|pki-validation)/.*";
546
+            $content .= "\n  RewriteRule . index.php [PT,E=PATH_INFO:$1]";
547
+            $content .= "\n  RewriteBase " . $rewriteBase;
548
+            $content .= "\n  <IfModule mod_env.c>";
549
+            $content .= "\n    SetEnv front_controller_active true";
550
+            $content .= "\n    <IfModule mod_dir.c>";
551
+            $content .= "\n      DirectorySlash off";
552
+            $content .= "\n    </IfModule>";
553
+            $content .= "\n  </IfModule>";
554
+            $content .= "\n</IfModule>";
555
+        }
556
+
557
+        if ($content !== '') {
558
+            //suppress errors in case we don't have permissions for it
559
+            return (bool)@file_put_contents($setupHelper->pathToHtaccess(), $htaccessContent . $content . "\n");
560
+        }
561
+
562
+        return false;
563
+    }
564
+
565
+    public static function protectDataDirectory() {
566
+        //Require all denied
567
+        $now = date('Y-m-d H:i:s');
568
+        $content = "# Generated by Nextcloud on $now\n";
569
+        $content .= "# Section for Apache 2.4 to 2.6\n";
570
+        $content .= "<IfModule mod_authz_core.c>\n";
571
+        $content .= "  Require all denied\n";
572
+        $content .= "</IfModule>\n";
573
+        $content .= "<IfModule mod_access_compat.c>\n";
574
+        $content .= "  Order Allow,Deny\n";
575
+        $content .= "  Deny from all\n";
576
+        $content .= "  Satisfy All\n";
577
+        $content .= "</IfModule>\n\n";
578
+        $content .= "# Section for Apache 2.2\n";
579
+        $content .= "<IfModule !mod_authz_core.c>\n";
580
+        $content .= "  <IfModule !mod_access_compat.c>\n";
581
+        $content .= "    <IfModule mod_authz_host.c>\n";
582
+        $content .= "      Order Allow,Deny\n";
583
+        $content .= "      Deny from all\n";
584
+        $content .= "    </IfModule>\n";
585
+        $content .= "    Satisfy All\n";
586
+        $content .= "  </IfModule>\n";
587
+        $content .= "</IfModule>\n\n";
588
+        $content .= "# Section for Apache 2.2 to 2.6\n";
589
+        $content .= "<IfModule mod_autoindex.c>\n";
590
+        $content .= "  IndexIgnore *\n";
591
+        $content .= "</IfModule>";
592
+
593
+        $baseDir = \OC::$server->getConfig()->getSystemValue('datadirectory', \OC::$SERVERROOT . '/data');
594
+        file_put_contents($baseDir . '/.htaccess', $content);
595
+        file_put_contents($baseDir . '/index.html', '');
596
+    }
597
+
598
+    /**
599
+     * Return vendor from which this version was published
600
+     *
601
+     * @return string Get the vendor
602
+     *
603
+     * Copy of \OC\Updater::getVendor()
604
+     */
605
+    private function getVendor() {
606
+        // this should really be a JSON file
607
+        require \OC::$SERVERROOT . '/version.php';
608
+        /** @var string $vendor */
609
+        return (string)$vendor;
610
+    }
611 611
 }
Please login to merge, or discard this patch.
core/ajax/update.php 1 patch
Indentation   +180 added lines, -180 removed lines patch added patch discarded remove patch
@@ -34,7 +34,7 @@  discard block
 block discarded – undo
34 34
 use Symfony\Component\EventDispatcher\GenericEvent;
35 35
 
36 36
 if (strpos(@ini_get('disable_functions'), 'set_time_limit') === false) {
37
-	@set_time_limit(0);
37
+    @set_time_limit(0);
38 38
 }
39 39
 
40 40
 require_once '../../lib/base.php';
@@ -48,190 +48,190 @@  discard block
 block discarded – undo
48 48
 $eventSource->send('success', (string)$l->t('Preparing update'));
49 49
 
50 50
 class FeedBackHandler {
51
-	/** @var integer */
52
-	private $progressStateMax = 100;
53
-	/** @var integer */
54
-	private $progressStateStep = 0;
55
-	/** @var string */
56
-	private $currentStep;
57
-	/** @var \OCP\IEventSource */
58
-	private $eventSource;
59
-	/** @var \OCP\IL10N */
60
-	private $l10n;
61
-
62
-	public function __construct(\OCP\IEventSource $eventSource, \OCP\IL10N $l10n) {
63
-		$this->eventSource = $eventSource;
64
-		$this->l10n = $l10n;
65
-	}
66
-
67
-	public function handleRepairFeedback($event) {
68
-		if (!$event instanceof GenericEvent) {
69
-			return;
70
-		}
71
-
72
-		switch ($event->getSubject()) {
73
-			case '\OC\Repair::startProgress':
74
-				$this->progressStateMax = $event->getArgument(0);
75
-				$this->progressStateStep = 0;
76
-				$this->currentStep = $event->getArgument(1);
77
-				break;
78
-			case '\OC\Repair::advance':
79
-				$this->progressStateStep += $event->getArgument(0);
80
-				$desc = $event->getArgument(1);
81
-				if (empty($desc)) {
82
-					$desc = $this->currentStep;
83
-				}
84
-				$this->eventSource->send('success', (string)$this->l10n->t('[%d / %d]: %s', [$this->progressStateStep, $this->progressStateMax, $desc]));
85
-				break;
86
-			case '\OC\Repair::finishProgress':
87
-				$this->progressStateMax = $this->progressStateStep;
88
-				$this->eventSource->send('success', (string)$this->l10n->t('[%d / %d]: %s', [$this->progressStateStep, $this->progressStateMax, $this->currentStep]));
89
-				break;
90
-			case '\OC\Repair::step':
91
-				$this->eventSource->send('success', (string)$this->l10n->t('Repair step:') . ' ' . $event->getArgument(0));
92
-				break;
93
-			case '\OC\Repair::info':
94
-				$this->eventSource->send('success', (string)$this->l10n->t('Repair info:') . ' ' . $event->getArgument(0));
95
-				break;
96
-			case '\OC\Repair::warning':
97
-				$this->eventSource->send('notice', (string)$this->l10n->t('Repair warning:') . ' ' . $event->getArgument(0));
98
-				break;
99
-			case '\OC\Repair::error':
100
-				$this->eventSource->send('notice', (string)$this->l10n->t('Repair error:') . ' ' . $event->getArgument(0));
101
-				break;
102
-		}
103
-	}
51
+    /** @var integer */
52
+    private $progressStateMax = 100;
53
+    /** @var integer */
54
+    private $progressStateStep = 0;
55
+    /** @var string */
56
+    private $currentStep;
57
+    /** @var \OCP\IEventSource */
58
+    private $eventSource;
59
+    /** @var \OCP\IL10N */
60
+    private $l10n;
61
+
62
+    public function __construct(\OCP\IEventSource $eventSource, \OCP\IL10N $l10n) {
63
+        $this->eventSource = $eventSource;
64
+        $this->l10n = $l10n;
65
+    }
66
+
67
+    public function handleRepairFeedback($event) {
68
+        if (!$event instanceof GenericEvent) {
69
+            return;
70
+        }
71
+
72
+        switch ($event->getSubject()) {
73
+            case '\OC\Repair::startProgress':
74
+                $this->progressStateMax = $event->getArgument(0);
75
+                $this->progressStateStep = 0;
76
+                $this->currentStep = $event->getArgument(1);
77
+                break;
78
+            case '\OC\Repair::advance':
79
+                $this->progressStateStep += $event->getArgument(0);
80
+                $desc = $event->getArgument(1);
81
+                if (empty($desc)) {
82
+                    $desc = $this->currentStep;
83
+                }
84
+                $this->eventSource->send('success', (string)$this->l10n->t('[%d / %d]: %s', [$this->progressStateStep, $this->progressStateMax, $desc]));
85
+                break;
86
+            case '\OC\Repair::finishProgress':
87
+                $this->progressStateMax = $this->progressStateStep;
88
+                $this->eventSource->send('success', (string)$this->l10n->t('[%d / %d]: %s', [$this->progressStateStep, $this->progressStateMax, $this->currentStep]));
89
+                break;
90
+            case '\OC\Repair::step':
91
+                $this->eventSource->send('success', (string)$this->l10n->t('Repair step:') . ' ' . $event->getArgument(0));
92
+                break;
93
+            case '\OC\Repair::info':
94
+                $this->eventSource->send('success', (string)$this->l10n->t('Repair info:') . ' ' . $event->getArgument(0));
95
+                break;
96
+            case '\OC\Repair::warning':
97
+                $this->eventSource->send('notice', (string)$this->l10n->t('Repair warning:') . ' ' . $event->getArgument(0));
98
+                break;
99
+            case '\OC\Repair::error':
100
+                $this->eventSource->send('notice', (string)$this->l10n->t('Repair error:') . ' ' . $event->getArgument(0));
101
+                break;
102
+        }
103
+    }
104 104
 }
105 105
 
106 106
 if (\OCP\Util::needUpgrade()) {
107 107
 
108
-	$config = \OC::$server->getSystemConfig();
109
-	if ($config->getValue('upgrade.disable-web', false)) {
110
-		$eventSource->send('failure', (string)$l->t('Please use the command line updater because automatic updating is disabled in the config.php.'));
111
-		$eventSource->close();
112
-		exit();
113
-	}
114
-
115
-	// if a user is currently logged in, their session must be ignored to
116
-	// avoid side effects
117
-	\OC_User::setIncognitoMode(true);
118
-
119
-	$logger = \OC::$server->getLogger();
120
-	$config = \OC::$server->getConfig();
121
-	$updater = new \OC\Updater(
122
-			$config,
123
-			\OC::$server->getIntegrityCodeChecker(),
124
-			$logger,
125
-			\OC::$server->query(\OC\Installer::class)
126
-	);
127
-	$incompatibleApps = [];
128
-
129
-	$dispatcher = \OC::$server->getEventDispatcher();
130
-	$dispatcher->addListener('\OC\DB\Migrator::executeSql', function($event) use ($eventSource, $l) {
131
-		if ($event instanceof GenericEvent) {
132
-			$eventSource->send('success', (string)$l->t('[%d / %d]: %s', [$event[0], $event[1], $event->getSubject()]));
133
-		}
134
-	});
135
-	$dispatcher->addListener('\OC\DB\Migrator::checkTable', function($event) use ($eventSource, $l) {
136
-		if ($event instanceof GenericEvent) {
137
-			$eventSource->send('success', (string)$l->t('[%d / %d]: Checking table %s', [$event[0], $event[1], $event->getSubject()]));
138
-		}
139
-	});
140
-	$feedBack = new FeedBackHandler($eventSource, $l);
141
-	$dispatcher->addListener('\OC\Repair::startProgress', [$feedBack, 'handleRepairFeedback']);
142
-	$dispatcher->addListener('\OC\Repair::advance', [$feedBack, 'handleRepairFeedback']);
143
-	$dispatcher->addListener('\OC\Repair::finishProgress', [$feedBack, 'handleRepairFeedback']);
144
-	$dispatcher->addListener('\OC\Repair::step', [$feedBack, 'handleRepairFeedback']);
145
-	$dispatcher->addListener('\OC\Repair::info', [$feedBack, 'handleRepairFeedback']);
146
-	$dispatcher->addListener('\OC\Repair::warning', [$feedBack, 'handleRepairFeedback']);
147
-	$dispatcher->addListener('\OC\Repair::error', [$feedBack, 'handleRepairFeedback']);
148
-
149
-	$updater->listen('\OC\Updater', 'maintenanceEnabled', function () use ($eventSource, $l) {
150
-		$eventSource->send('success', (string)$l->t('Turned on maintenance mode'));
151
-	});
152
-	$updater->listen('\OC\Updater', 'maintenanceDisabled', function () use ($eventSource, $l) {
153
-		$eventSource->send('success', (string)$l->t('Turned off maintenance mode'));
154
-	});
155
-	$updater->listen('\OC\Updater', 'maintenanceActive', function () use ($eventSource, $l) {
156
-		$eventSource->send('success', (string)$l->t('Maintenance mode is kept active'));
157
-	});
158
-	$updater->listen('\OC\Updater', 'dbUpgradeBefore', function () use($eventSource, $l) {
159
-		$eventSource->send('success', (string)$l->t('Updating database schema'));
160
-	});
161
-	$updater->listen('\OC\Updater', 'dbUpgrade', function () use ($eventSource, $l) {
162
-		$eventSource->send('success', (string)$l->t('Updated database'));
163
-	});
164
-	$updater->listen('\OC\Updater', 'dbSimulateUpgradeBefore', function () use($eventSource, $l) {
165
-		$eventSource->send('success', (string)$l->t('Checking whether the database schema can be updated (this can take a long time depending on the database size)'));
166
-	});
167
-	$updater->listen('\OC\Updater', 'dbSimulateUpgrade', function () use ($eventSource, $l) {
168
-		$eventSource->send('success', (string)$l->t('Checked database schema update'));
169
-	});
170
-	$updater->listen('\OC\Updater', 'appUpgradeCheckBefore', function () use ($eventSource, $l) {
171
-		$eventSource->send('success', (string)$l->t('Checking updates of apps'));
172
-	});
173
-	$updater->listen('\OC\Updater', 'checkAppStoreAppBefore', function ($app) use ($eventSource, $l) {
174
-		$eventSource->send('success', (string)$l->t('Checking for update of app "%s" in appstore', [$app]));
175
-	});
176
-	$updater->listen('\OC\Updater', 'upgradeAppStoreApp', function ($app) use ($eventSource, $l) {
177
-		$eventSource->send('success', (string)$l->t('Update app "%s" from appstore', [$app]));
178
-	});
179
-	$updater->listen('\OC\Updater', 'checkAppStoreApp', function ($app) use ($eventSource, $l) {
180
-		$eventSource->send('success', (string)$l->t('Checked for update of app "%s" in appstore', [$app]));
181
-	});
182
-	$updater->listen('\OC\Updater', 'appSimulateUpdate', function ($app) use ($eventSource, $l) {
183
-		$eventSource->send('success', (string)$l->t('Checking whether the database schema for %s can be updated (this can take a long time depending on the database size)', [$app]));
184
-	});
185
-	$updater->listen('\OC\Updater', 'appUpgradeCheck', function () use ($eventSource, $l) {
186
-		$eventSource->send('success', (string)$l->t('Checked database schema update for apps'));
187
-	});
188
-	$updater->listen('\OC\Updater', 'appUpgrade', function ($app, $version) use ($eventSource, $l) {
189
-		$eventSource->send('success', (string)$l->t('Updated "%1$s" to %2$s', [$app, $version]));
190
-	});
191
-	$updater->listen('\OC\Updater', 'incompatibleAppDisabled', function ($app) use (&$incompatibleApps) {
192
-		$incompatibleApps[]= $app;
193
-	});
194
-	$updater->listen('\OC\Updater', 'failure', function ($message) use ($eventSource, $config) {
195
-		$eventSource->send('failure', $message);
196
-		$eventSource->close();
197
-		$config->setSystemValue('maintenance', false);
198
-	});
199
-	$updater->listen('\OC\Updater', 'setDebugLogLevel', function ($logLevel, $logLevelName) use($eventSource, $l) {
200
-		$eventSource->send('success', (string)$l->t('Set log level to debug'));
201
-	});
202
-	$updater->listen('\OC\Updater', 'resetLogLevel', function ($logLevel, $logLevelName) use($eventSource, $l) {
203
-		$eventSource->send('success', (string)$l->t('Reset log level'));
204
-	});
205
-	$updater->listen('\OC\Updater', 'startCheckCodeIntegrity', function () use($eventSource, $l) {
206
-		$eventSource->send('success', (string)$l->t('Starting code integrity check'));
207
-	});
208
-	$updater->listen('\OC\Updater', 'finishedCheckCodeIntegrity', function () use($eventSource, $l) {
209
-		$eventSource->send('success', (string)$l->t('Finished code integrity check'));
210
-	});
211
-
212
-	try {
213
-		$updater->upgrade();
214
-	} catch (\Exception $e) {
215
-		\OC::$server->getLogger()->logException($e, [
216
-			'level' => ILogger::ERROR,
217
-			'app' => 'update',
218
-		]);
219
-		$eventSource->send('failure', get_class($e) . ': ' . $e->getMessage());
220
-		$eventSource->close();
221
-		exit();
222
-	}
223
-
224
-	$disabledApps = [];
225
-	foreach ($incompatibleApps as $app) {
226
-		$disabledApps[$app] = (string) $l->t('%s (incompatible)', [$app]);
227
-	}
228
-
229
-	if (!empty($disabledApps)) {
230
-		$eventSource->send('notice',
231
-			(string)$l->t('Following apps have been disabled: %s', [implode(', ', $disabledApps)]));
232
-	}
108
+    $config = \OC::$server->getSystemConfig();
109
+    if ($config->getValue('upgrade.disable-web', false)) {
110
+        $eventSource->send('failure', (string)$l->t('Please use the command line updater because automatic updating is disabled in the config.php.'));
111
+        $eventSource->close();
112
+        exit();
113
+    }
114
+
115
+    // if a user is currently logged in, their session must be ignored to
116
+    // avoid side effects
117
+    \OC_User::setIncognitoMode(true);
118
+
119
+    $logger = \OC::$server->getLogger();
120
+    $config = \OC::$server->getConfig();
121
+    $updater = new \OC\Updater(
122
+            $config,
123
+            \OC::$server->getIntegrityCodeChecker(),
124
+            $logger,
125
+            \OC::$server->query(\OC\Installer::class)
126
+    );
127
+    $incompatibleApps = [];
128
+
129
+    $dispatcher = \OC::$server->getEventDispatcher();
130
+    $dispatcher->addListener('\OC\DB\Migrator::executeSql', function($event) use ($eventSource, $l) {
131
+        if ($event instanceof GenericEvent) {
132
+            $eventSource->send('success', (string)$l->t('[%d / %d]: %s', [$event[0], $event[1], $event->getSubject()]));
133
+        }
134
+    });
135
+    $dispatcher->addListener('\OC\DB\Migrator::checkTable', function($event) use ($eventSource, $l) {
136
+        if ($event instanceof GenericEvent) {
137
+            $eventSource->send('success', (string)$l->t('[%d / %d]: Checking table %s', [$event[0], $event[1], $event->getSubject()]));
138
+        }
139
+    });
140
+    $feedBack = new FeedBackHandler($eventSource, $l);
141
+    $dispatcher->addListener('\OC\Repair::startProgress', [$feedBack, 'handleRepairFeedback']);
142
+    $dispatcher->addListener('\OC\Repair::advance', [$feedBack, 'handleRepairFeedback']);
143
+    $dispatcher->addListener('\OC\Repair::finishProgress', [$feedBack, 'handleRepairFeedback']);
144
+    $dispatcher->addListener('\OC\Repair::step', [$feedBack, 'handleRepairFeedback']);
145
+    $dispatcher->addListener('\OC\Repair::info', [$feedBack, 'handleRepairFeedback']);
146
+    $dispatcher->addListener('\OC\Repair::warning', [$feedBack, 'handleRepairFeedback']);
147
+    $dispatcher->addListener('\OC\Repair::error', [$feedBack, 'handleRepairFeedback']);
148
+
149
+    $updater->listen('\OC\Updater', 'maintenanceEnabled', function () use ($eventSource, $l) {
150
+        $eventSource->send('success', (string)$l->t('Turned on maintenance mode'));
151
+    });
152
+    $updater->listen('\OC\Updater', 'maintenanceDisabled', function () use ($eventSource, $l) {
153
+        $eventSource->send('success', (string)$l->t('Turned off maintenance mode'));
154
+    });
155
+    $updater->listen('\OC\Updater', 'maintenanceActive', function () use ($eventSource, $l) {
156
+        $eventSource->send('success', (string)$l->t('Maintenance mode is kept active'));
157
+    });
158
+    $updater->listen('\OC\Updater', 'dbUpgradeBefore', function () use($eventSource, $l) {
159
+        $eventSource->send('success', (string)$l->t('Updating database schema'));
160
+    });
161
+    $updater->listen('\OC\Updater', 'dbUpgrade', function () use ($eventSource, $l) {
162
+        $eventSource->send('success', (string)$l->t('Updated database'));
163
+    });
164
+    $updater->listen('\OC\Updater', 'dbSimulateUpgradeBefore', function () use($eventSource, $l) {
165
+        $eventSource->send('success', (string)$l->t('Checking whether the database schema can be updated (this can take a long time depending on the database size)'));
166
+    });
167
+    $updater->listen('\OC\Updater', 'dbSimulateUpgrade', function () use ($eventSource, $l) {
168
+        $eventSource->send('success', (string)$l->t('Checked database schema update'));
169
+    });
170
+    $updater->listen('\OC\Updater', 'appUpgradeCheckBefore', function () use ($eventSource, $l) {
171
+        $eventSource->send('success', (string)$l->t('Checking updates of apps'));
172
+    });
173
+    $updater->listen('\OC\Updater', 'checkAppStoreAppBefore', function ($app) use ($eventSource, $l) {
174
+        $eventSource->send('success', (string)$l->t('Checking for update of app "%s" in appstore', [$app]));
175
+    });
176
+    $updater->listen('\OC\Updater', 'upgradeAppStoreApp', function ($app) use ($eventSource, $l) {
177
+        $eventSource->send('success', (string)$l->t('Update app "%s" from appstore', [$app]));
178
+    });
179
+    $updater->listen('\OC\Updater', 'checkAppStoreApp', function ($app) use ($eventSource, $l) {
180
+        $eventSource->send('success', (string)$l->t('Checked for update of app "%s" in appstore', [$app]));
181
+    });
182
+    $updater->listen('\OC\Updater', 'appSimulateUpdate', function ($app) use ($eventSource, $l) {
183
+        $eventSource->send('success', (string)$l->t('Checking whether the database schema for %s can be updated (this can take a long time depending on the database size)', [$app]));
184
+    });
185
+    $updater->listen('\OC\Updater', 'appUpgradeCheck', function () use ($eventSource, $l) {
186
+        $eventSource->send('success', (string)$l->t('Checked database schema update for apps'));
187
+    });
188
+    $updater->listen('\OC\Updater', 'appUpgrade', function ($app, $version) use ($eventSource, $l) {
189
+        $eventSource->send('success', (string)$l->t('Updated "%1$s" to %2$s', [$app, $version]));
190
+    });
191
+    $updater->listen('\OC\Updater', 'incompatibleAppDisabled', function ($app) use (&$incompatibleApps) {
192
+        $incompatibleApps[]= $app;
193
+    });
194
+    $updater->listen('\OC\Updater', 'failure', function ($message) use ($eventSource, $config) {
195
+        $eventSource->send('failure', $message);
196
+        $eventSource->close();
197
+        $config->setSystemValue('maintenance', false);
198
+    });
199
+    $updater->listen('\OC\Updater', 'setDebugLogLevel', function ($logLevel, $logLevelName) use($eventSource, $l) {
200
+        $eventSource->send('success', (string)$l->t('Set log level to debug'));
201
+    });
202
+    $updater->listen('\OC\Updater', 'resetLogLevel', function ($logLevel, $logLevelName) use($eventSource, $l) {
203
+        $eventSource->send('success', (string)$l->t('Reset log level'));
204
+    });
205
+    $updater->listen('\OC\Updater', 'startCheckCodeIntegrity', function () use($eventSource, $l) {
206
+        $eventSource->send('success', (string)$l->t('Starting code integrity check'));
207
+    });
208
+    $updater->listen('\OC\Updater', 'finishedCheckCodeIntegrity', function () use($eventSource, $l) {
209
+        $eventSource->send('success', (string)$l->t('Finished code integrity check'));
210
+    });
211
+
212
+    try {
213
+        $updater->upgrade();
214
+    } catch (\Exception $e) {
215
+        \OC::$server->getLogger()->logException($e, [
216
+            'level' => ILogger::ERROR,
217
+            'app' => 'update',
218
+        ]);
219
+        $eventSource->send('failure', get_class($e) . ': ' . $e->getMessage());
220
+        $eventSource->close();
221
+        exit();
222
+    }
223
+
224
+    $disabledApps = [];
225
+    foreach ($incompatibleApps as $app) {
226
+        $disabledApps[$app] = (string) $l->t('%s (incompatible)', [$app]);
227
+    }
228
+
229
+    if (!empty($disabledApps)) {
230
+        $eventSource->send('notice',
231
+            (string)$l->t('Following apps have been disabled: %s', [implode(', ', $disabledApps)]));
232
+    }
233 233
 } else {
234
-	$eventSource->send('notice', (string)$l->t('Already up to date'));
234
+    $eventSource->send('notice', (string)$l->t('Already up to date'));
235 235
 }
236 236
 
237 237
 $eventSource->send('done', '');
Please login to merge, or discard this patch.
core/Command/User/Report.php 1 patch
Indentation   +44 added lines, -44 removed lines patch added patch discarded remove patch
@@ -33,56 +33,56 @@
 block discarded – undo
33 33
 use Symfony\Component\Console\Output\OutputInterface;
34 34
 
35 35
 class Report extends Command {
36
-	/** @var IUserManager */
37
-	protected $userManager;
36
+    /** @var IUserManager */
37
+    protected $userManager;
38 38
 
39
-	/**
40
-	 * @param IUserManager $userManager
41
-	 */
42
-	public function __construct(IUserManager $userManager) {
43
-		$this->userManager = $userManager;
44
-		parent::__construct();
45
-	}
39
+    /**
40
+     * @param IUserManager $userManager
41
+     */
42
+    public function __construct(IUserManager $userManager) {
43
+        $this->userManager = $userManager;
44
+        parent::__construct();
45
+    }
46 46
 
47
-	protected function configure() {
48
-		$this
49
-			->setName('user:report')
50
-			->setDescription('shows how many users have access');
51
-	}
47
+    protected function configure() {
48
+        $this
49
+            ->setName('user:report')
50
+            ->setDescription('shows how many users have access');
51
+    }
52 52
 
53
-	protected function execute(InputInterface $input, OutputInterface $output) {
54
-		$table = new Table($output);
55
-		$table->setHeaders(['User Report', '']);
56
-		$userCountArray = $this->countUsers();
57
-		if(!empty($userCountArray)) {
58
-			$total = 0;
59
-			$rows = [];
60
-			foreach($userCountArray as $classname => $users) {
61
-				$total += $users;
62
-				$rows[] = [$classname, $users];
63
-			}
53
+    protected function execute(InputInterface $input, OutputInterface $output) {
54
+        $table = new Table($output);
55
+        $table->setHeaders(['User Report', '']);
56
+        $userCountArray = $this->countUsers();
57
+        if(!empty($userCountArray)) {
58
+            $total = 0;
59
+            $rows = [];
60
+            foreach($userCountArray as $classname => $users) {
61
+                $total += $users;
62
+                $rows[] = [$classname, $users];
63
+            }
64 64
 
65
-			$rows[] = [' '];
66
-			$rows[] = ['total users', $total];
67
-		} else {
68
-			$rows[] = ['No backend enabled that supports user counting', ''];
69
-		}
65
+            $rows[] = [' '];
66
+            $rows[] = ['total users', $total];
67
+        } else {
68
+            $rows[] = ['No backend enabled that supports user counting', ''];
69
+        }
70 70
 
71
-		$userDirectoryCount = $this->countUserDirectories();
72
-		$rows[] = [' '];
73
-		$rows[] = ['user directories', $userDirectoryCount];
71
+        $userDirectoryCount = $this->countUserDirectories();
72
+        $rows[] = [' '];
73
+        $rows[] = ['user directories', $userDirectoryCount];
74 74
 
75
-		$table->setRows($rows);
76
-		$table->render();
77
-	}
75
+        $table->setRows($rows);
76
+        $table->render();
77
+    }
78 78
 
79
-	private function countUsers() {
80
-		return $this->userManager->countUsers();
81
-	}
79
+    private function countUsers() {
80
+        return $this->userManager->countUsers();
81
+    }
82 82
 
83
-	private function countUserDirectories() {
84
-		$dataview = new \OC\Files\View('/');
85
-		$userDirectories = $dataview->getDirectoryContent('/', 'httpd/unix-directory');
86
-		return count($userDirectories);
87
-	}
83
+    private function countUserDirectories() {
84
+        $dataview = new \OC\Files\View('/');
85
+        $userDirectories = $dataview->getDirectoryContent('/', 'httpd/unix-directory');
86
+        return count($userDirectories);
87
+    }
88 88
 }
Please login to merge, or discard this patch.