2121use OCP \IUserSession ;
2222use OCP \Mail \IEmailValidator ;
2323use OCP \Share \IShare ;
24+ use RuntimeException ;
2425
2526class MailPlugin implements ISearchPlugin {
2627 protected bool $ shareWithGroupOnly ;
@@ -76,66 +77,114 @@ public function search($search, $limit, $offset, ISearchResult $searchResult): b
7677 $ currentUserId = $ this ->userSession ->getUser ()->getUID ();
7778 $ userGroups = $ this ->groupManager ->getUserGroupIds ($ this ->userSession ->getUser ());
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 ' ];
100118 }
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- }
109119
110- if (!filter_var ($ emailAddress , FILTER_VALIDATE_EMAIL )) {
120+ if (!filter_var ($ emailAddress , FILTER_VALIDATE_EMAIL )) {
121+ continue ;
122+ }
123+
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 && array_intersect ($ contactGroups , array_diff ($ userGroups , $ this ->shareWithGroupOnlyExcludeGroupsList )) === []) {
137+ continue ;
116138 }
117- $ exactEmailMatch = strtolower ($ emailAddress ) === $ lowerSearch ;
118139
119- if (isset ($ contact ['isLocalSystemBook ' ])) {
120- $ contactUser = $ this ->userManager ->get ($ contact ['UID ' ]);
121- if ($ contactUser === null ) {
140+ if ($ exactEmailMatch && $ this ->shareeEnumerationFullMatch ) {
141+ try {
142+ $ cloud = $ this ->cloudIdManager ->resolveCloudId ($ contact ['CLOUD ' ][0 ] ?? '' );
143+ } catch (\InvalidArgumentException $ e ) {
122144 continue ;
123145 }
124- $ contactGroups = $ this ->groupManager ->getUserGroupIds ($ contactUser );
125146
126- if ($ this ->shareWithGroupOnly && array_intersect ($ contactGroups , array_diff ($ userGroups , $ this ->shareWithGroupOnlyExcludeGroupsList )) === []) {
127- continue ;
147+ if ($ this ->shareType === IShare::TYPE_USER && !$ this ->isCurrentUser ($ cloud ) && !$ searchResult ->hasResult ($ type , $ cloud ->getUser ())) {
148+ $ singleResult = [[
149+ 'label ' => $ displayName ,
150+ 'uuid ' => $ contact ['UID ' ] ?? $ emailAddress ,
151+ 'name ' => $ contact ['FN ' ] ?? $ displayName ,
152+ 'value ' => [
153+ 'shareType ' => IShare::TYPE_USER ,
154+ 'shareWith ' => $ cloud ->getUser (),
155+ ],
156+ 'shareWithDisplayNameUnique ' => !empty ($ emailAddress ) ? $ emailAddress : $ cloud ->getUser ()
157+ ]];
158+ $ searchResult ->addResultSet ($ type , [], $ singleResult );
159+ $ searchResult ->markExactIdMatch ($ type );
128160 }
161+ return false ;
162+ }
129163
130- if ($ exactEmailMatch && $ this ->shareeEnumerationFullMatch ) {
131- try {
132- $ cloud = $ this ->cloudIdManager ->resolveCloudId ($ contact ['CLOUD ' ][0 ] ?? '' );
133- } catch (\InvalidArgumentException $ e ) {
164+ if ($ this ->shareeEnumeration && $ this ->shareType === IShare::TYPE_USER ) {
165+ try {
166+ if (!isset ($ contact ['CLOUD ' ])) {
134167 continue ;
135168 }
169+ $ cloud = $ this ->cloudIdManager ->resolveCloudId ($ contact ['CLOUD ' ][0 ] ?? '' );
170+ } catch (\InvalidArgumentException $ e ) {
171+ continue ;
172+ }
173+ $ addToWide = !($ this ->shareeEnumerationInGroupOnly || $ this ->shareeEnumerationPhone );
174+
175+ if (!$ addToWide && $ this ->shareeEnumerationPhone && $ this ->knownUserService ->isKnownToUser ($ currentUserId , $ contact ['UID ' ])) {
176+ $ addToWide = true ;
177+ }
136178
137- if ($ this ->shareType === IShare::TYPE_USER && !$ this ->isCurrentUser ($ cloud ) && !$ searchResult ->hasResult ($ userType , $ cloud ->getUser ())) {
138- $ singleResult = [[
179+ if (!$ addToWide && $ this ->shareeEnumerationInGroupOnly ) {
180+ $ addToWide = array_intersect ($ contactGroups , $ userGroups ) !== [];
181+ }
182+
183+ if ($ addToWide && !$ this ->isCurrentUser ($ cloud ) && !$ searchResult ->hasResult ($ type , $ cloud ->getUser ())) {
184+ if ($ count ++ >= $ limit ) {
185+ $ hasMore = true ;
186+ } else {
187+ $ results ['wide ' ][] = [
139188 'label ' => $ displayName ,
140189 'uuid ' => $ contact ['UID ' ] ?? $ emailAddress ,
141190 'name ' => $ contact ['FN ' ] ?? $ displayName ,
@@ -144,115 +193,69 @@ public function search($search, $limit, $offset, ISearchResult $searchResult): b
144193 'shareWith ' => $ cloud ->getUser (),
145194 ],
146195 'shareWithDisplayNameUnique ' => !empty ($ emailAddress ) ? $ emailAddress : $ cloud ->getUser ()
147-
148- ]];
149- $ searchResult ->addResultSet ($ userType , [], $ singleResult );
150- $ searchResult ->markExactIdMatch ($ userType );
196+ ];
151197 }
152- return false ;
153198 }
199+ }
154200
155- if ($ this ->shareeEnumeration ) {
156- try {
157- if (!isset ($ contact ['CLOUD ' ])) {
158- continue ;
159- }
160- $ cloud = $ this ->cloudIdManager ->resolveCloudId ($ contact ['CLOUD ' ][0 ] ?? '' );
161- } catch (\InvalidArgumentException $ e ) {
162- continue ;
163- }
164-
165- $ addToWide = !($ this ->shareeEnumerationInGroupOnly || $ this ->shareeEnumerationPhone );
166- if (!$ addToWide && $ this ->shareeEnumerationPhone && $ this ->knownUserService ->isKnownToUser ($ currentUserId , $ contact ['UID ' ])) {
167- $ addToWide = true ;
168- }
201+ continue ;
202+ }
169203
170- if (!$ addToWide && $ this ->shareeEnumerationInGroupOnly ) {
171- $ addToWide = array_intersect ($ contactGroups , $ userGroups ) !== [];
172- }
173- if ($ addToWide && !$ this ->isCurrentUser ($ cloud ) && !$ searchResult ->hasResult ($ userType , $ cloud ->getUser ())) {
174- if ($ this ->shareType === IShare::TYPE_USER ) {
175- $ userResults ['wide ' ][] = [
176- 'label ' => $ displayName ,
177- 'uuid ' => $ contact ['UID ' ] ?? $ emailAddress ,
178- 'name ' => $ contact ['FN ' ] ?? $ displayName ,
179- 'value ' => [
180- 'shareType ' => IShare::TYPE_USER ,
181- 'shareWith ' => $ cloud ->getUser (),
182- ],
183- 'shareWithDisplayNameUnique ' => !empty ($ emailAddress ) ? $ emailAddress : $ cloud ->getUser ()
184- ];
185- }
186- continue ;
187- }
188- }
189- continue ;
190- }
204+ if ($ this ->shareType !== IShare::TYPE_EMAIL ) {
205+ continue ;
206+ }
191207
192- if ($ this ->shareType !== IShare::TYPE_EMAIL ) {
193- continue ;
208+ if ($ count ++ >= $ limit ) {
209+ $ hasMore = true ;
210+ } elseif ($ exactEmailMatch || (isset ($ contact ['FN ' ]) && strtolower ($ contact ['FN ' ]) === $ lowerSearch )) {
211+ if ($ exactEmailMatch ) {
212+ $ searchResult ->markExactIdMatch ($ type );
194213 }
195214
196- if ($ exactEmailMatch
197- || (isset ($ contact ['FN ' ]) && strtolower ($ contact ['FN ' ]) === $ lowerSearch )) {
198- if ($ exactEmailMatch ) {
199- $ searchResult ->markExactIdMatch ($ emailType );
200- }
201- $ result ['exact ' ][] = [
202- 'label ' => $ displayName ,
203- 'uuid ' => $ contact ['UID ' ] ?? $ emailAddress ,
204- 'name ' => $ contact ['FN ' ] ?? $ displayName ,
205- 'type ' => $ emailAddressType ?? '' ,
206- 'value ' => [
207- 'shareType ' => IShare::TYPE_EMAIL ,
208- 'shareWith ' => $ emailAddress ,
209- ],
210- ];
211- } else {
212- $ result ['wide ' ][] = [
213- 'label ' => $ displayName ,
214- 'uuid ' => $ contact ['UID ' ] ?? $ emailAddress ,
215- 'name ' => $ contact ['FN ' ] ?? $ displayName ,
216- 'type ' => $ emailAddressType ?? '' ,
217- 'value ' => [
218- 'shareType ' => IShare::TYPE_EMAIL ,
219- 'shareWith ' => $ emailAddress ,
220- ],
221- ];
222- }
215+ $ results ['exact ' ][] = [
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+ } else {
226+ $ results ['wide ' ][] = [
227+ 'label ' => $ displayName ,
228+ 'uuid ' => $ contact ['UID ' ] ?? $ emailAddress ,
229+ 'name ' => $ contact ['FN ' ] ?? $ displayName ,
230+ 'type ' => $ emailAddressType ?? '' ,
231+ 'value ' => [
232+ 'shareType ' => IShare::TYPE_EMAIL ,
233+ 'shareWith ' => $ emailAddress ,
234+ ],
235+ ];
223236 }
224237 }
225238 }
226239
227- $ reachedEnd = true ;
228- if ($ this ->shareeEnumeration ) {
229- $ reachedEnd = (count ($ result ['wide ' ]) < $ offset + $ limit )
230- && (count ($ userResults ['wide ' ]) < $ offset + $ limit );
231-
232- $ result ['wide ' ] = array_slice ($ result ['wide ' ], $ offset , $ limit );
233- $ userResults ['wide ' ] = array_slice ($ userResults ['wide ' ], $ offset , $ limit );
234- }
235-
236240 if ($ this ->shareType === IShare::TYPE_EMAIL
237- && !$ searchResult ->hasExactIdMatch ($ emailType ) && $ this ->emailValidator ->isValid ($ search )) {
238- $ result ['exact ' ][] = [
239- 'label ' => $ search ,
240- 'uuid ' => $ search ,
241- 'value ' => [
242- 'shareType ' => IShare::TYPE_EMAIL ,
243- 'shareWith ' => $ search ,
244- ],
245- ];
241+ && !$ searchResult ->hasExactIdMatch ($ type ) && $ this ->emailValidator ->isValid ($ search )) {
242+ if ($ count ++ >= $ limit ) {
243+ $ hasMore = true ;
244+ } else {
245+ $ results ['exact ' ][] = [
246+ 'label ' => $ search ,
247+ 'uuid ' => $ search ,
248+ 'value ' => [
249+ 'shareType ' => IShare::TYPE_EMAIL ,
250+ 'shareWith ' => $ search ,
251+ ],
252+ ];
253+ }
246254 }
247255
248- if ($ this ->shareType === IShare::TYPE_USER && !empty ($ userResults ['wide ' ])) {
249- $ searchResult ->addResultSet ($ userType , $ userResults ['wide ' ], []);
250- }
251- if ($ this ->shareType === IShare::TYPE_EMAIL ) {
252- $ searchResult ->addResultSet ($ emailType , $ result ['wide ' ], $ result ['exact ' ]);
253- }
256+ $ searchResult ->addResultSet ($ type , $ results ['wide ' ], $ results ['exact ' ]);
254257
255- return ! $ reachedEnd ;
258+ return $ hasMore ;
256259 }
257260
258261 public function isCurrentUser (ICloudId $ cloud ): bool {
0 commit comments