2121use OCP \IUserSession ;
2222use OCP \Mail \IEmailValidator ;
2323use OCP \Share \IShare ;
24+ use RuntimeException ;
2425
2526class MailPlugin implements ISearchPlugin {
2627 protected bool $ shareWithGroupOnly ;
@@ -76,69 +77,118 @@ public function search($search, $limit, $offset, ISearchResult $searchResult): b
7677 $ currentUserId = $ this ->userSession ->getUser ()->getUID ();
7778 $ userGroups = null ;
7879
79- $ result = $ userResults = ['wide ' => [], 'exact ' => []];
80- $ userType = new SearchResultType ('users ' );
81- $ emailType = new SearchResultType ('emails ' );
80+ $ hasMore = false ;
81+ $ count = 0 ;
82+ $ results = ['wide ' => [], 'exact ' => []];
83+ $ type = match ($ this ->shareType ) {
84+ IShare::TYPE_USER => new SearchResultType ('users ' ),
85+ IShare::TYPE_EMAIL => new SearchResultType ('emails ' ),
86+ default => throw new RuntimeException (),
87+ };
8288
8389 // Search in contacts
8490 $ addressBookContacts = $ this ->contactsManager ->search (
8591 $ search ,
8692 ['EMAIL ' , 'FN ' ],
8793 [
88- 'limit ' => $ limit ,
94+ // We request one more, so we can check if there are more results available
95+ 'limit ' => $ limit + 1 ,
8996 'offset ' => $ offset ,
9097 'enumeration ' => $ this ->shareeEnumeration ,
9198 'fullmatch ' => $ this ->shareeEnumerationFullMatch ,
9299 ]
93100 );
94101 $ lowerSearch = strtolower ($ search );
95102 foreach ($ addressBookContacts as $ contact ) {
96- if (isset ($ contact ['EMAIL ' ])) {
97- $ emailAddresses = $ contact ['EMAIL ' ];
98- if (\is_string ($ emailAddresses )) {
99- $ emailAddresses = [$ emailAddresses ];
103+ if (!isset ($ contact ['EMAIL ' ])) {
104+ continue ;
105+ }
106+
107+ $ emailAddresses = $ contact ['EMAIL ' ];
108+ if (\is_string ($ emailAddresses )) {
109+ $ emailAddresses = [$ emailAddresses ];
110+ }
111+ foreach ($ emailAddresses as $ emailAddress ) {
112+ $ displayName = $ emailAddress ;
113+ $ emailAddressType = null ;
114+ if (\is_array ($ emailAddress )) {
115+ $ emailAddressData = $ emailAddress ;
116+ $ emailAddress = $ emailAddressData ['value ' ];
117+ $ emailAddressType = $ emailAddressData ['type ' ];
118+ }
119+
120+ if (!filter_var ($ emailAddress , FILTER_VALIDATE_EMAIL )) {
121+ continue ;
100122 }
101- foreach ($ emailAddresses as $ type => $ emailAddress ) {
102- $ displayName = $ emailAddress ;
103- $ emailAddressType = null ;
104- if (\is_array ($ emailAddress )) {
105- $ emailAddressData = $ emailAddress ;
106- $ emailAddress = $ emailAddressData ['value ' ];
107- $ emailAddressType = $ emailAddressData ['type ' ];
108- }
109123
110- if (!filter_var ($ emailAddress , FILTER_VALIDATE_EMAIL )) {
124+ if (isset ($ contact ['FN ' ])) {
125+ $ displayName = $ contact ['FN ' ] . ' ( ' . $ emailAddress . ') ' ;
126+ }
127+ $ exactEmailMatch = strtolower ($ emailAddress ) === $ lowerSearch ;
128+
129+ if (isset ($ contact ['isLocalSystemBook ' ])) {
130+ $ contactUser = $ this ->userManager ->get ($ contact ['UID ' ]);
131+ if ($ contactUser === null ) {
111132 continue ;
112133 }
113134
114- if (isset ($ contact ['FN ' ])) {
115- $ displayName = $ contact ['FN ' ] . ' ( ' . $ emailAddress . ') ' ;
135+ $ contactGroups = $ this ->groupManager ->getUserGroupIds ($ contactUser );
136+ if ($ this ->shareWithGroupOnly ) {
137+ $ userGroups ??= $ this ->groupManager ->getUserGroupIds ($ this ->userSession ->getUser ());
138+ if (array_intersect ($ contactGroups , array_diff ($ userGroups , $ this ->shareWithGroupOnlyExcludeGroupsList )) === []) {
139+ continue ;
140+ }
116141 }
117- $ exactEmailMatch = strtolower ($ emailAddress ) === $ lowerSearch ;
118142
119- if (isset ($ contact ['isLocalSystemBook ' ])) {
120- $ contactUser = $ this ->userManager ->get ($ contact ['UID ' ]);
121- if ($ contactUser === null ) {
143+ if ($ exactEmailMatch && $ this ->shareeEnumerationFullMatch ) {
144+ try {
145+ $ cloud = $ this ->cloudIdManager ->resolveCloudId ($ contact ['CLOUD ' ][0 ] ?? '' );
146+ } catch (\InvalidArgumentException $ e ) {
122147 continue ;
123148 }
124- $ contactGroups = $ this ->groupManager ->getUserGroupIds ($ contactUser );
125149
126- if ($ this ->shareWithGroupOnly ) {
127- $ userGroups ??= $ this ->groupManager ->getUserGroupIds ($ this ->userSession ->getUser ());
128- if (array_intersect ($ contactGroups , array_diff ($ userGroups , $ this ->shareWithGroupOnlyExcludeGroupsList )) === []) {
129- continue ;
130- }
150+ if ($ this ->shareType === IShare::TYPE_USER && !$ this ->isCurrentUser ($ cloud ) && !$ searchResult ->hasResult ($ type , $ cloud ->getUser ())) {
151+ $ singleResult = [[
152+ 'label ' => $ displayName ,
153+ 'uuid ' => $ contact ['UID ' ] ?? $ emailAddress ,
154+ 'name ' => $ contact ['FN ' ] ?? $ displayName ,
155+ 'value ' => [
156+ 'shareType ' => IShare::TYPE_USER ,
157+ 'shareWith ' => $ cloud ->getUser (),
158+ ],
159+ 'shareWithDisplayNameUnique ' => !empty ($ emailAddress ) ? $ emailAddress : $ cloud ->getUser ()
160+ ]];
161+ $ searchResult ->addResultSet ($ type , [], $ singleResult );
162+ $ searchResult ->markExactIdMatch ($ type );
131163 }
164+ return false ;
165+ }
132166
133- if ($ exactEmailMatch && $ this ->shareeEnumerationFullMatch ) {
134- try {
135- $ cloud = $ this ->cloudIdManager ->resolveCloudId ($ contact ['CLOUD ' ][0 ] ?? '' );
136- } catch (\InvalidArgumentException $ e ) {
167+ if ($ this ->shareeEnumeration && $ this ->shareType === IShare::TYPE_USER ) {
168+ try {
169+ if (!isset ($ contact ['CLOUD ' ])) {
137170 continue ;
138171 }
172+ $ cloud = $ this ->cloudIdManager ->resolveCloudId ($ contact ['CLOUD ' ][0 ] ?? '' );
173+ } catch (\InvalidArgumentException $ e ) {
174+ continue ;
175+ }
176+ $ addToWide = !($ this ->shareeEnumerationInGroupOnly || $ this ->shareeEnumerationPhone );
139177
140- if ($ this ->shareType === IShare::TYPE_USER && !$ this ->isCurrentUser ($ cloud ) && !$ searchResult ->hasResult ($ userType , $ cloud ->getUser ())) {
141- $ singleResult = [[
178+ if (!$ addToWide && $ this ->shareeEnumerationPhone && $ this ->knownUserService ->isKnownToUser ($ currentUserId , $ contact ['UID ' ])) {
179+ $ addToWide = true ;
180+ }
181+
182+ if (!$ addToWide && $ this ->shareeEnumerationInGroupOnly ) {
183+ $ userGroups ??= $ this ->groupManager ->getUserGroupIds ($ this ->userSession ->getUser ());
184+ $ addToWide = array_intersect ($ contactGroups , $ userGroups ) !== [];
185+ }
186+
187+ if ($ addToWide && !$ this ->isCurrentUser ($ cloud ) && !$ searchResult ->hasResult ($ type , $ cloud ->getUser ())) {
188+ if ($ count ++ >= $ limit ) {
189+ $ hasMore = true ;
190+ } else {
191+ $ results ['wide ' ][] = [
142192 'label ' => $ displayName ,
143193 'uuid ' => $ contact ['UID ' ] ?? $ emailAddress ,
144194 'name ' => $ contact ['FN ' ] ?? $ displayName ,
@@ -147,116 +197,69 @@ public function search($search, $limit, $offset, ISearchResult $searchResult): b
147197 'shareWith ' => $ cloud ->getUser (),
148198 ],
149199 'shareWithDisplayNameUnique ' => !empty ($ emailAddress ) ? $ emailAddress : $ cloud ->getUser ()
150-
151- ]];
152- $ searchResult ->addResultSet ($ userType , [], $ singleResult );
153- $ searchResult ->markExactIdMatch ($ userType );
200+ ];
154201 }
155- return false ;
156202 }
203+ }
157204
158- if ($ this ->shareeEnumeration ) {
159- try {
160- if (!isset ($ contact ['CLOUD ' ])) {
161- continue ;
162- }
163- $ cloud = $ this ->cloudIdManager ->resolveCloudId ($ contact ['CLOUD ' ][0 ] ?? '' );
164- } catch (\InvalidArgumentException $ e ) {
165- continue ;
166- }
167-
168- $ addToWide = !($ this ->shareeEnumerationInGroupOnly || $ this ->shareeEnumerationPhone );
169- if (!$ addToWide && $ this ->shareeEnumerationPhone && $ this ->knownUserService ->isKnownToUser ($ currentUserId , $ contact ['UID ' ])) {
170- $ addToWide = true ;
171- }
205+ continue ;
206+ }
172207
173- if (!$ addToWide && $ this ->shareeEnumerationInGroupOnly ) {
174- $ userGroups ??= $ this ->groupManager ->getUserGroupIds ($ this ->userSession ->getUser ());
175- $ addToWide = array_intersect ($ contactGroups , $ userGroups ) !== [];
176- }
177- if ($ addToWide && !$ this ->isCurrentUser ($ cloud ) && !$ searchResult ->hasResult ($ userType , $ cloud ->getUser ())) {
178- if ($ this ->shareType === IShare::TYPE_USER ) {
179- $ userResults ['wide ' ][] = [
180- 'label ' => $ displayName ,
181- 'uuid ' => $ contact ['UID ' ] ?? $ emailAddress ,
182- 'name ' => $ contact ['FN ' ] ?? $ displayName ,
183- 'value ' => [
184- 'shareType ' => IShare::TYPE_USER ,
185- 'shareWith ' => $ cloud ->getUser (),
186- ],
187- 'shareWithDisplayNameUnique ' => !empty ($ emailAddress ) ? $ emailAddress : $ cloud ->getUser ()
188- ];
189- }
190- continue ;
191- }
192- }
193- continue ;
194- }
208+ if ($ this ->shareType !== IShare::TYPE_EMAIL ) {
209+ continue ;
210+ }
195211
196- if ($ this ->shareType !== IShare::TYPE_EMAIL ) {
197- continue ;
212+ if ($ count ++ >= $ limit ) {
213+ $ hasMore = true ;
214+ } elseif ($ exactEmailMatch || (isset ($ contact ['FN ' ]) && strtolower ($ contact ['FN ' ]) === $ lowerSearch )) {
215+ if ($ exactEmailMatch ) {
216+ $ searchResult ->markExactIdMatch ($ type );
198217 }
199218
200- if ($ exactEmailMatch
201- || (isset ($ contact ['FN ' ]) && strtolower ($ contact ['FN ' ]) === $ lowerSearch )) {
202- if ($ exactEmailMatch ) {
203- $ searchResult ->markExactIdMatch ($ emailType );
204- }
205- $ result ['exact ' ][] = [
206- 'label ' => $ displayName ,
207- 'uuid ' => $ contact ['UID ' ] ?? $ emailAddress ,
208- 'name ' => $ contact ['FN ' ] ?? $ displayName ,
209- 'type ' => $ emailAddressType ?? '' ,
210- 'value ' => [
211- 'shareType ' => IShare::TYPE_EMAIL ,
212- 'shareWith ' => $ emailAddress ,
213- ],
214- ];
215- } else {
216- $ result ['wide ' ][] = [
217- 'label ' => $ displayName ,
218- 'uuid ' => $ contact ['UID ' ] ?? $ emailAddress ,
219- 'name ' => $ contact ['FN ' ] ?? $ displayName ,
220- 'type ' => $ emailAddressType ?? '' ,
221- 'value ' => [
222- 'shareType ' => IShare::TYPE_EMAIL ,
223- 'shareWith ' => $ emailAddress ,
224- ],
225- ];
226- }
219+ $ results ['exact ' ][] = [
220+ 'label ' => $ displayName ,
221+ 'uuid ' => $ contact ['UID ' ] ?? $ emailAddress ,
222+ 'name ' => $ contact ['FN ' ] ?? $ displayName ,
223+ 'type ' => $ emailAddressType ?? '' ,
224+ 'value ' => [
225+ 'shareType ' => IShare::TYPE_EMAIL ,
226+ 'shareWith ' => $ emailAddress ,
227+ ],
228+ ];
229+ } else {
230+ $ results ['wide ' ][] = [
231+ 'label ' => $ displayName ,
232+ 'uuid ' => $ contact ['UID ' ] ?? $ emailAddress ,
233+ 'name ' => $ contact ['FN ' ] ?? $ displayName ,
234+ 'type ' => $ emailAddressType ?? '' ,
235+ 'value ' => [
236+ 'shareType ' => IShare::TYPE_EMAIL ,
237+ 'shareWith ' => $ emailAddress ,
238+ ],
239+ ];
227240 }
228241 }
229242 }
230243
231- $ reachedEnd = true ;
232- if ($ this ->shareeEnumeration ) {
233- $ reachedEnd = (count ($ result ['wide ' ]) < $ offset + $ limit )
234- && (count ($ userResults ['wide ' ]) < $ offset + $ limit );
235-
236- $ result ['wide ' ] = array_slice ($ result ['wide ' ], $ offset , $ limit );
237- $ userResults ['wide ' ] = array_slice ($ userResults ['wide ' ], $ offset , $ limit );
238- }
239-
240244 if ($ this ->shareType === IShare::TYPE_EMAIL
241- && !$ searchResult ->hasExactIdMatch ($ emailType ) && $ this ->emailValidator ->isValid ($ search )) {
242- $ result ['exact ' ][] = [
243- 'label ' => $ search ,
244- 'uuid ' => $ search ,
245- 'value ' => [
246- 'shareType ' => IShare::TYPE_EMAIL ,
247- 'shareWith ' => $ search ,
248- ],
249- ];
245+ && !$ searchResult ->hasExactIdMatch ($ type ) && $ this ->emailValidator ->isValid ($ search )) {
246+ if ($ count ++ >= $ limit ) {
247+ $ hasMore = true ;
248+ } else {
249+ $ results ['exact ' ][] = [
250+ 'label ' => $ search ,
251+ 'uuid ' => $ search ,
252+ 'value ' => [
253+ 'shareType ' => IShare::TYPE_EMAIL ,
254+ 'shareWith ' => $ search ,
255+ ],
256+ ];
257+ }
250258 }
251259
252- if ($ this ->shareType === IShare::TYPE_USER && !empty ($ userResults ['wide ' ])) {
253- $ searchResult ->addResultSet ($ userType , $ userResults ['wide ' ], []);
254- }
255- if ($ this ->shareType === IShare::TYPE_EMAIL ) {
256- $ searchResult ->addResultSet ($ emailType , $ result ['wide ' ], $ result ['exact ' ]);
257- }
260+ $ searchResult ->addResultSet ($ type , $ results ['wide ' ], $ results ['exact ' ]);
258261
259- return ! $ reachedEnd ;
262+ return $ hasMore ;
260263 }
261264
262265 public function isCurrentUser (ICloudId $ cloud ): bool {
0 commit comments