<?php
namespace Zotlabs\Module;

require_once('include/event.php');

require_once('include/auth.php');
require_once('include/security.php');

class Cdav extends \Zotlabs\Web\Controller {

	function init() {

		$record = null;
		$channel_login = false;

		if((argv(1) !== 'calendar') && (argv(1) !== 'addressbook')) {

			foreach([ 'REDIRECT_REMOTE_USER', 'HTTP_AUTHORIZATION' ] as $head) {

				/* Basic authentication */

				if(array_key_exists($head,$_SERVER) && substr(trim($_SERVER[$head]),0,5) === 'Basic') {
					$userpass = @base64_decode(substr(trim($_SERVER[$head]),6)) ;
					if(strlen($userpass)) {
						list($name, $password) = explode(':', $userpass);
						$_SERVER['PHP_AUTH_USER'] = $name;
						$_SERVER['PHP_AUTH_PW']   = $password;
					}
					break;
				}

				/* Signature authentication */

				if(array_key_exists($head,$_SERVER) && substr(trim($_SERVER[$head]),0,9) === 'Signature') {
					if($head !== 'HTTP_AUTHORIZATION') {
						$_SERVER['HTTP_AUTHORIZATION'] = $_SERVER[$head];
						continue;
					}

					$sigblock = \Zotlabs\Web\HTTPSig::parse_sigheader($_SERVER[$head]);
					if($sigblock) {
						$keyId = str_replace('acct:','',$sigblock['keyId']);
						if($keyId) {
							$r = q("select * from hubloc where hubloc_addr = '%s' limit 1",
								dbesc($keyId)
							);
							if($r) {
								$c = channelx_by_hash($r[0]['hubloc_hash']);
								if($c) {
									$a = q("select * from account where account_id = %d limit 1",
										intval($c['channel_account_id'])
									);
									if($a) {
										$record = [ 'channel' => $c, 'account' => $a[0] ];
										$channel_login = $c['channel_id'];
									}
								}
							}
							if(! $record)
								continue;

							if($record) {
								$verified = \Zotlabs\Web\HTTPSig::verify('',$record['channel']['channel_pubkey']);
								if(! ($verified && $verified['header_signed'] && $verified['header_valid'])) {
									$record = null;
								}
								if($record['account']) {
							        authenticate_success($record['account']);
							        if($channel_login) {
							            change_channel($channel_login);
									}
								}
								break;
							}
						}
					}
				}
			}


			/**
			 * This server combines both CardDAV and CalDAV functionality into a single
			 * server. It is assumed that the server runs at the root of a HTTP domain (be
			 * that a domainname-based vhost or a specific TCP port.
			 *
			 * This example also assumes that you're using SQLite and the database has
			 * already been setup (along with the database tables).
			 *
			 * You may choose to use MySQL instead, just change the PDO connection
			 * statement.
			 */

			/**
			 * UTC or GMT is easy to work with, and usually recommended for any
			 * application.
			 */
			date_default_timezone_set('UTC');

			/**
			 * Make sure this setting is turned on and reflect the root url for your WebDAV
			 * server.
			 *
			 * This can be for example the root / or a complete path to your server script.
			 */

			$baseUri = '/cdav/';

			/**
			 * Database
			 *
			 */

			$pdo = \DBA::$dba->db;

			// Autoloader
			require_once 'vendor/autoload.php';

			/**
			 * The backends. Yes we do really need all of them.
			 *
			 * This allows any developer to subclass just any of them and hook into their
			 * own backend systems.
			 */

			$auth = new \Zotlabs\Storage\BasicAuth();
			$auth->setRealm(ucfirst(\Zotlabs\Lib\System::get_platform_name()) . 'CalDAV/CardDAV');

			if (local_channel()) {
				logger('loggedin');
				$channel = \App::get_channel();
				$auth->setCurrentUser($channel['channel_address']);
				$auth->channel_id = $channel['channel_id'];
				$auth->channel_hash = $channel['channel_hash'];
				$auth->channel_account_id = $channel['channel_account_id'];
				if($channel['channel_timezone'])
					$auth->setTimezone($channel['channel_timezone']);
				$auth->observer = $channel['channel_hash'];

				$principalUri = 'principals/' . $channel['channel_address'];
				if(!cdav_principal($principalUri)) {
					$this->activate($pdo, $channel);
					if(!cdav_principal($principalUri)) {
						return;
					}
				}

			}


			$principalBackend = new \Sabre\DAVACL\PrincipalBackend\PDO($pdo);
			$carddavBackend   = new \Sabre\CardDAV\Backend\PDO($pdo);
			$caldavBackend    = new \Sabre\CalDAV\Backend\PDO($pdo);

			/**
			 * The directory tree
			 *
			 * Basically this is an array which contains the 'top-level' directories in the
			 * WebDAV server.
			 */

			$nodes = [
				// /principals
				new \Sabre\CalDAV\Principal\Collection($principalBackend),
				// /calendars
				new \Sabre\CalDAV\CalendarRoot($principalBackend, $caldavBackend),
				// /addressbook
				new \Sabre\CardDAV\AddressBookRoot($principalBackend, $carddavBackend),
			];

			// The object tree needs in turn to be passed to the server class

			$server = new \Sabre\DAV\Server($nodes);

			if(isset($baseUri))
				$server->setBaseUri($baseUri);

			// Plugins
			$server->addPlugin(new \Sabre\DAV\Auth\Plugin($auth));
			//$server->addPlugin(new \Sabre\DAV\Browser\Plugin());
			$server->addPlugin(new \Sabre\DAV\Sync\Plugin());
			$server->addPlugin(new \Sabre\DAV\Sharing\Plugin());
			$server->addPlugin(new \Sabre\DAVACL\Plugin());

			// CalDAV plugins
			$server->addPlugin(new \Sabre\CalDAV\Plugin());
			$server->addPlugin(new \Sabre\CalDAV\SharingPlugin());
			//$server->addPlugin(new \Sabre\CalDAV\Schedule\Plugin());
			$server->addPlugin(new \Sabre\CalDAV\ICSExportPlugin());

			// CardDAV plugins
			$server->addPlugin(new \Sabre\CardDAV\Plugin());
			$server->addPlugin(new \Sabre\CardDAV\VCFExportPlugin());

			// And off we go!
			$server->exec();

			killme();

		}

	}

	function post() {
		if(! local_channel())
			return;

		$channel = \App::get_channel();
		$principalUri = 'principals/' . $channel['channel_address'];

		if(!cdav_principal($principalUri))
			return;

		$pdo = \DBA::$dba->db;

		require_once 'vendor/autoload.php';

		if(argc() == 2 && argv(1) === 'calendar') {

			$caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo);
			$calendars = $caldavBackend->getCalendarsForUser($principalUri);

			//create new calendar
			if($_REQUEST['{DAV:}displayname'] && $_REQUEST['create']) {
				do {
					$duplicate = false;
					$calendarUri = random_string(40);

					$r = q("SELECT uri FROM calendarinstances WHERE principaluri = '%s' AND uri = '%s' LIMIT 1",
						dbesc($principalUri),
						dbesc($calendarUri)
					);

					if (count($r))
						$duplicate = true;
				} while ($duplicate == true);

				$properties = [
					'{DAV:}displayname' => $_REQUEST['{DAV:}displayname'],
					'{http://apple.com/ns/ical/}calendar-color' => $_REQUEST['color'],
					'{urn:ietf:params:xml:ns:caldav}calendar-description' => $channel['channel_name']
				];

				$id = $caldavBackend->createCalendar($principalUri, $calendarUri, $properties);

				// set new calendar to be visible
				set_pconfig(local_channel(), 'cdav_calendar' , $id[0], 1);
			}

			//create new calendar object via ajax request
			if($_REQUEST['submit'] === 'create_event' && $_REQUEST['title'] && $_REQUEST['target'] && $_REQUEST['dtstart']) {

				$id = explode(':', $_REQUEST['target']);

				if(!cdav_perms($id[0],$calendars,true))
					return;

				$title = $_REQUEST['title'];
				$dtstart = new \DateTime($_REQUEST['dtstart']);
				if($_REQUEST['dtend'])
					$dtend = new \DateTime($_REQUEST['dtend']);
				$description = $_REQUEST['description'];
				$location = $_REQUEST['location'];

				do {
					$duplicate = false;
					$objectUri = random_string(40) . '.ics';

					$r = q("SELECT uri FROM calendarobjects WHERE calendarid = %s AND uri = '%s' LIMIT 1",
						intval($id[0]),
						dbesc($objectUri)
					);

					if (count($r))
						$duplicate = true;
				} while ($duplicate == true);


				$vcalendar = new \Sabre\VObject\Component\VCalendar([
				    'VEVENT' => [
					'SUMMARY' => $title,
					'DTSTART' => $dtstart
				    ]
				]);
				if($dtend)
					$vcalendar->VEVENT->add('DTEND', $dtend);
				if($description)
					$vcalendar->VEVENT->add('DESCRIPTION', $description);
				if($location)
					$vcalendar->VEVENT->add('LOCATION', $location);

				$calendarData = $vcalendar->serialize();

				$caldavBackend->createCalendarObject($id, $objectUri, $calendarData);

				killme();
			}

			//edit calendar name and color
			if($_REQUEST['{DAV:}displayname'] && $_REQUEST['edit'] && $_REQUEST['id']) {

				$id = explode(':', $_REQUEST['id']);

				if(! cdav_perms($id[0],$calendars))
					return;

				$mutations = [
					'{DAV:}displayname' => $_REQUEST['{DAV:}displayname'],
					'{http://apple.com/ns/ical/}calendar-color' => $_REQUEST['color']
				];

				$patch = new \Sabre\DAV\PropPatch($mutations);

				$caldavBackend->updateCalendar($id, $patch);

				$patch->commit();

			}

			//edit calendar object via ajax request
			if($_REQUEST['submit'] === 'update_event' && $_REQUEST['uri'] && $_REQUEST['title'] && $_REQUEST['target'] && $_REQUEST['dtstart']) {

				$id = explode(':', $_REQUEST['target']);

				if(!cdav_perms($id[0],$calendars,true))
					return;

				$uri = $_REQUEST['uri'];
				$title = $_REQUEST['title'];
				$dtstart = new \DateTime($_REQUEST['dtstart']);
				$dtend = $_REQUEST['dtend'] ? new \DateTime($_REQUEST['dtend']) : '';
				$description = $_REQUEST['description'];
				$location = $_REQUEST['location'];

				$object = $caldavBackend->getCalendarObject($id, $uri);

				$vcalendar = \Sabre\VObject\Reader::read($object['calendardata']);

				if($title)
					$vcalendar->VEVENT->SUMMARY = $title;
				if($dtstart)
					$vcalendar->VEVENT->DTSTART = $dtstart;
				if($dtend)
					$vcalendar->VEVENT->DTEND = $dtend;
				else
					unset($vcalendar->VEVENT->DTEND);
				if($description)
					$vcalendar->VEVENT->DESCRIPTION = $description;
				if($location)
					$vcalendar->VEVENT->LOCATION = $location;

				$calendarData = $vcalendar->serialize();

				$caldavBackend->updateCalendarObject($id, $uri, $calendarData);

				killme();
			}

			//delete calendar object via ajax request
			if($_REQUEST['delete'] && $_REQUEST['uri'] && $_REQUEST['target']) {

				$id = explode(':', $_REQUEST['target']);

				if(!cdav_perms($id[0],$calendars,true))
					return;

				$uri = $_REQUEST['uri'];

				$caldavBackend->deleteCalendarObject($id, $uri);

				killme();
			}

			//edit calendar object date/timeme via ajax request (drag and drop)
			if($_REQUEST['update'] && $_REQUEST['id'] && $_REQUEST['uri']) {

				$id = [$_REQUEST['id'][0], $_REQUEST['id'][1]];

				if(!cdav_perms($id[0],$calendars,true))
					return;

				$uri = $_REQUEST['uri'];
				$dtstart = new \DateTime($_REQUEST['dtstart']);
				$dtend = $_REQUEST['dtend'] ? new \DateTime($_REQUEST['dtend']) : '';

				$object = $caldavBackend->getCalendarObject($id, $uri);

				$vcalendar = \Sabre\VObject\Reader::read($object['calendardata']);

				if($dtstart) {
					$vcalendar->VEVENT->DTSTART = $dtstart;
				}
				if($dtend) {
					$vcalendar->VEVENT->DTEND = $dtend;
				}
				else {
					unset($vcalendar->VEVENT->DTEND);
				}

				$calendarData = $vcalendar->serialize();

				$caldavBackend->updateCalendarObject($id, $uri, $calendarData);

				killme();
			}

			//share a calendar - this only works on local system (with channels on the same server)
			if($_REQUEST['sharee'] && $_REQUEST['share']) {

				$id = [intval($_REQUEST['calendarid']), intval($_REQUEST['instanceid'])];

				if(! cdav_perms($id[0],$calendars))
					return;

				$hash = $_REQUEST['sharee'];

				$sharee_arr = channelx_by_hash($hash);

				$sharee = new \Sabre\DAV\Xml\Element\Sharee();

				$sharee->href = 'mailto:' . $sharee_arr['xchan_addr'];
				$sharee->principal = 'principals/' . $sharee_arr['channel_address'];
				$sharee->access = intval($_REQUEST['access']);
				$sharee->properties = ['{DAV:}displayname' => $channel['channel_name']];

				$caldavBackend->updateInvites($id, [$sharee]);
			}
		}

		if(argc() >= 2 && argv(1) === 'addressbook') {

			$carddavBackend = new \Sabre\CardDAV\Backend\PDO($pdo);
			$addressbooks = $carddavBackend->getAddressBooksForUser($principalUri);

			//create new addressbook
			if($_REQUEST['{DAV:}displayname'] && $_REQUEST['create']) {
				do {
					$duplicate = false;
					$addressbookUri = random_string(20);

					$r = q("SELECT uri FROM addressbooks WHERE principaluri = '%s' AND uri = '%s' LIMIT 1",
						dbesc($principalUri),
						dbesc($addressbookUri)
					);

					if (count($r))
						$duplicate = true;
				} while ($duplicate == true);

				$properties = ['{DAV:}displayname' => $_REQUEST['{DAV:}displayname']];

				$carddavBackend->createAddressBook($principalUri, $addressbookUri, $properties);
			}

			//edit addressbook
			if($_REQUEST['{DAV:}displayname'] && $_REQUEST['edit'] && intval($_REQUEST['id'])) {

				$id = $_REQUEST['id'];

				if(! cdav_perms($id,$addressbooks))
					return;

				$mutations = [
					'{DAV:}displayname' => $_REQUEST['{DAV:}displayname']
				];

				$patch = new \Sabre\DAV\PropPatch($mutations);

				$carddavBackend->updateAddressBook($id, $patch);

				$patch->commit();
			}

			//create addressbook card
			if($_REQUEST['create'] && $_REQUEST['target'] && $_REQUEST['fn']) {
				$id = $_REQUEST['target'];

				do {
					$duplicate = false;
					$uri = random_string(40) . '.vcf';

					$r = q("SELECT uri FROM cards WHERE addressbookid = %s AND uri = '%s' LIMIT 1",
						intval($id),
						dbesc($uri)
					);

					if (count($r))
						$duplicate = true;
				} while ($duplicate == true);

				//TODO: this mostly duplictes the procedure in update addressbook card. should move this part to a function to avoid duplication
				$fn = $_REQUEST['fn'];

				$vcard = new \Sabre\VObject\Component\VCard([
					'FN' => $fn,
					'N' => array_reverse(explode(' ', $fn))
				]);

				$org = $_REQUEST['org'];
				if($org) {
					$vcard->ORG = $org;
				}

				$title = $_REQUEST['title'];
				if($title) {
					$vcard->TITLE = $title;
				}

				$tel = $_REQUEST['tel'];
				$tel_type = $_REQUEST['tel_type'];
				if($tel) {
					$i = 0;
					foreach($tel as $item) {
						if($item) {
							$vcard->add('TEL', $item, ['type' => $tel_type[$i]]);
						}
						$i++;
					}
				}

				$email = $_REQUEST['email'];
				$email_type = $_REQUEST['email_type'];
				if($email) {
					$i = 0;
					foreach($email as $item) {
						if($item) {
							$vcard->add('EMAIL', $item, ['type' => $email_type[$i]]);
						}
						$i++;
					}
				}

				$impp = $_REQUEST['impp'];
				$impp_type = $_REQUEST['impp_type'];
				if($impp) {
					$i = 0;
					foreach($impp as $item) {
						if($item) {
							$vcard->add('IMPP', $item, ['type' => $impp_type[$i]]);
						}
						$i++;
					}
				}

				$url = $_REQUEST['url'];
				$url_type = $_REQUEST['url_type'];
				if($url) {
					$i = 0;
					foreach($url as $item) {
						if($item) {
							$vcard->add('URL', $item, ['type' => $url_type[$i]]);
						}
						$i++;
					}
				}

				$adr = $_REQUEST['adr'];
				$adr_type = $_REQUEST['adr_type'];

				if($adr) {
					$i = 0;
					foreach($adr as $item) {
						if($item) {
							$vcard->add('ADR', $item, ['type' => $adr_type[$i]]);
						}
						$i++;
					}
				}

				$note = $_REQUEST['note'];
				if($note) {
					$vcard->NOTE = $note;
				}

				$cardData = $vcard->serialize();

				$carddavBackend->createCard($id, $uri, $cardData);

			}

			//edit addressbook card
			if($_REQUEST['update'] && $_REQUEST['uri'] && $_REQUEST['target']) {

				$id = $_REQUEST['target'];

				if(!cdav_perms($id,$addressbooks))
					return;

				$uri = $_REQUEST['uri'];

				$object = $carddavBackend->getCard($id, $uri);
				$vcard = \Sabre\VObject\Reader::read($object['carddata']);

				$fn = $_REQUEST['fn'];
				if($fn) {
					$vcard->FN = $fn;
					$vcard->N = array_reverse(explode(' ', $fn));
				}

				$org = $_REQUEST['org'];
				if($org) {
					$vcard->ORG = $org;
				}
				else {
					unset($vcard->ORG);
				}

				$title = $_REQUEST['title'];
				if($title) {
					$vcard->TITLE = $title;
				}
				else {
					unset($vcard->TITLE);
				}

				$tel = $_REQUEST['tel'];
				$tel_type = $_REQUEST['tel_type'];
				if($tel) {
					$i = 0;
					unset($vcard->TEL);
					foreach($tel as $item) {
						if($item) {
							$vcard->add('TEL', $item, ['type' => $tel_type[$i]]);
						}
						$i++;
					}
				}
				else {
					unset($vcard->TEL);
				}

				$email = $_REQUEST['email'];
				$email_type = $_REQUEST['email_type'];
				if($email) {
					$i = 0;
					unset($vcard->EMAIL);
					foreach($email as $item) {
						if($item) {
							$vcard->add('EMAIL', $item, ['type' => $email_type[$i]]);
						}
						$i++;
					}
				}
				else {
					unset($vcard->EMAIL);
				}

				$impp = $_REQUEST['impp'];
				$impp_type = $_REQUEST['impp_type'];
				if($impp) {
					$i = 0;
					unset($vcard->IMPP);
					foreach($impp as $item) {
						if($item) {
							$vcard->add('IMPP', $item, ['type' => $impp_type[$i]]);
						}
						$i++;
					}
				}
				else {
					unset($vcard->IMPP);
				}

				$url = $_REQUEST['url'];
				$url_type = $_REQUEST['url_type'];
				if($url) {
					$i = 0;
					unset($vcard->URL);
					foreach($url as $item) {
						if($item) {
							$vcard->add('URL', $item, ['type' => $url_type[$i]]);
						}
						$i++;
					}
				}
				else {
					unset($vcard->URL);
				}

				$adr = $_REQUEST['adr'];
				$adr_type = $_REQUEST['adr_type'];
				if($adr) {
					$i = 0;
					unset($vcard->ADR);
					foreach($adr as $item) {
						if($item) {
							$vcard->add('ADR', $item, ['type' => $adr_type[$i]]);
						}
						$i++;
					}
				}
				else {
					unset($vcard->ADR);
				}

				$note = $_REQUEST['note'];
				if($note) {
					$vcard->NOTE = $note;
				}
				else {
					unset($vcard->NOTE);
				}

				$cardData = $vcard->serialize();

				$carddavBackend->updateCard($id, $uri, $cardData);
			}

			//delete addressbook card
			if($_REQUEST['delete'] && $_REQUEST['uri'] && $_REQUEST['target']) {

				$id = $_REQUEST['target'];

				if(!cdav_perms($id,$addressbooks))
					return;

				$uri = $_REQUEST['uri'];

				$carddavBackend->deleteCard($id, $uri);
			}
		}

		//Import calendar or addressbook
		if(($_FILES) && array_key_exists('userfile',$_FILES) && intval($_FILES['userfile']['size']) && $_REQUEST['target']) {

			$src = @file_get_contents($_FILES['userfile']['tmp_name']);

			if($src) {

				if($_REQUEST['c_upload']) {
					$id = explode(':', $_REQUEST['target']);
					$ext = 'ics';
					$table = 'calendarobjects';
					$column = 'calendarid';
					$objects = new \Sabre\VObject\Splitter\ICalendar($src);
					$profile = \Sabre\VObject\Node::PROFILE_CALDAV;
					$backend = new \Sabre\CalDAV\Backend\PDO($pdo);
				}

				if($_REQUEST['a_upload']) {
					$id[] = intval($_REQUEST['target']);
					$ext = 'vcf';
					$table = 'cards';
					$column = 'addressbookid';
					$objects = new \Sabre\VObject\Splitter\VCard($src);
					$profile = \Sabre\VObject\Node::PROFILE_CARDDAV;
					$backend = new \Sabre\CardDAV\Backend\PDO($pdo);
				}

				while ($object = $objects->getNext()) {

					if($_REQUEST['a_upload']) {
						$object = $object->convert(\Sabre\VObject\Document::VCARD40);
					}

					$ret = $object->validate($profile & \Sabre\VObject\Node::REPAIR);

					//level 3 Means that the document is invalid,
					//level 2 means a warning. A warning means it's valid but it could cause interopability issues,
					//level 1 means that there was a problem earlier, but the problem was automatically repaired.

					if($ret[0]['level'] < 3) {
						do {
							$duplicate = false;
							$objectUri = random_string(40) . '.' . $ext;

							$r = q("SELECT uri FROM $table WHERE $column = %d AND uri = '%s' LIMIT 1",
								dbesc($id[0]),
								dbesc($objectUri)
							);

							if (count($r))
								$duplicate = true;
						} while ($duplicate == true);

						if($_REQUEST['c_upload']) {
							$backend->createCalendarObject($id, $objectUri, $object->serialize());
						}

						if($_REQUEST['a_upload']) {
							$backend->createCard($id[0], $objectUri, $object->serialize());
						}
					}
					else {
						if($_REQUEST['c_upload']) {
							notice( '<strong>' . t('INVALID EVENT DISMISSED!') . '</strong>' . EOL .
								'<strong>' . t('Summary: ') . '</strong>' . (($object->VEVENT->SUMMARY) ? $object->VEVENT->SUMMARY : t('Unknown')) . EOL .
								'<strong>' . t('Date: ') . '</strong>' . (($object->VEVENT->DTSTART) ? $object->VEVENT->DTSTART : t('Unknown')) . EOL .
								'<strong>' . t('Reason: ') . '</strong>' . $ret[0]['message'] . EOL
							);
						}

						if($_REQUEST['a_upload']) {
							notice( '<strong>' . t('INVALID CARD DISMISSED!') . '</strong>' . EOL .
								'<strong>' . t('Name: ') . '</strong>' . (($object->FN) ? $object->FN : t('Unknown')) . EOL .
								'<strong>' . t('Reason: ') . '</strong>' . $ret[0]['message'] . EOL
							);
						}
					}
				}
			}
			@unlink($src);
		}
	}

	function get() {

		if(!local_channel())
			return;

		$channel = \App::get_channel();
		$principalUri = 'principals/' . $channel['channel_address'];

		$pdo = \DBA::$dba->db;

		require_once 'vendor/autoload.php';

		head_add_css('cdav.css');

		if(!cdav_principal($principalUri)) {
			$this->activate($pdo, $channel);
			if(!cdav_principal($principalUri)) {
				return;
			}
		}

		if(argv(1) === 'calendar') {
			nav_set_selected('CalDAV');
			$caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo);
			$calendars = $caldavBackend->getCalendarsForUser($principalUri);
		}

		//Display calendar(s) here
		if(argc() == 2 && argv(1) === 'calendar') {

			head_add_css('/library/fullcalendar/fullcalendar.css');
			head_add_css('cdav_calendar.css');

			head_add_js('/library/moment/moment.min.js', 1);
			head_add_js('/library/fullcalendar/fullcalendar.min.js', 1);
			head_add_js('/library/fullcalendar/locale-all.js', 1);

			foreach($calendars as $calendar) {
				$editable = (($calendar['share-access'] == 2) ? 'false' : 'true');  // false/true must be string since we're passing it to javascript
				$color = (($calendar['{http://apple.com/ns/ical/}calendar-color']) ? $calendar['{http://apple.com/ns/ical/}calendar-color'] : '#3a87ad');
				$sharer = (($calendar['share-access'] == 3) ? $calendar['{urn:ietf:params:xml:ns:caldav}calendar-description'] : '');
				$switch = get_pconfig(local_channel(), 'cdav_calendar', $calendar['id'][0]);
				if($switch) {
					$sources .= '{
						url: \'/cdav/calendar/json/' . $calendar['id'][0] . '/' . $calendar['id'][1] . '\',
						color: \'' . $color . '\'
					 }, ';
				}

				if($calendar['share-access'] != 2) {
					$writable_calendars[] = [
						'displayname' => $calendar['{DAV:}displayname'],
						'sharer' => $sharer,
						'id' => $calendar['id']
					];
				}
			}

			$sources = rtrim($sources, ', ');

			$first_day = get_pconfig(local_channel(),'system','cal_first_day');
			$first_day = (($first_day) ? $first_day : 0);

			$title = ['title', t('Event title')];
			$dtstart = ['dtstart', t('Start date and time'), '', t('Example: YYYY-MM-DD HH:mm')];
			$dtend = ['dtend', t('End date and time'), '', t('Example: YYYY-MM-DD HH:mm')];
			$description = ['description', t('Description')];
			$location = ['location', t('Location')];

			$o .= replace_macros(get_markup_template('cdav_calendar.tpl'), [
				'$sources' => $sources,
				'$color' => $color,
				'$lang' => \App::$language,
				'$first_day' => $first_day,
				'$prev'	=> t('Previous'),
				'$next'	=> t('Next'),
				'$today' => t('Today'),
				'$month' => t('Month'),
				'$week' => t('Week'),
				'$day' => t('Day'),
				'$list_month' => t('List month'),
				'$list_week' => t('List week'),
				'$list_day' => t('List day'),
				'$title' => $title,
				'$writable_calendars' => $writable_calendars,
				'$dtstart' => $dtstart,
				'$dtend' => $dtend,
				'$description' => $description,
				'$location' => $location,
				'$more' => t('More'),
				'$less' => t('Less'),
				'$calendar_select_label' => t('Select calendar'),
				'$delete' => t('Delete'),
				'$delete_all' => t('Delete all'),
				'$cancel' => t('Cancel'),
				'$recurrence_warning' => t('Sorry! Editing of recurrent events is not yet implemented.')
			]);

			return $o;

		}

		//Provide json data for calendar
		if(argc() == 5 && argv(1) === 'calendar' && argv(2) === 'json'  && intval(argv(3)) && intval(argv(4))) {

			$id = [argv(3), argv(4)];

			if(! cdav_perms($id[0],$calendars))
				killme();

			if (x($_GET,'start'))
				$start = new \DateTime($_GET['start']);
			if (x($_GET,'end'))
				$end = new \DateTime($_GET['end']);

			$filters['name'] = 'VCALENDAR';
			$filters['prop-filters'][0]['name'] = 'VEVENT';
			$filters['comp-filters'][0]['name'] = 'VEVENT';
			$filters['comp-filters'][0]['time-range']['start'] = $start;
			$filters['comp-filters'][0]['time-range']['end'] = $end;

			$uris = $caldavBackend->calendarQuery($id, $filters);
			if($uris) {

				$objects = $caldavBackend->getMultipleCalendarObjects($id, $uris);

				foreach($objects as $object) {

					$vcalendar = \Sabre\VObject\Reader::read($object['calendardata']);

					if(isset($vcalendar->VEVENT->RRULE))
						$vcalendar = $vcalendar->expand($start, $end);

					foreach($vcalendar->VEVENT as $vevent) {
						$title = (string)$vevent->SUMMARY;
						$dtstart = (string)$vevent->DTSTART;
						$dtend = (string)$vevent->DTEND;
						$description = (string)$vevent->DESCRIPTION;
						$location = (string)$vevent->LOCATION;

						$rw = ((cdav_perms($id[0],$calendars,true)) ? true : false);
						$recurrent = ((isset($vevent->{'RECURRENCE-ID'})) ? true : false);

						$editable = $rw ? true : false;

						if($recurrent)
							$editable = false;

						$allDay = false;

						// allDay event rules
						if(!strpos($dtstart, 'T') && !strpos($dtend, 'T'))
							$allDay = true;
						if(strpos($dtstart, 'T000000') && strpos($dtend, 'T000000'))
							$allDay = true;

						$events[] = [
							'calendar_id' => $id,
							'uri' => $object['uri'],
							'title' => $title,
							'start' => $dtstart,
							'end' => $dtend,
							'description' => $description,
							'location' => $location,
							'allDay' => $allDay,
							'editable' => $editable,
							'recurrent' => $recurrent,
							'rw' => $rw
						];
					}
				}
				json_return_and_die($events);
			}
			else {
				killme();
			}
		}

		//enable/disable calendars
		if(argc() == 5 && argv(1) === 'calendar' && argv(2) === 'switch'  && intval(argv(3)) && (argv(4) == 1 || argv(4) == 0)) {
			$id = argv(3);

			if(! cdav_perms($id,$calendars))
				killme();

			set_pconfig(local_channel(), 'cdav_calendar' , argv(3), argv(4));
			killme();
		}

		//drop calendar
		if(argc() == 5 && argv(1) === 'calendar' && argv(2) === 'drop' && intval(argv(3)) && intval(argv(4))) {
			$id = [argv(3), argv(4)];

			if(! cdav_perms($id[0],$calendars))
				killme();

			$caldavBackend->deleteCalendar($id);
			killme();
		}

		//drop sharee
		if(argc() == 6 && argv(1) === 'calendar' && argv(2) === 'dropsharee'  && intval(argv(3)) && intval(argv(4))) {

			$id = [argv(3), argv(4)];
			$hash = argv(5);

			if(! cdav_perms($id[0],$calendars))
				killme();

			$sharee_arr = channelx_by_hash($hash);

			$sharee = new \Sabre\DAV\Xml\Element\Sharee();

			$sharee->href = 'mailto:' . $sharee_arr['xchan_addr'];
			$sharee->principal = 'principals/' . $sharee_arr['channel_address'];
			$sharee->access = 4;
			$caldavBackend->updateInvites($id, [$sharee]);

			killme();
		}


		if(argv(1) === 'addressbook') {
			nav_set_selected('CardDAV');
			$carddavBackend = new \Sabre\CardDAV\Backend\PDO($pdo);
			$addressbooks = $carddavBackend->getAddressBooksForUser($principalUri);
		}

		//Display Adressbook here
		if(argc() == 3 && argv(1) === 'addressbook' && intval(argv(2))) {

			$id = argv(2);

			$displayname = cdav_perms($id,$addressbooks);

			if(!$displayname)
				return;

			head_add_css('cdav_addressbook.css');

			$o = '';

			$sabrecards = $carddavBackend->getCards($id);
			foreach($sabrecards as $sabrecard) {
				$uris[] = $sabrecard['uri'];
			}

			if($uris) {
				$objects = $carddavBackend->getMultipleCards($id, $uris);

				foreach($objects as $object) {
					$vcard = \Sabre\VObject\Reader::read($object['carddata']);

					$photo = '';
					if($vcard->PHOTO) {
						$photo_value = strtolower($vcard->PHOTO->getValueType()); // binary or uri
						if($photo_value === 'binary') {
							$photo_type = strtolower($vcard->PHOTO['TYPE']); // mime jpeg, png or gif
							$photo = 'data:image/' . $photo_type . ';base64,' . base64_encode((string)$vcard->PHOTO);
						}
						else {
							$url = parse_url((string)$vcard->PHOTO);
							$photo = 'data:' . $url['path'];
						}
					}

					$fn = '';
					if($vcard->FN) {
						$fn = (string)$vcard->FN;
					}

					$org = '';
					if($vcard->ORG) {
						$org = (string)$vcard->ORG;
					}

					$title = '';
					if($vcard->TITLE) {
						$title = (string)$vcard->TITLE;
					}

					$tels = [];
					if($vcard->TEL) {
						foreach($vcard->TEL as $tel) {
							$type = (($tel['TYPE']) ? translate_type((string)$tel['TYPE']) : '');
							$tels[] = [
								'type' => $type,
								'nr' => (string)$tel
							];
						}
					}

					$emails = [];
					if($vcard->EMAIL) {
						foreach($vcard->EMAIL as $email) {
							$type = (($email['TYPE']) ? translate_type((string)$email['TYPE']) : '');
							$emails[] = [
								'type' => $type,
								'address' => (string)$email
							];
						}
					}

					$impps = [];
					if($vcard->IMPP) {
						foreach($vcard->IMPP as $impp) {
							$type = (($impp['TYPE']) ? translate_type((string)$impp['TYPE']) : '');
							$impps[] = [
								'type' => $type,
								'address' => (string)$impp
							];
						}
					}

					$urls = [];
					if($vcard->URL) {
						foreach($vcard->URL as $url) {
							$type = (($url['TYPE']) ? translate_type((string)$url['TYPE']) : '');
							$urls[] = [
								'type' => $type,
								'address' => (string)$url
							];
						}
					}

					$adrs = [];
					if($vcard->ADR) {
						foreach($vcard->ADR as $adr) {
							$type = (($adr['TYPE']) ? translate_type((string)$adr['TYPE']) : '');
							$adrs[] = [
								'type' => $type,
								'address' => $adr->getParts()
							];
						}
					}

					$note = '';
					if($vcard->NOTE) {
						$note = (string)$vcard->NOTE;
					}

					$cards[] = [
						'id' => $object['id'],
						'uri' => $object['uri'],

						'photo' => $photo,
						'fn' => $fn,
						'org' => $org,
						'title' => $title,
						'tels' => $tels,
						'emails' => $emails,
						'impps' => $impps,
						'urls' => $urls,
						'adrs' => $adrs,
						'note' => $note
					];
				}

				usort($cards, function($a, $b) { return strcasecmp($a['fn'], $b['fn']); });
			}

			$o .= replace_macros(get_markup_template('cdav_addressbook.tpl'), [
				'$id' => $id,
				'$cards' => $cards,
				'$displayname' => $displayname,
				'$name_label' => t('Name'),
				'$org_label' => t('Organisation'),
				'$title_label' => t('Title'),
				'$tel_label' => t('Phone'),
				'$email_label' => t('Email'),
				'$impp_label' => t('Instant messenger'),
				'$url_label' => t('Website'),
				'$adr_label' => t('Address'),
				'$note_label' => t('Note'),
				'$mobile' => t('Mobile'),
				'$home' => t('Home'),
				'$work' => t('Work'),
				'$other' => t('Other'),
				'$add_card' => t('Add Contact'),
				'$add_field' => t('Add Field'),
				'$create' => t('Create'),
				'$update' => t('Update'),
				'$delete' => t('Delete'),
				'$cancel' => t('Cancel'),
				'$po_box' => t('P.O. Box'),
				'$extra' => t('Additional'),
				'$street' => t('Street'),
				'$locality' => t('Locality'),
				'$region' => t('Region'),
				'$zip_code' => t('ZIP Code'),
				'$country' => t('Country')
			]);

			return $o;
		}

		//delete addressbook
		if(argc() > 3 && argv(1) === 'addressbook' && argv(2) === 'drop' && intval(argv(3))) {
			$id = argv(3);

			if(! cdav_perms($id,$addressbooks))
				return;

			$carddavBackend->deleteAddressBook($id);
			killme();
		}

	}

	function activate($pdo, $channel) {

		if(! $channel)
			return;

		$uri = 'principals/' . $channel['channel_address'];
		

		$r = q("select * from principals where uri = '%s' limit 1",
			dbesc($uri)
		);
		if($r) {
			$r = q("update principals set email = '%s', displayname = '%s' where uri = '%s' ",
				dbesc($channel['xchan_addr']),
				dbesc($channel['channel_name']),
				dbesc($uri)
			);
		}
		else {
			$r = q("insert into principals ( uri, email, displayname ) values('%s','%s','%s') ",
				dbesc($uri),
				dbesc($channel['xchan_addr']),
				dbesc($channel['channel_name'])
			);

			//create default calendar
			$caldavBackend = new \Sabre\CalDAV\Backend\PDO($pdo);
			$properties = [
				'{DAV:}displayname' => t('Default Calendar'),
				'{http://apple.com/ns/ical/}calendar-color' => '#3a87ad',
				'{urn:ietf:params:xml:ns:caldav}calendar-description' => $channel['channel_name']
			];

			$id = $caldavBackend->createCalendar($uri, 'default', $properties);
			set_pconfig(local_channel(), 'cdav_calendar' , $id[0], 1);

			//create default addressbook
			$carddavBackend = new \Sabre\CardDAV\Backend\PDO($pdo);
			$properties = ['{DAV:}displayname' => t('Default Addressbook')];
			$carddavBackend->createAddressBook($uri, 'default', $properties);

		}
	}


}