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