Total Complexity | 82 |
Total Lines | 432 |
Duplicated Lines | 0 % |
Changes | 2 | ||
Bugs | 0 | Features | 0 |
Complex classes like addressbook_bo often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use addressbook_bo, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
24 | class addressbook_bo extends Api\Contacts |
||
25 | { |
||
26 | static public $pgp_key_regexp = '/-----BEGIN PGP PUBLIC KEY BLOCK-----.*-----END PGP PUBLIC KEY BLOCK-----\r?\n?/s'; |
||
27 | |||
28 | /** |
||
29 | * Search addressbook for PGP public keys of given recipients |
||
30 | * |
||
31 | * EMail addresses are lowercased to make search case-insensitive |
||
32 | * |
||
33 | * @param string|int|array $recipients (array of) email addresses or numeric account-ids |
||
34 | * @return array email|account_id => key pairs |
||
35 | */ |
||
36 | public function get_pgp_keys($recipients) |
||
37 | { |
||
38 | return $this->get_keys($recipients, true); |
||
39 | } |
||
40 | |||
41 | /** |
||
42 | * Keyserver URL and CA to verify ssl connection |
||
43 | */ |
||
44 | const KEYSERVER = 'https://hkps.pool.sks-keyservers.net/pks/lookup?op=get&exact=on&search='; |
||
45 | const KEYSERVER_CA = '/addressbook/doc/sks-keyservers.netCA.pem'; |
||
46 | |||
47 | /** |
||
48 | * Search keyserver for PGP public keys |
||
49 | * |
||
50 | * @param int|string|array $recipients (array of) email addresses or numeric account-ids |
||
51 | * @param array $result =array() |
||
52 | */ |
||
53 | public static function get_pgp_keyserver($recipients, array $result=array()) |
||
54 | { |
||
55 | foreach($recipients as $recipient) |
||
56 | { |
||
57 | $id = $recipient; |
||
58 | if (is_numeric($recipient)) |
||
59 | { |
||
60 | $recipient = $GLOBALS['egw']->accounts->id2name($recipient, 'account_email'); |
||
61 | } |
||
62 | $matches = null; |
||
63 | if (($response = file_get_contents(self::KEYSERVER.urlencode($recipient), false, stream_context_create(array( |
||
64 | 'ssl' => array( |
||
65 | 'verify_peer' => true, |
||
66 | 'cafile' => EGW_SERVER_ROOT.self::KEYSERVER_CA, |
||
67 | ) |
||
68 | )))) && preg_match(self::$pgp_key_regexp, $response, $matches)) |
||
69 | { |
||
70 | $result[$id] = $matches[0]; |
||
71 | } |
||
72 | } |
||
73 | return $result; |
||
74 | } |
||
75 | |||
76 | /** |
||
77 | * Search addressbook for PGP public keys of given recipients |
||
78 | * |
||
79 | * EMail addresses are lowercased to make search case-insensitive |
||
80 | * |
||
81 | * @param string|int|array $recipients (array of) email addresses or numeric account-ids |
||
82 | * @return array email|account_id => key pairs |
||
83 | */ |
||
84 | public function ajax_get_pgp_keys($recipients) |
||
85 | { |
||
86 | if (!$recipients) return array(); |
||
87 | |||
88 | if (!is_array($recipients)) $recipients = array($recipients); |
||
89 | |||
90 | $result = $this->get_pgp_keys($recipients); |
||
91 | |||
92 | if (($missing = array_diff($recipients, array_keys($result)))) |
||
93 | { |
||
94 | $result = self::get_pgp_keyserver($missing, $result); |
||
95 | } |
||
96 | //error_log(__METHOD__."(".array2string($recipients).") returning ".array2string($result)); |
||
97 | Api\Json\Response::get()->data($result); |
||
98 | } |
||
99 | |||
100 | /** |
||
101 | * Set PGP keys for given email or account_id, if user has necessary rights |
||
102 | * |
||
103 | * @param array $keys email|account_id => public key pairs to store |
||
104 | * @param boolean $allow_user_updates =null for admins, set config to allow regular users to store their pgp key |
||
105 | * @return int number of pgp keys stored |
||
106 | */ |
||
107 | public function ajax_set_pgp_keys($keys, $allow_user_updates=null) |
||
108 | { |
||
109 | $message = $this->set_keys($keys, true, $allow_user_updates); |
||
110 | // add all keys to public keyserver too |
||
111 | $message .= "\n".lang('%1 key(s) added to public keyserver "%2".', |
||
112 | self::set_pgp_keyserver($keys), PARSE_URL(self::KEYSERVER_ADD, PHP_URL_HOST)); |
||
|
|||
113 | |||
114 | Api\Json\Response::get()->data($message); |
||
115 | } |
||
116 | |||
117 | /** |
||
118 | * Keyserver add URL |
||
119 | */ |
||
120 | const KEYSERVER_ADD = 'https://hkps.pool.sks-keyservers.net/pks/add'; |
||
121 | |||
122 | /** |
||
123 | * Upload PGP keys to public keyserver |
||
124 | * |
||
125 | * @param array $keys email|account_id => public key pairs to store |
||
126 | * @return int number of pgp keys stored |
||
127 | */ |
||
128 | public static function set_pgp_keyserver($keys) |
||
155 | } |
||
156 | |||
157 | /** |
||
158 | * Where to store public key depending on type and storage backend |
||
159 | * |
||
160 | * @param boolean $pgp true: PGP, false: S/Mime |
||
161 | * @param array $contact =null contact array to pass to get_backend() |
||
162 | * @return boolean true: store as file, false: store with contact |
||
163 | */ |
||
164 | public function pubkey_use_file($pgp, array $contact=null) |
||
167 | } |
||
168 | |||
169 | /** |
||
170 | * Set keys for given email or account_id and key type based on regexp (SMIME or PGP), if user has necessary rights |
||
171 | * |
||
172 | * @param array $keys email|account_id => public key pairs to store |
||
173 | * @param boolean $pgp true: PGP, false: S/Mime |
||
174 | * @param boolean $allow_user_updates = null for admins, set config to allow regular users to store their key |
||
175 | * |
||
176 | * @return string message of the update operation result |
||
177 | */ |
||
178 | public function set_keys ($keys, $pgp, $allow_user_updates = null) |
||
179 | { |
||
180 | if (isset($allow_user_updates) && isset($GLOBALS['egw_info']['user']['apps']['admin'])) |
||
181 | { |
||
182 | $update = false; |
||
183 | if ($allow_user_updates && !in_array('pubkey', $this->own_account_acl)) |
||
184 | { |
||
185 | $this->own_account_acl[] = 'pubkey'; |
||
186 | $update = true; |
||
187 | } |
||
188 | elseif (!$allow_user_updates && ($key = array_search('pubkey', $this->own_account_acl)) !== false) |
||
189 | { |
||
190 | unset($this->own_account_acl[$key]); |
||
191 | $update = true; |
||
192 | } |
||
193 | if ($update) |
||
194 | { |
||
195 | Config::save_value('own_account_acl', $this->own_account_acl, 'phpgwapi'); |
||
196 | } |
||
197 | } |
||
198 | |||
199 | $key_regexp = $pgp ? self::$pgp_key_regexp : Api\Mail\Smime::$certificate_regexp; |
||
200 | $file = $pgp ? Api\Contacts::FILES_PGP_PUBKEY : Api\Contacts::FILES_SMIME_PUBKEY; |
||
201 | |||
202 | $criteria = array(); |
||
203 | foreach($keys as $recipient => $key) |
||
204 | { |
||
205 | if (!preg_match($key_regexp, $key)) |
||
206 | { |
||
207 | return lang('File is not a %1 public key!', $pgp ? lang('PGP') : lang('S/MIME')); |
||
208 | } |
||
209 | |||
210 | if (is_numeric($recipient)) |
||
211 | { |
||
212 | $criteria['egw_addressbook.account_id'][] = (int)$recipient; |
||
213 | } |
||
214 | else |
||
215 | { |
||
216 | $criteria['contact_email'][] = $recipient; |
||
217 | } |
||
218 | } |
||
219 | if (!$criteria) return 0; |
||
220 | |||
221 | $updated = 0; |
||
222 | $filters = array(null); |
||
223 | // if accounts-backend is NOT SQL, we need to search the accounts separate |
||
224 | if ($this->so_accounts) |
||
225 | { |
||
226 | $filters[] = array('owner' => '0'); |
||
227 | } |
||
228 | foreach($filters as $filter) |
||
229 | { |
||
230 | foreach((array)$this->search($criteria, false, '', '', '', false, 'OR', false, $filter) as $contact) |
||
231 | { |
||
232 | if ($contact['account_id'] && isset($keys[$contact['account_id']])) |
||
233 | { |
||
234 | $key = $keys[$contact['account_id']]; |
||
235 | } |
||
236 | elseif (isset($keys[$contact['email']])) |
||
237 | { |
||
238 | $key = $keys[$contact['email']]; |
||
239 | } |
||
240 | |||
241 | // key is stored in file for sql backend or allways for pgp key |
||
242 | $path = null; |
||
243 | if ($contact['id'] && $this->pubkey_use_file($pgp, $contact)) |
||
244 | { |
||
245 | $path = Api\Link::vfs_path('addressbook', $contact['id'], $file); |
||
246 | $contact['files'] |= $pgp ? self::FILES_BIT_PGP_PUBKEY : self::FILES_BIT_SMIME_PUBKEY; |
||
247 | // remove evtl. existing old pubkey |
||
248 | if (preg_match($key_regexp, $contact['pubkey'])) |
||
249 | { |
||
250 | $contact['pubkey'] = preg_replace($key_regexp, '', $contact['pubkey']); |
||
251 | } |
||
252 | $updated++; |
||
253 | } |
||
254 | elseif (empty($contact['pubkey']) || !preg_match($key_regexp, $contact['pubkey'])) |
||
255 | { |
||
256 | $contact['pubkey'] .= $key; |
||
257 | } |
||
258 | else |
||
259 | { |
||
260 | $contact['pubkey'] = preg_replace($key_regexp, $key, $contact['pubkey']); |
||
261 | } |
||
262 | $contact['photo_unchanged'] = true; // otherwise photo will be lost, because $contact['jpegphoto'] is not set |
||
263 | if ($this->check_perms(Acl::EDIT, $contact) && $this->save($contact)) |
||
264 | { |
||
265 | if ($path) |
||
266 | { |
||
267 | // check_perms && save check ACL, in case of access only via own-account we have to use root to allow the update |
||
268 | $backup = Api\Vfs::$is_root; Api\Vfs::$is_root = true; |
||
269 | if (file_put_contents($path, $key)) ++$updated; |
||
270 | Api\Vfs::$is_root = $backup; |
||
271 | } |
||
272 | else |
||
273 | { |
||
274 | ++$updated; |
||
275 | } |
||
276 | } |
||
277 | } |
||
278 | } |
||
279 | if ($criteria == array('egw.addressbook.account_id' => array((int)$GLOBALS['egw_info']['user']['account_id']))) |
||
280 | { |
||
281 | $message = !$updated ? lang('Permissiong denied! Ask your administrator to allow regular uses to update their public keys.') : |
||
282 | lang('Your new public key has been stored in accounts addressbook.'); |
||
283 | } |
||
284 | else |
||
285 | { |
||
286 | $message = !$updated ? false: lang('%1 public keys added.', $updated); |
||
287 | } |
||
288 | return $message; |
||
289 | } |
||
290 | |||
291 | /** |
||
292 | * Search addressbook for keys of given recipients |
||
293 | * |
||
294 | * EMail addresses are lowercased to make search case-insensitive |
||
295 | * |
||
296 | * @param string|int|array $recipients (array of) email addresses or numeric account-ids or "contact:$id" for contacts by id |
||
297 | * @param boolean $pgp true: PGP, false: S/Mime public keys |
||
298 | * @return array email|account_id => key pairs |
||
299 | */ |
||
300 | protected function get_keys ($recipients, $pgp) |
||
301 | { |
||
302 | if (!$recipients) return array(); |
||
303 | |||
304 | if (!is_array($recipients)) $recipients = array($recipients); |
||
305 | |||
306 | $criteria = $result = array(); |
||
307 | foreach($recipients as &$recipient) |
||
308 | { |
||
309 | if (is_numeric($recipient)) |
||
310 | { |
||
311 | $criteria['egw_addressbook.account_id'][] = (int)$recipient; |
||
312 | } |
||
313 | else |
||
314 | { |
||
315 | $criteria['contact_email_home'][] = $criteria['contact_email'][] = $recipient = strtolower($recipient); |
||
316 | } |
||
317 | } |
||
318 | $filters = array(null); |
||
319 | // if accounts-backend is NOT SQL, we need to search the accounts separate |
||
320 | if ($this->so_accounts) |
||
321 | { |
||
322 | $filters[] = array('owner' => '0'); |
||
323 | } |
||
324 | foreach ($filters as $filter) |
||
325 | { |
||
326 | foreach((array)$this->search($criteria, array('account_id', 'contact_email', 'contact_email_home', 'contact_pubkey', 'contact_id'), |
||
327 | '', '', '', false, 'OR', false, $filter) as $contact) |
||
328 | { |
||
329 | // first check for file and second for pubkey field (LDAP, AD or old SQL) |
||
330 | if (($content = $this->get_key($contact, $pgp))) |
||
331 | { |
||
332 | $contact['email'] = strtolower($contact['email']); |
||
333 | if (empty($criteria['account_id']) || in_array($contact['email'], $recipients)) |
||
334 | { |
||
335 | if (in_array($contact['email_home'], $recipients)) |
||
336 | { |
||
337 | $result[$contact['email_home']] = $content; |
||
338 | } |
||
339 | else |
||
340 | { |
||
341 | $result[$contact['email']] = $content; |
||
342 | } |
||
343 | } |
||
344 | else |
||
345 | { |
||
346 | $result[$contact['account_id']] = $content; |
||
347 | } |
||
348 | } |
||
349 | } |
||
350 | } |
||
351 | return $result; |
||
352 | } |
||
353 | |||
354 | /** |
||
355 | * Extract PGP or S/Mime pubkey from contact array |
||
356 | * |
||
357 | * @param array $contact |
||
358 | * @param boolean $pgp |
||
359 | * @return string pubkey or NULL |
||
360 | */ |
||
361 | function get_key(array $contact, $pgp) |
||
362 | { |
||
363 | if ($pgp) |
||
364 | { |
||
365 | $key_regexp = self::$pgp_key_regexp; |
||
366 | $file = Api\Contacts::FILES_PGP_PUBKEY; |
||
367 | } |
||
368 | else |
||
369 | { |
||
370 | $key_regexp = Api\Mail\Smime::$certificate_regexp; |
||
371 | $file = Api\Contacts::FILES_SMIME_PUBKEY; |
||
372 | } |
||
373 | $matches = null; |
||
374 | if (file_exists($path = Api\Link::vfs_path('addressbook', $contact['id'], $file)) && |
||
375 | ($content = file_get_contents($path)) && |
||
376 | preg_match($key_regexp, $content, $matches) || |
||
377 | preg_match($key_regexp, $contact['pubkey'], $matches)) |
||
378 | { |
||
379 | return $matches[0]; |
||
380 | } |
||
381 | return null; |
||
382 | } |
||
383 | |||
384 | /** |
||
385 | * Search addressbook for SMIME Certificate keys of given recipients |
||
386 | * |
||
387 | * EMail addresses are lowercased to make search case-insensitive |
||
388 | * |
||
389 | * @param string|int|array $recipients (array of) email addresses or numeric account-ids |
||
390 | * @return array email|account_id => key pairs |
||
391 | */ |
||
392 | public function get_smime_keys($recipients) |
||
393 | { |
||
394 | return $this->get_keys($recipients, false); |
||
395 | } |
||
396 | |||
397 | /** |
||
398 | * Set SMIME keys for given email or account_id, if user has necessary rights |
||
399 | * |
||
400 | * @param array $keys email|account_id => public key pairs to store |
||
401 | * @param boolean $allow_user_updates =null for admins, set config to allow regular users to store their smime key |
||
402 | * |
||
403 | * @return string message of the update operation result |
||
404 | */ |
||
405 | public function set_smime_keys($keys, $allow_user_updates=null) |
||
406 | { |
||
407 | return $this->set_keys($keys, false, $allow_user_updates); |
||
408 | } |
||
409 | |||
410 | /** |
||
411 | * Saves contact |
||
412 | * |
||
413 | * Reimplemented to strip pubkeys pasted into pubkey field or imported and store them as files in Vfs. |
||
414 | * We allways store PGP pubkeys to Vfs, but S/Mime ones only for SQL backend, not for LDAP or AD. |
||
415 | * |
||
416 | * @param array &$contact contact array from etemplate::exec |
||
417 | * @param boolean $ignore_acl =false should the acl be checked or not |
||
418 | * @param boolean $touch_modified =true should modified/r be updated |
||
419 | * @return int|string|boolean id on success, false on failure, the error-message is in $this->error |
||
420 | */ |
||
421 | function save(&$contact, $ignore_acl=false, $touch_modified=true) |
||
456 | } |
||
457 | } |
||
458 |
This check compares calls to functions or methods with their respective definitions. If the call has more arguments than are defined, it raises an issue.
If a function is defined several times with a different number of parameters, the check may pick up the wrong definition and report false positives. One codebase where this has been known to happen is Wordpress. Please note the @ignore annotation hint above.