Example #1
0
function delete_collection($id)
{
    $params = array(':collection_id' => $id);
    $qry = new AwlQuery('SELECT child.collection_id AS child_id FROM collection child JOIN collection parent ON (parent.dav_name = child.parent_container) WHERE parent.collection_id = :collection_id', $params);
    if ($qry->Exec('DELETE', __LINE__, __FILE__) && $qry->rows() > 0) {
        while ($row = $qry->Fetch()) {
            delete_collection($row->child_id);
        }
    }
    if ($qry->QDo("SELECT write_sync_change(collection_id, 404, caldav_data.dav_name) FROM caldav_data WHERE collection_id = :collection_id", $params) && $qry->QDo("DELETE FROM property WHERE dav_name LIKE (SELECT dav_name FROM collection WHERE collection_id = :collection_id) || '%'", $params) && $qry->QDo("DELETE FROM locks WHERE dav_name LIKE (SELECT dav_name FROM collection WHERE collection_id = :collection_id) || '%'", $params) && $qry->QDo("DELETE FROM caldav_data WHERE collection_id = :collection_id", $params) && $qry->QDo("DELETE FROM collection WHERE collection_id = :collection_id", $params)) {
        @dbg_error_log("DELETE", "DELETE (collection): User: %d, ETag: %s, Path: %s", $session->user_no, $request->etag_if_match, $request->path);
        return true;
    }
    return false;
}
Example #2
0
/**
* Functions for managing external BIND resources
*
*
* @package   davical
* @subpackage   external-bind
* @author    Rob Ostensen <*****@*****.**>
* @copyright Rob Ostensen
* @license   http://gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
function create_external($path, $is_calendar, $is_addressbook)
{
    global $request;
    if (!function_exists("curl_init")) {
        dbg_error_log("external", "external resource cannot be fetched without curl, please install curl");
        $request->DoResponse(503, translate('PHP5 curl support is required for external binds'));
        return;
    }
    $resourcetypes = '<DAV::collection/>';
    if ($is_calendar) {
        $resourcetypes .= '<urn:ietf:params:xml:ns:caldav:calendar/>';
    }
    $qry = new AwlQuery();
    if (!$qry->QDo('INSERT INTO collection ( user_no, parent_container, dav_name, dav_etag, dav_displayname,
                                 is_calendar, is_addressbook, resourcetypes, created, modified )
              VALUES( :user_no, :parent_container, :dav_name, :dav_etag, :dav_displayname,
                      :is_calendar, :is_addressbook, :resourcetypes, current_timestamp, current_timestamp )', array(':user_no' => $request->user_no, ':parent_container' => '/.external/', ':dav_name' => $path, ':dav_etag' => md5($request->user_no . $path), ':dav_displayname' => $path, ':is_calendar' => $is_calendar ? 't' : 'f', ':is_addressbook' => $is_addressbook ? 't' : 'f', ':resourcetypes' => $resourcetypes))) {
        $request->DoResponse(500, translate('Error writing calendar details to database.'));
    }
}
Example #3
0
 /**
  * Write the roles associated with the user
  * @return Success.
  */
 function WriteRoles()
 {
     global $c, $session;
     if (isset($_POST['roles']) && is_array($_POST['roles'])) {
         $roles = "";
         $params = array();
         foreach ($_POST['roles'] as $k => $v) {
             if ($v && $v != "off") {
                 $roles .= $roles == '' ? '' : ', ';
                 $roles .= AwlQuery::quote($k);
             }
         }
         $qry = new AwlQuery();
         if ($roles == '') {
             $succeeded = $qry->QDo('DELETE FROM role_member WHERE user_no = ' . $this->user_no);
         } else {
             $succeeded = $qry->Begin();
             $sql = 'DELETE FROM role_member WHERE user_no = ' . $this->user_no;
             $sql .= ' AND role_no NOT IN (SELECT role_no FROM roles WHERE role_name IN (' . $roles . ') )';
             if ($succeeded) {
                 $succeeded = $qry->QDo($sql);
             }
             $sql = 'INSERT INTO role_member (role_no, user_no)';
             $sql .= ' SELECT role_no, ' . $this->user_no . ' FROM roles WHERE role_name IN (' . $roles . ')';
             $sql .= ' EXCEPT SELECT role_no, user_no FROM role_member';
             if ($succeeded) {
                 $succeeded = $qry->QDo($sql);
             }
             if ($succeeded) {
                 $qry->Commit();
             } else {
                 $qry->Rollback();
             }
         }
         if (!$succeeded) {
             $c->messages[] = i18n('ERROR: There was a database error writing the roles information!');
             $c->messages[] = i18n('Please note the time and advise the administrator of your system.');
             return false;
         }
     }
     return true;
 }
Example #4
0
$segment = $segment[0]->GetContent();
if (preg_match('{[/\\\\]}', $segment)) {
    $request->PreconditionFailed(403, 'DAV::name-allowed', translate('That destination name contains invalid characters.'));
}
$href = $xmltree->GetElements('DAV::href');
$href = $href[0]->GetContent();
$destination_path = $parent_container . $segment . '/';
$destination = new DAVResource($destination_path);
if ($destination->Exists()) {
    $request->PreconditionFailed(403, 'DAV::can-overwrite', translate('A resource already exists at the destination.'));
}
//  external binds shouldn't ever point back to ourselves but they should be a valid http[s] url
if (preg_match('{^https?://([^/]+)(:[0-9]\\+)?/.+$}', $href, $matches) && strcasecmp($matches[0], 'localhost') !== 0 && strcasecmp($matches[0], '127.0.0.1') !== 0 && strcasecmp($matches[0], $_SERVER['SERVER_NAME']) !== 0 && strcasecmp($matches[0], $_SERVER['SERVER_ADDR']) !== 0) {
    require_once 'external-fetch.php';
    $qry = new AwlQuery();
    $qry->QDo('SELECT collection_id FROM collection WHERE dav_name = :dav_name ', array(':dav_name' => '/.external/' . md5($href)));
    if ($qry->rows() == 1 && ($row = $qry->Fetch())) {
        $dav_id = $row->collection_id;
    } else {
        create_external('/.external/' . md5($href), true, false);
        $qry->QDo('SELECT collection_id FROM collection WHERE dav_name = :dav_name ', array(':dav_name' => '/.external/' . md5($href)));
        if ($qry->rows() != 1 || !($row = $qry->Fetch())) {
            $request->DoResponse(500, translate('Database Error'));
        }
        $dav_id = $row->collection_id;
    }
    $sql = 'INSERT INTO dav_binding ( bound_source_id, access_ticket_id, dav_owner_id, parent_container, dav_name, dav_displayname, external_url, type )
  VALUES( :target_id, :ticket_id, :session_principal, :parent_container, :dav_name, :displayname, :external_url, :external_type )';
    $params = array(':target_id' => $dav_id, ':ticket_id' => null, ':parent_container' => $parent->dav_name(), ':session_principal' => $session->principal_id, ':dav_name' => $destination_path, ':displayname' => $segment, ':external_url' => $href, ':external_type' => 'calendar');
    $qry = new AwlQuery($sql, $params);
    if ($qry->Exec('BIND', __LINE__, __FILE__)) {
Example #5
0
            $request->DoResponse(207, $reply->Render('multistatus', new XMLElement('response', $failure)), 'text/xml; charset="utf-8"');
        }
    }
}
$sql = 'SELECT * FROM collection WHERE dav_name = :dav_name';
$qry = new AwlQuery($sql, array(':dav_name' => $request->path));
if (!$qry->Exec('MKCOL', __LINE__, __FILE__)) {
    $request->DoResponse(500, translate('Error querying database.'));
}
if ($qry->rows() != 0) {
    $request->DoResponse(405, translate('A collection already exists at that location.'));
}
$qry = new AwlQuery();
$qry->Begin();
if (!$qry->QDo('INSERT INTO collection ( user_no, parent_container, dav_name, dav_etag, dav_displayname,
                                 is_calendar, is_addressbook, resourcetypes, created, modified )
              VALUES( :user_no, :parent_container, :dav_name, :dav_etag, :dav_displayname,
                      :is_calendar, :is_addressbook, :resourcetypes, current_timestamp, current_timestamp )', array(':user_no' => $request->user_no, ':parent_container' => $parent_container, ':dav_name' => $request->path, ':dav_etag' => md5($request->user_no . $request->path), ':dav_displayname' => $displayname, ':is_calendar' => $is_calendar ? 't' : 'f', ':is_addressbook' => $is_addressbook ? 't' : 'f', ':resourcetypes' => $resourcetypes))) {
    $request->DoResponse(500, translate('Error writing calendar details to database.'));
}
foreach ($dav_properties as $k => $v) {
    if (!$qry->QDo('SELECT set_dav_property( :dav_name, :user_no::integer, :tag::text, :value::text )', array(':dav_name' => $request->path, ':user_no' => $request->user_no, ':tag' => $k, ':value' => $v))) {
        $request->DoResponse(500, translate('Error writing calendar properties to database.'));
    }
}
if (!$qry->Commit()) {
    $request->DoResponse(500, translate('Error writing calendar details to database.'));
}
dbg_error_log('MKCOL', 'New calendar "%s" created named "%s" for user "%d" in parent "%s"', $request->path, $displayname, $session->user_no, $parent_container);
header('Cache-Control: no-cache');
/** RFC4791 mandates this at 5.3.1 */
$request->DoResponse(201, '');
Example #6
0
    /**
    * E-mails a temporary password in response to a request from a user.
    *
    * This could be called from somewhere within the application that allows
    * someone to set up a user and invite them.
    *
    * This function includes EMail.php to actually send the password.
    */
    function EmailTemporaryPassword($username, $email_address, $body_template = "")
    {
        global $c;
        $password_sent = false;
        $where = "";
        $params = array();
        if (isset($username) && $username != "") {
            $where = 'WHERE active AND lower(usr.username) = :lcusername';
            $params[':lcusername'] = strtolower($username);
        } else {
            if (isset($email_address) && $email_address != "") {
                $where = 'WHERE active AND lower(usr.email) = :lcemail';
                $params[':lcemail'] = strtolower($email_address);
            }
        }
        if ($where != '') {
            if (!isset($body_template) || $body_template == "") {
                $body_template = <<<EOTEXT

@@debugging@@A temporary password has been requested for @@system_name@@.

Temporary Password: @@password@@

This has been applied to the following usernames:

@@usernames@@
and will be valid for 24 hours.

If you have any problems, please contact the system administrator.

EOTEXT;
            }
            $qry = new AwlQuery('SELECT * FROM usr ' . $where, $params);
            $qry->Exec('Session::EmailTemporaryPassword');
            if ($qry->rows() > 0) {
                $q2 = new AwlQuery();
                $q2->Begin();
                while ($row = $qry->Fetch()) {
                    $mail = new EMail("Access to {$c->system_name}");
                    $mail->SetFrom($c->admin_email);
                    $usernames = "";
                    $debug_to = "";
                    if (isset($c->debug_email)) {
                        $debug_to = "This e-mail would normally be sent to:\n ";
                        $mail->AddTo("Tester <{$c->debug_email}>");
                    }
                    $tmp_passwd = '';
                    for ($i = 0; $i < 8; $i++) {
                        $tmp_passwd .= substr('ABCDEFGHIJKLMNOPQRSTUVWXYZ+#.-=*%@0123456789abcdefghijklmnopqrstuvwxyz', rand(0, 69), 1);
                    }
                    $q2->QDo('INSERT INTO tmp_password (user_no, password) VALUES(?,?)', array($row->user_no, $tmp_passwd));
                    if (isset($c->debug_email)) {
                        $debug_to .= "{$row->fullname} <{$row->email}> ";
                    } else {
                        $mail->AddTo("{$row->fullname} <{$row->email}>");
                    }
                    $usernames .= "        {$row->username}\n";
                    if ($mail->To != "") {
                        if (isset($c->debug_email)) {
                            $debug_to .= "\n============================================================\n";
                        }
                        $sql .= "COMMIT;";
                        $qry = new AwlQuery($sql);
                        $qry->Exec("Session::SendTemporaryPassword");
                        $body = str_replace('@@system_name@@', $c->system_name, $body_template);
                        $body = str_replace('@@password@@', $tmp_passwd, $body);
                        $body = str_replace('@@usernames@@', $usernames, $body);
                        $body = str_replace('@@debugging@@', $debug_to, $body);
                        $mail->SetBody($body);
                        $mail->Send();
                        $password_sent = true;
                    }
                }
            }
        }
        return $password_sent;
    }
Example #7
0
 /**
 CREATE TABLE addressbook_address_email (
 dav_id INT8 NOT NULL REFERENCES caldav_data(dav_id) ON UPDATE CASCADE ON DELETE CASCADE,
 type TEXT,
 email TEXT,
 property TEXT -- The full text of the property
 );
 */
 function WriteEmails($dav_id)
 {
     $emails = $this->GetProperties('EMAIL');
     $qry = new AwlQuery();
     // Only run a local transaction if we're not in one already.
     $in_transaction = $qry->TransactionState() == 1;
     if (!$in_transaction) {
         $qry->Begin();
     }
     $params = array(':dav_id' => $dav_id);
     $qry->QDo('DELETE FROM addressbook_address_email WHERE dav_id = :dav_id', $params);
     foreach ($emails as $email) {
         $params[':type'] = $email->GetParameterValue('TYPE');
         $params[':email'] = $email->Value();
         $params[':property'] = $email->Render();
         $qry->QDo('INSERT INTO addressbook_address_email (dav_id, type, email, property) VALUES( :dav_id, :type, :email, :property)', $params);
     }
     if (!$in_transaction) {
         $qry->Commit();
     }
 }
Example #8
0
if (isset($request->etag_none_match) && $request->etag_none_match != '*' && $dest->Exists()) {
    $request->PreconditionFailed(412, 'if-none-match', translate('A resource already exists at the destination.'));
}
if (isset($request->etag_if_match) && $request->etag_if_match != $dest->unique_tag()) {
    $request->PreconditionFailed(412, 'if-match', sprintf('Existing resource ETag of "%s" does not match "%s"', $dest->unique_tag(), $request->etag_if_match));
}
$collection_id = $container->GetProperty('collection_id');
$qry = new AwlQuery();
$qry->Begin();
$etag = md5($request->raw_post);
$params = array(':user_no' => $dest->GetProperty('user_no'), ':dav_name' => $dest->bound_from(), ':etag' => $etag, ':dav_data' => $request->raw_post, ':session_user' => $session->user_no);
if ($dest->Exists()) {
    $sql = 'UPDATE caldav_data SET caldav_data=:dav_data, dav_etag=:etag, logged_user=:session_user,
          modified=current_timestamp, user_no=:user_no, caldav_type=\'VCARD\' WHERE dav_name=:dav_name';
    $response_code = 200;
    $qry->QDo($sql, $params);
    $qry->QDo("SELECT dav_id FROM caldav_data WHERE dav_name = :dav_name ", array(':dav_name' => $params[':dav_name']));
} else {
    $sql = 'INSERT INTO caldav_data ( user_no, dav_name, dav_etag, caldav_data, caldav_type, logged_user, created, modified, collection_id )
          VALUES( :user_no, :dav_name, :etag, :dav_data, \'VCARD\', :session_user, current_timestamp, current_timestamp, :collection_id )';
    $params[':collection_id'] = $collection_id;
    $response_code = 201;
    $qry->QDo($sql, $params);
    $qry->QDo("SELECT currval('dav_id_seq') AS dav_id");
}
$row = $qry->Fetch();
require_once 'vcard.php';
$vcard = new vCard($request->raw_post);
$vcard->Write($row->dav_id, $dest->Exists());
$qry->QDo("SELECT write_sync_change( {$collection_id}, {$response_code}, :dav_name)", array(':dav_name' => $dest->bound_from()));
if (!$qry->Commit()) {
Example #9
0
    $request->DoResponse(412);
}
if (isset($request->etag_if_match) && $request->etag_if_match != $dest->unique_tag()) {
    $request->DoResponse(412);
}
$collection_id = $container->GetProperty('collection_id');
$qry = new AwlQuery();
$qry->Begin();
$etag = md5($request->raw_post);
$params = array(':user_no' => $dest->GetProperty('user_no'), ':dav_name' => $dest->bound_from(), ':etag' => $etag, ':dav_data' => $request->raw_post, ':session_user' => $session->user_no);
if ($dest->Exists()) {
    $sql = 'UPDATE caldav_data SET caldav_data=:dav_data, dav_etag=:etag, logged_user=:session_user,
          modified=current_timestamp WHERE user_no=:user_no AND dav_name=:dav_name';
    $response_code = 200;
} else {
    $sql = 'INSERT INTO caldav_data ( user_no, dav_name, dav_etag, caldav_data, logged_user, created, modified, collection_id )
          VALUES( :user_no, :dav_name, :etag, :dav_data, :session_user, current_timestamp, current_timestamp, :collection_id )';
    $params[':collection_id'] = $collection_id;
    $response_code = 201;
}
$qry->QDo($sql, $params);
$qry->QDo("SELECT write_sync_change( {$collection_id}, {$response_code}, :dav_name)", array(':dav_name' => $dest->bound_from()));
$qry = new AwlQuery('COMMIT');
if (!$qry->Exec('move')) {
    rollback(500);
}
header('ETag: "' . $etag . '"');
if ($response_code == 200) {
    $response_code = 204;
}
$request->DoResponse($response_code);
Example #10
0
         $tzcomponent = $setting->GetPath('urn:ietf:params:xml:ns:caldav:calendar-timezone');
         $tzstring = $tzcomponent[0]->GetContent();
         $calendar = new vCalendar($tzstring);
         $timezones = $calendar->GetComponents('VTIMEZONE');
         if (count($timezones) == 0) {
             break;
         }
         $tz = $timezones[0];
         // Backward compatibility
         $tzid = $tz->GetPValue('TZID');
         $params = array(':tzid' => $tzid);
         $qry = new AwlQuery('SELECT 1 FROM timezones WHERE tzid = :tzid', $params);
         if ($qry->Exec('PUT', __LINE__, __FILE__) && $qry->rows() == 0) {
             $params[':olson_name'] = $calendar->GetOlsonName($tz);
             $params[':vtimezone'] = isset($tz) ? $tz->Render() : null;
             $qry->QDo('INSERT INTO timezones (tzid, olson_name, active, vtimezone) VALUES(:tzid,:olson_name,false,:vtimezone)', $params);
         }
         $qry->QDo('UPDATE collection SET timezone = :tzid WHERE dav_name = :dav_name', array(':tzid' => $tzid, ':dav_name' => $dav_resource->dav_name()));
     } else {
         add_failure('set', $tag, 'HTTP/1.1 409 Conflict', translate("calendar-timezone property is only valid for a calendar."));
     }
     break;
     /**
      * The following properties are read-only, so they will cause the request to fail
      */
 /**
  * The following properties are read-only, so they will cause the request to fail
  */
 case 'http://calendarserver.org/ns/:getctag':
 case 'DAV::owner':
 case 'DAV::principal-collection-set':
Example #11
0
 /**
  * Returns the current sync_token for this collection, or the containing collection
  */
 function sync_token($cachedOK = true)
 {
     dbg_error_log('DAVResource', 'Request for a%scached sync-token', $cachedOK ? ' ' : 'n un');
     if ($this->IsPrincipal()) {
         return null;
     }
     if ($this->collection_id() == 0) {
         return null;
     }
     if (!isset($this->sync_token) || !$cachedOK) {
         $sql = 'SELECT new_sync_token( 0, :collection_id) AS sync_token';
         $params = array(':collection_id' => $this->collection_id());
         $qry = new AwlQuery($sql, $params);
         if (!$qry->Exec() || !($row = $qry->Fetch())) {
             if (!$qry->QDo('SELECT new_sync_token( 0, :collection_id) AS sync_token', $params)) {
                 throw new Exception('Problem with database query');
             }
             $row = $qry->Fetch();
         }
         $this->sync_token = 'data:,' . $row->sync_token;
     }
     dbg_error_log('DAVResource', 'Returning sync token of "%s"', $this->sync_token);
     return $this->sync_token;
 }
Example #12
0
<alias>US/Eastern</alias>
<local-name lang="en_US">America/New_York</local-name>
<summary>
*/
$q2 = new AwlQuery();
$qry = new AwlQuery($sql, $params);
if ($qry->Exec('tz/list', __LINE__, __FILE__) && $qry->rows() > 0) {
    while ($tz = $qry->Fetch()) {
        $elements = array(new XMLElement('tzid', $tz->tzid), new XMLElement('last-modified', $tz->last_modified));
        if ($tz->active != 't') {
            $elements[] = new XMLElement('inactive');
        }
        if ($tz->tzid != $tz->olson_name) {
            $elements[] = new XMLElement('alias', $tz->olson_name);
        }
        if ($q2->QDo('SELECT * FROM tz_aliases WHERE our_tzno = ?', array($tz->our_tzno))) {
            while ($alias = $q2->Fetch()) {
                $elements[] = new XMLElement('alias', $alias->tzalias);
            }
        }
        if (!empty($lang) && $q2->QDo('SELECT * FROM tz_localnames WHERE our_tzno = ? AND locale = ?', array($tz->our_tzno, $lang)) && $q2->rows() > 0) {
            while ($local = $q2->Fetch()) {
                $attr = array('lang' => $local->locale);
                if ($local->preferred == 't') {
                    $attr['preferred'] = 'true';
                }
                $elements[] = new XMLElement('local-name', $local->localised_name, $attr);
            }
        } else {
            $elements[] = new XMLElement('local-name', $tz->tzid, empty($lang) ? null : array('lang' => $lang));
        }
 /**
  * Writes the data to a member in the collection and returns the segment_name of the 
  * resource in our internal namespace.
  *  
  * @param vCalendar $member_dav_name The path to the resource to be deleted.
  * @return boolean Success is true, or false on failure.
  */
 function actualDeleteCalendarMember($member_dav_name)
 {
     global $session, $caldav_context;
     // A quick sanity check...
     $segment_name = str_replace($this->dav_name(), '', $member_dav_name);
     if (strstr($segment_name, '/') !== false) {
         @dbg_error_log("DELETE", "DELETE: Refused to delete member '%s' from calendar '%s'!", $member_dav_name, $this->dav_name());
         return false;
     }
     // We need to serialise access to this process just for this collection
     $cache = getCacheInstance();
     $myLock = $cache->acquireLock('collection-' . $this->dav_name());
     $qry = new AwlQuery();
     $params = array(':dav_name' => $member_dav_name);
     if ($qry->QDo("SELECT write_sync_change(collection_id, 404, caldav_data.dav_name) FROM caldav_data WHERE dav_name = :dav_name", $params) && $qry->QDo("DELETE FROM property WHERE dav_name = :dav_name", $params) && $qry->QDo("DELETE FROM locks WHERE dav_name = :dav_name", $params) && $qry->QDo("DELETE FROM caldav_data WHERE dav_name = :dav_name", $params)) {
         @dbg_error_log("DELETE", "DELETE: Calendar member %s deleted from calendar '%s'", $member_dav_name, $this->dav_name());
         $cache->releaseLock($myLock);
         return true;
     }
     $cache->releaseLock($myLock);
     return false;
 }
Example #14
0
$qry = new AwlQuery($sql, $params);
if (!$qry->Exec()) {
    exit(1);
}
if ($qry->rows() < 1) {
    $sql = 'SELECT our_tzno, tzid, active, olson_name, vtimezone, etag, ';
    $sql .= 'to_char(last_modified,\'Dy, DD Mon IYYY HH24:MI:SS "GMT"\') AS last_modified ';
    $sql .= 'FROM timezones JOIN tz_aliases USING(our_tzno) WHERE tzalias=:tzid';
    if (!$qry->Exec()) {
        exit(1);
    }
    if ($qry->rows() < 1) {
        $request->DoResponse(404);
    }
}
$tz = $qry->Fetch();
$vtz = new vCalendar($tz->vtimezone);
$vtz->AddProperty('TZ-URL', $c->protocol_server_port . $_SERVER['REQUEST_URI']);
$vtz->AddProperty('TZNAME', $tz->olson_name);
if ($qry->QDo('SELECT * FROM tz_localnames WHERE our_tzno = :our_tzno', array(':our_tzno' => $tz->our_tzno)) && $qry->rows()) {
    while ($name = $qry->Fetch()) {
        if (strpos($_SERVER['QUERY_STRING'], 'lang=' . $name->locale) !== false) {
            $vtz->AddProperty('TZNAME', $name->localised_name, array('LANGUAGE', str_replace('_', '-', $name->locale)));
        }
    }
}
header('ETag: "' . $tz->etag . '"');
header('Last-Modified: ' . $tz->last_modified);
header('Content-Disposition: Attachment; Filename="' . str_replace('/', '-', $tzid . '.ics"'));
$request->DoResponse(200, $vtz->Render(), 'text/calendar; charset=UTF-8');
exit(0);
/**
* Actually write the resource to the database.  All checking of whether this is reasonable
* should be done before this is called.
* 
* @param DAVResource $resource The resource being written
* @param string $caldav_data The actual data to be written
* @param DAVResource $collection The collection containing the resource being written
* @param int $author The user_no who wants to put this resource on the server
* @param string $etag An etag unique for this event
* @param string $put_action_type INSERT or UPDATE depending on what we are to do
* @param boolean $caldav_context True, if we are responding via CalDAV, false for other ways of calling this
* @param string Either 'INSERT' or 'UPDATE': the type of action we are doing
* @param boolean $log_action Whether to log the fact that we are writing this into an action log (if configured)
* @param string $weak_etag An etag that is NOT modified on ATTENDEE changes for this event
* 
* @return boolean True for success, false for failure.
*/
function write_resource(DAVResource $resource, $caldav_data, DAVResource $collection, $author, &$etag, $put_action_type, $caldav_context, $log_action = true, $weak_etag = null)
{
    global $tz_regex, $session;
    $path = $resource->bound_from();
    $user_no = $collection->user_no();
    $vcal = new vCalendar($caldav_data);
    $resources = $vcal->GetComponents('VTIMEZONE', false);
    // Not matching VTIMEZONE
    if (!isset($resources[0])) {
        $resource_type = 'Unknown';
        /** @todo Handle writing non-calendar resources, like address book entries or random file data */
        rollback_on_error($caldav_context, $user_no, $path, translate('No calendar content'), 412);
        return false;
    } else {
        $first = $resources[0];
        if (!$first instanceof vComponent) {
            print $vcal->Render();
            fatal('This is not a vComponent!');
        }
        $resource_type = $first->GetType();
    }
    $collection_id = $collection->collection_id();
    $qry = new AwlQuery();
    $qry->Begin();
    $dav_params = array(':etag' => $etag, ':dav_data' => $caldav_data, ':caldav_type' => $resource_type, ':session_user' => $author, ':weak_etag' => $weak_etag);
    $calitem_params = array(':etag' => $etag);
    if ($put_action_type == 'INSERT') {
        $qry->QDo('SELECT nextval(\'dav_id_seq\') AS dav_id, null AS caldav_data');
    } else {
        $qry->QDo('SELECT dav_id, caldav_data FROM caldav_data WHERE dav_name = :dav_name ', array(':dav_name' => $path));
    }
    if ($qry->rows() != 1 || !($row = $qry->Fetch())) {
        // No dav_id?  => We're toast!
        trace_bug('No dav_id for "%s" on %s!!!', $path, $create_resource ? 'create' : 'update');
        rollback_on_error($caldav_context, $user_no, $path);
        return false;
    }
    $dav_id = $row->dav_id;
    $old_dav_data = $row->caldav_data;
    $dav_params[':dav_id'] = $dav_id;
    $calitem_params[':dav_id'] = $dav_id;
    $due = null;
    if ($first->GetType() == 'VTODO') {
        $due = $first->GetPValue('DUE');
    }
    $calitem_params[':due'] = $due;
    $dtstart = $first->GetPValue('DTSTART');
    if (empty($dtstart)) {
        $dtstart = $due;
    }
    $calitem_params[':dtstart'] = $dtstart;
    $dtend = $first->GetPValue('DTEND');
    if (isset($dtend) && $dtend != '') {
        dbg_error_log('PUT', ' DTEND: "%s", DTSTART: "%s", DURATION: "%s"', $dtend, $dtstart, $first->GetPValue('DURATION'));
        $calitem_params[':dtend'] = $dtend;
        $dtend = ':dtend';
    } else {
        // In this case we'll construct the SQL directly as a calculation relative to :dtstart
        $dtend = 'NULL';
        if ($first->GetPValue('DURATION') != '' and $dtstart != '') {
            $duration = trim(preg_replace('#[PT]#', ' ', $first->GetPValue('DURATION')));
            if ($duration == '') {
                $duration = '0 seconds';
            }
            $dtend = '(:dtstart::timestamp with time zone + :duration::interval)';
            $calitem_params[':duration'] = $duration;
        } elseif ($first->GetType() == 'VEVENT') {
            /**
             * From RFC2445 4.6.1:
             * For cases where a "VEVENT" calendar component specifies a "DTSTART"
             * property with a DATE data type but no "DTEND" property, the events
             * non-inclusive end is the end of the calendar date specified by the
             * "DTSTART" property. For cases where a "VEVENT" calendar component specifies
             * a "DTSTART" property with a DATE-TIME data type but no "DTEND" property,
             * the event ends on the same calendar date and time of day specified by the
             * "DTSTART" property.
             *
             * So we're looking for 'VALUE=DATE', to identify the duration, effectively.
             *
             */
            $dtstart_prop = $first->GetProperty('DTSTART');
            $value_type = $dtstart_prop->GetParameterValue('VALUE');
            dbg_error_log('PUT', 'DTSTART without DTEND. DTSTART value type is %s', $value_type);
            if (isset($value_type) && $value_type == 'DATE') {
                $dtend = '(:dtstart::timestamp with time zone::date + \'1 day\'::interval)';
            } else {
                $dtend = ':dtstart';
            }
        }
    }
    $dtstamp = $first->GetPValue('DTSTAMP');
    if (!isset($dtstamp) || $dtstamp == '') {
        // Strictly, we're dealing with an out of spec component here, but we'll try and survive
        $dtstamp = gmdate('Ymd\\THis\\Z');
    }
    $calitem_params[':dtstamp'] = $dtstamp;
    $last_modified = $first->GetPValue('LAST-MODIFIED');
    if (!isset($last_modified) || $last_modified == '') {
        $last_modified = $dtstamp;
    }
    $dav_params[':modified'] = $last_modified;
    $calitem_params[':modified'] = $last_modified;
    $created = $first->GetPValue('CREATED');
    if ($created == '00001231T000000Z') {
        $created = '20001231T000000Z';
    }
    $class = $first->GetPValue('CLASS');
    /* Check and see if we should over ride the class. */
    /** @todo is there some way we can move this out of this function? Or at least get rid of the need for the SQL query here. */
    if (public_events_only($user_no, $path)) {
        $class = 'PUBLIC';
    }
    /*
     * It seems that some calendar clients don't set a class...
     * RFC2445, 4.8.1.3:
     * Default is PUBLIC
     */
    if (!isset($class) || $class == '') {
        $class = 'PUBLIC';
    }
    $calitem_params[':class'] = $class;
    /** Calculate what timezone to set, first, if possible */
    $last_olson = 'Turkmenikikamukau';
    // I really hope this location doesn't exist!
    $tzid = GetTZID($first);
    if (!empty($tzid)) {
        $timezones = $vcal->GetComponents('VTIMEZONE');
        foreach ($timezones as $k => $tz) {
            if ($tz->GetPValue('TZID') != $tzid) {
                /**
                 * We'll skip any tz definitions that are for a TZID other than the DTSTART/DUE on the first VEVENT/VTODO 
                 */
                dbg_error_log('ERROR', ' Event uses TZID[%s], skipping included TZID[%s]!', $tz->GetPValue('TZID'), $tzid);
                continue;
            }
            $olson = olson_from_tzstring($tzid);
            if (empty($olson)) {
                $olson = $tz->GetPValue('X-LIC-LOCATION');
                if (!empty($olson)) {
                    $olson = olson_from_tzstring($olson);
                }
            }
        }
        dbg_error_log('PUT', ' Using TZID[%s] and location of [%s]', $tzid, isset($olson) ? $olson : '');
        if (!empty($olson) && $olson != $last_olson && preg_match($tz_regex, $olson)) {
            dbg_error_log('PUT', ' Setting timezone to %s', $olson);
            if ($olson != '') {
                $qry->QDo('SET TIMEZONE TO \'' . $olson . "'");
            }
            $last_olson = $olson;
        }
        $params = array(':tzid' => $tzid);
        $qry = new AwlQuery('SELECT 1 FROM timezones WHERE tzid = :tzid', $params);
        if ($qry->Exec('PUT', __LINE__, __FILE__) && $qry->rows() == 0) {
            $params[':olson_name'] = $olson;
            $params[':vtimezone'] = isset($tz) ? $tz->Render() : null;
            $qry->QDo('INSERT INTO timezones (tzid, olson_name, active, vtimezone) VALUES(:tzid,:olson_name,false,:vtimezone)', $params);
        }
        if (!isset($olson) || $olson == '') {
            $olson = $tzid;
        }
    }
    $qry->QDo('SELECT new_sync_token(0,' . $collection_id . ')');
    $calitem_params[':tzid'] = $tzid;
    $calitem_params[':uid'] = $first->GetPValue('UID');
    $calitem_params[':summary'] = $first->GetPValue('SUMMARY');
    $calitem_params[':location'] = $first->GetPValue('LOCATION');
    $calitem_params[':transp'] = $first->GetPValue('TRANSP');
    $calitem_params[':description'] = $first->GetPValue('DESCRIPTION');
    $calitem_params[':rrule'] = $first->GetPValue('RRULE');
    $calitem_params[':url'] = $first->GetPValue('URL');
    $calitem_params[':priority'] = $first->GetPValue('PRIORITY');
    $calitem_params[':percent_complete'] = $first->GetPValue('PERCENT-COMPLETE');
    $calitem_params[':status'] = $first->GetPValue('STATUS');
    if (!$collection->IsSchedulingCollection()) {
        if (do_scheduling_requests($vcal, $put_action_type == 'INSERT', $old_dav_data, true)) {
            $dav_params[':dav_data'] = $vcal->Render(null, true);
            $etag = null;
        }
    }
    if (!isset($dav_params[':modified'])) {
        $dav_params[':modified'] = 'now';
    }
    if ($put_action_type == 'INSERT') {
        $sql = 'INSERT INTO caldav_data ( dav_id, user_no, dav_name, dav_etag, caldav_data, caldav_type, logged_user, created, modified, collection_id, weak_etag )
            VALUES( :dav_id, :user_no, :dav_name, :etag, :dav_data, :caldav_type, :session_user, :created, :modified, :collection_id, :weak_etag )';
        $dav_params[':collection_id'] = $collection_id;
        $dav_params[':user_no'] = $user_no;
        $dav_params[':dav_name'] = $path;
        $dav_params[':created'] = isset($created) && $created != '' ? $created : $dtstamp;
    } else {
        $sql = 'UPDATE caldav_data SET caldav_data=:dav_data, dav_etag=:etag, caldav_type=:caldav_type, logged_user=:session_user,
            modified=:modified, weak_etag=:weak_etag WHERE dav_id=:dav_id';
    }
    $qry = new AwlQuery($sql, $dav_params);
    if (!$qry->Exec('PUT', __LINE__, __FILE__)) {
        fatal('Insert into calendar_item failed...');
        rollback_on_error($caldav_context, $user_no, $path);
        return false;
    }
    if ($put_action_type == 'INSERT') {
        $sql = <<<EOSQL
INSERT INTO calendar_item (user_no, dav_name, dav_id, dav_etag, uid, dtstamp,
                dtstart, dtend, summary, location, class, transp,
                description, rrule, tz_id, last_modified, url, priority,
                created, due, percent_complete, status, collection_id )
   VALUES ( :user_no, :dav_name, :dav_id, :etag, :uid, :dtstamp,
                :dtstart, {$dtend}, :summary, :location, :class, :transp,
                :description, :rrule, :tzid, :modified, :url, :priority,
                :created, :due, :percent_complete, :status, :collection_id )
EOSQL;
        $sync_change = 201;
        $calitem_params[':collection_id'] = $collection_id;
        $calitem_params[':user_no'] = $user_no;
        $calitem_params[':dav_name'] = $path;
        $calitem_params[':created'] = $dav_params[':created'];
    } else {
        $sql = <<<EOSQL
UPDATE calendar_item SET dav_etag=:etag, uid=:uid, dtstamp=:dtstamp,
                dtstart=:dtstart, dtend={$dtend}, summary=:summary, location=:location,
                class=:class, transp=:transp, description=:description, rrule=:rrule,
                tz_id=:tzid, last_modified=:modified, url=:url, priority=:priority,
                due=:due, percent_complete=:percent_complete, status=:status
       WHERE dav_id=:dav_id
EOSQL;
        $sync_change = 200;
    }
    write_alarms($dav_id, $first);
    write_attendees($dav_id, $vcal);
    if ($log_action && function_exists('log_caldav_action')) {
        log_caldav_action($put_action_type, $first->GetPValue('UID'), $user_no, $collection_id, $path);
    } else {
        if ($log_action) {
            dbg_error_log('PUT', 'No log_caldav_action( %s, %s, %s, %s, %s) can be called.', $put_action_type, $first->GetPValue('UID'), $user_no, $collection_id, $path);
        }
    }
    $qry = new AwlQuery($sql, $calitem_params);
    if (!$qry->Exec('PUT', __LINE__, __FILE__)) {
        rollback_on_error($caldav_context, $user_no, $path);
        return false;
    }
    $qry->QDo("SELECT write_sync_change( {$collection_id}, {$sync_change}, :dav_name)", array(':dav_name' => $path));
    $qry->Commit();
    if (function_exists('post_commit_action')) {
        post_commit_action($put_action_type, $first->GetPValue('UID'), $user_no, $collection_id, $path);
    }
    // Uncache anything to do with the collection
    $cache = getCacheInstance();
    $cache_ns = 'collection-' . preg_replace('{/[^/]*$}', '/', $path);
    $cache->delete($cache_ns, null);
    dbg_error_log('PUT', 'User: %d, ETag: %s, Path: %s', $author, $etag, $path);
    return true;
    // Success!
}
Example #16
0
$_SERVER['SERVER_NAME'] = $c->domain_name;
require_once 'AwlQuery.php';
$c->want_dbversion = array(1, 2, 11);
$c->schema_version = 0;
$qry = new AwlQuery('SELECT schema_major, schema_minor, schema_patch FROM awl_db_revision ORDER BY schema_id DESC LIMIT 1;');
if ($qry->Exec('always', __LINE__, __FILE__) && ($row = $qry->Fetch())) {
    $c->schema_version = doubleval(sprintf('%d%03d.%03d', $row->schema_major, $row->schema_minor, $row->schema_patch));
    $c->wanted_version = doubleval(sprintf('%d%03d.%03d', $c->want_dbversion[0], $c->want_dbversion[1], $c->want_dbversion[2]));
    $c->schema_major = $row->schema_major;
    $c->schema_minor = $row->schema_minor;
    $c->schema_patch = $row->schema_patch;
    if ($c->schema_version < $c->wanted_version) {
        $c->messages[] = sprintf('Database schema needs upgrading. Current: %d.%d.%d, Desired: %d.%d.%d', $row->schema_major, $row->schema_minor, $row->schema_patch, $c->want_dbversion[0], $c->want_dbversion[1], $c->want_dbversion[2]);
    }
    if (isset($c->default_timezone)) {
        $qry->QDo('SET TIMEZONE TO ?', $c->default_timezone);
    }
}
require_once 'Principal.php';
/**
 * Return the HTTP status code description for a given code. Hopefully
 * this is an efficient way to code this.
 * @return string The text for a give HTTP status code, in english
 */
function getStatusMessage($status)
{
    switch ($status) {
        case 100:
            $ans = 'Continue';
            break;
        case 101:
Example #17
0
function handle_freebusy_request($ic)
{
    global $c, $session, $request, $ical;
    $request->NeedPrivilege('CALDAV:schedule-send-freebusy');
    $reply = new XMLDocument(array("DAV:" => "", "urn:ietf:params:xml:ns:caldav" => "C"));
    $responses = array();
    $fbq_start = $ic->GetPValue('DTSTART');
    $fbq_end = $ic->GetPValue('DTEND');
    if (!(isset($fbq_start) || isset($fbq_end))) {
        $request->DoResponse(400, 'All valid freebusy requests MUST contain a DTSTART and a DTEND');
    }
    $range_start = new RepeatRuleDateTime($fbq_start);
    $range_end = new RepeatRuleDateTime($fbq_end);
    $attendees = $ic->GetProperties('ATTENDEE');
    if (preg_match('# iCal/\\d#', $_SERVER['HTTP_USER_AGENT'])) {
        dbg_error_log("POST", "Non-compliant iCal request.  Using X-WR-ATTENDEE property");
        $wr_attendees = $ic->GetProperties('X-WR-ATTENDEE');
        foreach ($wr_attendees as $k => $v) {
            $attendees[] = $v;
        }
    }
    dbg_error_log("POST", "Responding with free/busy for %d attendees", count($attendees));
    foreach ($attendees as $k => $attendee) {
        $attendee_email = preg_replace('/^mailto:/', '', $attendee->Value());
        dbg_error_log("POST", "Calculating free/busy for %s", $attendee_email);
        /** @todo Refactor this so we only do one query here and loop through the results */
        $params = array(':session_principal' => $session->principal_id, ':scan_depth' => $c->permission_scan_depth, ':email' => $attendee_email);
        $qry = new AwlQuery('SELECT pprivs(:session_principal::int8,principal_id,:scan_depth::int) AS p, username FROM usr JOIN principal USING(user_no) WHERE lower(usr.email) = lower(:email)', $params);
        if (!$qry->Exec('POST', __LINE__, __FILE__)) {
            $request->DoResponse(501, 'Database error');
        }
        if ($qry->rows() > 1) {
            // Unlikely, but if we get more than one result we'll do an exact match instead.
            if (!$qry->QDo('SELECT pprivs(:session_principal::int8,principal_id,:scan_depth::int) AS p, username FROM usr JOIN principal USING(user_no) WHERE usr.email = :email', $params)) {
                $request->DoResponse(501, 'Database error');
            }
            if ($qry->rows() == 0) {
                /** Sigh... Go back to the original case-insensitive match */
                $qry->QDo('SELECT pprivs(:session_principal::int8,principal_id,:scan_depth::int) AS p, username FROM usr JOIN principal USING(user_no) WHERE lower(usr.email) = lower(:email)', $params);
            }
        }
        $response = $reply->NewXMLElement("response", false, false, 'urn:ietf:params:xml:ns:caldav');
        $reply->CalDAVElement($response, "recipient", $reply->href($attendee->Value()));
        if ($qry->rows() == 0) {
            $remote = new iSchedule();
            $answer = $remote->sendRequest($attendee->Value(), 'VFREEBUSY/REQUEST', $ical->Render());
            if ($answer === false) {
                $reply->CalDAVElement($response, "request-status", "3.7;Invalid Calendar User");
                $reply->CalDAVElement($response, "calendar-data");
                $responses[] = $response;
                continue;
            }
            foreach ($answer as $a) {
                if ($a === false) {
                    $reply->CalDAVElement($response, "request-status", "3.7;Invalid Calendar User");
                    $reply->CalDAVElement($response, "calendar-data");
                } elseif (substr($a, 0, 1) >= 1) {
                    $reply->CalDAVElement($response, "request-status", $a);
                    $reply->CalDAVElement($response, "calendar-data");
                } else {
                    $reply->CalDAVElement($response, "request-status", "2.0;Success");
                    $reply->CalDAVElement($response, "calendar-data", $a);
                }
                $responses[] = $response;
            }
            continue;
        }
        if (!($attendee_usr = $qry->Fetch())) {
            $request->DoResponse(501, 'Database error');
        }
        if ((privilege_to_bits('schedule-query-freebusy') & bindec($attendee_usr->p)) == 0) {
            $reply->CalDAVElement($response, "request-status", "3.8;No authority");
            $reply->CalDAVElement($response, "calendar-data");
            $responses[] = $response;
            continue;
        }
        $attendee_path_match = '^/' . $attendee_usr->username . '/';
        $fb = get_freebusy($attendee_path_match, $range_start, $range_end, bindec($attendee_usr->p));
        $fb->AddProperty('UID', $ic->GetPValue('UID'));
        $fb->SetProperties($ic->GetProperties('ORGANIZER'), 'ORGANIZER');
        $fb->AddProperty($attendee);
        $vcal = new vCalendar(array('METHOD' => 'REPLY'));
        $vcal->AddComponent($fb);
        $response = $reply->NewXMLElement("response", false, false, 'urn:ietf:params:xml:ns:caldav');
        $reply->CalDAVElement($response, "recipient", $reply->href($attendee->Value()));
        $reply->CalDAVElement($response, "request-status", "2.0;Success");
        // Cargo-cult setting
        $reply->CalDAVElement($response, "calendar-data", $vcal->Render());
        $responses[] = $response;
    }
    $response = $reply->NewXMLElement("schedule-response", $responses, $reply->GetXmlNsArray(), 'urn:ietf:params:xml:ns:caldav');
    $request->XMLResponse(200, $response);
}
}
$bound_from = $collection->bound_from();
$collection_path = $collection->dav_name();
$request_via_binding = $bound_from != $collection_path;
$params = array(':collection_id' => $collection->GetProperty('collection_id'), ':sync_token' => $sync_token);
$sql = "SELECT new_sync_token( :sync_token, :collection_id)";
$qry = new AwlQuery($sql, $params);
if (!$qry->Exec("REPORT", __LINE__, __FILE__) || $qry->rows() <= 0) {
    $request->DoResponse(500, translate("Database error"));
}
$row = $qry->Fetch();
if (!isset($row->new_sync_token)) {
    /** If we got a null back then they gave us a sync token we know not of, so provide a full sync */
    $sync_token = 0;
    $params[':sync_token'] = $sync_token;
    if (!$qry->QDo($sql, $params) || $qry->rows() <= 0) {
        $request->DoResponse(500, translate("Database error"));
    }
    $row = $qry->Fetch();
}
$new_token = $row->new_sync_token;
if ($sync_token == $new_token) {
    // No change, so we just re-send the old token.
    $responses[] = new XMLElement('sync-token', 'data:,' . $new_token);
} else {
    $hide_older = '';
    if (isset($c->hide_older_than) && intval($c->hide_older_than) > 0) {
        $hide_older = " AND (CASE WHEN caldav_data.caldav_type<>'VEVENT' OR calendar_item.dtstart IS NULL THEN true ELSE calendar_item.dtstart > (now() - interval '" . intval($c->hide_older_than) . " days') END)";
    }
    $hide_todo = '';
    if (isset($c->hide_TODO) && ($c->hide_TODO === true || is_string($c->hide_TODO) && preg_match($c->hide_TODO, $_SERVER['HTTP_USER_AGENT'])) && !$collection->HavePrivilegeTo('all')) {
Example #19
0
function write_zone_aliases($tzid, $aliases)
{
    global $added_aliases;
    foreach ($aliases as $alias_node) {
        $alias = $alias_node->GetContent();
        $params = array(':tzid' => $tzid, ':alias' => $alias);
        $qry = new AwlQuery('SELECT * FROM tz_aliases JOIN timezones USING(our_tzno) WHERE tzid=:tzid AND tzalias=:alias', $params);
        if ($qry->Exec('tz/update', __LINE__, __FILE__) && $qry->rows() < 1) {
            $qry->QDo('INSERT INTO tz_aliases(our_tzno,tzalias) SELECT our_tzno, :alias FROM timezones WHERE tzid = :tzid', $params);
            $added_aliases++;
        }
    }
}
Example #20
0
    /**
     * Writes the data to a member in the collection and returns the segment_name of the resource in our internal namespace. 
     * @param $data iCalendar The resource to be written.
     * @param $create_resource boolean True if this is a new resource.
     * @param $segment_name The name of the resource within the collection.
     */
    function WriteCalendarMember($data, $create_resource, $segment_name = null)
    {
        if (!$this->IsSchedulingCollection() && !$this->IsCalendar()) {
            return false;
        }
        // function write_resource( $user_no, $path, $caldav_data, $collection_id, $author, $etag, $ic, $put_action_type, $caldav_context, $log_action=true, $weak_etag=null ) {
        global $tz_regex;
        $resources = $ic->GetComponents('VTIMEZONE', false);
        // Not matching VTIMEZONE
        if (!isset($resources[0])) {
            $resource_type = 'Unknown';
            /** @TODO: Handle writing non-calendar resources, like address book entries or random file data */
            rollback_on_error($caldav_context, $user_no, $path, translate('No calendar content'), 412);
            return false;
        } else {
            $first = $resources[0];
            $resource_type = $first->GetType();
        }
        $qry = new AwlQuery();
        $qry->Begin();
        $params = array(':dav_name' => $path, ':user_no' => $user_no, ':etag' => $etag, ':dav_data' => $caldav_data, ':caldav_type' => $resource_type, ':session_user' => $author, ':weak_etag' => $weak_etag);
        if ($put_action_type == 'INSERT') {
            create_scheduling_requests($vcal);
            $sql = 'INSERT INTO caldav_data ( user_no, dav_name, dav_etag, caldav_data, caldav_type, logged_user, created, modified, collection_id, weak_etag )
            VALUES( :user_no, :dav_name, :etag, :dav_data, :caldav_type, :session_user, current_timestamp, current_timestamp, :collection_id, :weak_etag )';
            $params[':collection_id'] = $collection_id;
        } else {
            update_scheduling_requests($vcal);
            $sql = 'UPDATE caldav_data SET caldav_data=:dav_data, dav_etag=:etag, caldav_type=:caldav_type, logged_user=:session_user,
            modified=current_timestamp, weak_etag=:weak_etag WHERE user_no=:user_no AND dav_name=:dav_name';
        }
        if (!$qry->QDo($sql, $params)) {
            rollback_on_error($caldav_context, $user_no, $path);
            return false;
        }
        $qry->QDo('SELECT dav_id FROM caldav_data WHERE dav_name = :dav_name ', array(':dav_name' => $path));
        if ($qry->rows() == 1 && ($row = $qry->Fetch())) {
            $dav_id = $row->dav_id;
        }
        $calitem_params = array(':dav_name' => $path, ':user_no' => $user_no, ':etag' => $etag);
        $dtstart = $first->GetPValue('DTSTART');
        $calitem_params[':dtstart'] = $dtstart;
        if ((!isset($dtstart) || $dtstart == '') && $first->GetPValue('DUE') != '') {
            $dtstart = $first->GetPValue('DUE');
        }
        $dtend = $first->GetPValue('DTEND');
        if (isset($dtend) && $dtend != '') {
            dbg_error_log('PUT', ' DTEND: "%s", DTSTART: "%s", DURATION: "%s"', $dtend, $dtstart, $first->GetPValue('DURATION'));
            $calitem_params[':dtend'] = $dtend;
            $dtend = ':dtend';
        } else {
            $dtend = 'NULL';
            if ($first->GetPValue('DURATION') != '' and $dtstart != '') {
                $duration = preg_replace('#[PT]#', ' ', $first->GetPValue('DURATION'));
                $dtend = '(:dtstart::timestamp with time zone + :duration::interval)';
                $calitem_params[':duration'] = $duration;
            } elseif ($first->GetType() == 'VEVENT') {
                /**
                 * From RFC2445 4.6.1:
                 * For cases where a "VEVENT" calendar component specifies a "DTSTART"
                 * property with a DATE data type but no "DTEND" property, the events
                 * non-inclusive end is the end of the calendar date specified by the
                 * "DTSTART" property. For cases where a "VEVENT" calendar component specifies
                 * a "DTSTART" property with a DATE-TIME data type but no "DTEND" property,
                 * the event ends on the same calendar date and time of day specified by the
                 * "DTSTART" property.
                 *
                 * So we're looking for 'VALUE=DATE', to identify the duration, effectively.
                 *
                 */
                $value_type = $first->GetPParamValue('DTSTART', 'VALUE');
                dbg_error_log('PUT', 'DTSTART without DTEND. DTSTART value type is %s', $value_type);
                if (isset($value_type) && $value_type == 'DATE') {
                    $dtend = '(:dtstart::timestamp with time zone::date + \'1 day\'::interval)';
                } else {
                    $dtend = ':dtstart';
                }
            }
        }
        $last_modified = $first->GetPValue('LAST-MODIFIED');
        if (!isset($last_modified) || $last_modified == '') {
            $last_modified = gmdate('Ymd\\THis\\Z');
        }
        $calitem_params[':modified'] = $last_modified;
        $dtstamp = $first->GetPValue('DTSTAMP');
        if (!isset($dtstamp) || $dtstamp == '') {
            $dtstamp = $last_modified;
        }
        $calitem_params[':dtstamp'] = $dtstamp;
        $class = $first->GetPValue('CLASS');
        /* Check and see if we should over ride the class. */
        /** @TODO: is there some way we can move this out of this function? Or at least get rid of the need for the SQL query here. */
        if (public_events_only($user_no, $path)) {
            $class = 'PUBLIC';
        }
        /*
         * It seems that some calendar clients don't set a class...
         * RFC2445, 4.8.1.3:
         * Default is PUBLIC
         */
        if (!isset($class) || $class == '') {
            $class = 'PUBLIC';
        }
        $calitem_params[':class'] = $class;
        /** Calculate what timezone to set, first, if possible */
        $last_tz_locn = 'Turkmenikikamukau';
        // I really hope this location doesn't exist!
        $tzid = $first->GetPParamValue('DTSTART', 'TZID');
        if (!isset($tzid) || $tzid == '') {
            $tzid = $first->GetPParamValue('DUE', 'TZID');
        }
        $timezones = $ic->GetComponents('VTIMEZONE');
        foreach ($timezones as $k => $tz) {
            if ($tz->GetPValue('TZID') != $tzid) {
                /**
                 * We'll pretend they didn't forget to give us a TZID and that they
                 * really hope the server is running in the timezone they supplied... but be noisy about it.
                 */
                dbg_error_log('ERROR', ' Event includes TZID[%s] but uses TZID[%s]!', $tz->GetPValue('TZID'), $tzid);
                $tzid = $tz->GetPValue('TZID');
            }
            // This is the one
            $tz_locn = $tz->GetPValue('X-LIC-LOCATION');
            if (!isset($tz_locn)) {
                if (preg_match('#([^/]+/[^/]+)$#', $tzid, $matches)) {
                    $tz_locn = $matches[1];
                } else {
                    if (isset($tzid) && $tzid != '') {
                        dbg_error_log('ERROR', ' Couldn\'t guess Olsen TZ from TZID[%s].  This may end in tears...', $tzid);
                    }
                }
            } else {
                if (!preg_match($tz_regex, $tz_locn)) {
                    if (preg_match('#([^/]+/[^/]+)$#', $tzid, $matches)) {
                        $tz_locn = $matches[1];
                    }
                }
            }
            dbg_error_log('PUT', ' Using TZID[%s] and location of [%s]', $tzid, isset($tz_locn) ? $tz_locn : '');
            if (isset($tz_locn) && $tz_locn != $last_tz_locn && preg_match($tz_regex, $tz_locn)) {
                dbg_error_log('PUT', ' Setting timezone to %s', $tz_locn);
                if ($tz_locn != '') {
                    $qry->QDo('SET TIMEZONE TO \'' . $tz_locn . "'");
                }
                $last_tz_locn = $tz_locn;
            }
            $params = array(':tzid' => $tzid);
            $qry = new AwlQuery('SELECT tz_locn FROM time_zone WHERE tz_id = :tzid', $params);
            if ($qry->Exec('PUT', __LINE__, __FILE__) && $qry->rows() == 0) {
                $params[':tzlocn'] = $tz_locn;
                $params[':tzspec'] = isset($tz) ? $tz->Render() : null;
                $qry->QDo('INSERT INTO time_zone (tz_id, tz_locn, tz_spec) VALUES(:tzid,:tzlocn,:tzspec)', $params);
            }
            if (!isset($tz_locn) || $tz_locn == '') {
                $tz_locn = $tzid;
            }
        }
        $created = $first->GetPValue('CREATED');
        if ($created == '00001231T000000Z') {
            $created = '20001231T000000Z';
        }
        $calitem_params[':created'] = $created;
        $calitem_params[':tzid'] = $tzid;
        $calitem_params[':uid'] = $first->GetPValue('UID');
        $calitem_params[':summary'] = $first->GetPValue('SUMMARY');
        $calitem_params[':location'] = $first->GetPValue('LOCATION');
        $calitem_params[':transp'] = $first->GetPValue('TRANSP');
        $calitem_params[':description'] = $first->GetPValue('DESCRIPTION');
        $calitem_params[':rrule'] = $first->GetPValue('RRULE');
        $calitem_params[':url'] = $first->GetPValue('URL');
        $calitem_params[':priority'] = $first->GetPValue('PRIORITY');
        $calitem_params[':due'] = $first->GetPValue('DUE');
        $calitem_params[':percent_complete'] = $first->GetPValue('PERCENT-COMPLETE');
        $calitem_params[':status'] = $first->GetPValue('STATUS');
        if ($put_action_type == 'INSERT') {
            $sql = <<<EOSQL
INSERT INTO calendar_item (user_no, dav_name, dav_id, dav_etag, uid, dtstamp,
                dtstart, dtend, summary, location, class, transp,
                description, rrule, tz_id, last_modified, url, priority,
                created, due, percent_complete, status, collection_id )
   VALUES ( :user_no, :dav_name, currval('dav_id_seq'), :etag, :uid, :dtstamp,
                :dtstart, {$dtend}, :summary, :location, :class, :transp,
                :description, :rrule, :tzid, :modified, :url, :priority,
                :created, :due, :percent_complete, :status, {$collection_id} )
EOSQL;
            $sync_change = 201;
        } else {
            $sql = <<<EOSQL
UPDATE calendar_item SET dav_etag=:etag, uid=:uid, dtstamp=:dtstamp,
                dtstart=:dtstart, dtend={$dtend}, summary=:summary, location=:location, class=:class, transp=:transp,
                description=:description, rrule=:rrule, tz_id=:tzid, last_modified=:modified, url=:url, priority=:priority,
                created=:created, due=:due, percent_complete=:percent_complete, status=:status
       WHERE user_no=:user_no AND dav_name=:dav_name
EOSQL;
            $sync_change = 200;
        }
        write_alarms($dav_id, $first);
        write_attendees($dav_id, $first);
        if ($log_action && function_exists('log_caldav_action')) {
            log_caldav_action($put_action_type, $first->GetPValue('UID'), $user_no, $collection_id, $path);
        } else {
            if ($log_action) {
                dbg_error_log('PUT', 'No log_caldav_action( %s, %s, %s, %s, %s) can be called.', $put_action_type, $first->GetPValue('UID'), $user_no, $collection_id, $path);
            }
        }
        $qry = new AwlQuery($sql, $calitem_params);
        if (!$qry->Exec('PUT', __LINE__, __FILE__)) {
            rollback_on_error($caldav_context, $user_no, $path);
            return false;
        }
        $qry->QDo("SELECT write_sync_change( {$collection_id}, {$sync_change}, :dav_name)", array(':dav_name' => $path));
        $qry->Commit();
        dbg_error_log('PUT', 'User: %d, ETag: %s, Path: %s', $author, $etag, $path);
        return $segment_name;
    }
Example #21
0
* stuffs the property somewhere we will be able to retrieve it from later.
*/
$qry = new AwlQuery();
$qry->Begin();
$setcalendar = count($xmltree->GetPath('/DAV::propertyupdate/DAV::set/DAV::prop/DAV::resourcetype/urn:ietf:params:xml:ns:caldav:calendar'));
foreach ($setprops as $k => $setting) {
    $tag = $setting->GetTag();
    $content = $setting->RenderContent();
    switch ($tag) {
        case 'DAV::displayname':
            /**
             * Can't set displayname on resources - only collections or principals
             */
            if ($dav_resource->IsCollection() || $dav_resource->IsPrincipal()) {
                if ($dav_resource->IsBinding()) {
                    $qry->QDo('UPDATE dav_binding SET dav_displayname = :displayname WHERE dav_name = :dav_name', array(':displayname' => $content, ':dav_name' => $dav_resource->dav_name()));
                } else {
                    if ($dav_resource->IsPrincipal()) {
                        $qry->QDo('UPDATE dav_principal SET fullname = :displayname, displayname = :displayname, modified = current_timestamp WHERE user_no = :user_no', array(':displayname' => $content, ':user_no' => $request->user_no));
                    } else {
                        $qry->QDo('UPDATE collection SET dav_displayname = :displayname, modified = current_timestamp WHERE dav_name = :dav_name', array(':displayname' => $content, ':dav_name' => $dav_resource->dav_name()));
                    }
                }
                $success[$tag] = 1;
            } else {
                $failure['set-' . $tag] = new XMLElement('propstat', array(new XMLElement('prop', new XMLElement($tag)), new XMLElement('status', 'HTTP/1.1 403 Forbidden'), new XMLElement('responsedescription', array(new XMLElement('error', new XMLElement('cannot-modify-protected-property')), translate("The displayname may only be set on collections, principals or bindings.")))));
            }
            break;
        case 'DAV::resourcetype':
            /**
             * We only allow resourcetype setting on a normal collection, and not on a resource, a principal or a bind.
Example #22
0
/**
* Update the local cache of the remote user details
* @param object $usr The user details we read from the remote.
*/
function UpdateUserFromExternal(&$usr)
{
    global $c;
    /**
     * When we're doing the create we will usually need to generate a user number
     */
    if (!isset($usr->user_no) || intval($usr->user_no) == 0) {
        $qry = new AwlQuery("SELECT nextval('usr_user_no_seq');");
        $qry->Exec('Login', __LINE__, __FILE__);
        $sequence_value = $qry->Fetch(true);
        // Fetch as an array
        $usr->user_no = $sequence_value[0];
    }
    $qry = new AwlQuery('SELECT * FROM usr WHERE user_no = :user_no', array(':user_no' => $usr->user_no));
    if ($qry->Exec('Login', __LINE__, __FILE__) && $qry->rows() == 1) {
        $type = "UPDATE";
        if ($old = $qry->Fetch()) {
            $changes = false;
            foreach ($usr as $k => $v) {
                if ($old->{$k} != $v) {
                    $changes = true;
                    dbg_error_log("Login", "User '%s' field '%s' changed from '%s' to '%s'", $usr->username, $k, $old->{$k}, $v);
                    break;
                }
            }
            if (!$changes) {
                dbg_error_log("Login", "No changes to user record for '%s' - leaving as-is.", $usr->username);
                if (isset($usr->active) && $usr->active == 'f') {
                    return false;
                }
                return;
                // Normal case, if there are no changes
            } else {
                dbg_error_log("Login", "Changes to user record for '%s' - updating.", $usr->username);
            }
        }
    } else {
        $type = "INSERT";
    }
    $params = array();
    if ($type != 'INSERT') {
        $params[':user_no'] = $usr->user_no;
    }
    $qry = new AwlQuery(sql_from_object($usr, $type, 'usr', 'WHERE user_no= :user_no'), $params);
    $qry->Exec('Login', __LINE__, __FILE__);
    /**
     * We disallow login by inactive users _after_ we have updated the local copy
     */
    if (isset($usr->active) && ($usr->active === 'f' || $usr->active === false)) {
        return false;
    }
    if ($type == 'INSERT') {
        $qry = new AwlQuery('INSERT INTO principal( type_id, user_no, displayname, default_privileges) SELECT 1, user_no, fullname, :privs::INT::BIT(24) FROM usr WHERE username=:username', array(':privs' => privilege_to_bits($c->default_privileges), ':username' => $usr->username));
        $qry->Exec('Login', __LINE__, __FILE__);
        CreateHomeCalendar($usr->username);
    } else {
        if ($usr->fullname != $old->{'fullname'}) {
            // Also update the displayname if the fullname has been updated.
            $qry->QDo('UPDATE principal SET displayname=:new_display WHERE user_no=:user_no', array(':new_display' => $usr->fullname, ':user_no' => $usr->user_no));
        }
    }
}
     SELECT user_no, parent_container, :archive_dav_name, random(),
            'Archive of ' || dav_displayname, true, current_timestamp, current_timestamp, false, false, false,
            resourcetypes, schedule_transp, timezone, 
            'Archive of ' || CASE WHEN description IS NULL OR description = '' THEN dav_name ELSE description END
       FROM collection 
      WHERE dav_name = :collection_dav_name
        AND NOT EXISTS(SELECT 1 FROM collection c2 WHERE c2.dav_name = :archive_dav_name)
EOSQL;
$collection_dav_name = sprintf('/%s/%s/', $args->principal, $args->collection);
$collection_archive = sprintf('/%s/%s-%s/', $args->principal, $args->collection, $args->archive_suffix);
$sqlargs = array(':collection_dav_name' => $collection_dav_name, ':archive_dav_name' => $collection_archive);
$qry = new AwlQuery($archive_collection_sql, $sqlargs);
$qry->query_time_warning = 5;
// Don't warn on queries unless they take more than 5 seconds.
if ($qry->Exec(__CLASS__, __LINE__, __FILE__)) {
    $qry->QDo('SELECT collection_id FROM collection WHERE dav_name = ?', $collection_dav_name);
    if ($qry->rows() != 1) {
        printf("Could not find source collection '%s'\n", $collection_dav_name);
        exit(1);
    }
    $row = $qry->Fetch();
    $source_collection_id = $row->collection_id;
    $qry->QDo('SELECT collection_id FROM collection WHERE dav_name = ?', $collection_archive);
    if ($qry->rows() != 1) {
        printf("Could not create archive collection '%s'!\n", $collection_archive);
        exit(2);
    }
    $row = $qry->Fetch();
    $archive_collection_id = $row->collection_id;
    $archive_sql = <<<EOSQL
UPDATE caldav_data