DAViCal
Loading...
Searching...
No Matches
Principal.php
1<?php
11
12require_once('AwlCache.php');
13
19class Principal {
20
25 private static $db_tablename = 'dav_principal';
26 private static $db_mandatory_fields = array(
27 'username',
28 );
29
30 public static function updateableFields() {
31 return array(
32 'username', 'email', 'user_active', 'modified', 'password', 'fullname',
33 'email_ok', 'date_format_type', 'locale', 'type_id', 'displayname', 'default_privileges'
34 );
35 }
36
42 private static $byUserno = array();
43 private static $byId = array();
44 private static $byEmail = array();
45
49 protected $username;
50 protected $user_no;
51 protected $principal_id;
52 protected $email;
53 protected $dav_name;
54 public $user_active;
55 public $created;
56 public $modified;
57 public $password;
58 public $fullname;
59 public $email_ok;
60 public $date_format_type;
61 public $locale;
62 public $type_id;
63 public $type_label;
64 public $displayname;
65 public $default_privileges;
66 public $is_principal;
67 public $is_calendar;
68 public $collection_id;
69 public $is_addressbook;
70 public $resourcetypes;
71 public $privileges;
72
77 protected $exists;
78
82 protected $url;
83
87 protected $original_request_url;
88
93 protected $by_email;
94
99 private $cacheNs;
100 private $cacheKey;
101
102 protected $collections;
103 protected $dead_properties;
104 protected $default_calendar;
105
123 function __construct( $type, $value, $use_cache=true ) {
124 global $c, $session;
125
126 $this->exists = false;
127 $this->by_email = false;
128 $this->original_request_url = null;
129
130 switch( $type ) {
131 case 'path':
132 $type = 'username';
133 $value = $this->usernameFromPath($value);
134 break;
135 case 'dav_name':
136 $type = 'username';
137 $value = substr($value, 1, -1);
138 break;
139 }
140
141
145 switch ( $type ) {
146 case 'user_no': $this->user_no = $value; break;
147 case 'principal_id': $this->principal_id = $value; break;
148 case 'email': $this->email = $value; break;
149 case 'username': $this->username = $value; break;
150 default:
151 throw new Exception('Can only retrieve a Principal by user_no,principal_id,username or email address');
152 }
153
154 $cache = getCacheInstance();
155 if ( $use_cache && isset($session->principal_id) ) {
156 switch ( $type ) {
157 case 'user_no':
158 if ( isset(self::$byUserno[$value]) ) {
159 $type = 'username';
160 $value = self::$byUserno[$value];
161 }
162 break;
163 case 'principal_id':
164 if ( isset(self::$byId[$value]) ) {
165 $type = 'username';
166 $value = self::$byId[$value];
167 }
168 break;
169 case 'email':
170 $this->by_email = true;
171 if ( isset(self::$byEmail[$value]) ) {
172 $type = 'username';
173 $value = self::$byEmail[$value];
174 }
175 break;
176 }
177
178 if ( $type == 'username' ) {
179 $this->username = $value;
180 $this->dav_name = '/'.$value.'/';
181 $this->url = ConstructURL( $this->dav_name, true );
182 $this->cacheNs = 'principal-/'.$value.'/';
183 $this->cacheKey = 'p-'.$session->principal_id;
184 $row = $cache->get('principal-/'.$value.'/', 'p-'.$session->principal_id );
185 if ( $row !== false ) {
186 self::$byId[$row->principal_id] = $row->username;
187 self::$byUserno[$row->user_no] = $row->username;
188 self::$byEmail[$row->email] = $row->username;
189 $this->assignRowValues($row);
190 $this->url = ConstructURL( $this->dav_name, true );
191 $this->exists = true;
192 return $this;
193 }
194 }
195 }
196
197 $sql = 'SELECT dav_principal.*, principal_type.label AS type_label, ';
198 if ( isset($session->principal_id) && $session->principal_id !== false ) {
199 $sql .= 'pprivs(:session_principal::int8,principal_id,:scan_depth::int) AS privileges ';
200 $params = array( ':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth );
201 }
202 else {
203 $sql .= '0::BIT(24) AS privileges ';
204 $params = array( );
205 }
206 $sql .= 'FROM dav_principal LEFT JOIN principal_type ON (type_id = principal_type_id) WHERE ';
207 switch ( $type ) {
208 case 'username':
209 $sql .= 'lower(username)=lower(text(:param))';
210 break;
211 case 'user_no':
212 $sql .= 'user_no=:param';
213 break;
214 case 'principal_id':
215 $sql .= 'principal_id=:param';
216 break;
217 case 'email':
218 $this->by_email = true;
219 $sql .= 'lower(email)=lower(:param)';
220 break;
221 }
222 $params[':param'] = $value;
223
224 $qry = new AwlQuery( $sql, $params );
225 if ( $qry->Exec('Principal',__LINE__,__FILE__) && $qry->rows() == 1 && $row = $qry->Fetch() ) {
226 $this->exists = true;
227 if ( isset($session->principal_id) ) {
228 self::$byId[$row->principal_id] = $row->username;
229 self::$byUserno[$row->user_no] = $row->username;
230 self::$byEmail[$row->email] = $row->username;
231 if ( !isset($this->cacheNs) ) {
232 $this->cacheNs = 'principal-'.$row->dav_name;
233 $this->cacheKey = 'p-'.$session->principal_id;
234 }
235 }
236 $this->assignRowValues($row);
237 $this->url = ConstructURL( $this->dav_name, true );
238
239 if (isset($this->cacheNs))
240 $cache->set($this->cacheNs, $this->cacheKey, $row, 864000 );
241
242 return $this;
243 }
244
245 if ( $type == 'username' && $value == 'unauthenticated' ) {
246 $this->assignGuestValues();
247 }
248 }
249
255 public function __get( $property ) {
256 return $this->{$property};
257 }
258
259
265 public function __isset( $property ) {
266 return isset($this->{$property});
267 }
268
269 private function assignGuestValues() {
270 $this->user_no = -1;
271 $this->exists = false;
272 if ( empty($this->username) ) $this->username = translate('unauthenticated');
273 $this->fullname = $this->displayname = translate('Unauthenticated User');
274 $this->email = false;
275 $this->is_principal = true;
276 $this->is_calendar = false;
277 $this->principal_id = -1;
278 $this->privileges = $this->default_privileges = 0;
279 }
280
281 private function assignRowValues( $db_row ) {
282 foreach( $db_row AS $k => $v ) {
283 $this->{$k} = $v;
284 }
285 }
286
287 public function Exists() {
288 return $this->exists;
289 }
290
291
292 public function byEmail() {
293 return $this->by_email;
294 }
295
296
301 private function usernameFromPath( $path ) {
302 global $session, $c;
303
304 if ( $path == '/' || $path == '' ) {
305 dbg_error_log( 'Principal', 'No useful path split possible' );
306 return $session->username;
307 }
308
309 $path_split = explode('/', $path );
310 @dbg_error_log( 'Principal', 'Path split into at least /// %s /// %s /// %s', $path_split[1], $path_split[2], $path_split[3] );
311
312 $username = $path_split[1];
313 if ( $path_split[1] == 'principals' && isset($path_split[3]) ) {
314 $username = $path_split[3];
315 $this->original_request_url = $path;
316 }
317 if ( substr($username,0,1) == '~' ) {
318 $username = substr($username,1);
319 $this->original_request_url = $path;
320 }
321
322 if ( isset($c->allow_by_email) && $c->allow_by_email && preg_match( '#^(\S+@\S+[.]\S+)$#', $username) ) {
323 // This might seem inefficient, but we cache the result, so the second time will not read from the DB
324 $p = new Principal('email',$username);
325 $username = $p->username;
326 $this->by_email = true;
327 }
328 return $username;
329 }
330
331
336 function username() {
337 return (isset($this->username)?$this->username:false);
338 }
339
340
345 function setUsername($new_username) {
346 if ( $this->exists && isset($this->username) ) return false;
347 $this->username = $new_username;
348 return $this->username;
349 }
350
351
356 function user_no() {
357 return (isset($this->user_no)?$this->user_no:false);
358 }
359
360
365 function principal_id() {
366 return (isset($this->principal_id)?$this->principal_id:false);
367 }
368
369
374 function email() {
375 return (isset($this->email)?$this->email:false);
376 }
377
378
383 function dav_name() {
384 if ( !isset($this->dav_name) ) {
385 if ( !isset($this->username) ) {
386 throw new Exception('Can\'t calculate dav_name for unknown username');
387 }
388 $this->dav_name = '/'.$this->username.'/';
389 }
390 return $this->dav_name;
391 }
392
393
397 protected function FetchDeadProperties() {
398 if ( isset($this->dead_properties) ) return;
399
400 $this->dead_properties = array();
401 $qry = new AwlQuery('SELECT property_name, property_value FROM property WHERE dav_name= :dav_name', array(':dav_name' => $this->dav_name()) );
402 if ( $qry->Exec('Principal') ) {
403 while ( $property = $qry->Fetch() ) {
404 $this->dead_properties[$property->property_name] = DAVResource::BuildDeadPropertyXML($property->property_name,$property->property_value);
405 }
406 }
407 }
408
409
414 protected function FetchCollections() {
415 if ( isset($this->collections) ) return;
416
417 // If this is an external binding, then user_no will most likely be empty.
418 if ( ! isset($this->user_no) ) return;
419
420 $this->collections = array();
421 $qry = new AwlQuery('SELECT * FROM collection WHERE user_no= :user_no', array(':user_no' => $this->user_no()) );
422 if ( $qry->Exec('Principal') ) {
423 while ( $collection = $qry->Fetch() ) {
424 $this->collections[$collection->dav_name] = $collection;
425 }
426 }
427 }
428
429
434 function default_calendar() {
435 global $c;
436
437 if ( !isset($this->default_calendar) ) {
438 $this->default_calendar = false;
439 if ( !isset($this->dead_properties) ) $this->FetchDeadProperties();
440 if ( isset($this->dead_properties['urn:ietf:params:xml:ns:caldav:schedule-default-calendar-URL']) ) {
441 $this->default_calendar = $this->dead_properties['urn:ietf:params:xml:ns:caldav:schedule-default-calendar-URL'];
442 }
443 else {
444 if ( !isset($this->collections) ) $this->FetchCollections();
445 $dav_name = $this->dav_name().$c->home_calendar_name.'/';
446 if ( isset($this->collections[$dav_name]) && ($this->collections[$dav_name]->is_calendar == 't') ) {
447 $this->default_calendar = $dav_name;
448 }
449 else {
450 $dav_name = $this->dav_name().'home/';
451 if ( isset($this->collections[$dav_name]) && ($this->collections[$dav_name]->is_calendar == 't') ) {
452 $this->default_calendar = $dav_name;
453 }
454 else if ( isset($this->collections) ) {
455 foreach( $this->collections AS $dav_name => $collection ) {
456 if ( $collection->is_calendar == 't' ) {
457 $this->default_calendar = $dav_name;
458 }
459 }
460 }
461 }
462 }
463 }
464 return $this->default_calendar;
465 }
466
467
474 public function url($type = 'principal', $internal=false ) {
475 global $c;
476
477 if ( $internal )
478 $result = $this->dav_name();
479 else {
480 if ( isset($this->original_request_url) && $type == 'principal' )
481 $result = $this->original_request_url;
482 else
483 $result = $this->url;
484 }
485
486 switch( $type ) {
487 case 'principal': break;
488 case 'schedule-default-calendar': $result = $this->default_calendar(); break;
489 case 'schedule-inbox': $result .= '.in/'; break;
490 case 'schedule-outbox': $result .= '.out/'; break;
491 case 'dropbox': $result .= '.drop/'; break;
492 case 'notifications': $result .= '.notify/'; break;
493 default:
494 fatal('Unknown internal URL type "'.$type.'"');
495 }
496 return ConstructURL(DeconstructURL($result));
497 }
498
499
500 public function internal_url($type = 'principal' ) {
501 return $this->url($type,true);
502 }
503
504
505 public function unCache() {
506 if ( !isset($this->cacheNs) ) return;
507 $cache = getCacheInstance();
508 $cache->delete($this->cacheNs, null );
509 }
510
511
512 private function Write( $field_values, $inserting=true ) {
513 global $c;
514 if ( is_array($field_values) ) $field_values = (object) $field_values;
515
516 if ( !isset($field_values->{'user_active'}) ) {
517 if ( isset($field_values->{'active'}) )
518 $field_values->{'user_active'} = $field_values->{'active'};
519 else if ( $inserting )
520 $field_values->{'user_active'} = true;
521 }
522 if ( !isset($field_values->{'modified'}) && isset($field_values->{'updated'}) )
523 $field_values->{'modified'} = $field_values->{'updated'};
524 if ( !isset($field_values->{'type_id'}) && $inserting )
525 $field_values->{'type_id'} = 1; // Default to 'person'
526 if ( !isset($field_values->{'default_privileges'}) && $inserting )
527 $field_values->{'default_privileges'} = sprintf('%024s',decbin(privilege_to_bits($c->default_privileges)));
528
529
530 $sql = '';
531 if ( $inserting ) {
532 $insert_fields = array();
533 $param_names = array();
534 }
535 else {
536 $update_list = array();
537 }
538 $sql_params = array();
539 foreach( self::updateableFields() AS $k ) {
540 if ( !isset($field_values->{$k}) && !isset($this->{$k}) ) continue;
541
542 $param_name = ':'.$k;
543 $sql_params[$param_name] = (isset($field_values->{$k}) ? $field_values->{$k} : $this->{$k});
544 if ( $k == 'default_privileges' ) {
545 $sql_params[$param_name] = sprintf('%024s',$sql_params[$param_name]);
546 $param_name = 'cast('.$param_name.' as text)::BIT(24)';
547 }
548 else if ( $k == 'modified'
549 && isset($field_values->{$k})
550 && preg_match('{^([23]\d\d\d[01]\d[0123]\d)T?([012]\d[0-5]\d[0-5]\d)$}', $field_values->{$k}, $matches) ) {
551 $sql_params[$param_name] = $matches[1] . 'T' . $matches[2];
552 }
553
554 if ( $inserting ) {
555 $param_names[] = $param_name;
556 $insert_fields[] = $k;
557 }
558 else {
559 $update_list[] = $k.'='.$param_name;
560 }
561 }
562
563 if ( $inserting && isset(self::$db_mandatory_fields) ) {
564 foreach( self::$db_mandatory_fields AS $k ) {
565 if ( !isset($sql_params[':'.$k]) ) {
566 throw new Exception( get_class($this).'::Create: Mandatory field "'.$k.'" is not set.');
567 }
568 }
569 if ( isset($this->user_no) ) {
570 $param_names[] = ':user_no';
571 $insert_fields[] = 'user_no';
572 $sql_params[':user_no'] = $this->user_no;
573 }
574 if ( isset($this->created) ) {
575 $param_names[] = ':created';
576 $insert_fields[] = 'created';
577 $sql_params[':created'] = $this->created;
578 }
579 $sql = 'INSERT INTO '.self::$db_tablename.' ('.implode(',',$insert_fields).') VALUES('.implode(',',$param_names).')';
580 }
581 else {
582 $sql = 'UPDATE '.self::$db_tablename.' SET '.implode(',',$update_list);
583 $sql .= ' WHERE principal_id=:principal_id';
584 $sql_params[':principal_id'] = $this->principal_id;
585 }
586
587 $qry = new AwlQuery($sql, $sql_params);
588 if ( $qry->Exec('Principal',__FILE__,__LINE__) ) {
589 $this->unCache();
590 $new_principal = new Principal('username', $sql_params[':username']);
591 foreach( $new_principal AS $k => $v ) {
592 $this->{$k} = $v;
593 }
594 }
595 }
596
597
598 public function Create( $field_values ) {
599 $this->Write($field_values, true);
600 }
601
602 public function Update( $field_values ) {
603 if ( !$this->Exists() ) {
604 throw new Exception( get_class($this).'::Create: Attempting to update non-existent record.');
605 }
606 $this->Write($field_values, false);
607 }
608
609 static public function cacheFlush( $where, $whereparams=array() ) {
610 $cache = getCacheInstance();
611 if ( !$cache->isActive() ) return;
612 $qry = new AwlQuery('SELECT dav_name FROM dav_principal WHERE '.$where, $whereparams );
613 if ( $qry->Exec('Principal',__FILE__,__LINE__) ) {
614 while( $row = $qry->Fetch() ) {
615 $cache->delete('principal-'.$row->dav_name, null);
616 }
617 }
618 }
619
620 static public function cacheDelete( $type, $value ) {
621 $cache = getCacheInstance();
622 if ( !$cache->isActive() ) return;
623 if ( $type == 'username' ) {
624 $value = '/'.$value.'/';
625 }
626 $cache->delete('principal-'.$value, null);
627 }
628}
static BuildDeadPropertyXML($property_name, $raw_string)
__isset( $property)
__construct( $type, $value, $use_cache=true)
__get( $property)
usernameFromPath( $path)
FetchDeadProperties()
FetchCollections()
default_calendar()
setUsername($new_username)