1<?php
2/**
3  * Helper Class for the DAVCard plugin
4  * This helper does the actual work.
5  * @license GPL 2 http://www.gnu.org/licenses/gpl-2.0.html
6  * @author  Andreas Böhler <dev@aboehler.at>
7  */
8
9// must be run within Dokuwiki
10if(!defined('DOKU_INC')) die();
11
12class helper_plugin_davcard extends DokuWiki_Plugin {
13
14  protected $sqlite = null;
15
16
17  /**
18    * Constructor to load the configuration
19    */
20  public function helper_plugin_davcard() {
21
22  }
23
24  /** Establish and initialize the database if not already done
25   * @return sqlite interface or false
26   */
27  private function getDB()
28  {
29      if($this->sqlite === null)
30      {
31        $this->sqlite = plugin_load('helper', 'sqlite');
32        if(!$this->sqlite)
33        {
34            dbglog('This plugin requires the sqlite plugin. Please install it.');
35            msg('This plugin requires the sqlite plugin. Please install it.', -1);
36            return false;
37        }
38        if(!$this->sqlite->init('davcard', DOKU_PLUGIN.'davcard/db/'))
39        {
40            $this->sqlite = null;
41            dbglog('Error initialising the SQLite DB for davcard');
42            return false;
43        }
44      }
45      return $this->sqlite;
46  }
47
48  /**
49   * Retrieve a contact by specifying details like the name
50   *
51   * @param int $id The address book ID
52   * @param string $type The type to look for
53   * @param array $params The parameter array
54   *
55   * @return array An array containing the results
56   */
57  private function getContactByDetails($id, $type, $params = array())
58  {
59    $write = false;
60    if(strpos($id, 'webdav://') === 0)
61    {
62        $wdc =& plugin_load('helper', 'webdavclient');
63        if(is_null($wdc))
64            return $this->getLang('no_wdc');
65        $connectionId = str_replace('webdav://', '', $id);
66        $settings = $wdc->getConnection($connectionId);
67
68        if($settings === false)
69            return array('formattedname' => $this->getLang('settings_not_found'), 'result' => false);
70        if($settings['type'] !== 'contacts')
71            return array('formattedname' => $this->getLang('wrong_type'), 'result' => false);
72
73        $entries = $wdc->getAddressbookEntries($connectionId);
74        $write = $settings['write'];
75    }
76    else
77    {
78        $acl = auth_quickaclcheck($id);
79        if($acl > AUTH_READ)
80        {
81            $write = true;
82        }
83        elseif($acl < AUTH_READ)
84        {
85            return array('formattedname' => $this->getLang('no_permission'), 'result' => false);
86        }
87        else
88        {
89            $write = false;
90        }
91        $addressbookid = $this->getAddressbookIdForPage($id);
92        $entries = $this->getAddressbookEntries($addressbookid);
93    }
94    foreach($entries as $entry)
95    {
96        switch($type)
97        {
98            case 'structuredname':
99                $contactdata = explode(';', strtolower($entry['structuredname']));
100                if(count($contactdata) < 2) // We need at least first and last name
101                    return array('formattedname' => sprintf($this->getLang('contact_not_found'), $params['firstname']. ' '.$params['lastname']), 'result' => false);
102                if(($params['lastname'] != '') &&
103                    ($contactdata[0] === $params['lastname'])
104                    || $params['lastname'] === '')
105                {
106                    // last name matched or no last name given
107                    if(($params['firstname'] != '') &&
108                       ($contactdata[1] === $params['firstname'])
109                       || $params['firstname'] === '')
110                    {
111                        // first name matched too or no first name given
112                        $info = $this->parseVcard($entry['contactdata'], $entry['uri'], $write);
113                        return $info;
114                    }
115                }
116            break;
117            case 'formattedname':
118                if(trim(strtolower($entry['formattedname'])) == $params['formattedname'])
119                {
120                    $info = $this->parseVcard($entry['contactdata'], $entry['uri'], $write);
121                    return $info;
122                }
123            break;
124            case 'email':
125                $info = $this->parseVcard($entry['contactdata'], $entry['uri'], $write);
126                foreach($info['mail'] as $data)
127                {
128                    if(trim(strtolower($data['mail'])) === $params['email'])
129                        return $info;
130                }
131            break;
132        }
133    }
134    return array('formattedname' => sprintf($this->getLang('contact_not_found'), $this->getLang('invalid_options')), 'result' => false);
135  }
136
137
138  /**
139   * Retreive all address book entries
140   *
141   * @param int $id The addressbook ID to retrieve
142   *
143   * @return array All address book entries
144   */
145  public function getAddressbookEntries($id)
146  {
147      $sqlite = $this->getDB();
148      if(!$sqlite)
149        return false;
150      $query = "SELECT contactdata, uri, formattedname, structuredname FROM addressbookobjects WHERE addressbookid = ? ORDER BY formattedname ASC";
151      $res = $sqlite->query($query, $id);
152      return $sqlite->res2arr($res);
153  }
154
155  /**
156   * Retrieve a contact by the structured name
157   *
158   * @param string $id The addressbook ID to work with
159   * @param string $firstname The contact's first name
160   * @param string $lastname The contact's last name
161   *
162   * @return array The contact's details
163   */
164  public function getContactByStructuredName($id, $firstname = '', $lastname = '')
165  {
166    return $this->getContactByDetails($id, 'structuredname',
167        array('firstname' => strtolower($firstname), 'lastname' => strtolower($lastname)));
168  }
169
170  /**
171   * Retrieve a contact by e-mail address
172   *
173   * @param string $id The address book ID
174   * @param string $email The E-Mail address
175   *
176   * @return array The contact's details
177   */
178  public function getContactByEmail($id, $email)
179  {
180      // FIXME: Maybe it's a good idea to save the e-mail in the database as well!
181      return $this->getContactByDetails($id, 'email', array('email' => strtolower($email)));
182  }
183
184  /**
185   * Retrieve a contact by formatted name
186   *
187   * @param string $id The address book  ID
188   * @param string $name The contact's formatted name
189   *
190   * @return array The contact's details
191   */
192  public function getContactByFormattedName($id, $name)
193  {
194      return $this->getContactByDetails($id, 'formattedname', array('formattedname' => strtolower($name)));
195  }
196
197  /**
198   * Retrieve a contact object by its URI
199   *
200   * @param string $ID The address book ID
201   * @param string $uri The object URI
202   *
203   * @return array An array containing the result
204   */
205  public function getContactByUri($id, $uri)
206  {
207    $write = false;
208    if(strpos($id, 'webdav://') === 0)
209    {
210        $wdc =& plugin_load('helper', 'webdavclient');
211        if(is_null($wdc))
212            return $this->getLang('no_wdc');
213        $connectionId = str_replace('webdav://', '', $id);
214        $settings = $wdc->getConnection($connectionId);
215
216        if($settings === false)
217            return array('formattedname' => $this->getLang('settings_not_found'), 'result' => false);
218        if($settings['type'] !== 'contacts')
219            return array('formattedname' => $this->getLang('wrong_type'), 'result' => false);
220
221        $row = $wdc->getAddressbookEntryByUri($connectionId, $uri);
222        $write = $settings['write'];
223    }
224    else
225    {
226        $acl = auth_quickaclcheck($id);
227        if($acl > AUTH_READ)
228        {
229            $write = true;
230        }
231        elseif($acl < AUTH_READ)
232        {
233            return array('formattedname' => $this->getLang('no_permission'), 'result' => false);
234        }
235        else
236        {
237            $write = false;
238        }
239        $addressbookid = $this->getAddressbookIdForPage($id);
240        $row = $this->getAddressbookEntryByUri($addressbookid, $uri);
241    }
242
243    if($row === false)
244        return array('formattedname' => sprintf($this->getLang('contact_not_found'), 'ID='.$id.' URI='.$uri), 'result' => false);
245    $info = $this->parseVcard($row['contactdata'], $row['uri'], $write);
246    $info['result'] = true;
247    return $info;
248  }
249
250  /**
251   * Retrieve an address book entry by URI (low-level version)
252   *
253   * @param int $id The address book ID
254   * @param string $uri The object URI
255   *
256   * @return array The contact's details
257   */
258  private function getAddressbookEntryByUri($id, $uri)
259  {
260      $sqlite = $this->getDB();
261      if(!$sqlite)
262        return false;
263      $query = "SELECT contactdata, addressbookid, etag, uri, formattedname, structuredname FROM addressbookobjects WHERE addressbookid = ? AND uri = ?";
264      $res = $sqlite->query($query, $id, $uri);
265      return $sqlite->res2row($res);
266  }
267
268  /**
269   * Set the addressbook name for a given page
270   *
271   * @param string $name The name to set
272   * @param string $description The address book description
273   * @param int $id (optional) The page ID
274   * @param string $userid (optional) The user's ID
275   *
276   * @return boolean true on success, otherwise false
277   */
278  public function setAddressbookNameForPage($name, $description, $id = null, $userid = null)
279  {
280      if(is_null($id))
281      {
282          global $ID;
283          $id = $ID;
284      }
285      if(is_null($userid))
286      {
287        if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
288        {
289          $userid = $_SERVER['REMOTE_USER'];
290        }
291        else
292        {
293          $userid = uniqid('davcard-');
294        }
295      }
296      $bookid = $this->getAddressbookIdForPage($id);
297      if($bookid === false)
298        return $this->createAddressbookForPage($name, $description, $id, $userid);
299
300      $sqlite = $this->getDB();
301      if(!$sqlite)
302        return false;
303      $query = "UPDATE addressbooks SET displayname = ?, description = ? WHERE id = ?";
304      $res = $sqlite->query($query, $name, $description, $bookid);
305      if($res !== false)
306        return true;
307      return false;
308  }
309
310  /**
311   * Get the address book ID associated with a given page
312   *
313   * @param string $id (optional) The page ID
314   *
315   * @return mixed The address book ID or false
316   */
317  public function getAddressbookIdForPage($id = null)
318  {
319      if(is_null($id))
320      {
321          global $ID;
322          $id = $ID;
323      }
324
325      $sqlite = $this->getDB();
326      if(!$sqlite)
327        return false;
328
329      $query = "SELECT addressbookid FROM pagetoaddressbookmapping WHERE page = ?";
330      $res = $sqlite->query($query, $id);
331      $row = $sqlite->res2row($res);
332      if(isset($row['addressbookid']))
333      {
334        $addrbkid = $row['addressbookid'];
335        return $addrbkid;
336      }
337      return false;
338  }
339
340  /**
341   * Create a new address book for a given page
342   *
343   * @param string $name The name of the new address book
344   * @param string $description The address book's description
345   * @param string $id (optional) The page ID
346   * @param string $userid (optional) The user's ID
347   *
348   * @return boolean True on success, otherwise false
349   */
350  public function createAddressbookForPage($name, $description, $id = null, $userid = null)
351  {
352      if(is_null($id))
353      {
354          global $ID;
355          $id = $ID;
356      }
357      if(is_null($userid))
358      {
359        if(isset($_SERVER['REMOTE_USER']) && !is_null($_SERVER['REMOTE_USER']))
360        {
361          $userid = $_SERVER['REMOTE_USER'];
362        }
363        else
364        {
365          $userid = uniqid('davcard-');
366        }
367      }
368
369      $sqlite = $this->getDB();
370      if(!$sqlite)
371        return false;
372
373      $values = array('principals/'.$userid,
374                      $name,
375                      str_replace(array('/', ' ', ':'), '_', $id),
376                      $description,
377                      1);
378      $query = "INSERT INTO addressbooks (principaluri, displayname, uri, description, synctoken) ".
379               "VALUES (?, ?, ?, ?, ?)";
380      $res = $sqlite->query($query, $values);
381      if($res === false)
382        return false;
383
384      // Get the new addressbook ID
385      $query = "SELECT id FROM addressbooks WHERE principaluri = ? AND displayname = ? AND ".
386               "uri = ? AND description = ? AND synctoken = ?";
387      $res = $sqlite->query($query, $values);
388      $row = $sqlite->res2row($res);
389
390      // Update the pagetocalendarmapping table with the new calendar ID
391      if(isset($row['id']))
392      {
393          $query = "INSERT INTO pagetoaddressbookmapping (page, addressbookid) VALUES (?, ?)";
394          $res = $sqlite->query($query, $id, $row['id']);
395          return ($res !== false);
396      }
397
398      return false;
399  }
400
401  /**
402   * Delete a contact entry from an address book by URI
403   *
404   * @param string $id The address book ID
405   * @param string $user The user's ID
406   * @param string $uri The object URI to delete
407   *
408   * @return boolean True on success, otherwise false
409   */
410  public function deleteContactEntryToAddressbookForPage($id, $user, $uri)
411  {
412      if(strpos($id, 'webdav://') === 0)
413      {
414        $wdc =& plugin_load('helper', 'webdavclient');
415        if(is_null($wdc))
416            return $this->getLang('no_wdc');
417        $connectionId = str_replace('webdav://', '', $id);
418        $settings = $wdc->getConnection($connectionId);
419
420        if($settings === false)
421            return array('formattedname' => $this->getLang('settings_not_found'), 'result' => false);
422        if($settings['type'] !== 'contacts')
423            return array('formattedname' => $this->getLang('wrong_type'), 'result' => false);
424
425        return $wdc->deleteAddressbookEntry($connectionId, $uri);
426      }
427
428      $sqlite = $this->getDB();
429      if(!$sqlite)
430        return false;
431
432      $addressbookid = $this->getAddressbookIdForPage($id);
433      $query = "DELETE FROM addressbookobjects WHERE uri = ? AND addressbookid = ?";
434
435      $res = $sqlite->query($query, $uri, $addressbookid);
436      if($res !== false)
437      {
438          $this->updateSyncTokenLog($addressbookid, $uri, 'deleted');
439          return true;
440      }
441      return false;
442  }
443
444  /**
445   * Edit a contact for a given address book
446   *
447   * @param string $id The address book ID
448   * @param string $user The user name
449   * @param string $uri The object URI
450   * @param array $params The new address book parameters
451   *
452   * @return boolean True on success, otherwise false
453   */
454  public function editContactEntryToAddressbookForPage($id, $user, $uri, $params)
455  {
456      require_once(DOKU_PLUGIN.'davcard/vendor/autoload.php');
457
458      if(strpos($id, 'webdav://') === 0)
459      {
460        $wdc =& plugin_load('helper', 'webdavclient');
461        if(is_null($wdc))
462            return $this->getLang('no_wdc');
463        $connectionId = str_replace('webdav://', '', $id);
464        $settings = $wdc->getConnection($connectionId);
465
466        if($settings === false)
467            return array('formattedname' => $this->getLang('settings_not_found'), 'result' => false);
468        if($settings['type'] !== 'contacts')
469            return array('formattedname' => $this->getLang('wrong_type'), 'result' => false);
470
471        $row = $wdc->getAddressbookEntryByUri($connectionId, $uri);
472      }
473      else
474      {
475        $addressbookid = $this->getAddressbookIdForPage($id);
476        $row = $this->getAddressbookEntryByUri($addressbookid, $uri);
477      }
478      $vcard = \Sabre\VObject\Reader::read($row['contactdata']);
479
480      $vcard->remove('ADR');
481      $vcard->remove('TEL');
482      $vcard->remove('EMAIL');
483
484      if(isset($params['phones']))
485      {
486          foreach($params['phones'] as $data)
487          {
488              $vcard->add('TEL', $data['number'], array('type' => $data['type']));
489          }
490      }
491
492      if(isset($params['email']))
493      {
494          foreach($params['email'] as $data)
495          {
496              $vcard->add('EMAIL', $data['mail'], array('type' => $data['type']));
497          }
498      }
499
500      if(isset($params['addresses']))
501      {
502          foreach($params['addresses'] as $data)
503          {
504              $vcard->add('ADR', array('', '', $data['street'], $data['city'], '', $data['zipcode'], $data['country']), array('type' => $data['type']));
505          }
506      }
507
508      $structuredname = explode(';', (string)$vcard->N);
509      $structuredname[0] = $params['lastname'];
510      $structuredname[1] = $params['firstname'];
511      $formattedname = $params['firstname'].' '.$params['lastname']; // FIXME: Make this configurable?
512      $vcard->N = $structuredname;
513      $vcard->FN = $formattedname;
514
515      $contactdata = $vcard->serialize();
516
517      if(strpos($id, 'webdav://') === 0)
518      {
519          return $wdc->editAddressbookEntry($connectionId, $uri, $contactdata);
520      }
521      else
522      {
523          $sqlite = $this->getDB();
524          if(!$sqlite)
525            return false;
526          $now = new \DateTime();
527          $query = "UPDATE addressbookobjects SET contactdata = ?, lastmodified = ?, etag = ?, size = ?, formattedname = ?, structuredname = ? WHERE addressbookid = ? AND uri = ?";
528          $res = $sqlite->query($query,
529                                      $contactdata,
530                                      $now->getTimestamp(),
531                                      md5($contactdata),
532                                      strlen($contactdata),
533                                      $formattedname,
534                                      implode(';', $structuredname),
535                                      $addressbookid,
536                                      $uri
537                                      );
538          if($res !== false)
539          {
540              $this->updateSyncTokenLog($addressbookid, $uri, 'modified');
541              return true;
542          }
543      }
544      return false;
545  }
546
547  /**
548   * Add a new contact entry to an address book page
549   *
550   * @param string $id The page ID
551   * @param string $user The user ID
552   * @param array $params The entry's parameters
553   *
554   * @return boolean True on success, otherwise false
555   */
556  public function addContactEntryToAddressbookForPage($id, $user, $params)
557  {
558      require_once(DOKU_PLUGIN.'davcard/vendor/autoload.php');
559      $vcard = new \Sabre\VObject\Component\VCard();
560      $formattedname = $params['firstname'].' '.$params['lastname']; // FIXME: Make this configurable?
561      $structuredname = array($params['lastname'], $params['firstname'], '', '', '');
562      $vcard->FN =  $formattedname;
563      $vcard->N = $structuredname;
564
565      if(isset($params['phones']))
566      {
567        foreach($params['phones'] as $data)
568        {
569            $vcard->add('TEL', $data['number'], array('type' => $data['type']));
570        }
571      }
572      if(isset($params['email']))
573      {
574          foreach($params['email'] as $data)
575          {
576              $vcard->add('EMAIL', $data['mail'], array('type' => $data['type']));
577          }
578      }
579      if(isset($params['addresses']))
580      {
581          foreach($params['addresses'] as $data)
582          {
583              $vcard->add('ADR', array('', '', $data['street'], $data['city'], '', $data['zipcode'], $data['country']), array('type' => $data['type']));
584          }
585      }
586
587      $contactdata = $vcard->serialize();
588
589      if(strpos($id, 'webdav://') === 0)
590      {
591          $wdc =& plugin_load('helper', 'webdavclient');
592          if(is_null($wdc))
593            return false;
594          $connectionId = str_replace('webdav://', '', $id);
595          return $wdc->addAddressbookEntry($connectionId, $contactdata);
596      }
597      else
598      {
599          $sqlite = $this->getDB();
600          if(!$sqlite)
601            return false;
602          $addressbookid = $this->getAddressbookIdForPage($id);
603          $uri = uniqid('dokuwiki-').'.vcf';
604          $now = new \DateTime();
605          $query = "INSERT INTO addressbookobjects (contactdata, uri, addressbookid, lastmodified, etag, size, formattedname, structuredname) VALUES (?, ?, ?, ?, ?, ?, ?, ?)";
606          $res = $sqlite->query($query,
607                                      $contactdata,
608                                      $uri,
609                                      $addressbookid,
610                                      $now->getTimestamp(),
611                                      md5($contactdata),
612                                      strlen($contactdata),
613                                      $formattedname,
614                                      implode(';', $structuredname)
615                                      );
616          // If successfully, update the sync token database
617          if($res !== false)
618          {
619              $this->updateSyncTokenLog($addressbookid, $uri, 'added');
620              return true;
621          }
622      }
623      return false;
624  }
625
626  /**
627   * Parse a VCard and extract important contact information
628   *
629   * @param string $card The VCard data
630   * @param string $uri The object URI
631   * @param boolean $write Writable
632   *
633   * @return array An array with parsed data
634   */
635  public function parseVcard($card, $uri, $write)
636  {
637    require_once(DOKU_PLUGIN.'davcard/vendor/autoload.php');
638
639    $vObject = \Sabre\VObject\Reader::read($card);
640
641    $formattedname = '';
642    $structuredname = '';
643    $tel = array();
644    $addr = array();
645    $mail = array();
646    $photo = array();
647    $birthday = '';
648    $note = '';
649    $title = '';
650    $url = '';
651
652    if(isset($vObject->FN))
653      $formattedname = (string)$vObject->FN;
654
655    if(isset($vObject->N))
656      $structuredname = join(';', $vObject->N->getParts());
657
658    if(isset($vObject->TEL))
659    {
660        foreach($vObject->TEL as $number)
661        {
662            if(isset($number['TYPE']))
663                $tel[] = array('type' => strtolower((string)$number['TYPE']), 'number' => (string)$number);
664            else
665                $tel[] = array('number' => (string)$number);
666        }
667    }
668
669    if(isset($vObject->ADR))
670    {
671        foreach($vObject->ADR as $adr)
672        {
673            if(isset($adr['TYPE']))
674                $addr[] = array('type' => strtolower((string)$adr['TYPE']), 'address' => $adr->getParts());
675            else
676                $addr[] = array('address' => $adr->getParts());
677        }
678    }
679
680    if(isset($vObject->EMAIL))
681    {
682        foreach($vObject->EMAIL as $email)
683        {
684            if(isset($email['TYPE']))
685                $mail[] = array('type' => strtolower((string)$email['TYPE']), 'mail' => (string)$email);
686            else
687                $mail[] = array('mail' => (string)$email);
688        }
689    }
690
691    if(isset($vObject->PHOTO))
692    {
693        if(isset($vObject->PHOTO['TYPE']))
694        {
695            $photo[] = array('type' => strtolower((string)$vObject->PHOTO['TYPE']), 'photo' => (string)$vObject->PHOTO);
696        }
697        else
698            $photo[] = array('photo' => (string)$vObject->PHOTO);
699    }
700
701    if(isset($vObject->BDAY))
702    {
703        $birthday = (string)$vObject->BDAY;
704        $birthday = str_replace('-', '', $birthday);
705    }
706
707    if(isset($vObject->NOTE))
708    {
709        $note = (string)$vObject->NOTE;
710    }
711
712    if(isset($vObject->TITLE))
713    {
714        $title = (string)$vObject->TITLE;
715    }
716
717    if(isset($vObject->URL))
718    {
719        $url = (string)$vObject->URL;
720    }
721
722    return array(
723        'formattedname' => $formattedname,
724        'structuredname' => $structuredname,
725        'tel' => $tel,
726        'mail' => $mail,
727        'addr' => $addr,
728        'uri' => $uri,
729        'photo' => $photo,
730        'birthday' => $birthday,
731        'note' => $note,
732        'title' => $title,
733        'url' => $url,
734        'result' => true,
735        'write' => $write
736    );
737  }
738
739  /**
740   * Retrieve the settings of a given address book
741   *
742   * @param int $addressbookid The addressbook's ID
743   *
744   * @return array The settings
745   */
746  public function getAddressbookSettings($addressbookid)
747  {
748      $sqlite = $this->getDB();
749      if(!$sqlite)
750        return false;
751      $query = "SELECT id, principaluri, displayname, uri, description, synctoken FROM addressbooks WHERE id= ? ";
752      $res = $sqlite->query($query, $addressbookid);
753      $row = $sqlite->res2row($res);
754      return $row;
755  }
756
757  /**
758   * Retrieve the current synctoken for an address book
759   *
760   * @param int $addressbookid The addressbook's ID
761   *
762   * @return string The current synctoken
763   */
764  public function getSyncTokenForAddressbook($addressbookid)
765  {
766      $row = $this->getAddressbookSettings($addressbookid);
767      if(isset($row['synctoken']))
768          return $row['synctoken'];
769      return false;
770  }
771
772  /**
773   * Helper function to convert the operation name to
774   * an operation code as stored in the database
775   *
776   * @param string $operationName The operation name
777   *
778   * @return mixed The operation code or false
779   */
780  public function operationNameToOperation($operationName)
781  {
782      switch($operationName)
783      {
784          case 'added':
785              return 1;
786          break;
787          case 'modified':
788              return 2;
789          break;
790          case 'deleted':
791              return 3;
792          break;
793      }
794      return false;
795  }
796
797  /**
798   * Update the synctoken log for a given address book
799   *
800   * @param string $addressbookid The addressbook ID to work with
801   * @param string $uri The object URI that was modified
802   * @param string $operation The operation that was performed
803   *
804   * @return boolean True on success, otherwise false
805   */
806  private function updateSyncTokenLog($addressbookid, $uri, $operation)
807  {
808      $currentToken = $this->getSyncTokenForAddressbook($addressbookid);
809      $operationCode = $this->operationNameToOperation($operation);
810      if(($operationCode === false) || ($currentToken === false))
811          return false;
812      $values = array($uri,
813                      $currentToken,
814                      $addressbookid,
815                      $operationCode
816      );
817      $sqlite = $this->getDB();
818      if(!$sqlite)
819        return false;
820      $query = "INSERT INTO addressbookchanges (uri, synctoken, addressbookid, operation) VALUES(?, ?, ?, ?)";
821      $res = $sqlite->query($query, $uri, $currentToken, $addressbookid, $operationCode);
822      if($res === false)
823        return false;
824      $currentToken++;
825      $query = "UPDATE addressbooks SET synctoken = ? WHERE id = ?";
826      $res = $sqlite->query($query, $currentToken, $addressbookid);
827      return ($res !== false);
828  }
829
830    /**
831   * Check the permission of a user for a given addressbook ID
832   *
833   * @param string $id The addressbook ID to check
834   * @return int AUTH_* constants
835   */
836  public function checkAddressbookPermission($id)
837  {
838      if(strpos($id, 'webdav://') === 0)
839      {
840          $wdc =& plugin_load('helper', 'webdavclient');
841          if(is_null($wdc))
842            return AUTH_NONE;
843          $connectionId = str_replace('webdav://', '', $id);
844          $settings = $wdc->getConnection($connectionId);
845          if($settings === false)
846            return AUTH_NONE;
847          if($settings['write'] === '1')
848            return AUTH_CREATE;
849          return AUTH_READ;
850      }
851      else
852      {
853          $addr = $this->getAddressbookIdForPage($id);
854          // We return AUTH_READ if the calendar does not exist. This makes
855          // davcard happy when there are just included addressbooks
856          if($addr === false)
857            return AUTH_READ;
858          return auth_quickaclcheck($id);
859      }
860  }
861
862}
863