Completed
Pull Request — developer (#4001)
by Thom
542:26 queued 508:45
created
libraries/SabreDAV/DAVACL/FS/File.php 2 patches
Unused Use Statements   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -2,9 +2,9 @@
 block discarded – undo
2 2
 
3 3
 namespace Sabre\DAVACL\FS;
4 4
 
5
-use Sabre\DAV\FSExt\File as BaseFile;
6 5
 use Sabre\DAVACL\IACL;
7 6
 use Sabre\DAV\Exception\Forbidden;
7
+use Sabre\DAV\FSExt\File as BaseFile;
8 8
 
9 9
 /**
10 10
  * This is an ACL-enabled file node.
Please login to merge, or discard this patch.
Indentation   +104 added lines, -104 removed lines patch added patch discarded remove patch
@@ -15,109 +15,109 @@
 block discarded – undo
15 15
  */
16 16
 class File extends BaseFile implements IACL {
17 17
 
18
-    /**
19
-     * A list of ACL rules.
20
-     *
21
-     * @var array
22
-     */
23
-    protected $acl;
24
-
25
-    /**
26
-     * Owner uri, or null for no owner.
27
-     *
28
-     * @var string|null
29
-     */
30
-    protected $owner;
31
-
32
-    /**
33
-     * Constructor
34
-     *
35
-     * @param string $path on-disk path.
36
-     * @param array $acl ACL rules.
37
-     * @param string|null $owner principal owner string.
38
-     */
39
-    public function __construct($path, array $acl, $owner = null) {
40
-
41
-        parent::__construct($path);
42
-        $this->acl = $acl;
43
-        $this->owner = $owner;
44
-
45
-    }
46
-
47
-    /**
48
-     * Returns the owner principal
49
-     *
50
-     * This must be a url to a principal, or null if there's no owner
51
-     *
52
-     * @return string|null
53
-     */
54
-    public function getOwner() {
55
-
56
-        return $this->owner;
57
-
58
-    }
59
-
60
-    /**
61
-     * Returns a group principal
62
-     *
63
-     * This must be a url to a principal, or null if there's no owner
64
-     *
65
-     * @return string|null
66
-     */
67
-    public function getGroup() {
68
-
69
-        return null;
70
-
71
-    }
72
-
73
-    /**
74
-     * Returns a list of ACE's for this node.
75
-     *
76
-     * Each ACE has the following properties:
77
-     *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
78
-     *     currently the only supported privileges
79
-     *   * 'principal', a url to the principal who owns the node
80
-     *   * 'protected' (optional), indicating that this ACE is not allowed to
81
-     *      be updated.
82
-     *
83
-     * @return array
84
-     */
85
-    public function getACL() {
86
-
87
-        return $this->acl;
88
-
89
-    }
90
-
91
-    /**
92
-     * Updates the ACL
93
-     *
94
-     * This method will receive a list of new ACE's as an array argument.
95
-     *
96
-     * @param array $acl
97
-     * @return void
98
-     */
99
-    public function setACL(array $acl) {
100
-
101
-        throw new Forbidden('Setting ACL is not allowed here');
102
-
103
-    }
104
-
105
-    /**
106
-     * Returns the list of supported privileges for this node.
107
-     *
108
-     * The returned data structure is a list of nested privileges.
109
-     * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
110
-     * standard structure.
111
-     *
112
-     * If null is returned from this method, the default privilege set is used,
113
-     * which is fine for most common usecases.
114
-     *
115
-     * @return array|null
116
-     */
117
-    public function getSupportedPrivilegeSet() {
118
-
119
-        return null;
120
-
121
-    }
18
+	/**
19
+	 * A list of ACL rules.
20
+	 *
21
+	 * @var array
22
+	 */
23
+	protected $acl;
24
+
25
+	/**
26
+	 * Owner uri, or null for no owner.
27
+	 *
28
+	 * @var string|null
29
+	 */
30
+	protected $owner;
31
+
32
+	/**
33
+	 * Constructor
34
+	 *
35
+	 * @param string $path on-disk path.
36
+	 * @param array $acl ACL rules.
37
+	 * @param string|null $owner principal owner string.
38
+	 */
39
+	public function __construct($path, array $acl, $owner = null) {
40
+
41
+		parent::__construct($path);
42
+		$this->acl = $acl;
43
+		$this->owner = $owner;
44
+
45
+	}
46
+
47
+	/**
48
+	 * Returns the owner principal
49
+	 *
50
+	 * This must be a url to a principal, or null if there's no owner
51
+	 *
52
+	 * @return string|null
53
+	 */
54
+	public function getOwner() {
55
+
56
+		return $this->owner;
57
+
58
+	}
59
+
60
+	/**
61
+	 * Returns a group principal
62
+	 *
63
+	 * This must be a url to a principal, or null if there's no owner
64
+	 *
65
+	 * @return string|null
66
+	 */
67
+	public function getGroup() {
68
+
69
+		return null;
70
+
71
+	}
72
+
73
+	/**
74
+	 * Returns a list of ACE's for this node.
75
+	 *
76
+	 * Each ACE has the following properties:
77
+	 *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
78
+	 *     currently the only supported privileges
79
+	 *   * 'principal', a url to the principal who owns the node
80
+	 *   * 'protected' (optional), indicating that this ACE is not allowed to
81
+	 *      be updated.
82
+	 *
83
+	 * @return array
84
+	 */
85
+	public function getACL() {
86
+
87
+		return $this->acl;
88
+
89
+	}
90
+
91
+	/**
92
+	 * Updates the ACL
93
+	 *
94
+	 * This method will receive a list of new ACE's as an array argument.
95
+	 *
96
+	 * @param array $acl
97
+	 * @return void
98
+	 */
99
+	public function setACL(array $acl) {
100
+
101
+		throw new Forbidden('Setting ACL is not allowed here');
102
+
103
+	}
104
+
105
+	/**
106
+	 * Returns the list of supported privileges for this node.
107
+	 *
108
+	 * The returned data structure is a list of nested privileges.
109
+	 * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
110
+	 * standard structure.
111
+	 *
112
+	 * If null is returned from this method, the default privilege set is used,
113
+	 * which is fine for most common usecases.
114
+	 *
115
+	 * @return array|null
116
+	 */
117
+	public function getSupportedPrivilegeSet() {
118
+
119
+		return null;
120
+
121
+	}
122 122
 
123 123
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/DAVACL/FS/HomeCollection.php 3 patches
Unused Use Statements   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -2,10 +2,10 @@
 block discarded – undo
2 2
 
3 3
 namespace Sabre\DAVACL\FS;
4 4
 
5
-use Sabre\DAV\Exception\Forbidden;
6 5
 use Sabre\DAVACL\AbstractPrincipalCollection;
7 6
 use Sabre\DAVACL\IACL;
8 7
 use Sabre\DAVACL\PrincipalBackend\BackendInterface;
8
+use Sabre\DAV\Exception\Forbidden;
9 9
 use Sabre\Uri;
10 10
 
11 11
 /**
Please login to merge, or discard this patch.
Indentation   +163 added lines, -163 removed lines patch added patch discarded remove patch
@@ -21,168 +21,168 @@
 block discarded – undo
21 21
  */
22 22
 class HomeCollection extends AbstractPrincipalCollection implements IACL {
23 23
 
24
-    /**
25
-     * Name of this collection.
26
-     *
27
-     * @var string
28
-     */
29
-    public $collectionName = 'home';
30
-
31
-    /**
32
-     * Path to where the users' files are actually stored.
33
-     *
34
-     * @var string
35
-     */
36
-    protected $storagePath;
37
-
38
-    /**
39
-     * Creates the home collection.
40
-     *
41
-     * @param BackendInterface $principalBackend
42
-     * @param string $storagePath Where the actual files are stored.
43
-     * @param string $principalPrefix list of principals to iterate.
44
-     */
45
-    public function __construct(BackendInterface $principalBackend, $storagePath, $principalPrefix = 'principals') {
46
-
47
-        parent::__construct($principalBackend, $principalPrefix);
48
-        $this->storagePath = $storagePath;
49
-
50
-    }
51
-
52
-    /**
53
-     * Returns the name of the node.
54
-     *
55
-     * This is used to generate the url.
56
-     *
57
-     * @return string
58
-     */
59
-    public function getName() {
60
-
61
-        return $this->collectionName;
62
-
63
-    }
64
-
65
-    /**
66
-     * Returns a principals' collection of files.
67
-     *
68
-     * The passed array contains principal information, and is guaranteed to
69
-     * at least contain a uri item. Other properties may or may not be
70
-     * supplied by the authentication backend.
71
-     *
72
-     * @param array $principalInfo
73
-     * @return void
74
-     */
75
-    public function getChildForPrincipal(array $principalInfo) {
76
-
77
-        $owner = $principalInfo['uri'];
78
-        $acl = [
79
-            [
80
-                'privilege' => '{DAV:}read',
81
-                'principal' => $owner,
82
-                'protected' => true,
83
-            ],
84
-            [
85
-                'privilege' => '{DAV:}write',
86
-                'principal' => $owner,
87
-                'protected' => true,
88
-            ],
89
-        ];
90
-
91
-        list(, $principalBaseName) = Uri\split($owner);
92
-
93
-        $path = $this->storagePath . '/' . $principalBaseName;
94
-
95
-        if (!is_dir($path)) {
96
-            mkdir($path, 0777, true);
97
-        }
98
-        return new Collection(
99
-            $path,
100
-            $acl,
101
-            $owner
102
-        );
103
-
104
-    }
105
-
106
-    /**
107
-     * Returns the owner principal
108
-     *
109
-     * This must be a url to a principal, or null if there's no owner
110
-     *
111
-     * @return string|null
112
-     */
113
-    public function getOwner() {
114
-
115
-        return null;
116
-
117
-    }
118
-
119
-    /**
120
-     * Returns a group principal
121
-     *
122
-     * This must be a url to a principal, or null if there's no owner
123
-     *
124
-     * @return string|null
125
-     */
126
-    public function getGroup() {
127
-
128
-        return null;
129
-
130
-    }
131
-
132
-    /**
133
-     * Returns a list of ACE's for this node.
134
-     *
135
-     * Each ACE has the following properties:
136
-     *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
137
-     *     currently the only supported privileges
138
-     *   * 'principal', a url to the principal who owns the node
139
-     *   * 'protected' (optional), indicating that this ACE is not allowed to
140
-     *      be updated.
141
-     *
142
-     * @return array
143
-     */
144
-    public function getACL() {
145
-
146
-        return [
147
-            [
148
-                'principal' => '{DAV:}authenticated',
149
-                'privilege' => '{DAV:}read',
150
-                'protected' => true,
151
-            ]
152
-        ];
153
-
154
-    }
155
-
156
-    /**
157
-     * Updates the ACL
158
-     *
159
-     * This method will receive a list of new ACE's as an array argument.
160
-     *
161
-     * @param array $acl
162
-     * @return void
163
-     */
164
-    public function setACL(array $acl) {
165
-
166
-        throw new Forbidden('Setting ACL is not allowed here');
167
-
168
-    }
169
-
170
-    /**
171
-     * Returns the list of supported privileges for this node.
172
-     *
173
-     * The returned data structure is a list of nested privileges.
174
-     * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
175
-     * standard structure.
176
-     *
177
-     * If null is returned from this method, the default privilege set is used,
178
-     * which is fine for most common usecases.
179
-     *
180
-     * @return array|null
181
-     */
182
-    public function getSupportedPrivilegeSet() {
183
-
184
-        return null;
185
-
186
-    }
24
+	/**
25
+	 * Name of this collection.
26
+	 *
27
+	 * @var string
28
+	 */
29
+	public $collectionName = 'home';
30
+
31
+	/**
32
+	 * Path to where the users' files are actually stored.
33
+	 *
34
+	 * @var string
35
+	 */
36
+	protected $storagePath;
37
+
38
+	/**
39
+	 * Creates the home collection.
40
+	 *
41
+	 * @param BackendInterface $principalBackend
42
+	 * @param string $storagePath Where the actual files are stored.
43
+	 * @param string $principalPrefix list of principals to iterate.
44
+	 */
45
+	public function __construct(BackendInterface $principalBackend, $storagePath, $principalPrefix = 'principals') {
46
+
47
+		parent::__construct($principalBackend, $principalPrefix);
48
+		$this->storagePath = $storagePath;
49
+
50
+	}
51
+
52
+	/**
53
+	 * Returns the name of the node.
54
+	 *
55
+	 * This is used to generate the url.
56
+	 *
57
+	 * @return string
58
+	 */
59
+	public function getName() {
60
+
61
+		return $this->collectionName;
62
+
63
+	}
64
+
65
+	/**
66
+	 * Returns a principals' collection of files.
67
+	 *
68
+	 * The passed array contains principal information, and is guaranteed to
69
+	 * at least contain a uri item. Other properties may or may not be
70
+	 * supplied by the authentication backend.
71
+	 *
72
+	 * @param array $principalInfo
73
+	 * @return void
74
+	 */
75
+	public function getChildForPrincipal(array $principalInfo) {
76
+
77
+		$owner = $principalInfo['uri'];
78
+		$acl = [
79
+			[
80
+				'privilege' => '{DAV:}read',
81
+				'principal' => $owner,
82
+				'protected' => true,
83
+			],
84
+			[
85
+				'privilege' => '{DAV:}write',
86
+				'principal' => $owner,
87
+				'protected' => true,
88
+			],
89
+		];
90
+
91
+		list(, $principalBaseName) = Uri\split($owner);
92
+
93
+		$path = $this->storagePath . '/' . $principalBaseName;
94
+
95
+		if (!is_dir($path)) {
96
+			mkdir($path, 0777, true);
97
+		}
98
+		return new Collection(
99
+			$path,
100
+			$acl,
101
+			$owner
102
+		);
103
+
104
+	}
105
+
106
+	/**
107
+	 * Returns the owner principal
108
+	 *
109
+	 * This must be a url to a principal, or null if there's no owner
110
+	 *
111
+	 * @return string|null
112
+	 */
113
+	public function getOwner() {
114
+
115
+		return null;
116
+
117
+	}
118
+
119
+	/**
120
+	 * Returns a group principal
121
+	 *
122
+	 * This must be a url to a principal, or null if there's no owner
123
+	 *
124
+	 * @return string|null
125
+	 */
126
+	public function getGroup() {
127
+
128
+		return null;
129
+
130
+	}
131
+
132
+	/**
133
+	 * Returns a list of ACE's for this node.
134
+	 *
135
+	 * Each ACE has the following properties:
136
+	 *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
137
+	 *     currently the only supported privileges
138
+	 *   * 'principal', a url to the principal who owns the node
139
+	 *   * 'protected' (optional), indicating that this ACE is not allowed to
140
+	 *      be updated.
141
+	 *
142
+	 * @return array
143
+	 */
144
+	public function getACL() {
145
+
146
+		return [
147
+			[
148
+				'principal' => '{DAV:}authenticated',
149
+				'privilege' => '{DAV:}read',
150
+				'protected' => true,
151
+			]
152
+		];
153
+
154
+	}
155
+
156
+	/**
157
+	 * Updates the ACL
158
+	 *
159
+	 * This method will receive a list of new ACE's as an array argument.
160
+	 *
161
+	 * @param array $acl
162
+	 * @return void
163
+	 */
164
+	public function setACL(array $acl) {
165
+
166
+		throw new Forbidden('Setting ACL is not allowed here');
167
+
168
+	}
169
+
170
+	/**
171
+	 * Returns the list of supported privileges for this node.
172
+	 *
173
+	 * The returned data structure is a list of nested privileges.
174
+	 * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
175
+	 * standard structure.
176
+	 *
177
+	 * If null is returned from this method, the default privilege set is used,
178
+	 * which is fine for most common usecases.
179
+	 *
180
+	 * @return array|null
181
+	 */
182
+	public function getSupportedPrivilegeSet() {
183
+
184
+		return null;
185
+
186
+	}
187 187
 
188 188
 }
Please login to merge, or discard this patch.
Doc Comments   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -70,7 +70,7 @@
 block discarded – undo
70 70
      * supplied by the authentication backend.
71 71
      *
72 72
      * @param array $principalInfo
73
-     * @return void
73
+     * @return Collection
74 74
      */
75 75
     public function getChildForPrincipal(array $principalInfo) {
76 76
 
Please login to merge, or discard this patch.
libraries/SabreDAV/DAVACL/Plugin.php 5 patches
Unused Use Statements   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -3,8 +3,8 @@
 block discarded – undo
3 3
 namespace Sabre\DAVACL;
4 4
 
5 5
 use Sabre\DAV;
6
-use Sabre\DAV\INode;
7 6
 use Sabre\DAV\Exception\BadRequest;
7
+use Sabre\DAV\INode;
8 8
 use Sabre\HTTP\RequestInterface;
9 9
 use Sabre\HTTP\ResponseInterface;
10 10
 use Sabre\Uri;
Please login to merge, or discard this patch.
Indentation   +1255 added lines, -1255 removed lines patch added patch discarded remove patch
@@ -25,1265 +25,1265 @@  discard block
 block discarded – undo
25 25
  */
26 26
 class Plugin extends DAV\ServerPlugin {
27 27
 
28
-    /**
29
-     * Recursion constants
30
-     *
31
-     * This only checks the base node
32
-     */
33
-    const R_PARENT = 1;
34
-
35
-    /**
36
-     * Recursion constants
37
-     *
38
-     * This checks every node in the tree
39
-     */
40
-    const R_RECURSIVE = 2;
41
-
42
-    /**
43
-     * Recursion constants
44
-     *
45
-     * This checks every parentnode in the tree, but not leaf-nodes.
46
-     */
47
-    const R_RECURSIVEPARENTS = 3;
48
-
49
-    /**
50
-     * Reference to server object.
51
-     *
52
-     * @var Sabre\DAV\Server
53
-     */
54
-    protected $server;
55
-
56
-    /**
57
-     * List of urls containing principal collections.
58
-     * Modify this if your principals are located elsewhere.
59
-     *
60
-     * @var array
61
-     */
62
-    public $principalCollectionSet = [
63
-        'principals',
64
-    ];
65
-
66
-    /**
67
-     * By default ACL is only enforced for nodes that have ACL support (the
68
-     * ones that implement IACL). For any other node, access is
69
-     * always granted.
70
-     *
71
-     * To override this behaviour you can turn this setting off. This is useful
72
-     * if you plan to fully support ACL in the entire tree.
73
-     *
74
-     * @var bool
75
-     */
76
-    public $allowAccessToNodesWithoutACL = true;
77
-
78
-    /**
79
-     * By default nodes that are inaccessible by the user, can still be seen
80
-     * in directory listings (PROPFIND on parent with Depth: 1)
81
-     *
82
-     * In certain cases it's desirable to hide inaccessible nodes. Setting this
83
-     * to true will cause these nodes to be hidden from directory listings.
84
-     *
85
-     * @var bool
86
-     */
87
-    public $hideNodesFromListings = false;
88
-
89
-    /**
90
-     * This list of properties are the properties a client can search on using
91
-     * the {DAV:}principal-property-search report.
92
-     *
93
-     * The keys are the property names, values are descriptions.
94
-     *
95
-     * @var array
96
-     */
97
-    public $principalSearchPropertySet = [
98
-        '{DAV:}displayname'                     => 'Display name',
99
-        '{http://sabredav.org/ns}email-address' => 'Email address',
100
-    ];
101
-
102
-    /**
103
-     * Any principal uri's added here, will automatically be added to the list
104
-     * of ACL's. They will effectively receive {DAV:}all privileges, as a
105
-     * protected privilege.
106
-     *
107
-     * @var array
108
-     */
109
-    public $adminPrincipals = [];
110
-
111
-    /**
112
-     * Returns a list of features added by this plugin.
113
-     *
114
-     * This list is used in the response of a HTTP OPTIONS request.
115
-     *
116
-     * @return array
117
-     */
118
-    public function getFeatures() {
119
-
120
-        return ['access-control', 'calendarserver-principal-property-search'];
121
-
122
-    }
123
-
124
-    /**
125
-     * Returns a list of available methods for a given url
126
-     *
127
-     * @param string $uri
128
-     * @return array
129
-     */
130
-    public function getMethods($uri) {
131
-
132
-        return ['ACL'];
133
-
134
-    }
135
-
136
-    /**
137
-     * Returns a plugin name.
138
-     *
139
-     * Using this name other plugins will be able to access other plugins
140
-     * using Sabre\DAV\Server::getPlugin
141
-     *
142
-     * @return string
143
-     */
144
-    public function getPluginName() {
145
-
146
-        return 'acl';
147
-
148
-    }
149
-
150
-    /**
151
-     * Returns a list of reports this plugin supports.
152
-     *
153
-     * This will be used in the {DAV:}supported-report-set property.
154
-     * Note that you still need to subscribe to the 'report' event to actually
155
-     * implement them
156
-     *
157
-     * @param string $uri
158
-     * @return array
159
-     */
160
-    public function getSupportedReportSet($uri) {
161
-
162
-        return [
163
-            '{DAV:}expand-property',
164
-            '{DAV:}principal-property-search',
165
-            '{DAV:}principal-search-property-set',
166
-        ];
167
-
168
-    }
169
-
170
-
171
-    /**
172
-     * Checks if the current user has the specified privilege(s).
173
-     *
174
-     * You can specify a single privilege, or a list of privileges.
175
-     * This method will throw an exception if the privilege is not available
176
-     * and return true otherwise.
177
-     *
178
-     * @param string $uri
179
-     * @param array|string $privileges
180
-     * @param int $recursion
181
-     * @param bool $throwExceptions if set to false, this method won't throw exceptions.
182
-     * @throws Sabre\DAVACL\Exception\NeedPrivileges
183
-     * @return bool
184
-     */
185
-    public function checkPrivileges($uri, $privileges, $recursion = self::R_PARENT, $throwExceptions = true) {
186
-
187
-        if (!is_array($privileges)) $privileges = [$privileges];
188
-
189
-        $acl = $this->getCurrentUserPrivilegeSet($uri);
190
-
191
-        if (is_null($acl)) {
192
-            if ($this->allowAccessToNodesWithoutACL) {
193
-                return true;
194
-            } else {
195
-                if ($throwExceptions)
196
-                    throw new Exception\NeedPrivileges($uri, $privileges);
197
-                else
198
-                    return false;
199
-
200
-            }
201
-        }
202
-
203
-        $failed = [];
204
-        foreach ($privileges as $priv) {
205
-
206
-            if (!in_array($priv, $acl)) {
207
-                $failed[] = $priv;
208
-            }
209
-
210
-        }
211
-
212
-        if ($failed) {
213
-            if ($throwExceptions)
214
-                throw new Exception\NeedPrivileges($uri, $failed);
215
-            else
216
-                return false;
217
-        }
218
-        return true;
28
+	/**
29
+	 * Recursion constants
30
+	 *
31
+	 * This only checks the base node
32
+	 */
33
+	const R_PARENT = 1;
34
+
35
+	/**
36
+	 * Recursion constants
37
+	 *
38
+	 * This checks every node in the tree
39
+	 */
40
+	const R_RECURSIVE = 2;
41
+
42
+	/**
43
+	 * Recursion constants
44
+	 *
45
+	 * This checks every parentnode in the tree, but not leaf-nodes.
46
+	 */
47
+	const R_RECURSIVEPARENTS = 3;
48
+
49
+	/**
50
+	 * Reference to server object.
51
+	 *
52
+	 * @var Sabre\DAV\Server
53
+	 */
54
+	protected $server;
55
+
56
+	/**
57
+	 * List of urls containing principal collections.
58
+	 * Modify this if your principals are located elsewhere.
59
+	 *
60
+	 * @var array
61
+	 */
62
+	public $principalCollectionSet = [
63
+		'principals',
64
+	];
65
+
66
+	/**
67
+	 * By default ACL is only enforced for nodes that have ACL support (the
68
+	 * ones that implement IACL). For any other node, access is
69
+	 * always granted.
70
+	 *
71
+	 * To override this behaviour you can turn this setting off. This is useful
72
+	 * if you plan to fully support ACL in the entire tree.
73
+	 *
74
+	 * @var bool
75
+	 */
76
+	public $allowAccessToNodesWithoutACL = true;
77
+
78
+	/**
79
+	 * By default nodes that are inaccessible by the user, can still be seen
80
+	 * in directory listings (PROPFIND on parent with Depth: 1)
81
+	 *
82
+	 * In certain cases it's desirable to hide inaccessible nodes. Setting this
83
+	 * to true will cause these nodes to be hidden from directory listings.
84
+	 *
85
+	 * @var bool
86
+	 */
87
+	public $hideNodesFromListings = false;
88
+
89
+	/**
90
+	 * This list of properties are the properties a client can search on using
91
+	 * the {DAV:}principal-property-search report.
92
+	 *
93
+	 * The keys are the property names, values are descriptions.
94
+	 *
95
+	 * @var array
96
+	 */
97
+	public $principalSearchPropertySet = [
98
+		'{DAV:}displayname'                     => 'Display name',
99
+		'{http://sabredav.org/ns}email-address' => 'Email address',
100
+	];
101
+
102
+	/**
103
+	 * Any principal uri's added here, will automatically be added to the list
104
+	 * of ACL's. They will effectively receive {DAV:}all privileges, as a
105
+	 * protected privilege.
106
+	 *
107
+	 * @var array
108
+	 */
109
+	public $adminPrincipals = [];
110
+
111
+	/**
112
+	 * Returns a list of features added by this plugin.
113
+	 *
114
+	 * This list is used in the response of a HTTP OPTIONS request.
115
+	 *
116
+	 * @return array
117
+	 */
118
+	public function getFeatures() {
119
+
120
+		return ['access-control', 'calendarserver-principal-property-search'];
121
+
122
+	}
123
+
124
+	/**
125
+	 * Returns a list of available methods for a given url
126
+	 *
127
+	 * @param string $uri
128
+	 * @return array
129
+	 */
130
+	public function getMethods($uri) {
131
+
132
+		return ['ACL'];
133
+
134
+	}
135
+
136
+	/**
137
+	 * Returns a plugin name.
138
+	 *
139
+	 * Using this name other plugins will be able to access other plugins
140
+	 * using Sabre\DAV\Server::getPlugin
141
+	 *
142
+	 * @return string
143
+	 */
144
+	public function getPluginName() {
145
+
146
+		return 'acl';
147
+
148
+	}
149
+
150
+	/**
151
+	 * Returns a list of reports this plugin supports.
152
+	 *
153
+	 * This will be used in the {DAV:}supported-report-set property.
154
+	 * Note that you still need to subscribe to the 'report' event to actually
155
+	 * implement them
156
+	 *
157
+	 * @param string $uri
158
+	 * @return array
159
+	 */
160
+	public function getSupportedReportSet($uri) {
161
+
162
+		return [
163
+			'{DAV:}expand-property',
164
+			'{DAV:}principal-property-search',
165
+			'{DAV:}principal-search-property-set',
166
+		];
167
+
168
+	}
169
+
170
+
171
+	/**
172
+	 * Checks if the current user has the specified privilege(s).
173
+	 *
174
+	 * You can specify a single privilege, or a list of privileges.
175
+	 * This method will throw an exception if the privilege is not available
176
+	 * and return true otherwise.
177
+	 *
178
+	 * @param string $uri
179
+	 * @param array|string $privileges
180
+	 * @param int $recursion
181
+	 * @param bool $throwExceptions if set to false, this method won't throw exceptions.
182
+	 * @throws Sabre\DAVACL\Exception\NeedPrivileges
183
+	 * @return bool
184
+	 */
185
+	public function checkPrivileges($uri, $privileges, $recursion = self::R_PARENT, $throwExceptions = true) {
186
+
187
+		if (!is_array($privileges)) $privileges = [$privileges];
188
+
189
+		$acl = $this->getCurrentUserPrivilegeSet($uri);
190
+
191
+		if (is_null($acl)) {
192
+			if ($this->allowAccessToNodesWithoutACL) {
193
+				return true;
194
+			} else {
195
+				if ($throwExceptions)
196
+					throw new Exception\NeedPrivileges($uri, $privileges);
197
+				else
198
+					return false;
199
+
200
+			}
201
+		}
202
+
203
+		$failed = [];
204
+		foreach ($privileges as $priv) {
205
+
206
+			if (!in_array($priv, $acl)) {
207
+				$failed[] = $priv;
208
+			}
209
+
210
+		}
211
+
212
+		if ($failed) {
213
+			if ($throwExceptions)
214
+				throw new Exception\NeedPrivileges($uri, $failed);
215
+			else
216
+				return false;
217
+		}
218
+		return true;
219 219
 
220
-    }
221
-
222
-    /**
223
-     * Returns the standard users' principal.
224
-     *
225
-     * This is one authorative principal url for the current user.
226
-     * This method will return null if the user wasn't logged in.
227
-     *
228
-     * @return string|null
229
-     */
230
-    public function getCurrentUserPrincipal() {
231
-
232
-        $authPlugin = $this->server->getPlugin('auth');
233
-        if (is_null($authPlugin)) return null;
234
-        /** @var $authPlugin Sabre\DAV\Auth\Plugin */
235
-
236
-        return $authPlugin->getCurrentPrincipal();
237
-
238
-    }
239
-
240
-
241
-    /**
242
-     * Returns a list of principals that's associated to the current
243
-     * user, either directly or through group membership.
244
-     *
245
-     * @return array
246
-     */
247
-    public function getCurrentUserPrincipals() {
248
-
249
-        $currentUser = $this->getCurrentUserPrincipal();
250
-
251
-        if (is_null($currentUser)) return [];
252
-
253
-        return array_merge(
254
-            [$currentUser],
255
-            $this->getPrincipalMembership($currentUser)
256
-        );
257
-
258
-    }
259
-
260
-    /**
261
-     * This array holds a cache for all the principals that are associated with
262
-     * a single principal.
263
-     *
264
-     * @var array
265
-     */
266
-    protected $principalMembershipCache = [];
267
-
268
-
269
-    /**
270
-     * Returns all the principal groups the specified principal is a member of.
271
-     *
272
-     * @param string $principal
273
-     * @return array
274
-     */
275
-    public function getPrincipalMembership($mainPrincipal) {
276
-
277
-        // First check our cache
278
-        if (isset($this->principalMembershipCache[$mainPrincipal])) {
279
-            return $this->principalMembershipCache[$mainPrincipal];
280
-        }
281
-
282
-        $check = [$mainPrincipal];
283
-        $principals = [];
284
-
285
-        while (count($check)) {
286
-
287
-            $principal = array_shift($check);
288
-
289
-            $node = $this->server->tree->getNodeForPath($principal);
290
-            if ($node instanceof IPrincipal) {
291
-                foreach ($node->getGroupMembership() as $groupMember) {
292
-
293
-                    if (!in_array($groupMember, $principals)) {
294
-
295
-                        $check[] = $groupMember;
296
-                        $principals[] = $groupMember;
297
-
298
-                    }
299
-
300
-                }
301
-
302
-            }
303
-
304
-        }
305
-
306
-        // Store the result in the cache
307
-        $this->principalMembershipCache[$mainPrincipal] = $principals;
308
-
309
-        return $principals;
310
-
311
-    }
312
-
313
-    /**
314
-     * Returns the supported privilege structure for this ACL plugin.
315
-     *
316
-     * See RFC3744 for more details. Currently we default on a simple,
317
-     * standard structure.
318
-     *
319
-     * You can either get the list of privileges by a uri (path) or by
320
-     * specifying a Node.
321
-     *
322
-     * @param string|INode $node
323
-     * @return array
324
-     */
325
-    public function getSupportedPrivilegeSet($node) {
326
-
327
-        if (is_string($node)) {
328
-            $node = $this->server->tree->getNodeForPath($node);
329
-        }
330
-
331
-        if ($node instanceof IACL) {
332
-            $result = $node->getSupportedPrivilegeSet();
333
-
334
-            if ($result)
335
-                return $result;
336
-        }
337
-
338
-        return self::getDefaultSupportedPrivilegeSet();
339
-
340
-    }
341
-
342
-    /**
343
-     * Returns a fairly standard set of privileges, which may be useful for
344
-     * other systems to use as a basis.
345
-     *
346
-     * @return array
347
-     */
348
-    static function getDefaultSupportedPrivilegeSet() {
349
-
350
-        return [
351
-            'privilege'  => '{DAV:}all',
352
-            'abstract'   => true,
353
-            'aggregates' => [
354
-                [
355
-                    'privilege'  => '{DAV:}read',
356
-                    'aggregates' => [
357
-                        [
358
-                            'privilege' => '{DAV:}read-acl',
359
-                            'abstract'  => false,
360
-                        ],
361
-                        [
362
-                            'privilege' => '{DAV:}read-current-user-privilege-set',
363
-                            'abstract'  => false,
364
-                        ],
365
-                    ],
366
-                ], // {DAV:}read
367
-                [
368
-                    'privilege'  => '{DAV:}write',
369
-                    'aggregates' => [
370
-                        [
371
-                            'privilege' => '{DAV:}write-acl',
372
-                            'abstract'  => false,
373
-                        ],
374
-                        [
375
-                            'privilege' => '{DAV:}write-properties',
376
-                            'abstract'  => false,
377
-                        ],
378
-                        [
379
-                            'privilege' => '{DAV:}write-content',
380
-                            'abstract'  => false,
381
-                        ],
382
-                        [
383
-                            'privilege' => '{DAV:}bind',
384
-                            'abstract'  => false,
385
-                        ],
386
-                        [
387
-                            'privilege' => '{DAV:}unbind',
388
-                            'abstract'  => false,
389
-                        ],
390
-                        [
391
-                            'privilege' => '{DAV:}unlock',
392
-                            'abstract'  => false,
393
-                        ],
394
-                    ],
395
-                ], // {DAV:}write
396
-            ],
397
-        ]; // {DAV:}all
398
-
399
-    }
400
-
401
-    /**
402
-     * Returns the supported privilege set as a flat list
403
-     *
404
-     * This is much easier to parse.
405
-     *
406
-     * The returned list will be index by privilege name.
407
-     * The value is a struct containing the following properties:
408
-     *   - aggregates
409
-     *   - abstract
410
-     *   - concrete
411
-     *
412
-     * @param string|INode $node
413
-     * @return array
414
-     */
415
-    final function getFlatPrivilegeSet($node) {
416
-
417
-        $privs = $this->getSupportedPrivilegeSet($node);
418
-
419
-        $fpsTraverse = null;
420
-        $fpsTraverse = function($priv, $concrete, &$flat) use (&$fpsTraverse) {
421
-
422
-            $myPriv = [
423
-                'privilege'  => $priv['privilege'],
424
-                'abstract'   => isset($priv['abstract']) && $priv['abstract'],
425
-                'aggregates' => [],
426
-                'concrete'   => isset($priv['abstract']) && $priv['abstract'] ? $concrete : $priv['privilege'],
427
-            ];
428
-
429
-            if (isset($priv['aggregates'])) {
430
-
431
-                foreach ($priv['aggregates'] as $subPriv) {
432
-
433
-                    $myPriv['aggregates'][] = $subPriv['privilege'];
434
-
435
-                }
436
-
437
-            }
438
-
439
-            $flat[$priv['privilege']] = $myPriv;
440
-
441
-            if (isset($priv['aggregates'])) {
442
-
443
-                foreach ($priv['aggregates'] as $subPriv) {
444
-
445
-                    $fpsTraverse($subPriv, $myPriv['concrete'], $flat);
446
-
447
-                }
448
-
449
-            }
450
-
451
-        };
452
-
453
-        $flat = [];
454
-        $fpsTraverse($privs, null, $flat);
455
-
456
-        return $flat;
457
-
458
-    }
459
-
460
-    /**
461
-     * Returns the full ACL list.
462
-     *
463
-     * Either a uri or a INode may be passed.
464
-     *
465
-     * null will be returned if the node doesn't support ACLs.
466
-     *
467
-     * @param string|DAV\INode $node
468
-     * @return array
469
-     */
470
-    public function getACL($node) {
471
-
472
-        if (is_string($node)) {
473
-            $node = $this->server->tree->getNodeForPath($node);
474
-        }
475
-        if (!$node instanceof IACL) {
476
-            return null;
477
-        }
478
-        $acl = $node->getACL();
479
-        foreach ($this->adminPrincipals as $adminPrincipal) {
480
-            $acl[] = [
481
-                'principal' => $adminPrincipal,
482
-                'privilege' => '{DAV:}all',
483
-                'protected' => true,
484
-            ];
485
-        }
486
-        return $acl;
220
+	}
221
+
222
+	/**
223
+	 * Returns the standard users' principal.
224
+	 *
225
+	 * This is one authorative principal url for the current user.
226
+	 * This method will return null if the user wasn't logged in.
227
+	 *
228
+	 * @return string|null
229
+	 */
230
+	public function getCurrentUserPrincipal() {
231
+
232
+		$authPlugin = $this->server->getPlugin('auth');
233
+		if (is_null($authPlugin)) return null;
234
+		/** @var $authPlugin Sabre\DAV\Auth\Plugin */
235
+
236
+		return $authPlugin->getCurrentPrincipal();
237
+
238
+	}
239
+
240
+
241
+	/**
242
+	 * Returns a list of principals that's associated to the current
243
+	 * user, either directly or through group membership.
244
+	 *
245
+	 * @return array
246
+	 */
247
+	public function getCurrentUserPrincipals() {
248
+
249
+		$currentUser = $this->getCurrentUserPrincipal();
250
+
251
+		if (is_null($currentUser)) return [];
252
+
253
+		return array_merge(
254
+			[$currentUser],
255
+			$this->getPrincipalMembership($currentUser)
256
+		);
257
+
258
+	}
259
+
260
+	/**
261
+	 * This array holds a cache for all the principals that are associated with
262
+	 * a single principal.
263
+	 *
264
+	 * @var array
265
+	 */
266
+	protected $principalMembershipCache = [];
267
+
268
+
269
+	/**
270
+	 * Returns all the principal groups the specified principal is a member of.
271
+	 *
272
+	 * @param string $principal
273
+	 * @return array
274
+	 */
275
+	public function getPrincipalMembership($mainPrincipal) {
276
+
277
+		// First check our cache
278
+		if (isset($this->principalMembershipCache[$mainPrincipal])) {
279
+			return $this->principalMembershipCache[$mainPrincipal];
280
+		}
281
+
282
+		$check = [$mainPrincipal];
283
+		$principals = [];
284
+
285
+		while (count($check)) {
286
+
287
+			$principal = array_shift($check);
288
+
289
+			$node = $this->server->tree->getNodeForPath($principal);
290
+			if ($node instanceof IPrincipal) {
291
+				foreach ($node->getGroupMembership() as $groupMember) {
292
+
293
+					if (!in_array($groupMember, $principals)) {
294
+
295
+						$check[] = $groupMember;
296
+						$principals[] = $groupMember;
297
+
298
+					}
299
+
300
+				}
301
+
302
+			}
303
+
304
+		}
305
+
306
+		// Store the result in the cache
307
+		$this->principalMembershipCache[$mainPrincipal] = $principals;
308
+
309
+		return $principals;
310
+
311
+	}
312
+
313
+	/**
314
+	 * Returns the supported privilege structure for this ACL plugin.
315
+	 *
316
+	 * See RFC3744 for more details. Currently we default on a simple,
317
+	 * standard structure.
318
+	 *
319
+	 * You can either get the list of privileges by a uri (path) or by
320
+	 * specifying a Node.
321
+	 *
322
+	 * @param string|INode $node
323
+	 * @return array
324
+	 */
325
+	public function getSupportedPrivilegeSet($node) {
326
+
327
+		if (is_string($node)) {
328
+			$node = $this->server->tree->getNodeForPath($node);
329
+		}
330
+
331
+		if ($node instanceof IACL) {
332
+			$result = $node->getSupportedPrivilegeSet();
333
+
334
+			if ($result)
335
+				return $result;
336
+		}
337
+
338
+		return self::getDefaultSupportedPrivilegeSet();
339
+
340
+	}
341
+
342
+	/**
343
+	 * Returns a fairly standard set of privileges, which may be useful for
344
+	 * other systems to use as a basis.
345
+	 *
346
+	 * @return array
347
+	 */
348
+	static function getDefaultSupportedPrivilegeSet() {
349
+
350
+		return [
351
+			'privilege'  => '{DAV:}all',
352
+			'abstract'   => true,
353
+			'aggregates' => [
354
+				[
355
+					'privilege'  => '{DAV:}read',
356
+					'aggregates' => [
357
+						[
358
+							'privilege' => '{DAV:}read-acl',
359
+							'abstract'  => false,
360
+						],
361
+						[
362
+							'privilege' => '{DAV:}read-current-user-privilege-set',
363
+							'abstract'  => false,
364
+						],
365
+					],
366
+				], // {DAV:}read
367
+				[
368
+					'privilege'  => '{DAV:}write',
369
+					'aggregates' => [
370
+						[
371
+							'privilege' => '{DAV:}write-acl',
372
+							'abstract'  => false,
373
+						],
374
+						[
375
+							'privilege' => '{DAV:}write-properties',
376
+							'abstract'  => false,
377
+						],
378
+						[
379
+							'privilege' => '{DAV:}write-content',
380
+							'abstract'  => false,
381
+						],
382
+						[
383
+							'privilege' => '{DAV:}bind',
384
+							'abstract'  => false,
385
+						],
386
+						[
387
+							'privilege' => '{DAV:}unbind',
388
+							'abstract'  => false,
389
+						],
390
+						[
391
+							'privilege' => '{DAV:}unlock',
392
+							'abstract'  => false,
393
+						],
394
+					],
395
+				], // {DAV:}write
396
+			],
397
+		]; // {DAV:}all
398
+
399
+	}
400
+
401
+	/**
402
+	 * Returns the supported privilege set as a flat list
403
+	 *
404
+	 * This is much easier to parse.
405
+	 *
406
+	 * The returned list will be index by privilege name.
407
+	 * The value is a struct containing the following properties:
408
+	 *   - aggregates
409
+	 *   - abstract
410
+	 *   - concrete
411
+	 *
412
+	 * @param string|INode $node
413
+	 * @return array
414
+	 */
415
+	final function getFlatPrivilegeSet($node) {
416
+
417
+		$privs = $this->getSupportedPrivilegeSet($node);
418
+
419
+		$fpsTraverse = null;
420
+		$fpsTraverse = function($priv, $concrete, &$flat) use (&$fpsTraverse) {
421
+
422
+			$myPriv = [
423
+				'privilege'  => $priv['privilege'],
424
+				'abstract'   => isset($priv['abstract']) && $priv['abstract'],
425
+				'aggregates' => [],
426
+				'concrete'   => isset($priv['abstract']) && $priv['abstract'] ? $concrete : $priv['privilege'],
427
+			];
428
+
429
+			if (isset($priv['aggregates'])) {
430
+
431
+				foreach ($priv['aggregates'] as $subPriv) {
432
+
433
+					$myPriv['aggregates'][] = $subPriv['privilege'];
434
+
435
+				}
436
+
437
+			}
438
+
439
+			$flat[$priv['privilege']] = $myPriv;
440
+
441
+			if (isset($priv['aggregates'])) {
442
+
443
+				foreach ($priv['aggregates'] as $subPriv) {
444
+
445
+					$fpsTraverse($subPriv, $myPriv['concrete'], $flat);
446
+
447
+				}
448
+
449
+			}
450
+
451
+		};
452
+
453
+		$flat = [];
454
+		$fpsTraverse($privs, null, $flat);
455
+
456
+		return $flat;
457
+
458
+	}
459
+
460
+	/**
461
+	 * Returns the full ACL list.
462
+	 *
463
+	 * Either a uri or a INode may be passed.
464
+	 *
465
+	 * null will be returned if the node doesn't support ACLs.
466
+	 *
467
+	 * @param string|DAV\INode $node
468
+	 * @return array
469
+	 */
470
+	public function getACL($node) {
471
+
472
+		if (is_string($node)) {
473
+			$node = $this->server->tree->getNodeForPath($node);
474
+		}
475
+		if (!$node instanceof IACL) {
476
+			return null;
477
+		}
478
+		$acl = $node->getACL();
479
+		foreach ($this->adminPrincipals as $adminPrincipal) {
480
+			$acl[] = [
481
+				'principal' => $adminPrincipal,
482
+				'privilege' => '{DAV:}all',
483
+				'protected' => true,
484
+			];
485
+		}
486
+		return $acl;
487 487
 
488
-    }
488
+	}
489 489
 
490
-    /**
491
-     * Returns a list of privileges the current user has
492
-     * on a particular node.
493
-     *
494
-     * Either a uri or a DAV\INode may be passed.
495
-     *
496
-     * null will be returned if the node doesn't support ACLs.
497
-     *
498
-     * @param string|DAV\INode $node
499
-     * @return array
500
-     */
501
-    public function getCurrentUserPrivilegeSet($node) {
490
+	/**
491
+	 * Returns a list of privileges the current user has
492
+	 * on a particular node.
493
+	 *
494
+	 * Either a uri or a DAV\INode may be passed.
495
+	 *
496
+	 * null will be returned if the node doesn't support ACLs.
497
+	 *
498
+	 * @param string|DAV\INode $node
499
+	 * @return array
500
+	 */
501
+	public function getCurrentUserPrivilegeSet($node) {
502 502
 
503
-        if (is_string($node)) {
504
-            $node = $this->server->tree->getNodeForPath($node);
505
-        }
503
+		if (is_string($node)) {
504
+			$node = $this->server->tree->getNodeForPath($node);
505
+		}
506 506
 
507
-        $acl = $this->getACL($node);
507
+		$acl = $this->getACL($node);
508 508
 
509
-        if (is_null($acl)) return null;
509
+		if (is_null($acl)) return null;
510 510
 
511
-        $principals = $this->getCurrentUserPrincipals();
511
+		$principals = $this->getCurrentUserPrincipals();
512 512
 
513
-        $collected = [];
513
+		$collected = [];
514 514
 
515
-        foreach ($acl as $ace) {
515
+		foreach ($acl as $ace) {
516 516
 
517
-            $principal = $ace['principal'];
517
+			$principal = $ace['principal'];
518 518
 
519
-            switch ($principal) {
519
+			switch ($principal) {
520 520
 
521
-                case '{DAV:}owner' :
522
-                    $owner = $node->getOwner();
523
-                    if ($owner && in_array($owner, $principals)) {
524
-                        $collected[] = $ace;
525
-                    }
526
-                    break;
521
+				case '{DAV:}owner' :
522
+					$owner = $node->getOwner();
523
+					if ($owner && in_array($owner, $principals)) {
524
+						$collected[] = $ace;
525
+					}
526
+					break;
527 527
 
528 528
 
529
-                // 'all' matches for every user
530
-                case '{DAV:}all' :
529
+				// 'all' matches for every user
530
+				case '{DAV:}all' :
531 531
 
532
-                // 'authenticated' matched for every user that's logged in.
533
-                // Since it's not possible to use ACL while not being logged
534
-                // in, this is also always true.
535
-                case '{DAV:}authenticated' :
536
-                    $collected[] = $ace;
537
-                    break;
538
-
539
-                // 'unauthenticated' can never occur either, so we simply
540
-                // ignore these.
541
-                case '{DAV:}unauthenticated' :
542
-                    break;
543
-
544
-                default :
545
-                    if (in_array($ace['principal'], $principals)) {
546
-                        $collected[] = $ace;
547
-                    }
548
-                    break;
549
-
550
-            }
551
-
552
-
553
-        }
554
-
555
-        // Now we deduct all aggregated privileges.
556
-        $flat = $this->getFlatPrivilegeSet($node);
557
-
558
-        $collected2 = [];
559
-        while (count($collected)) {
560
-
561
-            $current = array_pop($collected);
562
-            $collected2[] = $current['privilege'];
563
-
564
-            foreach ($flat[$current['privilege']]['aggregates'] as $subPriv) {
565
-                $collected2[] = $subPriv;
566
-                $collected[] = $flat[$subPriv];
567
-            }
568
-
569
-        }
570
-
571
-        return array_values(array_unique($collected2));
572
-
573
-    }
574
-
575
-
576
-    /**
577
-     * Returns a principal based on its uri.
578
-     *
579
-     * Returns null if the principal could not be found.
580
-     *
581
-     * @param string $uri
582
-     * @return null|string
583
-     */
584
-    public function getPrincipalByUri($uri) {
585
-
586
-        $result = null;
587
-        $collections = $this->principalCollectionSet;
588
-        foreach ($collections as $collection) {
589
-
590
-            $principalCollection = $this->server->tree->getNodeForPath($collection);
591
-            if (!$principalCollection instanceof IPrincipalCollection) {
592
-                // Not a principal collection, we're simply going to ignore
593
-                // this.
594
-                continue;
595
-            }
596
-
597
-            $result = $principalCollection->findByUri($uri);
598
-            if ($result) {
599
-                return $result;
600
-            }
601
-
602
-        }
603
-
604
-    }
605
-
606
-    /**
607
-     * Principal property search
608
-     *
609
-     * This method can search for principals matching certain values in
610
-     * properties.
611
-     *
612
-     * This method will return a list of properties for the matched properties.
613
-     *
614
-     * @param array $searchProperties    The properties to search on. This is a
615
-     *                                   key-value list. The keys are property
616
-     *                                   names, and the values the strings to
617
-     *                                   match them on.
618
-     * @param array $requestedProperties This is the list of properties to
619
-     *                                   return for every match.
620
-     * @param string $collectionUri      The principal collection to search on.
621
-     *                                   If this is ommitted, the standard
622
-     *                                   principal collection-set will be used.
623
-     * @param string $test               "allof" to use AND to search the
624
-     *                                   properties. 'anyof' for OR.
625
-     * @return array     This method returns an array structure similar to
626
-     *                  Sabre\DAV\Server::getPropertiesForPath. Returned
627
-     *                  properties are index by a HTTP status code.
628
-     */
629
-    public function principalSearch(array $searchProperties, array $requestedProperties, $collectionUri = null, $test = 'allof') {
630
-
631
-        if (!is_null($collectionUri)) {
632
-            $uris = [$collectionUri];
633
-        } else {
634
-            $uris = $this->principalCollectionSet;
635
-        }
636
-
637
-        $lookupResults = [];
638
-        foreach ($uris as $uri) {
639
-
640
-            $principalCollection = $this->server->tree->getNodeForPath($uri);
641
-            if (!$principalCollection instanceof IPrincipalCollection) {
642
-                // Not a principal collection, we're simply going to ignore
643
-                // this.
644
-                continue;
645
-            }
646
-
647
-            $results = $principalCollection->searchPrincipals($searchProperties, $test);
648
-            foreach ($results as $result) {
649
-                $lookupResults[] = rtrim($uri, '/') . '/' . $result;
650
-            }
651
-
652
-        }
653
-
654
-        $matches = [];
655
-
656
-        foreach ($lookupResults as $lookupResult) {
657
-
658
-            list($matches[]) = $this->server->getPropertiesForPath($lookupResult, $requestedProperties, 0);
659
-
660
-        }
661
-
662
-        return $matches;
663
-
664
-    }
665
-
666
-    /**
667
-     * Sets up the plugin
668
-     *
669
-     * This method is automatically called by the server class.
670
-     *
671
-     * @param DAV\Server $server
672
-     * @return void
673
-     */
674
-    public function initialize(DAV\Server $server) {
675
-
676
-        $this->server = $server;
677
-        $server->on('propFind',            [$this, 'propFind'], 20);
678
-        $server->on('beforeMethod',        [$this, 'beforeMethod'], 20);
679
-        $server->on('beforeBind',          [$this, 'beforeBind'], 20);
680
-        $server->on('beforeUnbind',        [$this, 'beforeUnbind'], 20);
681
-        $server->on('propPatch',           [$this, 'propPatch']);
682
-        $server->on('beforeUnlock',        [$this, 'beforeUnlock'], 20);
683
-        $server->on('report',              [$this, 'report']);
684
-        $server->on('method:ACL',          [$this, 'httpAcl']);
685
-        $server->on('onHTMLActionsPanel',  [$this, 'htmlActionsPanel']);
686
-
687
-        array_push($server->protectedProperties,
688
-            '{DAV:}alternate-URI-set',
689
-            '{DAV:}principal-URL',
690
-            '{DAV:}group-membership',
691
-            '{DAV:}principal-collection-set',
692
-            '{DAV:}current-user-principal',
693
-            '{DAV:}supported-privilege-set',
694
-            '{DAV:}current-user-privilege-set',
695
-            '{DAV:}acl',
696
-            '{DAV:}acl-restrictions',
697
-            '{DAV:}inherited-acl-set',
698
-            '{DAV:}owner',
699
-            '{DAV:}group'
700
-        );
701
-
702
-        // Automatically mapping nodes implementing IPrincipal to the
703
-        // {DAV:}principal resourcetype.
704
-        $server->resourceTypeMapping['Sabre\\DAVACL\\IPrincipal'] = '{DAV:}principal';
705
-
706
-        // Mapping the group-member-set property to the HrefList property
707
-        // class.
708
-        $server->xml->elementMap['{DAV:}group-member-set'] = 'Sabre\\DAV\\Xml\\Property\\Href';
709
-        $server->xml->elementMap['{DAV:}acl'] = 'Sabre\\DAVACL\\Xml\\Property\\Acl';
710
-        $server->xml->elementMap['{DAV:}expand-property'] = 'Sabre\\DAVACL\\Xml\\Request\\ExpandPropertyReport';
711
-        $server->xml->elementMap['{DAV:}principal-property-search'] = 'Sabre\\DAVACL\\Xml\\Request\\PrincipalPropertySearchReport';
712
-        $server->xml->elementMap['{DAV:}principal-search-property-set'] = 'Sabre\\DAVACL\\Xml\\Request\\PrincipalSearchPropertySetReport';
713
-
714
-    }
715
-
716
-    /* {{{ Event handlers */
717
-
718
-    /**
719
-     * Triggered before any method is handled
720
-     *
721
-     * @param RequestInterface $request
722
-     * @param ResponseInterface $response
723
-     * @return void
724
-     */
725
-    public function beforeMethod(RequestInterface $request, ResponseInterface $response) {
726
-
727
-        $method = $request->getMethod();
728
-        $path = $request->getPath();
729
-
730
-        $exists = $this->server->tree->nodeExists($path);
731
-
732
-        // If the node doesn't exists, none of these checks apply
733
-        if (!$exists) return;
734
-
735
-        switch ($method) {
736
-
737
-            case 'GET' :
738
-            case 'HEAD' :
739
-            case 'OPTIONS' :
740
-                // For these 3 we only need to know if the node is readable.
741
-                $this->checkPrivileges($path, '{DAV:}read');
742
-                break;
743
-
744
-            case 'PUT' :
745
-            case 'LOCK' :
746
-            case 'UNLOCK' :
747
-                // This method requires the write-content priv if the node
748
-                // already exists, and bind on the parent if the node is being
749
-                // created.
750
-                // The bind privilege is handled in the beforeBind event.
751
-                $this->checkPrivileges($path, '{DAV:}write-content');
752
-                break;
753
-
754
-
755
-            case 'PROPPATCH' :
756
-                $this->checkPrivileges($path, '{DAV:}write-properties');
757
-                break;
758
-
759
-            case 'ACL' :
760
-                $this->checkPrivileges($path, '{DAV:}write-acl');
761
-                break;
762
-
763
-            case 'COPY' :
764
-            case 'MOVE' :
765
-                // Copy requires read privileges on the entire source tree.
766
-                // If the target exists write-content normally needs to be
767
-                // checked, however, we're deleting the node beforehand and
768
-                // creating a new one after, so this is handled by the
769
-                // beforeUnbind event.
770
-                //
771
-                // The creation of the new node is handled by the beforeBind
772
-                // event.
773
-                //
774
-                // If MOVE is used beforeUnbind will also be used to check if
775
-                // the sourcenode can be deleted.
776
-                $this->checkPrivileges($path, '{DAV:}read', self::R_RECURSIVE);
777
-
778
-                break;
779
-
780
-        }
781
-
782
-    }
783
-
784
-    /**
785
-     * Triggered before a new node is created.
786
-     *
787
-     * This allows us to check permissions for any operation that creates a
788
-     * new node, such as PUT, MKCOL, MKCALENDAR, LOCK, COPY and MOVE.
789
-     *
790
-     * @param string $uri
791
-     * @return void
792
-     */
793
-    public function beforeBind($uri) {
794
-
795
-        list($parentUri) = Uri\split($uri);
796
-        $this->checkPrivileges($parentUri, '{DAV:}bind');
797
-
798
-    }
799
-
800
-    /**
801
-     * Triggered before a node is deleted
802
-     *
803
-     * This allows us to check permissions for any operation that will delete
804
-     * an existing node.
805
-     *
806
-     * @param string $uri
807
-     * @return void
808
-     */
809
-    public function beforeUnbind($uri) {
810
-
811
-        list($parentUri) = Uri\split($uri);
812
-        $this->checkPrivileges($parentUri, '{DAV:}unbind', self::R_RECURSIVEPARENTS);
813
-
814
-    }
815
-
816
-    /**
817
-     * Triggered before a node is unlocked.
818
-     *
819
-     * @param string $uri
820
-     * @param DAV\Locks\LockInfo $lock
821
-     * @return void
822
-     */
823
-    public function beforeUnlock($uri, DAV\Locks\LockInfo $lock) {
824
-
825
-
826
-    }
827
-
828
-    /**
829
-     * Triggered before properties are looked up in specific nodes.
830
-     *
831
-     * @param DAV\PropFind $propFind
832
-     * @param DAV\INode $node
833
-     * @param array $requestedProperties
834
-     * @param array $returnedProperties
835
-     * @return bool
836
-     */
837
-    public function propFind(DAV\PropFind $propFind, DAV\INode $node) {
838
-
839
-        $path = $propFind->getPath();
840
-
841
-        // Checking the read permission
842
-        if (!$this->checkPrivileges($path, '{DAV:}read', self::R_PARENT, false)) {
843
-            // User is not allowed to read properties
844
-
845
-            // Returning false causes the property-fetching system to pretend
846
-            // that the node does not exist, and will cause it to be hidden
847
-            // from listings such as PROPFIND or the browser plugin.
848
-            if ($this->hideNodesFromListings) {
849
-                return false;
850
-            }
851
-
852
-            // Otherwise we simply mark every property as 403.
853
-            foreach ($propFind->getRequestedProperties() as $requestedProperty) {
854
-                $propFind->set($requestedProperty, null, 403);
855
-            }
856
-
857
-            return;
858
-
859
-        }
860
-
861
-        /* Adding principal properties */
862
-        if ($node instanceof IPrincipal) {
863
-
864
-            $propFind->handle('{DAV:}alternate-URI-set', function() use ($node) {
865
-                return new DAV\Xml\Property\Href($node->getAlternateUriSet());
866
-            });
867
-            $propFind->handle('{DAV:}principal-URL', function() use ($node) {
868
-                return new DAV\Xml\Property\Href($node->getPrincipalUrl() . '/');
869
-            });
870
-            $propFind->handle('{DAV:}group-member-set', function() use ($node) {
871
-                $members = $node->getGroupMemberSet();
872
-                foreach ($members as $k => $member) {
873
-                    $members[$k] = rtrim($member, '/') . '/';
874
-                }
875
-                return new DAV\Xml\Property\Href($members);
876
-            });
877
-            $propFind->handle('{DAV:}group-membership', function() use ($node) {
878
-                $members = $node->getGroupMembership();
879
-                foreach ($members as $k => $member) {
880
-                    $members[$k] = rtrim($member, '/') . '/';
881
-                }
882
-                return new DAV\Xml\Property\Href($members);
883
-            });
884
-            $propFind->handle('{DAV:}displayname', [$node, 'getDisplayName']);
885
-
886
-        }
887
-
888
-        $propFind->handle('{DAV:}principal-collection-set', function() {
889
-
890
-            $val = $this->principalCollectionSet;
891
-            // Ensuring all collections end with a slash
892
-            foreach ($val as $k => $v) $val[$k] = $v . '/';
893
-            return new DAV\Xml\Property\Href($val);
894
-
895
-        });
896
-        $propFind->handle('{DAV:}current-user-principal', function() {
897
-            if ($url = $this->getCurrentUserPrincipal()) {
898
-                return new Xml\Property\Principal(Xml\Property\Principal::HREF, $url . '/');
899
-            } else {
900
-                return new Xml\Property\Principal(Xml\Property\Principal::UNAUTHENTICATED);
901
-            }
902
-        });
903
-        $propFind->handle('{DAV:}supported-privilege-set', function() use ($node) {
904
-            return new Xml\Property\SupportedPrivilegeSet($this->getSupportedPrivilegeSet($node));
905
-        });
906
-        $propFind->handle('{DAV:}current-user-privilege-set', function() use ($node, $propFind, $path) {
907
-            if (!$this->checkPrivileges($path, '{DAV:}read-current-user-privilege-set', self::R_PARENT, false)) {
908
-                $propFind->set('{DAV:}current-user-privilege-set', null, 403);
909
-            } else {
910
-                $val = $this->getCurrentUserPrivilegeSet($node);
911
-                if (!is_null($val)) {
912
-                    return new Xml\Property\CurrentUserPrivilegeSet($val);
913
-                }
914
-            }
915
-        });
916
-        $propFind->handle('{DAV:}acl', function() use ($node, $propFind, $path) {
917
-            /* The ACL property contains all the permissions */
918
-            if (!$this->checkPrivileges($path, '{DAV:}read-acl', self::R_PARENT, false)) {
919
-                $propFind->set('{DAV:}acl', null, 403);
920
-            } else {
921
-                $acl = $this->getACL($node);
922
-                if (!is_null($acl)) {
923
-                    return new Xml\Property\Acl($this->getACL($node));
924
-                }
925
-            }
926
-        });
927
-        $propFind->handle('{DAV:}acl-restrictions', function() {
928
-            return new Xml\Property\AclRestrictions();
929
-        });
930
-
931
-        /* Adding ACL properties */
932
-        if ($node instanceof IACL) {
933
-            $propFind->handle('{DAV:}owner', function() use ($node) {
934
-                return new DAV\Xml\Property\Href($node->getOwner() . '/');
935
-            });
936
-        }
937
-
938
-    }
939
-
940
-    /**
941
-     * This method intercepts PROPPATCH methods and make sure the
942
-     * group-member-set is updated correctly.
943
-     *
944
-     * @param string $path
945
-     * @param DAV\PropPatch $propPatch
946
-     * @return void
947
-     */
948
-    public function propPatch($path, DAV\PropPatch $propPatch) {
949
-
950
-        $propPatch->handle('{DAV:}group-member-set', function($value) use ($path) {
951
-            if (is_null($value)) {
952
-                $memberSet = [];
953
-            } elseif ($value instanceof DAV\Xml\Property\Href) {
954
-                $memberSet = array_map(
955
-                    [$this->server, 'calculateUri'],
956
-                    $value->getHrefs()
957
-                );
958
-            } else {
959
-                throw new DAV\Exception('The group-member-set property MUST be an instance of Sabre\DAV\Property\HrefList or null');
960
-            }
961
-            $node = $this->server->tree->getNodeForPath($path);
962
-            if (!($node instanceof IPrincipal)) {
963
-                // Fail
964
-                return false;
965
-            }
966
-
967
-            $node->setGroupMemberSet($memberSet);
968
-            // We must also clear our cache, just in case
969
-
970
-            $this->principalMembershipCache = [];
971
-
972
-            return true;
973
-        });
974
-
975
-    }
976
-
977
-    /**
978
-     * This method handles HTTP REPORT requests
979
-     *
980
-     * @param string $reportName
981
-     * @param mixed $report
982
-     * @param mixed $path
983
-     * @return bool
984
-     */
985
-    public function report($reportName, $report, $path) {
986
-
987
-        switch ($reportName) {
988
-
989
-            case '{DAV:}principal-property-search' :
990
-                $this->server->transactionType = 'report-principal-property-search';
991
-                $this->principalPropertySearchReport($report);
992
-                return false;
993
-            case '{DAV:}principal-search-property-set' :
994
-                $this->server->transactionType = 'report-principal-search-property-set';
995
-                $this->principalSearchPropertySetReport($report);
996
-                return false;
997
-            case '{DAV:}expand-property' :
998
-                $this->server->transactionType = 'report-expand-property';
999
-                $this->expandPropertyReport($report);
1000
-                return false;
1001
-
1002
-        }
1003
-
1004
-    }
1005
-
1006
-    /**
1007
-     * This method is responsible for handling the 'ACL' event.
1008
-     *
1009
-     * @param RequestInterface $request
1010
-     * @param ResponseInterface $response
1011
-     * @return bool
1012
-     */
1013
-    public function httpAcl(RequestInterface $request, ResponseInterface $response) {
1014
-
1015
-        $path = $request->getPath();
1016
-        $body = $request->getBodyAsString();
1017
-
1018
-        if (!$body) {
1019
-            throw new DAV\Exception\BadRequest('XML body expected in ACL request');
1020
-        }
1021
-
1022
-        $acl = $this->server->xml->expect('{DAV:}acl', $body);
1023
-        $newAcl = $acl->getPrivileges();
1024
-
1025
-        // Normalizing urls
1026
-        foreach ($newAcl as $k => $newAce) {
1027
-            $newAcl[$k]['principal'] = $this->server->calculateUri($newAce['principal']);
1028
-        }
1029
-        $node = $this->server->tree->getNodeForPath($path);
1030
-
1031
-        if (!$node instanceof IACL) {
1032
-            throw new DAV\Exception\MethodNotAllowed('This node does not support the ACL method');
1033
-        }
1034
-
1035
-        $oldAcl = $this->getACL($node);
1036
-
1037
-        $supportedPrivileges = $this->getFlatPrivilegeSet($node);
1038
-
1039
-        /* Checking if protected principals from the existing principal set are
532
+				// 'authenticated' matched for every user that's logged in.
533
+				// Since it's not possible to use ACL while not being logged
534
+				// in, this is also always true.
535
+				case '{DAV:}authenticated' :
536
+					$collected[] = $ace;
537
+					break;
538
+
539
+				// 'unauthenticated' can never occur either, so we simply
540
+				// ignore these.
541
+				case '{DAV:}unauthenticated' :
542
+					break;
543
+
544
+				default :
545
+					if (in_array($ace['principal'], $principals)) {
546
+						$collected[] = $ace;
547
+					}
548
+					break;
549
+
550
+			}
551
+
552
+
553
+		}
554
+
555
+		// Now we deduct all aggregated privileges.
556
+		$flat = $this->getFlatPrivilegeSet($node);
557
+
558
+		$collected2 = [];
559
+		while (count($collected)) {
560
+
561
+			$current = array_pop($collected);
562
+			$collected2[] = $current['privilege'];
563
+
564
+			foreach ($flat[$current['privilege']]['aggregates'] as $subPriv) {
565
+				$collected2[] = $subPriv;
566
+				$collected[] = $flat[$subPriv];
567
+			}
568
+
569
+		}
570
+
571
+		return array_values(array_unique($collected2));
572
+
573
+	}
574
+
575
+
576
+	/**
577
+	 * Returns a principal based on its uri.
578
+	 *
579
+	 * Returns null if the principal could not be found.
580
+	 *
581
+	 * @param string $uri
582
+	 * @return null|string
583
+	 */
584
+	public function getPrincipalByUri($uri) {
585
+
586
+		$result = null;
587
+		$collections = $this->principalCollectionSet;
588
+		foreach ($collections as $collection) {
589
+
590
+			$principalCollection = $this->server->tree->getNodeForPath($collection);
591
+			if (!$principalCollection instanceof IPrincipalCollection) {
592
+				// Not a principal collection, we're simply going to ignore
593
+				// this.
594
+				continue;
595
+			}
596
+
597
+			$result = $principalCollection->findByUri($uri);
598
+			if ($result) {
599
+				return $result;
600
+			}
601
+
602
+		}
603
+
604
+	}
605
+
606
+	/**
607
+	 * Principal property search
608
+	 *
609
+	 * This method can search for principals matching certain values in
610
+	 * properties.
611
+	 *
612
+	 * This method will return a list of properties for the matched properties.
613
+	 *
614
+	 * @param array $searchProperties    The properties to search on. This is a
615
+	 *                                   key-value list. The keys are property
616
+	 *                                   names, and the values the strings to
617
+	 *                                   match them on.
618
+	 * @param array $requestedProperties This is the list of properties to
619
+	 *                                   return for every match.
620
+	 * @param string $collectionUri      The principal collection to search on.
621
+	 *                                   If this is ommitted, the standard
622
+	 *                                   principal collection-set will be used.
623
+	 * @param string $test               "allof" to use AND to search the
624
+	 *                                   properties. 'anyof' for OR.
625
+	 * @return array     This method returns an array structure similar to
626
+	 *                  Sabre\DAV\Server::getPropertiesForPath. Returned
627
+	 *                  properties are index by a HTTP status code.
628
+	 */
629
+	public function principalSearch(array $searchProperties, array $requestedProperties, $collectionUri = null, $test = 'allof') {
630
+
631
+		if (!is_null($collectionUri)) {
632
+			$uris = [$collectionUri];
633
+		} else {
634
+			$uris = $this->principalCollectionSet;
635
+		}
636
+
637
+		$lookupResults = [];
638
+		foreach ($uris as $uri) {
639
+
640
+			$principalCollection = $this->server->tree->getNodeForPath($uri);
641
+			if (!$principalCollection instanceof IPrincipalCollection) {
642
+				// Not a principal collection, we're simply going to ignore
643
+				// this.
644
+				continue;
645
+			}
646
+
647
+			$results = $principalCollection->searchPrincipals($searchProperties, $test);
648
+			foreach ($results as $result) {
649
+				$lookupResults[] = rtrim($uri, '/') . '/' . $result;
650
+			}
651
+
652
+		}
653
+
654
+		$matches = [];
655
+
656
+		foreach ($lookupResults as $lookupResult) {
657
+
658
+			list($matches[]) = $this->server->getPropertiesForPath($lookupResult, $requestedProperties, 0);
659
+
660
+		}
661
+
662
+		return $matches;
663
+
664
+	}
665
+
666
+	/**
667
+	 * Sets up the plugin
668
+	 *
669
+	 * This method is automatically called by the server class.
670
+	 *
671
+	 * @param DAV\Server $server
672
+	 * @return void
673
+	 */
674
+	public function initialize(DAV\Server $server) {
675
+
676
+		$this->server = $server;
677
+		$server->on('propFind',            [$this, 'propFind'], 20);
678
+		$server->on('beforeMethod',        [$this, 'beforeMethod'], 20);
679
+		$server->on('beforeBind',          [$this, 'beforeBind'], 20);
680
+		$server->on('beforeUnbind',        [$this, 'beforeUnbind'], 20);
681
+		$server->on('propPatch',           [$this, 'propPatch']);
682
+		$server->on('beforeUnlock',        [$this, 'beforeUnlock'], 20);
683
+		$server->on('report',              [$this, 'report']);
684
+		$server->on('method:ACL',          [$this, 'httpAcl']);
685
+		$server->on('onHTMLActionsPanel',  [$this, 'htmlActionsPanel']);
686
+
687
+		array_push($server->protectedProperties,
688
+			'{DAV:}alternate-URI-set',
689
+			'{DAV:}principal-URL',
690
+			'{DAV:}group-membership',
691
+			'{DAV:}principal-collection-set',
692
+			'{DAV:}current-user-principal',
693
+			'{DAV:}supported-privilege-set',
694
+			'{DAV:}current-user-privilege-set',
695
+			'{DAV:}acl',
696
+			'{DAV:}acl-restrictions',
697
+			'{DAV:}inherited-acl-set',
698
+			'{DAV:}owner',
699
+			'{DAV:}group'
700
+		);
701
+
702
+		// Automatically mapping nodes implementing IPrincipal to the
703
+		// {DAV:}principal resourcetype.
704
+		$server->resourceTypeMapping['Sabre\\DAVACL\\IPrincipal'] = '{DAV:}principal';
705
+
706
+		// Mapping the group-member-set property to the HrefList property
707
+		// class.
708
+		$server->xml->elementMap['{DAV:}group-member-set'] = 'Sabre\\DAV\\Xml\\Property\\Href';
709
+		$server->xml->elementMap['{DAV:}acl'] = 'Sabre\\DAVACL\\Xml\\Property\\Acl';
710
+		$server->xml->elementMap['{DAV:}expand-property'] = 'Sabre\\DAVACL\\Xml\\Request\\ExpandPropertyReport';
711
+		$server->xml->elementMap['{DAV:}principal-property-search'] = 'Sabre\\DAVACL\\Xml\\Request\\PrincipalPropertySearchReport';
712
+		$server->xml->elementMap['{DAV:}principal-search-property-set'] = 'Sabre\\DAVACL\\Xml\\Request\\PrincipalSearchPropertySetReport';
713
+
714
+	}
715
+
716
+	/* {{{ Event handlers */
717
+
718
+	/**
719
+	 * Triggered before any method is handled
720
+	 *
721
+	 * @param RequestInterface $request
722
+	 * @param ResponseInterface $response
723
+	 * @return void
724
+	 */
725
+	public function beforeMethod(RequestInterface $request, ResponseInterface $response) {
726
+
727
+		$method = $request->getMethod();
728
+		$path = $request->getPath();
729
+
730
+		$exists = $this->server->tree->nodeExists($path);
731
+
732
+		// If the node doesn't exists, none of these checks apply
733
+		if (!$exists) return;
734
+
735
+		switch ($method) {
736
+
737
+			case 'GET' :
738
+			case 'HEAD' :
739
+			case 'OPTIONS' :
740
+				// For these 3 we only need to know if the node is readable.
741
+				$this->checkPrivileges($path, '{DAV:}read');
742
+				break;
743
+
744
+			case 'PUT' :
745
+			case 'LOCK' :
746
+			case 'UNLOCK' :
747
+				// This method requires the write-content priv if the node
748
+				// already exists, and bind on the parent if the node is being
749
+				// created.
750
+				// The bind privilege is handled in the beforeBind event.
751
+				$this->checkPrivileges($path, '{DAV:}write-content');
752
+				break;
753
+
754
+
755
+			case 'PROPPATCH' :
756
+				$this->checkPrivileges($path, '{DAV:}write-properties');
757
+				break;
758
+
759
+			case 'ACL' :
760
+				$this->checkPrivileges($path, '{DAV:}write-acl');
761
+				break;
762
+
763
+			case 'COPY' :
764
+			case 'MOVE' :
765
+				// Copy requires read privileges on the entire source tree.
766
+				// If the target exists write-content normally needs to be
767
+				// checked, however, we're deleting the node beforehand and
768
+				// creating a new one after, so this is handled by the
769
+				// beforeUnbind event.
770
+				//
771
+				// The creation of the new node is handled by the beforeBind
772
+				// event.
773
+				//
774
+				// If MOVE is used beforeUnbind will also be used to check if
775
+				// the sourcenode can be deleted.
776
+				$this->checkPrivileges($path, '{DAV:}read', self::R_RECURSIVE);
777
+
778
+				break;
779
+
780
+		}
781
+
782
+	}
783
+
784
+	/**
785
+	 * Triggered before a new node is created.
786
+	 *
787
+	 * This allows us to check permissions for any operation that creates a
788
+	 * new node, such as PUT, MKCOL, MKCALENDAR, LOCK, COPY and MOVE.
789
+	 *
790
+	 * @param string $uri
791
+	 * @return void
792
+	 */
793
+	public function beforeBind($uri) {
794
+
795
+		list($parentUri) = Uri\split($uri);
796
+		$this->checkPrivileges($parentUri, '{DAV:}bind');
797
+
798
+	}
799
+
800
+	/**
801
+	 * Triggered before a node is deleted
802
+	 *
803
+	 * This allows us to check permissions for any operation that will delete
804
+	 * an existing node.
805
+	 *
806
+	 * @param string $uri
807
+	 * @return void
808
+	 */
809
+	public function beforeUnbind($uri) {
810
+
811
+		list($parentUri) = Uri\split($uri);
812
+		$this->checkPrivileges($parentUri, '{DAV:}unbind', self::R_RECURSIVEPARENTS);
813
+
814
+	}
815
+
816
+	/**
817
+	 * Triggered before a node is unlocked.
818
+	 *
819
+	 * @param string $uri
820
+	 * @param DAV\Locks\LockInfo $lock
821
+	 * @return void
822
+	 */
823
+	public function beforeUnlock($uri, DAV\Locks\LockInfo $lock) {
824
+
825
+
826
+	}
827
+
828
+	/**
829
+	 * Triggered before properties are looked up in specific nodes.
830
+	 *
831
+	 * @param DAV\PropFind $propFind
832
+	 * @param DAV\INode $node
833
+	 * @param array $requestedProperties
834
+	 * @param array $returnedProperties
835
+	 * @return bool
836
+	 */
837
+	public function propFind(DAV\PropFind $propFind, DAV\INode $node) {
838
+
839
+		$path = $propFind->getPath();
840
+
841
+		// Checking the read permission
842
+		if (!$this->checkPrivileges($path, '{DAV:}read', self::R_PARENT, false)) {
843
+			// User is not allowed to read properties
844
+
845
+			// Returning false causes the property-fetching system to pretend
846
+			// that the node does not exist, and will cause it to be hidden
847
+			// from listings such as PROPFIND or the browser plugin.
848
+			if ($this->hideNodesFromListings) {
849
+				return false;
850
+			}
851
+
852
+			// Otherwise we simply mark every property as 403.
853
+			foreach ($propFind->getRequestedProperties() as $requestedProperty) {
854
+				$propFind->set($requestedProperty, null, 403);
855
+			}
856
+
857
+			return;
858
+
859
+		}
860
+
861
+		/* Adding principal properties */
862
+		if ($node instanceof IPrincipal) {
863
+
864
+			$propFind->handle('{DAV:}alternate-URI-set', function() use ($node) {
865
+				return new DAV\Xml\Property\Href($node->getAlternateUriSet());
866
+			});
867
+			$propFind->handle('{DAV:}principal-URL', function() use ($node) {
868
+				return new DAV\Xml\Property\Href($node->getPrincipalUrl() . '/');
869
+			});
870
+			$propFind->handle('{DAV:}group-member-set', function() use ($node) {
871
+				$members = $node->getGroupMemberSet();
872
+				foreach ($members as $k => $member) {
873
+					$members[$k] = rtrim($member, '/') . '/';
874
+				}
875
+				return new DAV\Xml\Property\Href($members);
876
+			});
877
+			$propFind->handle('{DAV:}group-membership', function() use ($node) {
878
+				$members = $node->getGroupMembership();
879
+				foreach ($members as $k => $member) {
880
+					$members[$k] = rtrim($member, '/') . '/';
881
+				}
882
+				return new DAV\Xml\Property\Href($members);
883
+			});
884
+			$propFind->handle('{DAV:}displayname', [$node, 'getDisplayName']);
885
+
886
+		}
887
+
888
+		$propFind->handle('{DAV:}principal-collection-set', function() {
889
+
890
+			$val = $this->principalCollectionSet;
891
+			// Ensuring all collections end with a slash
892
+			foreach ($val as $k => $v) $val[$k] = $v . '/';
893
+			return new DAV\Xml\Property\Href($val);
894
+
895
+		});
896
+		$propFind->handle('{DAV:}current-user-principal', function() {
897
+			if ($url = $this->getCurrentUserPrincipal()) {
898
+				return new Xml\Property\Principal(Xml\Property\Principal::HREF, $url . '/');
899
+			} else {
900
+				return new Xml\Property\Principal(Xml\Property\Principal::UNAUTHENTICATED);
901
+			}
902
+		});
903
+		$propFind->handle('{DAV:}supported-privilege-set', function() use ($node) {
904
+			return new Xml\Property\SupportedPrivilegeSet($this->getSupportedPrivilegeSet($node));
905
+		});
906
+		$propFind->handle('{DAV:}current-user-privilege-set', function() use ($node, $propFind, $path) {
907
+			if (!$this->checkPrivileges($path, '{DAV:}read-current-user-privilege-set', self::R_PARENT, false)) {
908
+				$propFind->set('{DAV:}current-user-privilege-set', null, 403);
909
+			} else {
910
+				$val = $this->getCurrentUserPrivilegeSet($node);
911
+				if (!is_null($val)) {
912
+					return new Xml\Property\CurrentUserPrivilegeSet($val);
913
+				}
914
+			}
915
+		});
916
+		$propFind->handle('{DAV:}acl', function() use ($node, $propFind, $path) {
917
+			/* The ACL property contains all the permissions */
918
+			if (!$this->checkPrivileges($path, '{DAV:}read-acl', self::R_PARENT, false)) {
919
+				$propFind->set('{DAV:}acl', null, 403);
920
+			} else {
921
+				$acl = $this->getACL($node);
922
+				if (!is_null($acl)) {
923
+					return new Xml\Property\Acl($this->getACL($node));
924
+				}
925
+			}
926
+		});
927
+		$propFind->handle('{DAV:}acl-restrictions', function() {
928
+			return new Xml\Property\AclRestrictions();
929
+		});
930
+
931
+		/* Adding ACL properties */
932
+		if ($node instanceof IACL) {
933
+			$propFind->handle('{DAV:}owner', function() use ($node) {
934
+				return new DAV\Xml\Property\Href($node->getOwner() . '/');
935
+			});
936
+		}
937
+
938
+	}
939
+
940
+	/**
941
+	 * This method intercepts PROPPATCH methods and make sure the
942
+	 * group-member-set is updated correctly.
943
+	 *
944
+	 * @param string $path
945
+	 * @param DAV\PropPatch $propPatch
946
+	 * @return void
947
+	 */
948
+	public function propPatch($path, DAV\PropPatch $propPatch) {
949
+
950
+		$propPatch->handle('{DAV:}group-member-set', function($value) use ($path) {
951
+			if (is_null($value)) {
952
+				$memberSet = [];
953
+			} elseif ($value instanceof DAV\Xml\Property\Href) {
954
+				$memberSet = array_map(
955
+					[$this->server, 'calculateUri'],
956
+					$value->getHrefs()
957
+				);
958
+			} else {
959
+				throw new DAV\Exception('The group-member-set property MUST be an instance of Sabre\DAV\Property\HrefList or null');
960
+			}
961
+			$node = $this->server->tree->getNodeForPath($path);
962
+			if (!($node instanceof IPrincipal)) {
963
+				// Fail
964
+				return false;
965
+			}
966
+
967
+			$node->setGroupMemberSet($memberSet);
968
+			// We must also clear our cache, just in case
969
+
970
+			$this->principalMembershipCache = [];
971
+
972
+			return true;
973
+		});
974
+
975
+	}
976
+
977
+	/**
978
+	 * This method handles HTTP REPORT requests
979
+	 *
980
+	 * @param string $reportName
981
+	 * @param mixed $report
982
+	 * @param mixed $path
983
+	 * @return bool
984
+	 */
985
+	public function report($reportName, $report, $path) {
986
+
987
+		switch ($reportName) {
988
+
989
+			case '{DAV:}principal-property-search' :
990
+				$this->server->transactionType = 'report-principal-property-search';
991
+				$this->principalPropertySearchReport($report);
992
+				return false;
993
+			case '{DAV:}principal-search-property-set' :
994
+				$this->server->transactionType = 'report-principal-search-property-set';
995
+				$this->principalSearchPropertySetReport($report);
996
+				return false;
997
+			case '{DAV:}expand-property' :
998
+				$this->server->transactionType = 'report-expand-property';
999
+				$this->expandPropertyReport($report);
1000
+				return false;
1001
+
1002
+		}
1003
+
1004
+	}
1005
+
1006
+	/**
1007
+	 * This method is responsible for handling the 'ACL' event.
1008
+	 *
1009
+	 * @param RequestInterface $request
1010
+	 * @param ResponseInterface $response
1011
+	 * @return bool
1012
+	 */
1013
+	public function httpAcl(RequestInterface $request, ResponseInterface $response) {
1014
+
1015
+		$path = $request->getPath();
1016
+		$body = $request->getBodyAsString();
1017
+
1018
+		if (!$body) {
1019
+			throw new DAV\Exception\BadRequest('XML body expected in ACL request');
1020
+		}
1021
+
1022
+		$acl = $this->server->xml->expect('{DAV:}acl', $body);
1023
+		$newAcl = $acl->getPrivileges();
1024
+
1025
+		// Normalizing urls
1026
+		foreach ($newAcl as $k => $newAce) {
1027
+			$newAcl[$k]['principal'] = $this->server->calculateUri($newAce['principal']);
1028
+		}
1029
+		$node = $this->server->tree->getNodeForPath($path);
1030
+
1031
+		if (!$node instanceof IACL) {
1032
+			throw new DAV\Exception\MethodNotAllowed('This node does not support the ACL method');
1033
+		}
1034
+
1035
+		$oldAcl = $this->getACL($node);
1036
+
1037
+		$supportedPrivileges = $this->getFlatPrivilegeSet($node);
1038
+
1039
+		/* Checking if protected principals from the existing principal set are
1040 1040
            not overwritten. */
1041
-        foreach ($oldAcl as $oldAce) {
1042
-
1043
-            if (!isset($oldAce['protected']) || !$oldAce['protected']) continue;
1044
-
1045
-            $found = false;
1046
-            foreach ($newAcl as $newAce) {
1047
-                if (
1048
-                    $newAce['privilege'] === $oldAce['privilege'] &&
1049
-                    $newAce['principal'] === $oldAce['principal'] &&
1050
-                    $newAce['protected']
1051
-                )
1052
-                $found = true;
1053
-            }
1054
-
1055
-            if (!$found)
1056
-                throw new Exception\AceConflict('This resource contained a protected {DAV:}ace, but this privilege did not occur in the ACL request');
1057
-
1058
-        }
1041
+		foreach ($oldAcl as $oldAce) {
1042
+
1043
+			if (!isset($oldAce['protected']) || !$oldAce['protected']) continue;
1044
+
1045
+			$found = false;
1046
+			foreach ($newAcl as $newAce) {
1047
+				if (
1048
+					$newAce['privilege'] === $oldAce['privilege'] &&
1049
+					$newAce['principal'] === $oldAce['principal'] &&
1050
+					$newAce['protected']
1051
+				)
1052
+				$found = true;
1053
+			}
1054
+
1055
+			if (!$found)
1056
+				throw new Exception\AceConflict('This resource contained a protected {DAV:}ace, but this privilege did not occur in the ACL request');
1057
+
1058
+		}
1059 1059
 
1060
-        foreach ($newAcl as $newAce) {
1061
-
1062
-            // Do we recognize the privilege
1063
-            if (!isset($supportedPrivileges[$newAce['privilege']])) {
1064
-                throw new Exception\NotSupportedPrivilege('The privilege you specified (' . $newAce['privilege'] . ') is not recognized by this server');
1065
-            }
1066
-
1067
-            if ($supportedPrivileges[$newAce['privilege']]['abstract']) {
1068
-                throw new Exception\NoAbstract('The privilege you specified (' . $newAce['privilege'] . ') is an abstract privilege');
1069
-            }
1070
-
1071
-            // Looking up the principal
1072
-            try {
1073
-                $principal = $this->server->tree->getNodeForPath($newAce['principal']);
1074
-            } catch (DAV\Exception\NotFound $e) {
1075
-                throw new Exception\NotRecognizedPrincipal('The specified principal (' . $newAce['principal'] . ') does not exist');
1076
-            }
1077
-            if (!($principal instanceof IPrincipal)) {
1078
-                throw new Exception\NotRecognizedPrincipal('The specified uri (' . $newAce['principal'] . ') is not a principal');
1079
-            }
1060
+		foreach ($newAcl as $newAce) {
1061
+
1062
+			// Do we recognize the privilege
1063
+			if (!isset($supportedPrivileges[$newAce['privilege']])) {
1064
+				throw new Exception\NotSupportedPrivilege('The privilege you specified (' . $newAce['privilege'] . ') is not recognized by this server');
1065
+			}
1066
+
1067
+			if ($supportedPrivileges[$newAce['privilege']]['abstract']) {
1068
+				throw new Exception\NoAbstract('The privilege you specified (' . $newAce['privilege'] . ') is an abstract privilege');
1069
+			}
1070
+
1071
+			// Looking up the principal
1072
+			try {
1073
+				$principal = $this->server->tree->getNodeForPath($newAce['principal']);
1074
+			} catch (DAV\Exception\NotFound $e) {
1075
+				throw new Exception\NotRecognizedPrincipal('The specified principal (' . $newAce['principal'] . ') does not exist');
1076
+			}
1077
+			if (!($principal instanceof IPrincipal)) {
1078
+				throw new Exception\NotRecognizedPrincipal('The specified uri (' . $newAce['principal'] . ') is not a principal');
1079
+			}
1080 1080
 
1081
-        }
1082
-        $node->setACL($newAcl);
1081
+		}
1082
+		$node->setACL($newAcl);
1083 1083
 
1084
-        $response->setStatus(200);
1085
-
1086
-        // Breaking the event chain, because we handled this method.
1087
-        return false;
1088
-
1089
-    }
1090
-
1091
-    /* }}} */
1092
-
1093
-    /* Reports {{{ */
1094
-
1095
-    /**
1096
-     * The expand-property report is defined in RFC3253 section 3-8.
1097
-     *
1098
-     * This report is very similar to a standard PROPFIND. The difference is
1099
-     * that it has the additional ability to look at properties containing a
1100
-     * {DAV:}href element, follow that property and grab additional elements
1101
-     * there.
1102
-     *
1103
-     * Other rfc's, such as ACL rely on this report, so it made sense to put
1104
-     * it in this plugin.
1105
-     *
1106
-     * @param Xml\Request\ExpandPropertyReport $report
1107
-     * @return void
1108
-     */
1109
-    protected function expandPropertyReport($report) {
1084
+		$response->setStatus(200);
1085
+
1086
+		// Breaking the event chain, because we handled this method.
1087
+		return false;
1088
+
1089
+	}
1090
+
1091
+	/* }}} */
1092
+
1093
+	/* Reports {{{ */
1094
+
1095
+	/**
1096
+	 * The expand-property report is defined in RFC3253 section 3-8.
1097
+	 *
1098
+	 * This report is very similar to a standard PROPFIND. The difference is
1099
+	 * that it has the additional ability to look at properties containing a
1100
+	 * {DAV:}href element, follow that property and grab additional elements
1101
+	 * there.
1102
+	 *
1103
+	 * Other rfc's, such as ACL rely on this report, so it made sense to put
1104
+	 * it in this plugin.
1105
+	 *
1106
+	 * @param Xml\Request\ExpandPropertyReport $report
1107
+	 * @return void
1108
+	 */
1109
+	protected function expandPropertyReport($report) {
1110 1110
 
1111
-        $depth = $this->server->getHTTPDepth(0);
1112
-        $requestUri = $this->server->getRequestUri();
1111
+		$depth = $this->server->getHTTPDepth(0);
1112
+		$requestUri = $this->server->getRequestUri();
1113 1113
 
1114
-        $result = $this->expandProperties($requestUri, $report->properties, $depth);
1114
+		$result = $this->expandProperties($requestUri, $report->properties, $depth);
1115 1115
 
1116
-        $xml = $this->server->xml->write(
1117
-            '{DAV:}multistatus',
1118
-            new DAV\Xml\Response\MultiStatus($result),
1119
-            $this->server->getBaseUri()
1120
-        );
1121
-        $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
1122
-        $this->server->httpResponse->setStatus(207);
1123
-        $this->server->httpResponse->setBody($xml);
1116
+		$xml = $this->server->xml->write(
1117
+			'{DAV:}multistatus',
1118
+			new DAV\Xml\Response\MultiStatus($result),
1119
+			$this->server->getBaseUri()
1120
+		);
1121
+		$this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
1122
+		$this->server->httpResponse->setStatus(207);
1123
+		$this->server->httpResponse->setBody($xml);
1124 1124
 
1125
-    }
1125
+	}
1126 1126
 
1127
-    /**
1128
-     * This method expands all the properties and returns
1129
-     * a list with property values
1130
-     *
1131
-     * @param array $path
1132
-     * @param array $requestedProperties the list of required properties
1133
-     * @param int $depth
1134
-     * @return array
1135
-     */
1136
-    protected function expandProperties($path, array $requestedProperties, $depth) {
1127
+	/**
1128
+	 * This method expands all the properties and returns
1129
+	 * a list with property values
1130
+	 *
1131
+	 * @param array $path
1132
+	 * @param array $requestedProperties the list of required properties
1133
+	 * @param int $depth
1134
+	 * @return array
1135
+	 */
1136
+	protected function expandProperties($path, array $requestedProperties, $depth) {
1137 1137
 
1138
-        $foundProperties = $this->server->getPropertiesForPath($path, array_keys($requestedProperties), $depth);
1138
+		$foundProperties = $this->server->getPropertiesForPath($path, array_keys($requestedProperties), $depth);
1139 1139
 
1140
-        $result = [];
1140
+		$result = [];
1141 1141
 
1142
-        foreach ($foundProperties as $node) {
1143
-
1144
-            foreach ($requestedProperties as $propertyName => $childRequestedProperties) {
1145
-
1146
-                // We're only traversing if sub-properties were requested
1147
-                if (count($childRequestedProperties) === 0) continue;
1148
-
1149
-                // We only have to do the expansion if the property was found
1150
-                // and it contains an href element.
1151
-                if (!array_key_exists($propertyName, $node[200])) continue;
1152
-
1153
-                if (!$node[200][$propertyName] instanceof DAV\Xml\Property\Href) {
1154
-                    continue;
1155
-                }
1156
-
1157
-                $childHrefs = $node[200][$propertyName]->getHrefs();
1158
-                $childProps = [];
1159
-
1160
-                foreach ($childHrefs as $href) {
1161
-                    // Gathering the result of the children
1162
-                    $childProps[] = [
1163
-                        'name'  => '{DAV:}response',
1164
-                        'value' => $this->expandProperties($href, $childRequestedProperties, 0)[0]
1165
-                    ];
1166
-                }
1167
-
1168
-                // Replacing the property with its expannded form.
1169
-                $node[200][$propertyName] = $childProps;
1170
-
1171
-            }
1172
-            $result[] = new DAV\Xml\Element\Response($node['href'], $node);
1173
-
1174
-        }
1175
-
1176
-        return $result;
1177
-
1178
-    }
1179
-
1180
-    /**
1181
-     * principalSearchPropertySetReport
1182
-     *
1183
-     * This method responsible for handing the
1184
-     * {DAV:}principal-search-property-set report. This report returns a list
1185
-     * of properties the client may search on, using the
1186
-     * {DAV:}principal-property-search report.
1187
-     *
1188
-     * @param Xml\Request\PrincipalSearchPropertySetReport $report
1189
-     * @return void
1190
-     */
1191
-    protected function principalSearchPropertySetReport($report) {
1192
-
1193
-        $httpDepth = $this->server->getHTTPDepth(0);
1194
-        if ($httpDepth !== 0) {
1195
-            throw new DAV\Exception\BadRequest('This report is only defined when Depth: 0');
1196
-        }
1197
-
1198
-        $writer = $this->server->xml->getWriter();
1199
-        $writer->openMemory();
1200
-        $writer->startDocument();
1201
-
1202
-        $writer->startElement('{DAV:}principal-search-property-set');
1203
-
1204
-        foreach ($this->principalSearchPropertySet as $propertyName => $description) {
1205
-
1206
-            $writer->startElement('{DAV:}principal-search-property');
1207
-            $writer->startElement('{DAV:}prop');
1208
-
1209
-            $writer->writeElement($propertyName);
1210
-
1211
-            $writer->endElement(); // prop
1212
-
1213
-            if ($description) {
1214
-                $writer->write([[
1215
-                    'name'       => '{DAV:}description',
1216
-                    'value'      => $description,
1217
-                    'attributes' => ['xml:lang' => 'en']
1218
-                ]]);
1219
-            }
1220
-
1221
-            $writer->endElement(); // principal-search-property
1222
-
1223
-
1224
-        }
1225
-
1226
-        $writer->endElement(); // principal-search-property-set
1227
-
1228
-        $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
1229
-        $this->server->httpResponse->setStatus(200);
1230
-        $this->server->httpResponse->setBody($writer->outputMemory());
1231
-
1232
-    }
1142
+		foreach ($foundProperties as $node) {
1143
+
1144
+			foreach ($requestedProperties as $propertyName => $childRequestedProperties) {
1145
+
1146
+				// We're only traversing if sub-properties were requested
1147
+				if (count($childRequestedProperties) === 0) continue;
1148
+
1149
+				// We only have to do the expansion if the property was found
1150
+				// and it contains an href element.
1151
+				if (!array_key_exists($propertyName, $node[200])) continue;
1152
+
1153
+				if (!$node[200][$propertyName] instanceof DAV\Xml\Property\Href) {
1154
+					continue;
1155
+				}
1156
+
1157
+				$childHrefs = $node[200][$propertyName]->getHrefs();
1158
+				$childProps = [];
1159
+
1160
+				foreach ($childHrefs as $href) {
1161
+					// Gathering the result of the children
1162
+					$childProps[] = [
1163
+						'name'  => '{DAV:}response',
1164
+						'value' => $this->expandProperties($href, $childRequestedProperties, 0)[0]
1165
+					];
1166
+				}
1167
+
1168
+				// Replacing the property with its expannded form.
1169
+				$node[200][$propertyName] = $childProps;
1170
+
1171
+			}
1172
+			$result[] = new DAV\Xml\Element\Response($node['href'], $node);
1173
+
1174
+		}
1175
+
1176
+		return $result;
1177
+
1178
+	}
1179
+
1180
+	/**
1181
+	 * principalSearchPropertySetReport
1182
+	 *
1183
+	 * This method responsible for handing the
1184
+	 * {DAV:}principal-search-property-set report. This report returns a list
1185
+	 * of properties the client may search on, using the
1186
+	 * {DAV:}principal-property-search report.
1187
+	 *
1188
+	 * @param Xml\Request\PrincipalSearchPropertySetReport $report
1189
+	 * @return void
1190
+	 */
1191
+	protected function principalSearchPropertySetReport($report) {
1192
+
1193
+		$httpDepth = $this->server->getHTTPDepth(0);
1194
+		if ($httpDepth !== 0) {
1195
+			throw new DAV\Exception\BadRequest('This report is only defined when Depth: 0');
1196
+		}
1197
+
1198
+		$writer = $this->server->xml->getWriter();
1199
+		$writer->openMemory();
1200
+		$writer->startDocument();
1201
+
1202
+		$writer->startElement('{DAV:}principal-search-property-set');
1203
+
1204
+		foreach ($this->principalSearchPropertySet as $propertyName => $description) {
1205
+
1206
+			$writer->startElement('{DAV:}principal-search-property');
1207
+			$writer->startElement('{DAV:}prop');
1208
+
1209
+			$writer->writeElement($propertyName);
1210
+
1211
+			$writer->endElement(); // prop
1212
+
1213
+			if ($description) {
1214
+				$writer->write([[
1215
+					'name'       => '{DAV:}description',
1216
+					'value'      => $description,
1217
+					'attributes' => ['xml:lang' => 'en']
1218
+				]]);
1219
+			}
1220
+
1221
+			$writer->endElement(); // principal-search-property
1222
+
1223
+
1224
+		}
1225
+
1226
+		$writer->endElement(); // principal-search-property-set
1227
+
1228
+		$this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
1229
+		$this->server->httpResponse->setStatus(200);
1230
+		$this->server->httpResponse->setBody($writer->outputMemory());
1231
+
1232
+	}
1233 1233
 
1234
-    /**
1235
-     * principalPropertySearchReport
1236
-     *
1237
-     * This method is responsible for handing the
1238
-     * {DAV:}principal-property-search report. This report can be used for
1239
-     * clients to search for groups of principals, based on the value of one
1240
-     * or more properties.
1241
-     *
1242
-     * @param Xml\Request\PrincipalPropertySearchReport $report
1243
-     * @return void
1244
-     */
1245
-    protected function principalPropertySearchReport($report) {
1246
-
1247
-        $uri = null;
1248
-        if (!$report->applyToPrincipalCollectionSet) {
1249
-            $uri = $this->server->httpRequest->getPath();
1250
-        }
1251
-        if ($this->server->getHttpDepth('0') !== 0) {
1252
-            throw new BadRequest('Depth must be 0');
1253
-        }
1254
-        $result = $this->principalSearch(
1255
-            $report->searchProperties,
1256
-            $report->properties,
1257
-            $uri,
1258
-            $report->test
1259
-        );
1260
-
1261
-        $prefer = $this->server->getHTTPPrefer();
1262
-
1263
-        $this->server->httpResponse->setStatus(207);
1264
-        $this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
1265
-        $this->server->httpResponse->setHeader('Vary', 'Brief,Prefer');
1266
-        $this->server->httpResponse->setBody($this->server->generateMultiStatus($result, $prefer['return'] === 'minimal'));
1267
-
1268
-    }
1269
-
1270
-    /* }}} */
1271
-
1272
-    /**
1273
-     * This method is used to generate HTML output for the
1274
-     * DAV\Browser\Plugin. This allows us to generate an interface users
1275
-     * can use to create new calendars.
1276
-     *
1277
-     * @param DAV\INode $node
1278
-     * @param string $output
1279
-     * @return bool
1280
-     */
1281
-    public function htmlActionsPanel(DAV\INode $node, &$output) {
1282
-
1283
-        if (!$node instanceof PrincipalCollection)
1284
-            return;
1285
-
1286
-        $output .= '<tr><td colspan="2"><form method="post" action="">
1234
+	/**
1235
+	 * principalPropertySearchReport
1236
+	 *
1237
+	 * This method is responsible for handing the
1238
+	 * {DAV:}principal-property-search report. This report can be used for
1239
+	 * clients to search for groups of principals, based on the value of one
1240
+	 * or more properties.
1241
+	 *
1242
+	 * @param Xml\Request\PrincipalPropertySearchReport $report
1243
+	 * @return void
1244
+	 */
1245
+	protected function principalPropertySearchReport($report) {
1246
+
1247
+		$uri = null;
1248
+		if (!$report->applyToPrincipalCollectionSet) {
1249
+			$uri = $this->server->httpRequest->getPath();
1250
+		}
1251
+		if ($this->server->getHttpDepth('0') !== 0) {
1252
+			throw new BadRequest('Depth must be 0');
1253
+		}
1254
+		$result = $this->principalSearch(
1255
+			$report->searchProperties,
1256
+			$report->properties,
1257
+			$uri,
1258
+			$report->test
1259
+		);
1260
+
1261
+		$prefer = $this->server->getHTTPPrefer();
1262
+
1263
+		$this->server->httpResponse->setStatus(207);
1264
+		$this->server->httpResponse->setHeader('Content-Type', 'application/xml; charset=utf-8');
1265
+		$this->server->httpResponse->setHeader('Vary', 'Brief,Prefer');
1266
+		$this->server->httpResponse->setBody($this->server->generateMultiStatus($result, $prefer['return'] === 'minimal'));
1267
+
1268
+	}
1269
+
1270
+	/* }}} */
1271
+
1272
+	/**
1273
+	 * This method is used to generate HTML output for the
1274
+	 * DAV\Browser\Plugin. This allows us to generate an interface users
1275
+	 * can use to create new calendars.
1276
+	 *
1277
+	 * @param DAV\INode $node
1278
+	 * @param string $output
1279
+	 * @return bool
1280
+	 */
1281
+	public function htmlActionsPanel(DAV\INode $node, &$output) {
1282
+
1283
+		if (!$node instanceof PrincipalCollection)
1284
+			return;
1285
+
1286
+		$output .= '<tr><td colspan="2"><form method="post" action="">
1287 1287
             <h3>Create new principal</h3>
1288 1288
             <input type="hidden" name="sabreAction" value="mkcol" />
1289 1289
             <input type="hidden" name="resourceType" value="{DAV:}principal" />
@@ -1294,28 +1294,28 @@  discard block
 block discarded – undo
1294 1294
             </form>
1295 1295
             </td></tr>';
1296 1296
 
1297
-        return false;
1298
-
1299
-    }
1300
-
1301
-    /**
1302
-     * Returns a bunch of meta-data about the plugin.
1303
-     *
1304
-     * Providing this information is optional, and is mainly displayed by the
1305
-     * Browser plugin.
1306
-     *
1307
-     * The description key in the returned array may contain html and will not
1308
-     * be sanitized.
1309
-     *
1310
-     * @return array
1311
-     */
1312
-    public function getPluginInfo() {
1313
-
1314
-        return [
1315
-            'name'        => $this->getPluginName(),
1316
-            'description' => 'Adds support for WebDAV ACL (rfc3744)',
1317
-            'link'        => 'http://sabre.io/dav/acl/',
1318
-        ];
1319
-
1320
-    }
1297
+		return false;
1298
+
1299
+	}
1300
+
1301
+	/**
1302
+	 * Returns a bunch of meta-data about the plugin.
1303
+	 *
1304
+	 * Providing this information is optional, and is mainly displayed by the
1305
+	 * Browser plugin.
1306
+	 *
1307
+	 * The description key in the returned array may contain html and will not
1308
+	 * be sanitized.
1309
+	 *
1310
+	 * @return array
1311
+	 */
1312
+	public function getPluginInfo() {
1313
+
1314
+		return [
1315
+			'name'        => $this->getPluginName(),
1316
+			'description' => 'Adds support for WebDAV ACL (rfc3744)',
1317
+			'link'        => 'http://sabre.io/dav/acl/',
1318
+		];
1319
+
1320
+	}
1321 1321
 }
Please login to merge, or discard this patch.
Doc Comments   +9 added lines, -11 removed lines patch added patch discarded remove patch
@@ -113,7 +113,7 @@  discard block
 block discarded – undo
113 113
      *
114 114
      * This list is used in the response of a HTTP OPTIONS request.
115 115
      *
116
-     * @return array
116
+     * @return string[]
117 117
      */
118 118
     public function getFeatures() {
119 119
 
@@ -125,7 +125,7 @@  discard block
 block discarded – undo
125 125
      * Returns a list of available methods for a given url
126 126
      *
127 127
      * @param string $uri
128
-     * @return array
128
+     * @return string[]
129 129
      */
130 130
     public function getMethods($uri) {
131 131
 
@@ -155,7 +155,7 @@  discard block
 block discarded – undo
155 155
      * implement them
156 156
      *
157 157
      * @param string $uri
158
-     * @return array
158
+     * @return string[]
159 159
      */
160 160
     public function getSupportedReportSet($uri) {
161 161
 
@@ -176,7 +176,7 @@  discard block
 block discarded – undo
176 176
      * and return true otherwise.
177 177
      *
178 178
      * @param string $uri
179
-     * @param array|string $privileges
179
+     * @param string $privileges
180 180
      * @param int $recursion
181 181
      * @param bool $throwExceptions if set to false, this method won't throw exceptions.
182 182
      * @throws Sabre\DAVACL\Exception\NeedPrivileges
@@ -269,7 +269,7 @@  discard block
 block discarded – undo
269 269
     /**
270 270
      * Returns all the principal groups the specified principal is a member of.
271 271
      *
272
-     * @param string $principal
272
+     * @param string $mainPrincipal
273 273
      * @return array
274 274
      */
275 275
     public function getPrincipalMembership($mainPrincipal) {
@@ -830,9 +830,7 @@  discard block
 block discarded – undo
830 830
      *
831 831
      * @param DAV\PropFind $propFind
832 832
      * @param DAV\INode $node
833
-     * @param array $requestedProperties
834
-     * @param array $returnedProperties
835
-     * @return bool
833
+     * @return false|null
836 834
      */
837 835
     public function propFind(DAV\PropFind $propFind, DAV\INode $node) {
838 836
 
@@ -980,7 +978,7 @@  discard block
 block discarded – undo
980 978
      * @param string $reportName
981 979
      * @param mixed $report
982 980
      * @param mixed $path
983
-     * @return bool
981
+     * @return false|null
984 982
      */
985 983
     public function report($reportName, $report, $path) {
986 984
 
@@ -1131,7 +1129,7 @@  discard block
 block discarded – undo
1131 1129
      * @param array $path
1132 1130
      * @param array $requestedProperties the list of required properties
1133 1131
      * @param int $depth
1134
-     * @return array
1132
+     * @return DAV\Xml\Element\Response[]
1135 1133
      */
1136 1134
     protected function expandProperties($path, array $requestedProperties, $depth) {
1137 1135
 
@@ -1276,7 +1274,7 @@  discard block
 block discarded – undo
1276 1274
      *
1277 1275
      * @param DAV\INode $node
1278 1276
      * @param string $output
1279
-     * @return bool
1277
+     * @return null|false
1280 1278
      */
1281 1279
     public function htmlActionsPanel(DAV\INode $node, &$output) {
1282 1280
 
Please login to merge, or discard this patch.
Spacing   +9 added lines, -9 removed lines patch added patch discarded remove patch
@@ -674,15 +674,15 @@
 block discarded – undo
674 674
     public function initialize(DAV\Server $server) {
675 675
 
676 676
         $this->server = $server;
677
-        $server->on('propFind',            [$this, 'propFind'], 20);
678
-        $server->on('beforeMethod',        [$this, 'beforeMethod'], 20);
679
-        $server->on('beforeBind',          [$this, 'beforeBind'], 20);
680
-        $server->on('beforeUnbind',        [$this, 'beforeUnbind'], 20);
681
-        $server->on('propPatch',           [$this, 'propPatch']);
682
-        $server->on('beforeUnlock',        [$this, 'beforeUnlock'], 20);
683
-        $server->on('report',              [$this, 'report']);
684
-        $server->on('method:ACL',          [$this, 'httpAcl']);
685
-        $server->on('onHTMLActionsPanel',  [$this, 'htmlActionsPanel']);
677
+        $server->on('propFind', [$this, 'propFind'], 20);
678
+        $server->on('beforeMethod', [$this, 'beforeMethod'], 20);
679
+        $server->on('beforeBind', [$this, 'beforeBind'], 20);
680
+        $server->on('beforeUnbind', [$this, 'beforeUnbind'], 20);
681
+        $server->on('propPatch', [$this, 'propPatch']);
682
+        $server->on('beforeUnlock', [$this, 'beforeUnlock'], 20);
683
+        $server->on('report', [$this, 'report']);
684
+        $server->on('method:ACL', [$this, 'httpAcl']);
685
+        $server->on('onHTMLActionsPanel', [$this, 'htmlActionsPanel']);
686 686
 
687 687
         array_push($server->protectedProperties,
688 688
             '{DAV:}alternate-URI-set',
Please login to merge, or discard this patch.
Braces   +49 added lines, -25 removed lines patch added patch discarded remove patch
@@ -184,7 +184,9 @@  discard block
 block discarded – undo
184 184
      */
185 185
     public function checkPrivileges($uri, $privileges, $recursion = self::R_PARENT, $throwExceptions = true) {
186 186
 
187
-        if (!is_array($privileges)) $privileges = [$privileges];
187
+        if (!is_array($privileges)) {
188
+        	$privileges = [$privileges];
189
+        }
188 190
 
189 191
         $acl = $this->getCurrentUserPrivilegeSet($uri);
190 192
 
@@ -192,10 +194,11 @@  discard block
 block discarded – undo
192 194
             if ($this->allowAccessToNodesWithoutACL) {
193 195
                 return true;
194 196
             } else {
195
-                if ($throwExceptions)
196
-                    throw new Exception\NeedPrivileges($uri, $privileges);
197
-                else
198
-                    return false;
197
+                if ($throwExceptions) {
198
+                                    throw new Exception\NeedPrivileges($uri, $privileges);
199
+                } else {
200
+                                    return false;
201
+                }
199 202
 
200 203
             }
201 204
         }
@@ -210,10 +213,11 @@  discard block
 block discarded – undo
210 213
         }
211 214
 
212 215
         if ($failed) {
213
-            if ($throwExceptions)
214
-                throw new Exception\NeedPrivileges($uri, $failed);
215
-            else
216
-                return false;
216
+            if ($throwExceptions) {
217
+                            throw new Exception\NeedPrivileges($uri, $failed);
218
+            } else {
219
+                            return false;
220
+            }
217 221
         }
218 222
         return true;
219 223
 
@@ -230,7 +234,9 @@  discard block
 block discarded – undo
230 234
     public function getCurrentUserPrincipal() {
231 235
 
232 236
         $authPlugin = $this->server->getPlugin('auth');
233
-        if (is_null($authPlugin)) return null;
237
+        if (is_null($authPlugin)) {
238
+        	return null;
239
+        }
234 240
         /** @var $authPlugin Sabre\DAV\Auth\Plugin */
235 241
 
236 242
         return $authPlugin->getCurrentPrincipal();
@@ -248,7 +254,9 @@  discard block
 block discarded – undo
248 254
 
249 255
         $currentUser = $this->getCurrentUserPrincipal();
250 256
 
251
-        if (is_null($currentUser)) return [];
257
+        if (is_null($currentUser)) {
258
+        	return [];
259
+        }
252 260
 
253 261
         return array_merge(
254 262
             [$currentUser],
@@ -331,8 +339,9 @@  discard block
 block discarded – undo
331 339
         if ($node instanceof IACL) {
332 340
             $result = $node->getSupportedPrivilegeSet();
333 341
 
334
-            if ($result)
335
-                return $result;
342
+            if ($result) {
343
+                            return $result;
344
+            }
336 345
         }
337 346
 
338 347
         return self::getDefaultSupportedPrivilegeSet();
@@ -506,7 +515,9 @@  discard block
 block discarded – undo
506 515
 
507 516
         $acl = $this->getACL($node);
508 517
 
509
-        if (is_null($acl)) return null;
518
+        if (is_null($acl)) {
519
+        	return null;
520
+        }
510 521
 
511 522
         $principals = $this->getCurrentUserPrincipals();
512 523
 
@@ -730,7 +741,9 @@  discard block
 block discarded – undo
730 741
         $exists = $this->server->tree->nodeExists($path);
731 742
 
732 743
         // If the node doesn't exists, none of these checks apply
733
-        if (!$exists) return;
744
+        if (!$exists) {
745
+        	return;
746
+        }
734 747
 
735 748
         switch ($method) {
736 749
 
@@ -889,7 +902,9 @@  discard block
 block discarded – undo
889 902
 
890 903
             $val = $this->principalCollectionSet;
891 904
             // Ensuring all collections end with a slash
892
-            foreach ($val as $k => $v) $val[$k] = $v . '/';
905
+            foreach ($val as $k => $v) {
906
+            	$val[$k] = $v . '/';
907
+            }
893 908
             return new DAV\Xml\Property\Href($val);
894 909
 
895 910
         });
@@ -1040,7 +1055,9 @@  discard block
 block discarded – undo
1040 1055
            not overwritten. */
1041 1056
         foreach ($oldAcl as $oldAce) {
1042 1057
 
1043
-            if (!isset($oldAce['protected']) || !$oldAce['protected']) continue;
1058
+            if (!isset($oldAce['protected']) || !$oldAce['protected']) {
1059
+            	continue;
1060
+            }
1044 1061
 
1045 1062
             $found = false;
1046 1063
             foreach ($newAcl as $newAce) {
@@ -1048,12 +1065,14 @@  discard block
 block discarded – undo
1048 1065
                     $newAce['privilege'] === $oldAce['privilege'] &&
1049 1066
                     $newAce['principal'] === $oldAce['principal'] &&
1050 1067
                     $newAce['protected']
1051
-                )
1052
-                $found = true;
1068
+                ) {
1069
+                                $found = true;
1070
+                }
1053 1071
             }
1054 1072
 
1055
-            if (!$found)
1056
-                throw new Exception\AceConflict('This resource contained a protected {DAV:}ace, but this privilege did not occur in the ACL request');
1073
+            if (!$found) {
1074
+                            throw new Exception\AceConflict('This resource contained a protected {DAV:}ace, but this privilege did not occur in the ACL request');
1075
+            }
1057 1076
 
1058 1077
         }
1059 1078
 
@@ -1144,11 +1163,15 @@  discard block
 block discarded – undo
1144 1163
             foreach ($requestedProperties as $propertyName => $childRequestedProperties) {
1145 1164
 
1146 1165
                 // We're only traversing if sub-properties were requested
1147
-                if (count($childRequestedProperties) === 0) continue;
1166
+                if (count($childRequestedProperties) === 0) {
1167
+                	continue;
1168
+                }
1148 1169
 
1149 1170
                 // We only have to do the expansion if the property was found
1150 1171
                 // and it contains an href element.
1151
-                if (!array_key_exists($propertyName, $node[200])) continue;
1172
+                if (!array_key_exists($propertyName, $node[200])) {
1173
+                	continue;
1174
+                }
1152 1175
 
1153 1176
                 if (!$node[200][$propertyName] instanceof DAV\Xml\Property\Href) {
1154 1177
                     continue;
@@ -1280,8 +1303,9 @@  discard block
 block discarded – undo
1280 1303
      */
1281 1304
     public function htmlActionsPanel(DAV\INode $node, &$output) {
1282 1305
 
1283
-        if (!$node instanceof PrincipalCollection)
1284
-            return;
1306
+        if (!$node instanceof PrincipalCollection) {
1307
+                    return;
1308
+        }
1285 1309
 
1286 1310
         $output .= '<tr><td colspan="2"><form method="post" action="">
1287 1311
             <h3>Create new principal</h3>
Please login to merge, or discard this patch.
libraries/SabreDAV/DAVACL/PrincipalCollection.php 2 patches
Unused Use Statements   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -2,8 +2,8 @@
 block discarded – undo
2 2
 
3 3
 namespace Sabre\DAVACL;
4 4
 
5
-use Sabre\DAV\Exception\InvalidResourceType;
6 5
 use Sabre\DAV\Exception\Forbidden;
6
+use Sabre\DAV\Exception\InvalidResourceType;
7 7
 use Sabre\DAV\IExtendedCollection;
8 8
 use Sabre\DAV\MkCol;
9 9
 
Please login to merge, or discard this patch.
Indentation   +128 added lines, -128 removed lines patch added patch discarded remove patch
@@ -19,133 +19,133 @@
 block discarded – undo
19 19
  */
20 20
 class PrincipalCollection extends AbstractPrincipalCollection implements IExtendedCollection, IACL {
21 21
 
22
-    /**
23
-     * This method returns a node for a principal.
24
-     *
25
-     * The passed array contains principal information, and is guaranteed to
26
-     * at least contain a uri item. Other properties may or may not be
27
-     * supplied by the authentication backend.
28
-     *
29
-     * @param array $principal
30
-     * @return \Sabre\DAV\INode
31
-     */
32
-    public function getChildForPrincipal(array $principal) {
33
-
34
-        return new Principal($this->principalBackend, $principal);
35
-
36
-    }
37
-
38
-    /**
39
-     * Creates a new collection.
40
-     *
41
-     * This method will receive a MkCol object with all the information about
42
-     * the new collection that's being created.
43
-     *
44
-     * The MkCol object contains information about the resourceType of the new
45
-     * collection. If you don't support the specified resourceType, you should
46
-     * throw Exception\InvalidResourceType.
47
-     *
48
-     * The object also contains a list of WebDAV properties for the new
49
-     * collection.
50
-     *
51
-     * You should call the handle() method on this object to specify exactly
52
-     * which properties you are storing. This allows the system to figure out
53
-     * exactly which properties you didn't store, which in turn allows other
54
-     * plugins (such as the propertystorage plugin) to handle storing the
55
-     * property for you.
56
-     *
57
-     * @param string $name
58
-     * @param MkCol $mkCol
59
-     * @throws Exception\InvalidResourceType
60
-     * @return void
61
-     */
62
-    public function createExtendedCollection($name, MkCol $mkCol) {
63
-
64
-        if (!$mkCol->hasResourceType('{DAV:}principal')) {
65
-            throw new InvalidResourceType('Only resources of type {DAV:}principal may be created here');
66
-        }
67
-
68
-        $this->principalBackend->createPrincipal(
69
-            $this->principalPrefix . '/' . $name,
70
-            $mkCol
71
-        );
72
-
73
-    }
74
-
75
-    /**
76
-     * Returns the owner principal
77
-     *
78
-     * This must be a url to a principal, or null if there's no owner
79
-     *
80
-     * @return string|null
81
-     */
82
-    public function getOwner() {
83
-        return null;
84
-    }
85
-
86
-    /**
87
-     * Returns a group principal
88
-     *
89
-     * This must be a url to a principal, or null if there's no owner
90
-     *
91
-     * @return string|null
92
-     */
93
-    public function getGroup() {
94
-        return null;
95
-    }
96
-
97
-    /**
98
-     * Returns a list of ACE's for this node.
99
-     *
100
-     * Each ACE has the following properties:
101
-     *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
102
-     *     currently the only supported privileges
103
-     *   * 'principal', a url to the principal who owns the node
104
-     *   * 'protected' (optional), indicating that this ACE is not allowed to
105
-     *      be updated.
106
-     *
107
-     * @return array
108
-     */
109
-    public function getACL() {
110
-        return [
111
-            [
112
-                'principal' => '{DAV:}authenticated',
113
-                'privilege' => '{DAV:}read',
114
-                'protected' => true,
115
-            ],
116
-        ];
117
-    }
118
-
119
-    /**
120
-     * Updates the ACL
121
-     *
122
-     * This method will receive a list of new ACE's as an array argument.
123
-     *
124
-     * @param array $acl
125
-     * @return void
126
-     */
127
-    public function setACL(array $acl) {
128
-
129
-        throw new Forbidden('Updating ACLs is not allowed on this node');
130
-
131
-    }
132
-
133
-    /**
134
-     * Returns the list of supported privileges for this node.
135
-     *
136
-     * The returned data structure is a list of nested privileges.
137
-     * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
138
-     * standard structure.
139
-     *
140
-     * If null is returned from this method, the default privilege set is used,
141
-     * which is fine for most common usecases.
142
-     *
143
-     * @return array|null
144
-     */
145
-    public function getSupportedPrivilegeSet() {
146
-
147
-        return null;
148
-
149
-    }
22
+	/**
23
+	 * This method returns a node for a principal.
24
+	 *
25
+	 * The passed array contains principal information, and is guaranteed to
26
+	 * at least contain a uri item. Other properties may or may not be
27
+	 * supplied by the authentication backend.
28
+	 *
29
+	 * @param array $principal
30
+	 * @return \Sabre\DAV\INode
31
+	 */
32
+	public function getChildForPrincipal(array $principal) {
33
+
34
+		return new Principal($this->principalBackend, $principal);
35
+
36
+	}
37
+
38
+	/**
39
+	 * Creates a new collection.
40
+	 *
41
+	 * This method will receive a MkCol object with all the information about
42
+	 * the new collection that's being created.
43
+	 *
44
+	 * The MkCol object contains information about the resourceType of the new
45
+	 * collection. If you don't support the specified resourceType, you should
46
+	 * throw Exception\InvalidResourceType.
47
+	 *
48
+	 * The object also contains a list of WebDAV properties for the new
49
+	 * collection.
50
+	 *
51
+	 * You should call the handle() method on this object to specify exactly
52
+	 * which properties you are storing. This allows the system to figure out
53
+	 * exactly which properties you didn't store, which in turn allows other
54
+	 * plugins (such as the propertystorage plugin) to handle storing the
55
+	 * property for you.
56
+	 *
57
+	 * @param string $name
58
+	 * @param MkCol $mkCol
59
+	 * @throws Exception\InvalidResourceType
60
+	 * @return void
61
+	 */
62
+	public function createExtendedCollection($name, MkCol $mkCol) {
63
+
64
+		if (!$mkCol->hasResourceType('{DAV:}principal')) {
65
+			throw new InvalidResourceType('Only resources of type {DAV:}principal may be created here');
66
+		}
67
+
68
+		$this->principalBackend->createPrincipal(
69
+			$this->principalPrefix . '/' . $name,
70
+			$mkCol
71
+		);
72
+
73
+	}
74
+
75
+	/**
76
+	 * Returns the owner principal
77
+	 *
78
+	 * This must be a url to a principal, or null if there's no owner
79
+	 *
80
+	 * @return string|null
81
+	 */
82
+	public function getOwner() {
83
+		return null;
84
+	}
85
+
86
+	/**
87
+	 * Returns a group principal
88
+	 *
89
+	 * This must be a url to a principal, or null if there's no owner
90
+	 *
91
+	 * @return string|null
92
+	 */
93
+	public function getGroup() {
94
+		return null;
95
+	}
96
+
97
+	/**
98
+	 * Returns a list of ACE's for this node.
99
+	 *
100
+	 * Each ACE has the following properties:
101
+	 *   * 'privilege', a string such as {DAV:}read or {DAV:}write. These are
102
+	 *     currently the only supported privileges
103
+	 *   * 'principal', a url to the principal who owns the node
104
+	 *   * 'protected' (optional), indicating that this ACE is not allowed to
105
+	 *      be updated.
106
+	 *
107
+	 * @return array
108
+	 */
109
+	public function getACL() {
110
+		return [
111
+			[
112
+				'principal' => '{DAV:}authenticated',
113
+				'privilege' => '{DAV:}read',
114
+				'protected' => true,
115
+			],
116
+		];
117
+	}
118
+
119
+	/**
120
+	 * Updates the ACL
121
+	 *
122
+	 * This method will receive a list of new ACE's as an array argument.
123
+	 *
124
+	 * @param array $acl
125
+	 * @return void
126
+	 */
127
+	public function setACL(array $acl) {
128
+
129
+		throw new Forbidden('Updating ACLs is not allowed on this node');
130
+
131
+	}
132
+
133
+	/**
134
+	 * Returns the list of supported privileges for this node.
135
+	 *
136
+	 * The returned data structure is a list of nested privileges.
137
+	 * See Sabre\DAVACL\Plugin::getDefaultSupportedPrivilegeSet for a simple
138
+	 * standard structure.
139
+	 *
140
+	 * If null is returned from this method, the default privilege set is used,
141
+	 * which is fine for most common usecases.
142
+	 *
143
+	 * @return array|null
144
+	 */
145
+	public function getSupportedPrivilegeSet() {
146
+
147
+		return null;
148
+
149
+	}
150 150
 
151 151
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/Event/coroutine.php 2 patches
Unused Use Statements   +1 added lines, -1 removed lines patch added patch discarded remove patch
@@ -2,8 +2,8 @@
 block discarded – undo
2 2
 
3 3
 namespace Sabre\Event;
4 4
 
5
-use Generator;
6 5
 use Exception;
6
+use Generator;
7 7
 
8 8
 /**
9 9
  * Turn asynchronous promise-based code into something that looks synchronous
Please login to merge, or discard this patch.
Indentation   +60 added lines, -60 removed lines patch added patch discarded remove patch
@@ -46,75 +46,75 @@
 block discarded – undo
46 46
  */
47 47
 function coroutine(callable $gen) {
48 48
 
49
-    $generator = $gen();
50
-    if (!$generator instanceof Generator) {
51
-        throw new \InvalidArgumentException('You must pass a generator function');
52
-    }
49
+	$generator = $gen();
50
+	if (!$generator instanceof Generator) {
51
+		throw new \InvalidArgumentException('You must pass a generator function');
52
+	}
53 53
 
54
-    // This is the value we're returning.
55
-    $promise = new Promise();
54
+	// This is the value we're returning.
55
+	$promise = new Promise();
56 56
 
57
-    $lastYieldResult = null;
57
+	$lastYieldResult = null;
58 58
 
59
-    /**
60
-     * So tempted to use the mythical y-combinator here, but it's not needed in
61
-     * PHP.
62
-     */
63
-    $advanceGenerator = function() use (&$advanceGenerator, $generator, $promise, &$lastYieldResult) {
59
+	/**
60
+	 * So tempted to use the mythical y-combinator here, but it's not needed in
61
+	 * PHP.
62
+	 */
63
+	$advanceGenerator = function() use (&$advanceGenerator, $generator, $promise, &$lastYieldResult) {
64 64
 
65
-        while ($generator->valid()) {
65
+		while ($generator->valid()) {
66 66
 
67
-            $yieldedValue = $generator->current();
68
-            if ($yieldedValue instanceof Promise) {
69
-                $yieldedValue->then(
70
-                    function($value) use ($generator, &$advanceGenerator, &$lastYieldResult) {
71
-                        $lastYieldResult = $value;
72
-                        $generator->send($value);
73
-                        $advanceGenerator();
74
-                    },
75
-                    function($reason) use ($generator, $advanceGenerator) {
76
-                        if ($reason instanceof Exception) {
77
-                            $generator->throw($reason);
78
-                        } elseif (is_scalar($reason)) {
79
-                            $generator->throw(new Exception($reason));
80
-                        } else {
81
-                            $type = is_object($reason) ? get_class($reason) : gettype($reason);
82
-                            $generator->throw(new Exception('Promise was rejected with reason of type: ' . $type));
83
-                        }
84
-                        $advanceGenerator();
85
-                    }
86
-                )->error(function($reason) use ($promise) {
87
-                    // This error handler would be called, if something in the
88
-                    // generator throws an exception, and it's not caught
89
-                    // locally.
90
-                    $promise->reject($reason);
91
-                });
92
-                // We need to break out of the loop, because $advanceGenerator
93
-                // will be called asynchronously when the promise has a result.
94
-                break;
95
-            } else {
96
-                // If the value was not a promise, we'll just let it pass through.
97
-                $lastYieldResult = $yieldedValue;
98
-                $generator->send($yieldedValue);
99
-            }
67
+			$yieldedValue = $generator->current();
68
+			if ($yieldedValue instanceof Promise) {
69
+				$yieldedValue->then(
70
+					function($value) use ($generator, &$advanceGenerator, &$lastYieldResult) {
71
+						$lastYieldResult = $value;
72
+						$generator->send($value);
73
+						$advanceGenerator();
74
+					},
75
+					function($reason) use ($generator, $advanceGenerator) {
76
+						if ($reason instanceof Exception) {
77
+							$generator->throw($reason);
78
+						} elseif (is_scalar($reason)) {
79
+							$generator->throw(new Exception($reason));
80
+						} else {
81
+							$type = is_object($reason) ? get_class($reason) : gettype($reason);
82
+							$generator->throw(new Exception('Promise was rejected with reason of type: ' . $type));
83
+						}
84
+						$advanceGenerator();
85
+					}
86
+				)->error(function($reason) use ($promise) {
87
+					// This error handler would be called, if something in the
88
+					// generator throws an exception, and it's not caught
89
+					// locally.
90
+					$promise->reject($reason);
91
+				});
92
+				// We need to break out of the loop, because $advanceGenerator
93
+				// will be called asynchronously when the promise has a result.
94
+				break;
95
+			} else {
96
+				// If the value was not a promise, we'll just let it pass through.
97
+				$lastYieldResult = $yieldedValue;
98
+				$generator->send($yieldedValue);
99
+			}
100 100
 
101
-        }
101
+		}
102 102
 
103
-        // If the generator is at the end, and we didn't run into an exception,
104
-        // we can fullfill the promise with the last thing that was yielded to
105
-        // us.
106
-        if (!$generator->valid() && $promise->state === Promise::PENDING) {
107
-            $promise->fulfill($lastYieldResult);
108
-        }
103
+		// If the generator is at the end, and we didn't run into an exception,
104
+		// we can fullfill the promise with the last thing that was yielded to
105
+		// us.
106
+		if (!$generator->valid() && $promise->state === Promise::PENDING) {
107
+			$promise->fulfill($lastYieldResult);
108
+		}
109 109
 
110
-    };
110
+	};
111 111
 
112
-    try {
113
-        $advanceGenerator();
114
-    } catch (Exception $e) {
115
-        $promise->reject($e);
116
-    }
112
+	try {
113
+		$advanceGenerator();
114
+	} catch (Exception $e) {
115
+		$promise->reject($e);
116
+	}
117 117
 
118
-    return $promise;
118
+	return $promise;
119 119
 
120 120
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/HTTP/functions.php 4 patches
Doc Comments   -1 removed lines patch added patch discarded remove patch
@@ -216,7 +216,6 @@
 block discarded – undo
216 216
  * Parameters are currently discarded. There's no known prefer value that
217 217
  * uses them.
218 218
  *
219
- * @param string|string[] $header
220 219
  * @return array
221 220
  */
222 221
 function parsePrefer($input) {
Please login to merge, or discard this patch.
Indentation   +234 added lines, -234 removed lines patch added patch discarded remove patch
@@ -31,40 +31,40 @@  discard block
 block discarded – undo
31 31
  */
32 32
 function parseDate($dateString) {
33 33
 
34
-    // Only the format is checked, valid ranges are checked by strtotime below
35
-    $month = '(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)';
36
-    $weekday = '(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)';
37
-    $wkday = '(Mon|Tue|Wed|Thu|Fri|Sat|Sun)';
38
-    $time = '([0-1]\d|2[0-3])(\:[0-5]\d){2}';
39
-    $date3 = $month . ' ([12]\d|3[01]| [1-9])';
40
-    $date2 = '(0[1-9]|[12]\d|3[01])\-' . $month . '\-\d{2}';
41
-    // 4-digit year cannot begin with 0 - unix timestamp begins in 1970
42
-    $date1 = '(0[1-9]|[12]\d|3[01]) ' . $month . ' [1-9]\d{3}';
43
-
44
-    // ANSI C's asctime() format
45
-    // 4-digit year cannot begin with 0 - unix timestamp begins in 1970
46
-    $asctime_date = $wkday . ' ' . $date3 . ' ' . $time . ' [1-9]\d{3}';
47
-    // RFC 850, obsoleted by RFC 1036
48
-    $rfc850_date = $weekday . ', ' . $date2 . ' ' . $time . ' GMT';
49
-    // RFC 822, updated by RFC 1123
50
-    $rfc1123_date = $wkday . ', ' . $date1 . ' ' . $time . ' GMT';
51
-    // allowed date formats by RFC 2616
52
-    $HTTP_date = "($rfc1123_date|$rfc850_date|$asctime_date)";
53
-
54
-    // allow for space around the string and strip it
55
-    $dateString = trim($dateString, ' ');
56
-    if (!preg_match('/^' . $HTTP_date . '$/', $dateString))
57
-        return false;
58
-
59
-    // append implicit GMT timezone to ANSI C time format
60
-    if (strpos($dateString, ' GMT') === false)
61
-        $dateString .= ' GMT';
62
-
63
-    try {
64
-        return new DateTime($dateString, new \DateTimeZone('UTC'));
65
-    } catch (\Exception $e) {
66
-        return false;
67
-    }
34
+	// Only the format is checked, valid ranges are checked by strtotime below
35
+	$month = '(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)';
36
+	$weekday = '(Monday|Tuesday|Wednesday|Thursday|Friday|Saturday|Sunday)';
37
+	$wkday = '(Mon|Tue|Wed|Thu|Fri|Sat|Sun)';
38
+	$time = '([0-1]\d|2[0-3])(\:[0-5]\d){2}';
39
+	$date3 = $month . ' ([12]\d|3[01]| [1-9])';
40
+	$date2 = '(0[1-9]|[12]\d|3[01])\-' . $month . '\-\d{2}';
41
+	// 4-digit year cannot begin with 0 - unix timestamp begins in 1970
42
+	$date1 = '(0[1-9]|[12]\d|3[01]) ' . $month . ' [1-9]\d{3}';
43
+
44
+	// ANSI C's asctime() format
45
+	// 4-digit year cannot begin with 0 - unix timestamp begins in 1970
46
+	$asctime_date = $wkday . ' ' . $date3 . ' ' . $time . ' [1-9]\d{3}';
47
+	// RFC 850, obsoleted by RFC 1036
48
+	$rfc850_date = $weekday . ', ' . $date2 . ' ' . $time . ' GMT';
49
+	// RFC 822, updated by RFC 1123
50
+	$rfc1123_date = $wkday . ', ' . $date1 . ' ' . $time . ' GMT';
51
+	// allowed date formats by RFC 2616
52
+	$HTTP_date = "($rfc1123_date|$rfc850_date|$asctime_date)";
53
+
54
+	// allow for space around the string and strip it
55
+	$dateString = trim($dateString, ' ');
56
+	if (!preg_match('/^' . $HTTP_date . '$/', $dateString))
57
+		return false;
58
+
59
+	// append implicit GMT timezone to ANSI C time format
60
+	if (strpos($dateString, ' GMT') === false)
61
+		$dateString .= ' GMT';
62
+
63
+	try {
64
+		return new DateTime($dateString, new \DateTimeZone('UTC'));
65
+	} catch (\Exception $e) {
66
+		return false;
67
+	}
68 68
 
69 69
 }
70 70
 
@@ -76,11 +76,11 @@  discard block
 block discarded – undo
76 76
  */
77 77
 function toDate(DateTime $dateTime) {
78 78
 
79
-    // We need to clone it, as we don't want to affect the existing
80
-    // DateTime.
81
-    $dateTime = clone $dateTime;
82
-    $dateTime->setTimeZone(new \DateTimeZone('GMT'));
83
-    return $dateTime->format('D, d M Y H:i:s \G\M\T');
79
+	// We need to clone it, as we don't want to affect the existing
80
+	// DateTime.
81
+	$dateTime = clone $dateTime;
82
+	$dateTime->setTimeZone(new \DateTimeZone('GMT'));
83
+	return $dateTime->format('D, d M Y H:i:s \G\M\T');
84 84
 
85 85
 }
86 86
 
@@ -106,89 +106,89 @@  discard block
 block discarded – undo
106 106
  */
107 107
 function negotiateContentType($acceptHeaderValue, array $availableOptions) {
108 108
 
109
-    if (!$acceptHeaderValue) {
110
-        // Grabbing the first in the list.
111
-        return reset($availableOptions);
112
-    }
113
-
114
-    $proposals = array_map(
115
-        'Sabre\HTTP\parseMimeType',
116
-        explode(',', $acceptHeaderValue)
117
-    );
118
-
119
-    // Ensuring array keys are reset.
120
-    $availableOptions = array_values($availableOptions);
121
-
122
-    $options = array_map(
123
-        'Sabre\HTTP\parseMimeType',
124
-        $availableOptions
125
-    );
126
-
127
-    $lastQuality = 0;
128
-    $lastSpecificity = 0;
129
-    $lastOptionIndex = 0;
130
-    $lastChoice = null;
131
-
132
-    foreach ($proposals as $proposal) {
133
-
134
-        // Ignoring broken values.
135
-        if (is_null($proposal)) continue;
136
-
137
-        // If the quality is lower we don't have to bother comparing.
138
-        if ($proposal['quality'] < $lastQuality) {
139
-            continue;
140
-        }
141
-
142
-        foreach ($options as $optionIndex => $option) {
143
-
144
-            if ($proposal['type'] !== '*' && $proposal['type'] !== $option['type']) {
145
-                // no match on type.
146
-                continue;
147
-            }
148
-            if ($proposal['subType'] !== '*' && $proposal['subType'] !== $option['subType']) {
149
-                // no match on subtype.
150
-                continue;
151
-            }
152
-
153
-            // Any parameters appearing on the options must appear on
154
-            // proposals.
155
-            foreach ($option['parameters'] as $paramName => $paramValue) {
156
-                if (!array_key_exists($paramName, $proposal['parameters'])) {
157
-                    continue 2;
158
-                }
159
-                if ($paramValue !== $proposal['parameters'][$paramName]) {
160
-                    continue 2;
161
-                }
162
-            }
163
-
164
-            // If we got here, we have a match on parameters, type and
165
-            // subtype. We need to calculate a score for how specific the
166
-            // match was.
167
-            $specificity =
168
-                ($proposal['type'] !== '*' ? 20 : 0) +
169
-                ($proposal['subType'] !== '*' ? 10 : 0) +
170
-                count($option['parameters']);
171
-
172
-
173
-            // Does this entry win?
174
-            if (
175
-                ($proposal['quality'] > $lastQuality) ||
176
-                ($proposal['quality'] === $lastQuality && $specificity > $lastSpecificity) ||
177
-                ($proposal['quality'] === $lastQuality && $specificity === $lastSpecificity && $optionIndex < $lastOptionIndex)
178
-            ) {
179
-
180
-                $lastQuality = $proposal['quality'];
181
-                $lastSpecificity = $specificity;
182
-                $lastOptionIndex = $optionIndex;
183
-                $lastChoice = $availableOptions[$optionIndex];
184
-
185
-            }
186
-
187
-        }
188
-
189
-    }
190
-
191
-    return $lastChoice;
109
+	if (!$acceptHeaderValue) {
110
+		// Grabbing the first in the list.
111
+		return reset($availableOptions);
112
+	}
113
+
114
+	$proposals = array_map(
115
+		'Sabre\HTTP\parseMimeType',
116
+		explode(',', $acceptHeaderValue)
117
+	);
118
+
119
+	// Ensuring array keys are reset.
120
+	$availableOptions = array_values($availableOptions);
121
+
122
+	$options = array_map(
123
+		'Sabre\HTTP\parseMimeType',
124
+		$availableOptions
125
+	);
126
+
127
+	$lastQuality = 0;
128
+	$lastSpecificity = 0;
129
+	$lastOptionIndex = 0;
130
+	$lastChoice = null;
131
+
132
+	foreach ($proposals as $proposal) {
133
+
134
+		// Ignoring broken values.
135
+		if (is_null($proposal)) continue;
136
+
137
+		// If the quality is lower we don't have to bother comparing.
138
+		if ($proposal['quality'] < $lastQuality) {
139
+			continue;
140
+		}
141
+
142
+		foreach ($options as $optionIndex => $option) {
143
+
144
+			if ($proposal['type'] !== '*' && $proposal['type'] !== $option['type']) {
145
+				// no match on type.
146
+				continue;
147
+			}
148
+			if ($proposal['subType'] !== '*' && $proposal['subType'] !== $option['subType']) {
149
+				// no match on subtype.
150
+				continue;
151
+			}
152
+
153
+			// Any parameters appearing on the options must appear on
154
+			// proposals.
155
+			foreach ($option['parameters'] as $paramName => $paramValue) {
156
+				if (!array_key_exists($paramName, $proposal['parameters'])) {
157
+					continue 2;
158
+				}
159
+				if ($paramValue !== $proposal['parameters'][$paramName]) {
160
+					continue 2;
161
+				}
162
+			}
163
+
164
+			// If we got here, we have a match on parameters, type and
165
+			// subtype. We need to calculate a score for how specific the
166
+			// match was.
167
+			$specificity =
168
+				($proposal['type'] !== '*' ? 20 : 0) +
169
+				($proposal['subType'] !== '*' ? 10 : 0) +
170
+				count($option['parameters']);
171
+
172
+
173
+			// Does this entry win?
174
+			if (
175
+				($proposal['quality'] > $lastQuality) ||
176
+				($proposal['quality'] === $lastQuality && $specificity > $lastSpecificity) ||
177
+				($proposal['quality'] === $lastQuality && $specificity === $lastSpecificity && $optionIndex < $lastOptionIndex)
178
+			) {
179
+
180
+				$lastQuality = $proposal['quality'];
181
+				$lastSpecificity = $specificity;
182
+				$lastOptionIndex = $optionIndex;
183
+				$lastChoice = $availableOptions[$optionIndex];
184
+
185
+			}
186
+
187
+		}
188
+
189
+	}
190
+
191
+	return $lastChoice;
192 192
 
193 193
 }
194 194
 
@@ -221,12 +221,12 @@  discard block
 block discarded – undo
221 221
  */
222 222
 function parsePrefer($input) {
223 223
 
224
-    $token = '[!#$%&\'*+\-.^_`~A-Za-z0-9]+';
224
+	$token = '[!#$%&\'*+\-.^_`~A-Za-z0-9]+';
225 225
 
226
-    // Work in progress
227
-    $word = '(?: [a-zA-Z0-9]+ | "[a-zA-Z0-9]*" )';
226
+	// Work in progress
227
+	$word = '(?: [a-zA-Z0-9]+ | "[a-zA-Z0-9]*" )';
228 228
 
229
-    $regex = <<<REGEX
229
+	$regex = <<<REGEX
230 230
 /
231 231
 ^
232 232
 (?<name> $token)      # Prefer property name
@@ -239,44 +239,44 @@  discard block
 block discarded – undo
239 239
 /x
240 240
 REGEX;
241 241
 
242
-    $output = [];
243
-    foreach (getHeaderValues($input) as $value) {
244
-
245
-        if (!preg_match($regex, $value, $matches)) {
246
-            // Ignore
247
-            continue;
248
-        }
249
-
250
-        // Mapping old values to their new counterparts
251
-        switch ($matches['name']) {
252
-            case 'return-asynch' :
253
-                $output['respond-async'] = true;
254
-                break;
255
-            case 'return-representation' :
256
-                $output['return'] = 'representation';
257
-                break;
258
-            case 'return-minimal' :
259
-                $output['return'] = 'minimal';
260
-                break;
261
-            case 'strict' :
262
-                $output['handling'] = 'strict';
263
-                break;
264
-            case 'lenient' :
265
-                $output['handling'] = 'lenient';
266
-                break;
267
-            default :
268
-                if (isset($matches['value'])) {
269
-                    $value = trim($matches['value'], '"');
270
-                } else {
271
-                    $value = true;
272
-                }
273
-                $output[strtolower($matches['name'])] = empty($value) ? true : $value;
274
-                break;
275
-        }
276
-
277
-    }
278
-
279
-    return $output;
242
+	$output = [];
243
+	foreach (getHeaderValues($input) as $value) {
244
+
245
+		if (!preg_match($regex, $value, $matches)) {
246
+			// Ignore
247
+			continue;
248
+		}
249
+
250
+		// Mapping old values to their new counterparts
251
+		switch ($matches['name']) {
252
+			case 'return-asynch' :
253
+				$output['respond-async'] = true;
254
+				break;
255
+			case 'return-representation' :
256
+				$output['return'] = 'representation';
257
+				break;
258
+			case 'return-minimal' :
259
+				$output['return'] = 'minimal';
260
+				break;
261
+			case 'strict' :
262
+				$output['handling'] = 'strict';
263
+				break;
264
+			case 'lenient' :
265
+				$output['handling'] = 'lenient';
266
+				break;
267
+			default :
268
+				if (isset($matches['value'])) {
269
+					$value = trim($matches['value'], '"');
270
+				} else {
271
+					$value = true;
272
+				}
273
+				$output[strtolower($matches['name'])] = empty($value) ? true : $value;
274
+				break;
275
+		}
276
+
277
+	}
278
+
279
+	return $output;
280 280
 
281 281
 }
282 282
 
@@ -300,16 +300,16 @@  discard block
 block discarded – undo
300 300
  */
301 301
 function getHeaderValues($values, $values2 = null) {
302 302
 
303
-    $values = (array)$values;
304
-    if ($values2) {
305
-        $values = array_merge($values, (array)$values2);
306
-    }
307
-    foreach ($values as $l1) {
308
-        foreach (explode(',', $l1) as $l2) {
309
-            $result[] = trim($l2);
310
-        }
311
-    }
312
-    return $result;
303
+	$values = (array)$values;
304
+	if ($values2) {
305
+		$values = array_merge($values, (array)$values2);
306
+	}
307
+	foreach ($values as $l1) {
308
+		foreach (explode(',', $l1) as $l2) {
309
+			$result[] = trim($l2);
310
+		}
311
+	}
312
+	return $result;
313 313
 
314 314
 }
315 315
 
@@ -326,52 +326,52 @@  discard block
 block discarded – undo
326 326
  */
327 327
 function parseMimeType($str) {
328 328
 
329
-    $parameters = [];
330
-    // If no q= parameter appears, then quality = 1.
331
-    $quality = 1;
332
-
333
-    $parts = explode(';', $str);
334
-
335
-    // The first part is the mime-type.
336
-    $mimeType = array_shift($parts);
337
-
338
-    $mimeType = explode('/', trim($mimeType));
339
-    if (count($mimeType) !== 2) {
340
-        // Illegal value
341
-        return null;
342
-    }
343
-    list($type, $subType) = $mimeType;
344
-
345
-    foreach ($parts as $part) {
346
-
347
-        $part = trim($part);
348
-        if (strpos($part, '=')) {
349
-            list($partName, $partValue) =
350
-                explode('=', $part, 2);
351
-        } else {
352
-            $partName = $part;
353
-            $partValue = null;
354
-        }
355
-
356
-        // The quality parameter, if it appears, also marks the end of
357
-        // the parameter list. Anything after the q= counts as an
358
-        // 'accept extension' and could introduce new semantics in
359
-        // content-negotation.
360
-        if ($partName !== 'q') {
361
-            $parameters[$partName] = $part;
362
-        } else {
363
-            $quality = (float)$partValue;
364
-            break; // Stop parsing parts
365
-        }
366
-
367
-    }
368
-
369
-    return [
370
-        'type'       => $type,
371
-        'subType'    => $subType,
372
-        'quality'    => $quality,
373
-        'parameters' => $parameters,
374
-    ];
329
+	$parameters = [];
330
+	// If no q= parameter appears, then quality = 1.
331
+	$quality = 1;
332
+
333
+	$parts = explode(';', $str);
334
+
335
+	// The first part is the mime-type.
336
+	$mimeType = array_shift($parts);
337
+
338
+	$mimeType = explode('/', trim($mimeType));
339
+	if (count($mimeType) !== 2) {
340
+		// Illegal value
341
+		return null;
342
+	}
343
+	list($type, $subType) = $mimeType;
344
+
345
+	foreach ($parts as $part) {
346
+
347
+		$part = trim($part);
348
+		if (strpos($part, '=')) {
349
+			list($partName, $partValue) =
350
+				explode('=', $part, 2);
351
+		} else {
352
+			$partName = $part;
353
+			$partValue = null;
354
+		}
355
+
356
+		// The quality parameter, if it appears, also marks the end of
357
+		// the parameter list. Anything after the q= counts as an
358
+		// 'accept extension' and could introduce new semantics in
359
+		// content-negotation.
360
+		if ($partName !== 'q') {
361
+			$parameters[$partName] = $part;
362
+		} else {
363
+			$quality = (float)$partValue;
364
+			break; // Stop parsing parts
365
+		}
366
+
367
+	}
368
+
369
+	return [
370
+		'type'       => $type,
371
+		'subType'    => $subType,
372
+		'quality'    => $quality,
373
+		'parameters' => $parameters,
374
+	];
375 375
 
376 376
 }
377 377
 
@@ -385,11 +385,11 @@  discard block
 block discarded – undo
385 385
  */
386 386
 function encodePath($path) {
387 387
 
388
-    return preg_replace_callback('/([^A-Za-z0-9_\-\.~\(\)\/:@])/', function($match) {
388
+	return preg_replace_callback('/([^A-Za-z0-9_\-\.~\(\)\/:@])/', function($match) {
389 389
 
390
-        return '%' . sprintf('%02x', ord($match[0]));
390
+		return '%' . sprintf('%02x', ord($match[0]));
391 391
 
392
-    }, $path);
392
+	}, $path);
393 393
 
394 394
 }
395 395
 
@@ -403,11 +403,11 @@  discard block
 block discarded – undo
403 403
  */
404 404
 function encodePathSegment($pathSegment) {
405 405
 
406
-    return preg_replace_callback('/([^A-Za-z0-9_\-\.~\(\):@])/', function($match) {
406
+	return preg_replace_callback('/([^A-Za-z0-9_\-\.~\(\):@])/', function($match) {
407 407
 
408
-        return '%' . sprintf('%02x', ord($match[0]));
408
+		return '%' . sprintf('%02x', ord($match[0]));
409 409
 
410
-    }, $pathSegment);
410
+	}, $pathSegment);
411 411
 }
412 412
 
413 413
 /**
@@ -418,7 +418,7 @@  discard block
 block discarded – undo
418 418
  */
419 419
 function decodePath($path) {
420 420
 
421
-    return decodePathSegment($path);
421
+	return decodePathSegment($path);
422 422
 
423 423
 }
424 424
 
@@ -430,16 +430,16 @@  discard block
 block discarded – undo
430 430
  */
431 431
 function decodePathSegment($path) {
432 432
 
433
-    $path = rawurldecode($path);
434
-    $encoding = mb_detect_encoding($path, ['UTF-8', 'ISO-8859-1']);
433
+	$path = rawurldecode($path);
434
+	$encoding = mb_detect_encoding($path, ['UTF-8', 'ISO-8859-1']);
435 435
 
436
-    switch ($encoding) {
436
+	switch ($encoding) {
437 437
 
438
-        case 'ISO-8859-1' :
439
-            $path = utf8_encode($path);
438
+		case 'ISO-8859-1' :
439
+			$path = utf8_encode($path);
440 440
 
441
-    }
441
+	}
442 442
 
443
-    return $path;
443
+	return $path;
444 444
 
445 445
 }
Please login to merge, or discard this patch.
Spacing   +3 added lines, -3 removed lines patch added patch discarded remove patch
@@ -300,9 +300,9 @@  discard block
 block discarded – undo
300 300
  */
301 301
 function getHeaderValues($values, $values2 = null) {
302 302
 
303
-    $values = (array)$values;
303
+    $values = (array) $values;
304 304
     if ($values2) {
305
-        $values = array_merge($values, (array)$values2);
305
+        $values = array_merge($values, (array) $values2);
306 306
     }
307 307
     foreach ($values as $l1) {
308 308
         foreach (explode(',', $l1) as $l2) {
@@ -360,7 +360,7 @@  discard block
 block discarded – undo
360 360
         if ($partName !== 'q') {
361 361
             $parameters[$partName] = $part;
362 362
         } else {
363
-            $quality = (float)$partValue;
363
+            $quality = (float) $partValue;
364 364
             break; // Stop parsing parts
365 365
         }
366 366
 
Please login to merge, or discard this patch.
Braces   +9 added lines, -5 removed lines patch added patch discarded remove patch
@@ -53,12 +53,14 @@  discard block
 block discarded – undo
53 53
 
54 54
     // allow for space around the string and strip it
55 55
     $dateString = trim($dateString, ' ');
56
-    if (!preg_match('/^' . $HTTP_date . '$/', $dateString))
57
-        return false;
56
+    if (!preg_match('/^' . $HTTP_date . '$/', $dateString)) {
57
+            return false;
58
+    }
58 59
 
59 60
     // append implicit GMT timezone to ANSI C time format
60
-    if (strpos($dateString, ' GMT') === false)
61
-        $dateString .= ' GMT';
61
+    if (strpos($dateString, ' GMT') === false) {
62
+            $dateString .= ' GMT';
63
+    }
62 64
 
63 65
     try {
64 66
         return new DateTime($dateString, new \DateTimeZone('UTC'));
@@ -132,7 +134,9 @@  discard block
 block discarded – undo
132 134
     foreach ($proposals as $proposal) {
133 135
 
134 136
         // Ignoring broken values.
135
-        if (is_null($proposal)) continue;
137
+        if (is_null($proposal)) {
138
+        	continue;
139
+        }
136 140
 
137 141
         // If the quality is lower we don't have to bother comparing.
138 142
         if ($proposal['quality'] < $lastQuality) {
Please login to merge, or discard this patch.
libraries/SabreDAV/HTTP/URLUtil.php 2 patches
Unused Use Statements   -2 removed lines patch added patch discarded remove patch
@@ -2,8 +2,6 @@
 block discarded – undo
2 2
 
3 3
 namespace Sabre\HTTP;
4 4
 
5
-use Sabre\URI;
6
-
7 5
 /**
8 6
  * URL utility class
9 7
  *
Please login to merge, or discard this patch.
Indentation   +82 added lines, -82 removed lines patch added patch discarded remove patch
@@ -17,87 +17,87 @@
 block discarded – undo
17 17
  */
18 18
 class URLUtil {
19 19
 
20
-    /**
21
-     * Encodes the path of a url.
22
-     *
23
-     * slashes (/) are treated as path-separators.
24
-     *
25
-     * @deprecated use \Sabre\HTTP\encodePath()
26
-     * @param string $path
27
-     * @return string
28
-     */
29
-    static function encodePath($path) {
30
-
31
-        return encodePath($path);
32
-
33
-    }
34
-
35
-    /**
36
-     * Encodes a 1 segment of a path
37
-     *
38
-     * Slashes are considered part of the name, and are encoded as %2f
39
-     *
40
-     * @deprecated use \Sabre\HTTP\encodePathSegment()
41
-     * @param string $pathSegment
42
-     * @return string
43
-     */
44
-    static function encodePathSegment($pathSegment) {
45
-
46
-        return encodePathSegment($pathSegment);
47
-
48
-    }
49
-
50
-    /**
51
-     * Decodes a url-encoded path
52
-     *
53
-     * @deprecated use \Sabre\HTTP\decodePath
54
-     * @param string $path
55
-     * @return string
56
-     */
57
-    static function decodePath($path) {
58
-
59
-        return decodePath($path);
60
-
61
-    }
62
-
63
-    /**
64
-     * Decodes a url-encoded path segment
65
-     *
66
-     * @deprecated use \Sabre\HTTP\decodePathSegment()
67
-     * @param string $path
68
-     * @return string
69
-     */
70
-    static function decodePathSegment($path) {
71
-
72
-        return decodePathSegment($path);
73
-
74
-    }
75
-
76
-    /**
77
-     * Returns the 'dirname' and 'basename' for a path.
78
-     *
79
-     * @deprecated Use Sabre\Uri\split().
80
-     * @param string $path
81
-     * @return array
82
-     */
83
-    static function splitPath($path) {
84
-
85
-        return Uri\split($path);
86
-
87
-    }
88
-
89
-    /**
90
-     * Resolves relative urls, like a browser would.
91
-     *
92
-     * @deprecated Use Sabre\Uri\resolve().
93
-     * @param string $basePath
94
-     * @param string $newPath
95
-     * @return string
96
-     */
97
-    static function resolve($basePath, $newPath) {
98
-
99
-        return Uri\resolve($basePath, $newPath);
100
-
101
-    }
20
+	/**
21
+	 * Encodes the path of a url.
22
+	 *
23
+	 * slashes (/) are treated as path-separators.
24
+	 *
25
+	 * @deprecated use \Sabre\HTTP\encodePath()
26
+	 * @param string $path
27
+	 * @return string
28
+	 */
29
+	static function encodePath($path) {
30
+
31
+		return encodePath($path);
32
+
33
+	}
34
+
35
+	/**
36
+	 * Encodes a 1 segment of a path
37
+	 *
38
+	 * Slashes are considered part of the name, and are encoded as %2f
39
+	 *
40
+	 * @deprecated use \Sabre\HTTP\encodePathSegment()
41
+	 * @param string $pathSegment
42
+	 * @return string
43
+	 */
44
+	static function encodePathSegment($pathSegment) {
45
+
46
+		return encodePathSegment($pathSegment);
47
+
48
+	}
49
+
50
+	/**
51
+	 * Decodes a url-encoded path
52
+	 *
53
+	 * @deprecated use \Sabre\HTTP\decodePath
54
+	 * @param string $path
55
+	 * @return string
56
+	 */
57
+	static function decodePath($path) {
58
+
59
+		return decodePath($path);
60
+
61
+	}
62
+
63
+	/**
64
+	 * Decodes a url-encoded path segment
65
+	 *
66
+	 * @deprecated use \Sabre\HTTP\decodePathSegment()
67
+	 * @param string $path
68
+	 * @return string
69
+	 */
70
+	static function decodePathSegment($path) {
71
+
72
+		return decodePathSegment($path);
73
+
74
+	}
75
+
76
+	/**
77
+	 * Returns the 'dirname' and 'basename' for a path.
78
+	 *
79
+	 * @deprecated Use Sabre\Uri\split().
80
+	 * @param string $path
81
+	 * @return array
82
+	 */
83
+	static function splitPath($path) {
84
+
85
+		return Uri\split($path);
86
+
87
+	}
88
+
89
+	/**
90
+	 * Resolves relative urls, like a browser would.
91
+	 *
92
+	 * @deprecated Use Sabre\Uri\resolve().
93
+	 * @param string $basePath
94
+	 * @param string $newPath
95
+	 * @return string
96
+	 */
97
+	static function resolve($basePath, $newPath) {
98
+
99
+		return Uri\resolve($basePath, $newPath);
100
+
101
+	}
102 102
 
103 103
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/HTTP/Util.php 2 patches
Doc Comments   +2 added lines, -2 removed lines patch added patch discarded remove patch
@@ -31,8 +31,8 @@
 block discarded – undo
31 31
      * Deprecated! Use negotiateContentType.
32 32
      *
33 33
      * @deprecated Use \Sabre\HTTP\NegotiateContentType
34
-     * @param string|null $acceptHeader
35
-     * @param array $availableOptions
34
+     * @param string|null $acceptHeaderValue
35
+     * @param string[] $availableOptions
36 36
      * @return string|null
37 37
      */
38 38
     static function negotiate($acceptHeaderValue, array $availableOptions) {
Please login to merge, or discard this patch.
Indentation   +47 added lines, -47 removed lines patch added patch discarded remove patch
@@ -13,62 +13,62 @@
 block discarded – undo
13 13
  */
14 14
 class Util {
15 15
 
16
-    /**
17
-     * Content negotiation
18
-     *
19
-     * @deprecated Use \Sabre\HTTP\negotiateContentType
20
-     * @param string|null $acceptHeaderValue
21
-     * @param array $availableOptions
22
-     * @return string|null
23
-     */
24
-    static function negotiateContentType($acceptHeaderValue, array $availableOptions) {
16
+	/**
17
+	 * Content negotiation
18
+	 *
19
+	 * @deprecated Use \Sabre\HTTP\negotiateContentType
20
+	 * @param string|null $acceptHeaderValue
21
+	 * @param array $availableOptions
22
+	 * @return string|null
23
+	 */
24
+	static function negotiateContentType($acceptHeaderValue, array $availableOptions) {
25 25
 
26
-        return negotiateContentType($acceptHeaderValue, $availableOptions);
26
+		return negotiateContentType($acceptHeaderValue, $availableOptions);
27 27
 
28
-    }
28
+	}
29 29
 
30
-    /**
31
-     * Deprecated! Use negotiateContentType.
32
-     *
33
-     * @deprecated Use \Sabre\HTTP\NegotiateContentType
34
-     * @param string|null $acceptHeader
35
-     * @param array $availableOptions
36
-     * @return string|null
37
-     */
38
-    static function negotiate($acceptHeaderValue, array $availableOptions) {
30
+	/**
31
+	 * Deprecated! Use negotiateContentType.
32
+	 *
33
+	 * @deprecated Use \Sabre\HTTP\NegotiateContentType
34
+	 * @param string|null $acceptHeader
35
+	 * @param array $availableOptions
36
+	 * @return string|null
37
+	 */
38
+	static function negotiate($acceptHeaderValue, array $availableOptions) {
39 39
 
40
-        return negotiateContentType($acceptHeaderValue, $availableOptions);
40
+		return negotiateContentType($acceptHeaderValue, $availableOptions);
41 41
 
42
-    }
42
+	}
43 43
 
44
-    /**
45
-     * Parses a RFC2616-compatible date string
46
-     *
47
-     * This method returns false if the date is invalid
48
-     *
49
-     * @deprecated Use parseDate
50
-     * @param string $dateHeader
51
-     * @return bool|DateTime
52
-     */
53
-    static function parseHTTPDate($dateHeader) {
44
+	/**
45
+	 * Parses a RFC2616-compatible date string
46
+	 *
47
+	 * This method returns false if the date is invalid
48
+	 *
49
+	 * @deprecated Use parseDate
50
+	 * @param string $dateHeader
51
+	 * @return bool|DateTime
52
+	 */
53
+	static function parseHTTPDate($dateHeader) {
54 54
 
55
-        return parseDate($dateHeader);
55
+		return parseDate($dateHeader);
56 56
 
57
-    }
57
+	}
58 58
 
59
-    /**
60
-     * Transforms a DateTime object to HTTP's most common date format.
61
-     *
62
-     * We're serializing it as the RFC 1123 date, which, for HTTP must be
63
-     * specified as GMT.
64
-     *
65
-     * @deprecated Use toDate
66
-     * @param \DateTime $dateTime
67
-     * @return string
68
-     */
69
-    static function toHTTPDate(\DateTime $dateTime) {
59
+	/**
60
+	 * Transforms a DateTime object to HTTP's most common date format.
61
+	 *
62
+	 * We're serializing it as the RFC 1123 date, which, for HTTP must be
63
+	 * specified as GMT.
64
+	 *
65
+	 * @deprecated Use toDate
66
+	 * @param \DateTime $dateTime
67
+	 * @return string
68
+	 */
69
+	static function toHTTPDate(\DateTime $dateTime) {
70 70
 
71
-        return toDate($dateTime);
71
+		return toDate($dateTime);
72 72
 
73
-    }
73
+	}
74 74
 }
Please login to merge, or discard this patch.
libraries/SabreDAV/VObject/Cli.php 4 patches
Unused Use Statements   +1 added lines, -2 removed lines patch added patch discarded remove patch
@@ -2,8 +2,7 @@
 block discarded – undo
2 2
 
3 3
 namespace Sabre\VObject;
4 4
 
5
-use
6
-    InvalidArgumentException;
5
+use InvalidArgumentException;
7 6
 
8 7
 /**
9 8
  * This is the CLI interface for sabre-vobject.
Please login to merge, or discard this patch.
Spacing   +5 added lines, -5 removed lines patch added patch discarded remove patch
@@ -335,10 +335,10 @@  discard block
 block discarded – undo
335 335
 
336 336
         switch ($vObj->name) {
337 337
             case 'VCALENDAR' :
338
-                $this->log("iCalendar: " . (string)$vObj->VERSION);
338
+                $this->log("iCalendar: " . (string) $vObj->VERSION);
339 339
                 break;
340 340
             case 'VCARD' :
341
-                $this->log("vCard: " . (string)$vObj->VERSION);
341
+                $this->log("vCard: " . (string) $vObj->VERSION);
342 342
                 break;
343 343
         }
344 344
 
@@ -382,10 +382,10 @@  discard block
 block discarded – undo
382 382
 
383 383
         switch ($vObj->name) {
384 384
             case 'VCALENDAR' :
385
-                $this->log("iCalendar: " . (string)$vObj->VERSION);
385
+                $this->log("iCalendar: " . (string) $vObj->VERSION);
386 386
                 break;
387 387
             case 'VCARD' :
388
-                $this->log("vCard: " . (string)$vObj->VERSION);
388
+                $this->log("vCard: " . (string) $vObj->VERSION);
389 389
                 break;
390 390
         }
391 391
 
@@ -652,7 +652,7 @@  discard block
 block discarded – undo
652 652
                 }
653 653
                 $first2 = true;
654 654
                 // Looping through property sub-values
655
-                foreach ((array)$part as $subPart) {
655
+                foreach ((array) $part as $subPart) {
656 656
                     if ($first2) {
657 657
                         $first2 = false;
658 658
                     } else {
Please login to merge, or discard this patch.
Indentation   +729 added lines, -729 removed lines patch added patch discarded remove patch
@@ -3,7 +3,7 @@  discard block
 block discarded – undo
3 3
 namespace Sabre\VObject;
4 4
 
5 5
 use
6
-    InvalidArgumentException;
6
+	InvalidArgumentException;
7 7
 
8 8
 /**
9 9
  * This is the CLI interface for sabre-vobject.
@@ -14,759 +14,759 @@  discard block
 block discarded – undo
14 14
  */
15 15
 class Cli {
16 16
 
17
-    /**
18
-     * No output.
19
-     *
20
-     * @var bool
21
-     */
22
-    protected $quiet = false;
23
-
24
-    /**
25
-     * Help display.
26
-     *
27
-     * @var bool
28
-     */
29
-    protected $showHelp = false;
30
-
31
-    /**
32
-     * Wether to spit out 'mimedir' or 'json' format.
33
-     *
34
-     * @var string
35
-     */
36
-    protected $format;
37
-
38
-    /**
39
-     * JSON pretty print.
40
-     *
41
-     * @var bool
42
-     */
43
-    protected $pretty;
44
-
45
-    /**
46
-     * Source file.
47
-     *
48
-     * @var string
49
-     */
50
-    protected $inputPath;
51
-
52
-    /**
53
-     * Destination file.
54
-     *
55
-     * @var string
56
-     */
57
-    protected $outputPath;
58
-
59
-    /**
60
-     * output stream.
61
-     *
62
-     * @var resource
63
-     */
64
-    protected $stdout;
65
-
66
-    /**
67
-     * stdin.
68
-     *
69
-     * @var resource
70
-     */
71
-    protected $stdin;
72
-
73
-    /**
74
-     * stderr.
75
-     *
76
-     * @var resource
77
-     */
78
-    protected $stderr;
79
-
80
-    /**
81
-     * Input format (one of json or mimedir).
82
-     *
83
-     * @var string
84
-     */
85
-    protected $inputFormat;
86
-
87
-    /**
88
-     * Makes the parser less strict.
89
-     *
90
-     * @var bool
91
-     */
92
-    protected $forgiving = false;
93
-
94
-    /**
95
-     * Main function.
96
-     *
97
-     * @return int
98
-     */
99
-    public function main(array $argv) {
100
-
101
-        // @codeCoverageIgnoreStart
102
-        // We cannot easily test this, so we'll skip it. Pretty basic anyway.
103
-
104
-        if (!$this->stderr) {
105
-            $this->stderr = fopen('php://stderr', 'w');
106
-        }
107
-        if (!$this->stdout) {
108
-            $this->stdout = fopen('php://stdout', 'w');
109
-        }
110
-        if (!$this->stdin) {
111
-            $this->stdin = fopen('php://stdin', 'r');
112
-        }
113
-
114
-        // @codeCoverageIgnoreEnd
115
-
116
-
117
-        try {
118
-
119
-            list($options, $positional) = $this->parseArguments($argv);
120
-
121
-            if (isset($options['q'])) {
122
-                $this->quiet = true;
123
-            }
124
-            $this->log($this->colorize('green', "sabre/vobject ") . $this->colorize('yellow', Version::VERSION));
125
-
126
-            foreach ($options as $name => $value) {
127
-
128
-                switch ($name) {
129
-
130
-                    case 'q' :
131
-                        // Already handled earlier.
132
-                        break;
133
-                    case 'h' :
134
-                    case 'help' :
135
-                        $this->showHelp();
136
-                        return 0;
137
-                        break;
138
-                    case 'format' :
139
-                        switch ($value) {
140
-
141
-                            // jcard/jcal documents
142
-                            case 'jcard' :
143
-                            case 'jcal' :
144
-
145
-                            // specific document versions
146
-                            case 'vcard21' :
147
-                            case 'vcard30' :
148
-                            case 'vcard40' :
149
-                            case 'icalendar20' :
150
-
151
-                            // specific formats
152
-                            case 'json' :
153
-                            case 'mimedir' :
154
-
155
-                            // icalendar/vcad
156
-                            case 'icalendar' :
157
-                            case 'vcard' :
158
-                                $this->format = $value;
159
-                                break;
160
-
161
-                            default :
162
-                                throw new InvalidArgumentException('Unknown format: ' . $value);
163
-
164
-                        }
165
-                        break;
166
-                    case 'pretty' :
167
-                        if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
168
-                            $this->pretty = true;
169
-                        }
170
-                        break;
171
-                    case 'forgiving' :
172
-                        $this->forgiving = true;
173
-                        break;
174
-                    case 'inputformat' :
175
-                        switch ($value) {
176
-                            // json formats
177
-                            case 'jcard' :
178
-                            case 'jcal' :
179
-                            case 'json' :
180
-                                $this->inputFormat = 'json';
181
-                                break;
182
-
183
-                            // mimedir formats
184
-                            case 'mimedir' :
185
-                            case 'icalendar' :
186
-                            case 'vcard' :
187
-                            case 'vcard21' :
188
-                            case 'vcard30' :
189
-                            case 'vcard40' :
190
-                            case 'icalendar20' :
191
-
192
-                                $this->inputFormat = 'mimedir';
193
-                                break;
194
-
195
-                            default :
196
-                                throw new InvalidArgumentException('Unknown format: ' . $value);
197
-
198
-                        }
199
-                        break;
200
-                    default :
201
-                        throw new InvalidArgumentException('Unknown option: ' . $name);
202
-
203
-                }
204
-
205
-            }
206
-
207
-            if (count($positional) === 0) {
208
-                $this->showHelp();
209
-                return 1;
210
-            }
211
-
212
-            if (count($positional) === 1) {
213
-                throw new InvalidArgumentException('Inputfile is a required argument');
214
-            }
215
-
216
-            if (count($positional) > 3) {
217
-                throw new InvalidArgumentException('Too many arguments');
218
-            }
219
-
220
-            if (!in_array($positional[0], ['validate', 'repair', 'convert', 'color'])) {
221
-                throw new InvalidArgumentException('Uknown command: ' . $positional[0]);
222
-            }
223
-
224
-        } catch (InvalidArgumentException $e) {
225
-            $this->showHelp();
226
-            $this->log('Error: ' . $e->getMessage(), 'red');
227
-            return 1;
228
-        }
229
-
230
-        $command = $positional[0];
231
-
232
-        $this->inputPath = $positional[1];
233
-        $this->outputPath = isset($positional[2]) ? $positional[2] : '-';
234
-
235
-        if ($this->outputPath !== '-') {
236
-            $this->stdout = fopen($this->outputPath, 'w');
237
-        }
238
-
239
-        if (!$this->inputFormat) {
240
-            if (substr($this->inputPath, -5) === '.json') {
241
-                $this->inputFormat = 'json';
242
-            } else {
243
-                $this->inputFormat = 'mimedir';
244
-            }
245
-        }
246
-        if (!$this->format) {
247
-            if (substr($this->outputPath, -5) === '.json') {
248
-                $this->format = 'json';
249
-            } else {
250
-                $this->format = 'mimedir';
251
-            }
252
-        }
253
-
254
-
255
-        $realCode = 0;
256
-
257
-        try {
258
-
259
-            while ($input = $this->readInput()) {
260
-
261
-                $returnCode = $this->$command($input);
262
-                if ($returnCode !== 0) $realCode = $returnCode;
263
-
264
-            }
265
-
266
-        } catch (EofException $e) {
267
-            // end of file
268
-        } catch (\Exception $e) {
269
-            $this->log('Error: ' . $e->getMessage(), 'red');
270
-            return 2;
271
-        }
272
-
273
-        return $realCode;
274
-
275
-    }
276
-
277
-    /**
278
-     * Shows the help message.
279
-     *
280
-     * @return void
281
-     */
282
-    protected function showHelp() {
283
-
284
-        $this->log('Usage:', 'yellow');
285
-        $this->log("  vobject [options] command [arguments]");
286
-        $this->log('');
287
-        $this->log('Options:', 'yellow');
288
-        $this->log($this->colorize('green', '  -q            ') . "Don't output anything.");
289
-        $this->log($this->colorize('green', '  -help -h      ') . "Display this help message.");
290
-        $this->log($this->colorize('green', '  --format      ') . "Convert to a specific format. Must be one of: vcard, vcard21,");
291
-        $this->log($this->colorize('green', '  --forgiving   ') . "Makes the parser less strict.");
292
-        $this->log("                vcard30, vcard40, icalendar20, jcal, jcard, json, mimedir.");
293
-        $this->log($this->colorize('green', '  --inputformat ') . "If the input format cannot be guessed from the extension, it");
294
-        $this->log("                must be specified here.");
295
-        // Only PHP 5.4 and up
296
-        if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
297
-            $this->log($this->colorize('green', '  --pretty      ') . "json pretty-print.");
298
-        }
299
-        $this->log('');
300
-        $this->log('Commands:', 'yellow');
301
-        $this->log($this->colorize('green', '  validate') . ' source_file              Validates a file for correctness.');
302
-        $this->log($this->colorize('green', '  repair') . ' source_file [output_file]  Repairs a file.');
303
-        $this->log($this->colorize('green', '  convert') . ' source_file [output_file] Converts a file.');
304
-        $this->log($this->colorize('green', '  color') . ' source_file                 Colorize a file, useful for debbugging.');
305
-        $this->log(
306
-        <<<HELP
17
+	/**
18
+	 * No output.
19
+	 *
20
+	 * @var bool
21
+	 */
22
+	protected $quiet = false;
23
+
24
+	/**
25
+	 * Help display.
26
+	 *
27
+	 * @var bool
28
+	 */
29
+	protected $showHelp = false;
30
+
31
+	/**
32
+	 * Wether to spit out 'mimedir' or 'json' format.
33
+	 *
34
+	 * @var string
35
+	 */
36
+	protected $format;
37
+
38
+	/**
39
+	 * JSON pretty print.
40
+	 *
41
+	 * @var bool
42
+	 */
43
+	protected $pretty;
44
+
45
+	/**
46
+	 * Source file.
47
+	 *
48
+	 * @var string
49
+	 */
50
+	protected $inputPath;
51
+
52
+	/**
53
+	 * Destination file.
54
+	 *
55
+	 * @var string
56
+	 */
57
+	protected $outputPath;
58
+
59
+	/**
60
+	 * output stream.
61
+	 *
62
+	 * @var resource
63
+	 */
64
+	protected $stdout;
65
+
66
+	/**
67
+	 * stdin.
68
+	 *
69
+	 * @var resource
70
+	 */
71
+	protected $stdin;
72
+
73
+	/**
74
+	 * stderr.
75
+	 *
76
+	 * @var resource
77
+	 */
78
+	protected $stderr;
79
+
80
+	/**
81
+	 * Input format (one of json or mimedir).
82
+	 *
83
+	 * @var string
84
+	 */
85
+	protected $inputFormat;
86
+
87
+	/**
88
+	 * Makes the parser less strict.
89
+	 *
90
+	 * @var bool
91
+	 */
92
+	protected $forgiving = false;
93
+
94
+	/**
95
+	 * Main function.
96
+	 *
97
+	 * @return int
98
+	 */
99
+	public function main(array $argv) {
100
+
101
+		// @codeCoverageIgnoreStart
102
+		// We cannot easily test this, so we'll skip it. Pretty basic anyway.
103
+
104
+		if (!$this->stderr) {
105
+			$this->stderr = fopen('php://stderr', 'w');
106
+		}
107
+		if (!$this->stdout) {
108
+			$this->stdout = fopen('php://stdout', 'w');
109
+		}
110
+		if (!$this->stdin) {
111
+			$this->stdin = fopen('php://stdin', 'r');
112
+		}
113
+
114
+		// @codeCoverageIgnoreEnd
115
+
116
+
117
+		try {
118
+
119
+			list($options, $positional) = $this->parseArguments($argv);
120
+
121
+			if (isset($options['q'])) {
122
+				$this->quiet = true;
123
+			}
124
+			$this->log($this->colorize('green', "sabre/vobject ") . $this->colorize('yellow', Version::VERSION));
125
+
126
+			foreach ($options as $name => $value) {
127
+
128
+				switch ($name) {
129
+
130
+					case 'q' :
131
+						// Already handled earlier.
132
+						break;
133
+					case 'h' :
134
+					case 'help' :
135
+						$this->showHelp();
136
+						return 0;
137
+						break;
138
+					case 'format' :
139
+						switch ($value) {
140
+
141
+							// jcard/jcal documents
142
+							case 'jcard' :
143
+							case 'jcal' :
144
+
145
+							// specific document versions
146
+							case 'vcard21' :
147
+							case 'vcard30' :
148
+							case 'vcard40' :
149
+							case 'icalendar20' :
150
+
151
+							// specific formats
152
+							case 'json' :
153
+							case 'mimedir' :
154
+
155
+							// icalendar/vcad
156
+							case 'icalendar' :
157
+							case 'vcard' :
158
+								$this->format = $value;
159
+								break;
160
+
161
+							default :
162
+								throw new InvalidArgumentException('Unknown format: ' . $value);
163
+
164
+						}
165
+						break;
166
+					case 'pretty' :
167
+						if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
168
+							$this->pretty = true;
169
+						}
170
+						break;
171
+					case 'forgiving' :
172
+						$this->forgiving = true;
173
+						break;
174
+					case 'inputformat' :
175
+						switch ($value) {
176
+							// json formats
177
+							case 'jcard' :
178
+							case 'jcal' :
179
+							case 'json' :
180
+								$this->inputFormat = 'json';
181
+								break;
182
+
183
+							// mimedir formats
184
+							case 'mimedir' :
185
+							case 'icalendar' :
186
+							case 'vcard' :
187
+							case 'vcard21' :
188
+							case 'vcard30' :
189
+							case 'vcard40' :
190
+							case 'icalendar20' :
191
+
192
+								$this->inputFormat = 'mimedir';
193
+								break;
194
+
195
+							default :
196
+								throw new InvalidArgumentException('Unknown format: ' . $value);
197
+
198
+						}
199
+						break;
200
+					default :
201
+						throw new InvalidArgumentException('Unknown option: ' . $name);
202
+
203
+				}
204
+
205
+			}
206
+
207
+			if (count($positional) === 0) {
208
+				$this->showHelp();
209
+				return 1;
210
+			}
211
+
212
+			if (count($positional) === 1) {
213
+				throw new InvalidArgumentException('Inputfile is a required argument');
214
+			}
215
+
216
+			if (count($positional) > 3) {
217
+				throw new InvalidArgumentException('Too many arguments');
218
+			}
219
+
220
+			if (!in_array($positional[0], ['validate', 'repair', 'convert', 'color'])) {
221
+				throw new InvalidArgumentException('Uknown command: ' . $positional[0]);
222
+			}
223
+
224
+		} catch (InvalidArgumentException $e) {
225
+			$this->showHelp();
226
+			$this->log('Error: ' . $e->getMessage(), 'red');
227
+			return 1;
228
+		}
229
+
230
+		$command = $positional[0];
231
+
232
+		$this->inputPath = $positional[1];
233
+		$this->outputPath = isset($positional[2]) ? $positional[2] : '-';
234
+
235
+		if ($this->outputPath !== '-') {
236
+			$this->stdout = fopen($this->outputPath, 'w');
237
+		}
238
+
239
+		if (!$this->inputFormat) {
240
+			if (substr($this->inputPath, -5) === '.json') {
241
+				$this->inputFormat = 'json';
242
+			} else {
243
+				$this->inputFormat = 'mimedir';
244
+			}
245
+		}
246
+		if (!$this->format) {
247
+			if (substr($this->outputPath, -5) === '.json') {
248
+				$this->format = 'json';
249
+			} else {
250
+				$this->format = 'mimedir';
251
+			}
252
+		}
253
+
254
+
255
+		$realCode = 0;
256
+
257
+		try {
258
+
259
+			while ($input = $this->readInput()) {
260
+
261
+				$returnCode = $this->$command($input);
262
+				if ($returnCode !== 0) $realCode = $returnCode;
263
+
264
+			}
265
+
266
+		} catch (EofException $e) {
267
+			// end of file
268
+		} catch (\Exception $e) {
269
+			$this->log('Error: ' . $e->getMessage(), 'red');
270
+			return 2;
271
+		}
272
+
273
+		return $realCode;
274
+
275
+	}
276
+
277
+	/**
278
+	 * Shows the help message.
279
+	 *
280
+	 * @return void
281
+	 */
282
+	protected function showHelp() {
283
+
284
+		$this->log('Usage:', 'yellow');
285
+		$this->log("  vobject [options] command [arguments]");
286
+		$this->log('');
287
+		$this->log('Options:', 'yellow');
288
+		$this->log($this->colorize('green', '  -q            ') . "Don't output anything.");
289
+		$this->log($this->colorize('green', '  -help -h      ') . "Display this help message.");
290
+		$this->log($this->colorize('green', '  --format      ') . "Convert to a specific format. Must be one of: vcard, vcard21,");
291
+		$this->log($this->colorize('green', '  --forgiving   ') . "Makes the parser less strict.");
292
+		$this->log("                vcard30, vcard40, icalendar20, jcal, jcard, json, mimedir.");
293
+		$this->log($this->colorize('green', '  --inputformat ') . "If the input format cannot be guessed from the extension, it");
294
+		$this->log("                must be specified here.");
295
+		// Only PHP 5.4 and up
296
+		if (version_compare(PHP_VERSION, '5.4.0') >= 0) {
297
+			$this->log($this->colorize('green', '  --pretty      ') . "json pretty-print.");
298
+		}
299
+		$this->log('');
300
+		$this->log('Commands:', 'yellow');
301
+		$this->log($this->colorize('green', '  validate') . ' source_file              Validates a file for correctness.');
302
+		$this->log($this->colorize('green', '  repair') . ' source_file [output_file]  Repairs a file.');
303
+		$this->log($this->colorize('green', '  convert') . ' source_file [output_file] Converts a file.');
304
+		$this->log($this->colorize('green', '  color') . ' source_file                 Colorize a file, useful for debbugging.');
305
+		$this->log(
306
+		<<<HELP
307 307
 
308 308
 If source_file is set as '-', STDIN will be used.
309 309
 If output_file is omitted, STDOUT will be used.
310 310
 All other output is sent to STDERR.
311 311
 
312 312
 HELP
313
-        );
314
-
315
-        $this->log('Examples:', 'yellow');
316
-        $this->log('   vobject convert contact.vcf contact.json');
317
-        $this->log('   vobject convert --format=vcard40 old.vcf new.vcf');
318
-        $this->log('   vobject convert --inputformat=json --format=mimedir - -');
319
-        $this->log('   vobject color calendar.ics');
320
-        $this->log('');
321
-        $this->log('https://github.com/fruux/sabre-vobject', 'purple');
322
-
323
-    }
324
-
325
-    /**
326
-     * Validates a VObject file.
327
-     *
328
-     * @param Component $vObj
329
-     *
330
-     * @return int
331
-     */
332
-    protected function validate(Component $vObj) {
333
-
334
-        $returnCode = 0;
335
-
336
-        switch ($vObj->name) {
337
-            case 'VCALENDAR' :
338
-                $this->log("iCalendar: " . (string)$vObj->VERSION);
339
-                break;
340
-            case 'VCARD' :
341
-                $this->log("vCard: " . (string)$vObj->VERSION);
342
-                break;
343
-        }
344
-
345
-        $warnings = $vObj->validate();
346
-        if (!count($warnings)) {
347
-            $this->log("  No warnings!");
348
-        } else {
349
-
350
-            $levels = [
351
-                1 => 'REPAIRED',
352
-                2 => 'WARNING',
353
-                3 => 'ERROR',
354
-            ];
355
-            $returnCode = 2;
356
-            foreach ($warnings as $warn) {
357
-
358
-                $extra = '';
359
-                if ($warn['node'] instanceof Property) {
360
-                    $extra = ' (property: "' . $warn['node']->name . '")';
361
-                }
362
-                $this->log("  [" . $levels[$warn['level']] . '] ' . $warn['message'] . $extra);
363
-
364
-            }
365
-
366
-        }
367
-
368
-        return $returnCode;
369
-
370
-    }
371
-
372
-    /**
373
-     * Repairs a VObject file.
374
-     *
375
-     * @param Component $vObj
376
-     *
377
-     * @return int
378
-     */
379
-    protected function repair(Component $vObj) {
380
-
381
-        $returnCode = 0;
382
-
383
-        switch ($vObj->name) {
384
-            case 'VCALENDAR' :
385
-                $this->log("iCalendar: " . (string)$vObj->VERSION);
386
-                break;
387
-            case 'VCARD' :
388
-                $this->log("vCard: " . (string)$vObj->VERSION);
389
-                break;
390
-        }
391
-
392
-        $warnings = $vObj->validate(Node::REPAIR);
393
-        if (!count($warnings)) {
394
-            $this->log("  No warnings!");
395
-        } else {
396
-
397
-            $levels = [
398
-                1 => 'REPAIRED',
399
-                2 => 'WARNING',
400
-                3 => 'ERROR',
401
-            ];
402
-            $returnCode = 2;
403
-            foreach ($warnings as $warn) {
404
-
405
-                $extra = '';
406
-                if ($warn['node'] instanceof Property) {
407
-                    $extra = ' (property: "' . $warn['node']->name . '")';
408
-                }
409
-                $this->log("  [" . $levels[$warn['level']] . '] ' . $warn['message'] . $extra);
410
-
411
-            }
412
-
413
-        }
414
-        fwrite($this->stdout, $vObj->serialize());
415
-
416
-        return $returnCode;
417
-
418
-    }
419
-
420
-    /**
421
-     * Converts a vObject file to a new format.
422
-     *
423
-     * @param Component $vObj
424
-     *
425
-     * @return int
426
-     */
427
-    protected function convert($vObj) {
428
-
429
-        $json = false;
430
-        $convertVersion = null;
431
-        $forceInput = null;
432
-
433
-        switch ($this->format) {
434
-            case 'json' :
435
-                $json = true;
436
-                if ($vObj->name === 'VCARD') {
437
-                    $convertVersion = Document::VCARD40;
438
-                }
439
-                break;
440
-            case 'jcard' :
441
-                $json = true;
442
-                $forceInput = 'VCARD';
443
-                $convertVersion = Document::VCARD40;
444
-                break;
445
-            case 'jcal' :
446
-                $json = true;
447
-                $forceInput = 'VCALENDAR';
448
-                break;
449
-            case 'mimedir' :
450
-            case 'icalendar' :
451
-            case 'icalendar20' :
452
-            case 'vcard' :
453
-                break;
454
-            case 'vcard21' :
455
-                $convertVersion = Document::VCARD21;
456
-                break;
457
-            case 'vcard30' :
458
-                $convertVersion = Document::VCARD30;
459
-                break;
460
-            case 'vcard40' :
461
-                $convertVersion = Document::VCARD40;
462
-                break;
463
-
464
-        }
465
-
466
-        if ($forceInput && $vObj->name !== $forceInput) {
467
-            throw new \Exception('You cannot convert a ' . strtolower($vObj->name) . ' to ' . $this->format);
468
-        }
469
-        if ($convertVersion) {
470
-            $vObj = $vObj->convert($convertVersion);
471
-        }
472
-        if ($json) {
473
-            $jsonOptions = 0;
474
-            if ($this->pretty) {
475
-                $jsonOptions = JSON_PRETTY_PRINT;
476
-            }
477
-            fwrite($this->stdout, json_encode($vObj->jsonSerialize(), $jsonOptions));
478
-        } else {
479
-            fwrite($this->stdout, $vObj->serialize());
480
-        }
481
-
482
-        return 0;
483
-
484
-    }
485
-
486
-    /**
487
-     * Colorizes a file.
488
-     *
489
-     * @param Component $vObj
490
-     *
491
-     * @return int
492
-     */
493
-    protected function color($vObj) {
494
-
495
-        fwrite($this->stdout, $this->serializeComponent($vObj));
496
-
497
-    }
498
-
499
-    /**
500
-     * Returns an ansi color string for a color name.
501
-     *
502
-     * @param string $color
503
-     *
504
-     * @return string
505
-     */
506
-    protected function colorize($color, $str, $resetTo = 'default') {
507
-
508
-        $colors = [
509
-            'cyan'    => '1;36',
510
-            'red'     => '1;31',
511
-            'yellow'  => '1;33',
512
-            'blue'    => '0;34',
513
-            'green'   => '0;32',
514
-            'default' => '0',
515
-            'purple'  => '0;35',
516
-        ];
517
-        return "\033[" . $colors[$color] . 'm' . $str . "\033[" . $colors[$resetTo] . "m";
518
-
519
-    }
520
-
521
-    /**
522
-     * Writes out a string in specific color.
523
-     *
524
-     * @param string $color
525
-     * @param string $str
526
-     *
527
-     * @return void
528
-     */
529
-    protected function cWrite($color, $str) {
530
-
531
-        fwrite($this->stdout, $this->colorize($color, $str));
532
-
533
-    }
534
-
535
-    protected function serializeComponent(Component $vObj) {
536
-
537
-        $this->cWrite('cyan', 'BEGIN');
538
-        $this->cWrite('red', ':');
539
-        $this->cWrite('yellow', $vObj->name . "\n");
540
-
541
-        /**
542
-         * Gives a component a 'score' for sorting purposes.
543
-         *
544
-         * This is solely used by the childrenSort method.
545
-         *
546
-         * A higher score means the item will be lower in the list.
547
-         * To avoid score collisions, each "score category" has a reasonable
548
-         * space to accomodate elements. The $key is added to the $score to
549
-         * preserve the original relative order of elements.
550
-         *
551
-         * @param int $key
552
-         * @param array $array
553
-         *
554
-         * @return int
555
-         */
556
-        $sortScore = function($key, $array) {
557
-
558
-            if ($array[$key] instanceof Component) {
559
-
560
-                // We want to encode VTIMEZONE first, this is a personal
561
-                // preference.
562
-                if ($array[$key]->name === 'VTIMEZONE') {
563
-                    $score = 300000000;
564
-                    return $score + $key;
565
-                } else {
566
-                    $score = 400000000;
567
-                    return $score + $key;
568
-                }
569
-            } else {
570
-                // Properties get encoded first
571
-                // VCARD version 4.0 wants the VERSION property to appear first
572
-                if ($array[$key] instanceof Property) {
573
-                    if ($array[$key]->name === 'VERSION') {
574
-                        $score = 100000000;
575
-                        return $score + $key;
576
-                    } else {
577
-                        // All other properties
578
-                        $score = 200000000;
579
-                        return $score + $key;
580
-                    }
581
-                }
582
-            }
583
-
584
-        };
585
-
586
-        $children = $vObj->children();
587
-        $tmp = $children;
588
-        uksort(
589
-            $children,
590
-            function($a, $b) use ($sortScore, $tmp) {
591
-
592
-                $sA = $sortScore($a, $tmp);
593
-                $sB = $sortScore($b, $tmp);
594
-
595
-                return $sA - $sB;
596
-
597
-            }
598
-        );
599
-
600
-        foreach ($children as $child) {
601
-            if ($child instanceof Component) {
602
-                $this->serializeComponent($child);
603
-            } else {
604
-                $this->serializeProperty($child);
605
-            }
606
-        }
607
-
608
-        $this->cWrite('cyan', 'END');
609
-        $this->cWrite('red', ':');
610
-        $this->cWrite('yellow', $vObj->name . "\n");
611
-
612
-    }
613
-
614
-    /**
615
-     * Colorizes a property.
616
-     *
617
-     * @param Property $property
618
-     *
619
-     * @return void
620
-     */
621
-    protected function serializeProperty(Property $property) {
622
-
623
-        if ($property->group) {
624
-            $this->cWrite('default', $property->group);
625
-            $this->cWrite('red', '.');
626
-        }
627
-
628
-        $this->cWrite('yellow', $property->name);
629
-
630
-        foreach ($property->parameters as $param) {
631
-
632
-            $this->cWrite('red', ';');
633
-            $this->cWrite('blue', $param->serialize());
634
-
635
-        }
636
-        $this->cWrite('red', ':');
637
-
638
-        if ($property instanceof Property\Binary) {
639
-
640
-            $this->cWrite('default', 'embedded binary stripped. (' . strlen($property->getValue()) . ' bytes)');
641
-
642
-        } else {
643
-
644
-            $parts = $property->getParts();
645
-            $first1 = true;
646
-            // Looping through property values
647
-            foreach ($parts as $part) {
648
-                if ($first1) {
649
-                    $first1 = false;
650
-                } else {
651
-                    $this->cWrite('red', $property->delimiter);
652
-                }
653
-                $first2 = true;
654
-                // Looping through property sub-values
655
-                foreach ((array)$part as $subPart) {
656
-                    if ($first2) {
657
-                        $first2 = false;
658
-                    } else {
659
-                        // The sub-value delimiter is always comma
660
-                        $this->cWrite('red', ',');
661
-                    }
662
-
663
-                    $subPart = strtr(
664
-                        $subPart,
665
-                        [
666
-                            '\\' => $this->colorize('purple', '\\\\', 'green'),
667
-                            ';'  => $this->colorize('purple', '\;', 'green'),
668
-                            ','  => $this->colorize('purple', '\,', 'green'),
669
-                            "\n" => $this->colorize('purple', "\\n\n\t", 'green'),
670
-                            "\r" => "",
671
-                        ]
672
-                    );
673
-
674
-                    $this->cWrite('green', $subPart);
675
-                }
676
-            }
677
-
678
-        }
679
-        $this->cWrite("default", "\n");
680
-
681
-    }
682
-
683
-    /**
684
-     * Parses the list of arguments.
685
-     *
686
-     * @param array $argv
687
-     *
688
-     * @return void
689
-     */
690
-    protected function parseArguments(array $argv) {
691
-
692
-        $positional = [];
693
-        $options = [];
313
+		);
314
+
315
+		$this->log('Examples:', 'yellow');
316
+		$this->log('   vobject convert contact.vcf contact.json');
317
+		$this->log('   vobject convert --format=vcard40 old.vcf new.vcf');
318
+		$this->log('   vobject convert --inputformat=json --format=mimedir - -');
319
+		$this->log('   vobject color calendar.ics');
320
+		$this->log('');
321
+		$this->log('https://github.com/fruux/sabre-vobject', 'purple');
322
+
323
+	}
324
+
325
+	/**
326
+	 * Validates a VObject file.
327
+	 *
328
+	 * @param Component $vObj
329
+	 *
330
+	 * @return int
331
+	 */
332
+	protected function validate(Component $vObj) {
333
+
334
+		$returnCode = 0;
335
+
336
+		switch ($vObj->name) {
337
+			case 'VCALENDAR' :
338
+				$this->log("iCalendar: " . (string)$vObj->VERSION);
339
+				break;
340
+			case 'VCARD' :
341
+				$this->log("vCard: " . (string)$vObj->VERSION);
342
+				break;
343
+		}
344
+
345
+		$warnings = $vObj->validate();
346
+		if (!count($warnings)) {
347
+			$this->log("  No warnings!");
348
+		} else {
349
+
350
+			$levels = [
351
+				1 => 'REPAIRED',
352
+				2 => 'WARNING',
353
+				3 => 'ERROR',
354
+			];
355
+			$returnCode = 2;
356
+			foreach ($warnings as $warn) {
357
+
358
+				$extra = '';
359
+				if ($warn['node'] instanceof Property) {
360
+					$extra = ' (property: "' . $warn['node']->name . '")';
361
+				}
362
+				$this->log("  [" . $levels[$warn['level']] . '] ' . $warn['message'] . $extra);
363
+
364
+			}
365
+
366
+		}
367
+
368
+		return $returnCode;
369
+
370
+	}
371
+
372
+	/**
373
+	 * Repairs a VObject file.
374
+	 *
375
+	 * @param Component $vObj
376
+	 *
377
+	 * @return int
378
+	 */
379
+	protected function repair(Component $vObj) {
380
+
381
+		$returnCode = 0;
382
+
383
+		switch ($vObj->name) {
384
+			case 'VCALENDAR' :
385
+				$this->log("iCalendar: " . (string)$vObj->VERSION);
386
+				break;
387
+			case 'VCARD' :
388
+				$this->log("vCard: " . (string)$vObj->VERSION);
389
+				break;
390
+		}
391
+
392
+		$warnings = $vObj->validate(Node::REPAIR);
393
+		if (!count($warnings)) {
394
+			$this->log("  No warnings!");
395
+		} else {
396
+
397
+			$levels = [
398
+				1 => 'REPAIRED',
399
+				2 => 'WARNING',
400
+				3 => 'ERROR',
401
+			];
402
+			$returnCode = 2;
403
+			foreach ($warnings as $warn) {
404
+
405
+				$extra = '';
406
+				if ($warn['node'] instanceof Property) {
407
+					$extra = ' (property: "' . $warn['node']->name . '")';
408
+				}
409
+				$this->log("  [" . $levels[$warn['level']] . '] ' . $warn['message'] . $extra);
410
+
411
+			}
412
+
413
+		}
414
+		fwrite($this->stdout, $vObj->serialize());
415
+
416
+		return $returnCode;
417
+
418
+	}
419
+
420
+	/**
421
+	 * Converts a vObject file to a new format.
422
+	 *
423
+	 * @param Component $vObj
424
+	 *
425
+	 * @return int
426
+	 */
427
+	protected function convert($vObj) {
428
+
429
+		$json = false;
430
+		$convertVersion = null;
431
+		$forceInput = null;
432
+
433
+		switch ($this->format) {
434
+			case 'json' :
435
+				$json = true;
436
+				if ($vObj->name === 'VCARD') {
437
+					$convertVersion = Document::VCARD40;
438
+				}
439
+				break;
440
+			case 'jcard' :
441
+				$json = true;
442
+				$forceInput = 'VCARD';
443
+				$convertVersion = Document::VCARD40;
444
+				break;
445
+			case 'jcal' :
446
+				$json = true;
447
+				$forceInput = 'VCALENDAR';
448
+				break;
449
+			case 'mimedir' :
450
+			case 'icalendar' :
451
+			case 'icalendar20' :
452
+			case 'vcard' :
453
+				break;
454
+			case 'vcard21' :
455
+				$convertVersion = Document::VCARD21;
456
+				break;
457
+			case 'vcard30' :
458
+				$convertVersion = Document::VCARD30;
459
+				break;
460
+			case 'vcard40' :
461
+				$convertVersion = Document::VCARD40;
462
+				break;
463
+
464
+		}
465
+
466
+		if ($forceInput && $vObj->name !== $forceInput) {
467
+			throw new \Exception('You cannot convert a ' . strtolower($vObj->name) . ' to ' . $this->format);
468
+		}
469
+		if ($convertVersion) {
470
+			$vObj = $vObj->convert($convertVersion);
471
+		}
472
+		if ($json) {
473
+			$jsonOptions = 0;
474
+			if ($this->pretty) {
475
+				$jsonOptions = JSON_PRETTY_PRINT;
476
+			}
477
+			fwrite($this->stdout, json_encode($vObj->jsonSerialize(), $jsonOptions));
478
+		} else {
479
+			fwrite($this->stdout, $vObj->serialize());
480
+		}
481
+
482
+		return 0;
483
+
484
+	}
485
+
486
+	/**
487
+	 * Colorizes a file.
488
+	 *
489
+	 * @param Component $vObj
490
+	 *
491
+	 * @return int
492
+	 */
493
+	protected function color($vObj) {
494
+
495
+		fwrite($this->stdout, $this->serializeComponent($vObj));
496
+
497
+	}
498
+
499
+	/**
500
+	 * Returns an ansi color string for a color name.
501
+	 *
502
+	 * @param string $color
503
+	 *
504
+	 * @return string
505
+	 */
506
+	protected function colorize($color, $str, $resetTo = 'default') {
507
+
508
+		$colors = [
509
+			'cyan'    => '1;36',
510
+			'red'     => '1;31',
511
+			'yellow'  => '1;33',
512
+			'blue'    => '0;34',
513
+			'green'   => '0;32',
514
+			'default' => '0',
515
+			'purple'  => '0;35',
516
+		];
517
+		return "\033[" . $colors[$color] . 'm' . $str . "\033[" . $colors[$resetTo] . "m";
518
+
519
+	}
520
+
521
+	/**
522
+	 * Writes out a string in specific color.
523
+	 *
524
+	 * @param string $color
525
+	 * @param string $str
526
+	 *
527
+	 * @return void
528
+	 */
529
+	protected function cWrite($color, $str) {
530
+
531
+		fwrite($this->stdout, $this->colorize($color, $str));
532
+
533
+	}
534
+
535
+	protected function serializeComponent(Component $vObj) {
536
+
537
+		$this->cWrite('cyan', 'BEGIN');
538
+		$this->cWrite('red', ':');
539
+		$this->cWrite('yellow', $vObj->name . "\n");
540
+
541
+		/**
542
+		 * Gives a component a 'score' for sorting purposes.
543
+		 *
544
+		 * This is solely used by the childrenSort method.
545
+		 *
546
+		 * A higher score means the item will be lower in the list.
547
+		 * To avoid score collisions, each "score category" has a reasonable
548
+		 * space to accomodate elements. The $key is added to the $score to
549
+		 * preserve the original relative order of elements.
550
+		 *
551
+		 * @param int $key
552
+		 * @param array $array
553
+		 *
554
+		 * @return int
555
+		 */
556
+		$sortScore = function($key, $array) {
557
+
558
+			if ($array[$key] instanceof Component) {
559
+
560
+				// We want to encode VTIMEZONE first, this is a personal
561
+				// preference.
562
+				if ($array[$key]->name === 'VTIMEZONE') {
563
+					$score = 300000000;
564
+					return $score + $key;
565
+				} else {
566
+					$score = 400000000;
567
+					return $score + $key;
568
+				}
569
+			} else {
570
+				// Properties get encoded first
571
+				// VCARD version 4.0 wants the VERSION property to appear first
572
+				if ($array[$key] instanceof Property) {
573
+					if ($array[$key]->name === 'VERSION') {
574
+						$score = 100000000;
575
+						return $score + $key;
576
+					} else {
577
+						// All other properties
578
+						$score = 200000000;
579
+						return $score + $key;
580
+					}
581
+				}
582
+			}
583
+
584
+		};
585
+
586
+		$children = $vObj->children();
587
+		$tmp = $children;
588
+		uksort(
589
+			$children,
590
+			function($a, $b) use ($sortScore, $tmp) {
591
+
592
+				$sA = $sortScore($a, $tmp);
593
+				$sB = $sortScore($b, $tmp);
594
+
595
+				return $sA - $sB;
596
+
597
+			}
598
+		);
599
+
600
+		foreach ($children as $child) {
601
+			if ($child instanceof Component) {
602
+				$this->serializeComponent($child);
603
+			} else {
604
+				$this->serializeProperty($child);
605
+			}
606
+		}
607
+
608
+		$this->cWrite('cyan', 'END');
609
+		$this->cWrite('red', ':');
610
+		$this->cWrite('yellow', $vObj->name . "\n");
611
+
612
+	}
613
+
614
+	/**
615
+	 * Colorizes a property.
616
+	 *
617
+	 * @param Property $property
618
+	 *
619
+	 * @return void
620
+	 */
621
+	protected function serializeProperty(Property $property) {
622
+
623
+		if ($property->group) {
624
+			$this->cWrite('default', $property->group);
625
+			$this->cWrite('red', '.');
626
+		}
627
+
628
+		$this->cWrite('yellow', $property->name);
629
+
630
+		foreach ($property->parameters as $param) {
631
+
632
+			$this->cWrite('red', ';');
633
+			$this->cWrite('blue', $param->serialize());
634
+
635
+		}
636
+		$this->cWrite('red', ':');
637
+
638
+		if ($property instanceof Property\Binary) {
639
+
640
+			$this->cWrite('default', 'embedded binary stripped. (' . strlen($property->getValue()) . ' bytes)');
641
+
642
+		} else {
643
+
644
+			$parts = $property->getParts();
645
+			$first1 = true;
646
+			// Looping through property values
647
+			foreach ($parts as $part) {
648
+				if ($first1) {
649
+					$first1 = false;
650
+				} else {
651
+					$this->cWrite('red', $property->delimiter);
652
+				}
653
+				$first2 = true;
654
+				// Looping through property sub-values
655
+				foreach ((array)$part as $subPart) {
656
+					if ($first2) {
657
+						$first2 = false;
658
+					} else {
659
+						// The sub-value delimiter is always comma
660
+						$this->cWrite('red', ',');
661
+					}
662
+
663
+					$subPart = strtr(
664
+						$subPart,
665
+						[
666
+							'\\' => $this->colorize('purple', '\\\\', 'green'),
667
+							';'  => $this->colorize('purple', '\;', 'green'),
668
+							','  => $this->colorize('purple', '\,', 'green'),
669
+							"\n" => $this->colorize('purple', "\\n\n\t", 'green'),
670
+							"\r" => "",
671
+						]
672
+					);
673
+
674
+					$this->cWrite('green', $subPart);
675
+				}
676
+			}
677
+
678
+		}
679
+		$this->cWrite("default", "\n");
680
+
681
+	}
682
+
683
+	/**
684
+	 * Parses the list of arguments.
685
+	 *
686
+	 * @param array $argv
687
+	 *
688
+	 * @return void
689
+	 */
690
+	protected function parseArguments(array $argv) {
691
+
692
+		$positional = [];
693
+		$options = [];
694 694
 		
695 695
 		$countArgv = count($argv);
696
-        for ($ii = 0; $ii < $countArgv; $ii++) {
696
+		for ($ii = 0; $ii < $countArgv; $ii++) {
697 697
 
698
-            // Skipping the first argument.
699
-            if ($ii === 0) continue;
698
+			// Skipping the first argument.
699
+			if ($ii === 0) continue;
700 700
 
701
-            $v = $argv[$ii];
701
+			$v = $argv[$ii];
702 702
 
703
-            if (substr($v, 0, 2) === '--') {
704
-                // This is a long-form option.
705
-                $optionName = substr($v, 2);
706
-                $optionValue = true;
707
-                if (strpos($optionName, '=')) {
708
-                    list($optionName, $optionValue) = explode('=', $optionName);
709
-                }
710
-                $options[$optionName] = $optionValue;
711
-            } elseif (substr($v, 0, 1) === '-' && strlen($v) > 1) {
712
-                // This is a short-form option.
713
-                foreach (str_split(substr($v, 1)) as $option) {
714
-                    $options[$option] = true;
715
-                }
703
+			if (substr($v, 0, 2) === '--') {
704
+				// This is a long-form option.
705
+				$optionName = substr($v, 2);
706
+				$optionValue = true;
707
+				if (strpos($optionName, '=')) {
708
+					list($optionName, $optionValue) = explode('=', $optionName);
709
+				}
710
+				$options[$optionName] = $optionValue;
711
+			} elseif (substr($v, 0, 1) === '-' && strlen($v) > 1) {
712
+				// This is a short-form option.
713
+				foreach (str_split(substr($v, 1)) as $option) {
714
+					$options[$option] = true;
715
+				}
716 716
 
717
-            } else {
717
+			} else {
718 718
 
719
-                $positional[] = $v;
719
+				$positional[] = $v;
720 720
 
721
-            }
721
+			}
722 722
 
723
-        }
723
+		}
724 724
 
725
-        return [$options, $positional];
725
+		return [$options, $positional];
726 726
 
727
-    }
727
+	}
728 728
 
729
-    protected $parser;
729
+	protected $parser;
730 730
 
731
-    /**
732
-     * Reads the input file.
733
-     *
734
-     * @return Component
735
-     */
736
-    protected function readInput() {
731
+	/**
732
+	 * Reads the input file.
733
+	 *
734
+	 * @return Component
735
+	 */
736
+	protected function readInput() {
737 737
 
738
-        if (!$this->parser) {
739
-            if ($this->inputPath !== '-') {
740
-                $this->stdin = fopen($this->inputPath, 'r');
741
-            }
738
+		if (!$this->parser) {
739
+			if ($this->inputPath !== '-') {
740
+				$this->stdin = fopen($this->inputPath, 'r');
741
+			}
742 742
 
743
-            if ($this->inputFormat === 'mimedir') {
744
-                $this->parser = new Parser\MimeDir($this->stdin, ($this->forgiving ? Reader::OPTION_FORGIVING : 0));
745
-            } else {
746
-                $this->parser = new Parser\Json($this->stdin, ($this->forgiving ? Reader::OPTION_FORGIVING : 0));
747
-            }
748
-        }
743
+			if ($this->inputFormat === 'mimedir') {
744
+				$this->parser = new Parser\MimeDir($this->stdin, ($this->forgiving ? Reader::OPTION_FORGIVING : 0));
745
+			} else {
746
+				$this->parser = new Parser\Json($this->stdin, ($this->forgiving ? Reader::OPTION_FORGIVING : 0));
747
+			}
748
+		}
749 749
 
750
-        return $this->parser->parse();
750
+		return $this->parser->parse();
751 751
 
752
-    }
752
+	}
753 753
 
754
-    /**
755
-     * Sends a message to STDERR.
756
-     *
757
-     * @param string $msg
758
-     *
759
-     * @return void
760
-     */
761
-    protected function log($msg, $color = 'default') {
754
+	/**
755
+	 * Sends a message to STDERR.
756
+	 *
757
+	 * @param string $msg
758
+	 *
759
+	 * @return void
760
+	 */
761
+	protected function log($msg, $color = 'default') {
762 762
 
763
-        if (!$this->quiet) {
764
-            if ($color !== 'default') {
765
-                $msg = $this->colorize($color, $msg);
766
-            }
767
-            fwrite($this->stderr, $msg . "\n");
768
-        }
763
+		if (!$this->quiet) {
764
+			if ($color !== 'default') {
765
+				$msg = $this->colorize($color, $msg);
766
+			}
767
+			fwrite($this->stderr, $msg . "\n");
768
+		}
769 769
 
770
-    }
770
+	}
771 771
 
772 772
 }
Please login to merge, or discard this patch.
Braces   +6 added lines, -2 removed lines patch added patch discarded remove patch
@@ -259,7 +259,9 @@  discard block
 block discarded – undo
259 259
             while ($input = $this->readInput()) {
260 260
 
261 261
                 $returnCode = $this->$command($input);
262
-                if ($returnCode !== 0) $realCode = $returnCode;
262
+                if ($returnCode !== 0) {
263
+                	$realCode = $returnCode;
264
+                }
263 265
 
264 266
             }
265 267
 
@@ -696,7 +698,9 @@  discard block
 block discarded – undo
696 698
         for ($ii = 0; $ii < $countArgv; $ii++) {
697 699
 
698 700
             // Skipping the first argument.
699
-            if ($ii === 0) continue;
701
+            if ($ii === 0) {
702
+            	continue;
703
+            }
700 704
 
701 705
             $v = $argv[$ii];
702 706
 
Please login to merge, or discard this patch.