Overview

Packages

  • awl
    • caldav-client-v2
    • RRule
  • davical
    • authentication
      • drivers
    • caldav
    • DAViCalSession
    • DAVTicket
    • external-bind
    • feed
    • HTTPAuthSession
    • iSchedule
    • iSchedule-POST
    • logging
    • metrics
    • Principal
    • propfind
    • PublicSession
    • Request
    • Resource
    • tzservice
  • None

Classes

  • AtomEntry
  • AtomFeed
  • AtomXHTMLContent
  • CalDAVClient
  • CalDAVRequest
  • CalendarInfo
  • CheckResult
  • DAViCalSession
  • DAVPrincipal
  • DAVResource
  • DAVTicket
  • FakeSession
  • HTTPAuthSession
  • imapPamDriver
  • iSchedule
  • ldapDriver
  • Principal
  • PublicSession
  • pwauthPamDriver
  • RepeatRule
  • RepeatRuleDateRange
  • RepeatRuleDateTime
  • RepeatRuleTimeZone
  • Rfc5545Duration
  • rimapPamDriver
  • setupFakeSession
  • squidPamDriver
  • Tools
  • VCard
  • VTimezone
  • WritableCollection

Functions

  • access_ticket_browser
  • add_failure
  • add_proxy_response
  • array_values_mapping
  • auth_functions_deprecated
  • AuthExternalAWL
  • binding_row_editor
  • bindings_to_other_browser
  • bindings_to_us_browser
  • bits_to_privilege
  • build_dependencies_table
  • build_privileges_html
  • build_site_statistics
  • BuildSqlFilter
  • caldav_get_feed
  • calquery_apply_filter
  • cardquery_apply_filter
  • catch_setup_errors
  • check_awl_version
  • check_calendar
  • check_curl
  • check_database_connection
  • check_datetime
  • check_davical_version
  • check_for_expansion
  • check_gettext
  • check_iconv
  • check_ldap
  • check_magic_quotes_gpc
  • check_magic_quotes_runtime
  • check_pdo
  • check_pdo_pgsql
  • check_pgsql
  • check_real_php
  • check_schema_version
  • check_string
  • check_suhosin_server_strip
  • check_xml
  • checkiSchedule
  • collection_privilege_format_function
  • component_to_xml
  • confirm_delete_bind_in
  • confirm_delete_binding
  • confirm_delete_collection
  • confirm_delete_principal
  • confirm_delete_ticket
  • ConstructURL
  • controlRequestContainer
  • create_external
  • CreateDefaultRelationships
  • CreateHomeCalendar
  • CreateHomeCollections
  • DateToISODate
  • DeconstructURL
  • delete_collection
  • deliverItipCancel
  • display_status
  • do_error
  • do_scheduling_for_delete
  • do_scheduling_reply
  • do_scheduling_requests
  • doImipMessage
  • doItipAttendeeReply
  • doItipOrganizerCancel
  • early_catch_fatal_error
  • early_exception_handler
  • edit_binding_row
  • edit_grant_row_collection
  • edit_grant_row_principal
  • edit_group_row
  • edit_ticket_row
  • errorResponse
  • expand_event_instances
  • expand_properties
  • expand_timezone_onsets
  • export_iCalendar
  • fetch_external
  • fix_unique_member
  • generateKeys
  • get_address_properties
  • get_collection_contents
  • get_freebusy
  • get_href_containers
  • get_phpinfo
  • getComponentRange
  • GetItip
  • getPrincipalByID
  • getStaticLdap
  • getStatusMessage
  • GetTZID
  • getUserByEMail
  • getUserByID
  • getUserByName
  • getVCalendarRange
  • grant_row_editor
  • group_members_browser
  • group_memberships_browser
  • group_row_editor
  • handle_cancel_request
  • handle_freebusy_request
  • handle_schedule_reply
  • handle_schedule_request
  • handle_subaction
  • hyperlink
  • i18n
  • IMAP_PAM_check
  • import_addressbook_collection
  • import_calendar_collection
  • import_collection
  • ischedule_cancel
  • ischedule_freebusy_request
  • ischedule_get
  • ischedule_request
  • ISODateToHTTPDate
  • late_catch_fatal_error
  • LDAP_check
  • local_session_sql
  • log_caldav_action
  • log_setup_error
  • logRequestHeaders
  • make_help_link
  • obfuscated_event
  • olson_from_vtimezone
  • principal_collection_browser
  • principal_editor
  • principal_grants_browser
  • principal_privilege_format_function
  • print_metric
  • privilege_to_bits
  • privileges_to_XML
  • process_ace
  • processItipCancel
  • property_response
  • public_events_only
  • PWAUTH_PAM_check
  • rdate_expand
  • RIMAP_check
  • rollback
  • rollback_on_error
  • rrule_expand
  • send_dav_header
  • send_page_header
  • simple_write_resource
  • SqlFilterCardDAV
  • SqlFilterFragment
  • SQUID_PAM_check
  • SRVFormat
  • SRVOk
  • sync_LDAP
  • sync_LDAP_groups
  • sync_user_from_LDAP
  • ticket_row_editor
  • unicodeToUtf8
  • update_external
  • UpdateCollectionTimezones
  • UpdateUserFromExternal
  • utf8ToUnicode
  • write_alarms
  • write_attendees
  • write_resource
  • Overview
  • Package
  • Class
  • Tree
  • Deprecated
  • Todo
   1:    2:    3:    4:    5:    6:    7:    8:    9:   10:   11:   12:   13:   14:   15:   16:   17:   18:   19:   20:   21:   22:   23:   24:   25:   26:   27:   28:   29:   30:   31:   32:   33:   34:   35:   36:   37:   38:   39:   40:   41:   42:   43:   44:   45:   46:   47:   48:   49:   50:   51:   52:   53:   54:   55:   56:   57:   58:   59:   60:   61:   62:   63:   64:   65:   66:   67:   68:   69:   70:   71:   72:   73:   74:   75:   76:   77:   78:   79:   80:   81:   82:   83:   84:   85:   86:   87:   88:   89:   90:   91:   92:   93:   94:   95:   96:   97:   98:   99:  100:  101:  102:  103:  104:  105:  106:  107:  108:  109:  110:  111:  112:  113:  114:  115:  116:  117:  118:  119:  120:  121:  122:  123:  124:  125:  126:  127:  128:  129:  130:  131:  132:  133:  134:  135:  136:  137:  138:  139:  140:  141:  142:  143:  144:  145:  146:  147:  148:  149:  150:  151:  152:  153:  154:  155:  156:  157:  158:  159:  160:  161:  162:  163:  164:  165:  166:  167:  168:  169:  170:  171:  172:  173:  174:  175:  176:  177:  178:  179:  180:  181:  182:  183:  184:  185:  186:  187:  188:  189:  190:  191:  192:  193:  194:  195:  196:  197:  198:  199:  200:  201:  202:  203:  204:  205:  206:  207:  208:  209:  210:  211:  212:  213:  214:  215:  216:  217:  218:  219:  220:  221:  222:  223:  224:  225:  226:  227:  228:  229:  230:  231:  232:  233:  234:  235:  236:  237:  238:  239:  240:  241:  242:  243:  244:  245:  246:  247:  248:  249:  250:  251:  252:  253:  254:  255:  256:  257:  258:  259:  260:  261:  262:  263:  264:  265:  266:  267:  268:  269:  270:  271:  272:  273:  274:  275:  276:  277:  278:  279:  280:  281:  282:  283:  284:  285:  286:  287:  288:  289:  290:  291:  292:  293:  294:  295:  296:  297:  298:  299:  300:  301:  302:  303:  304:  305:  306:  307:  308:  309:  310:  311:  312:  313:  314:  315:  316:  317:  318:  319:  320:  321:  322:  323:  324:  325:  326:  327:  328:  329:  330:  331:  332:  333:  334:  335:  336:  337:  338:  339:  340:  341:  342:  343:  344:  345:  346:  347:  348:  349:  350:  351:  352:  353:  354:  355:  356:  357:  358:  359:  360:  361:  362:  363:  364:  365:  366:  367:  368:  369:  370:  371:  372:  373:  374:  375:  376:  377:  378:  379:  380:  381:  382:  383:  384:  385:  386:  387:  388:  389:  390:  391:  392:  393:  394:  395:  396:  397:  398:  399:  400:  401:  402:  403:  404:  405:  406:  407:  408:  409:  410:  411:  412:  413:  414:  415:  416:  417:  418:  419:  420:  421:  422:  423:  424:  425:  426:  427:  428:  429:  430:  431:  432:  433:  434:  435:  436:  437:  438:  439:  440:  441:  442:  443:  444:  445:  446:  447:  448:  449:  450:  451:  452:  453:  454:  455:  456:  457:  458:  459:  460:  461:  462:  463:  464:  465:  466:  467:  468:  469:  470:  471:  472:  473:  474:  475:  476:  477:  478:  479:  480:  481:  482:  483:  484:  485:  486:  487:  488:  489:  490:  491:  492:  493:  494:  495:  496:  497:  498:  499:  500:  501:  502:  503:  504:  505:  506:  507:  508:  509:  510:  511:  512:  513:  514:  515:  516:  517:  518:  519:  520:  521:  522:  523:  524:  525:  526:  527:  528:  529:  530:  531:  532:  533:  534:  535:  536:  537:  538:  539:  540:  541:  542:  543:  544:  545:  546:  547:  548:  549:  550:  551:  552:  553:  554:  555:  556:  557:  558:  559:  560:  561:  562:  563:  564:  565:  566:  567:  568:  569:  570:  571:  572:  573:  574:  575:  576:  577:  578:  579:  580:  581:  582:  583:  584:  585:  586:  587:  588:  589:  590:  591:  592:  593:  594:  595:  596:  597:  598:  599:  600:  601:  602:  603:  604:  605:  606:  607:  608:  609:  610:  611:  612:  613:  614:  615:  616:  617:  618:  619:  620:  621:  622:  623:  624:  625:  626:  627:  628:  629:  630:  631:  632:  633:  634:  635:  636:  637:  638:  639:  640:  641:  642:  643:  644:  645:  646:  647:  648:  649:  650:  651:  652:  653:  654:  655:  656:  657:  658:  659:  660:  661:  662:  663:  664:  665:  666:  667:  668:  669:  670:  671:  672:  673:  674:  675:  676:  677:  678:  679:  680:  681:  682:  683:  684:  685:  686:  687:  688:  689:  690:  691:  692:  693:  694:  695:  696:  697:  698:  699:  700:  701:  702:  703:  704:  705:  706:  707:  708:  709:  710:  711:  712:  713:  714:  715:  716:  717:  718:  719:  720:  721:  722:  723:  724:  725:  726:  727:  728:  729:  730:  731:  732:  733:  734:  735:  736:  737:  738:  739:  740:  741:  742:  743:  744:  745:  746:  747:  748:  749:  750:  751:  752:  753:  754:  755:  756:  757:  758:  759:  760:  761:  762:  763:  764:  765:  766:  767:  768:  769:  770:  771:  772:  773:  774:  775:  776:  777:  778:  779:  780:  781:  782:  783:  784:  785:  786:  787:  788:  789:  790:  791:  792:  793:  794:  795:  796:  797:  798:  799:  800:  801:  802:  803:  804:  805:  806:  807:  808:  809:  810:  811:  812:  813:  814:  815:  816:  817:  818:  819:  820:  821:  822:  823:  824:  825:  826:  827:  828:  829:  830:  831:  832:  833:  834:  835:  836:  837:  838:  839:  840:  841:  842:  843:  844:  845:  846:  847:  848:  849:  850:  851:  852:  853:  854:  855:  856:  857:  858:  859:  860:  861:  862:  863:  864:  865:  866:  867:  868:  869:  870:  871:  872:  873:  874:  875:  876:  877:  878:  879:  880:  881:  882:  883:  884:  885:  886:  887:  888:  889:  890:  891:  892:  893:  894:  895:  896:  897:  898:  899:  900:  901:  902:  903:  904:  905:  906:  907:  908:  909:  910:  911:  912:  913:  914:  915:  916:  917:  918:  919:  920:  921:  922:  923:  924:  925:  926:  927:  928:  929:  930:  931:  932:  933:  934:  935:  936:  937:  938:  939:  940:  941:  942:  943:  944:  945:  946:  947:  948:  949:  950:  951:  952:  953:  954:  955:  956:  957:  958:  959:  960:  961:  962:  963:  964:  965:  966:  967:  968:  969:  970:  971:  972:  973:  974:  975:  976:  977:  978:  979:  980:  981:  982:  983:  984:  985:  986:  987:  988:  989:  990:  991:  992:  993:  994:  995:  996:  997:  998:  999: 1000: 1001: 1002: 1003: 1004: 1005: 1006: 1007: 1008: 1009: 1010: 1011: 1012: 1013: 1014: 1015: 1016: 1017: 1018: 1019: 1020: 1021: 1022: 1023: 1024: 1025: 1026: 1027: 1028: 1029: 1030: 1031: 1032: 1033: 1034: 1035: 1036: 1037: 1038: 1039: 1040: 1041: 1042: 1043: 1044: 1045: 1046: 1047: 1048: 1049: 1050: 1051: 1052: 1053: 1054: 1055: 1056: 1057: 1058: 1059: 1060: 1061: 1062: 1063: 1064: 1065: 1066: 1067: 1068: 1069: 1070: 1071: 1072: 1073: 1074: 1075: 1076: 1077: 1078: 1079: 1080: 1081: 1082: 1083: 1084: 1085: 1086: 1087: 1088: 1089: 1090: 1091: 1092: 1093: 1094: 1095: 1096: 1097: 1098: 1099: 1100: 1101: 1102: 1103: 1104: 1105: 1106: 1107: 1108: 1109: 1110: 1111: 1112: 1113: 1114: 1115: 1116: 1117: 1118: 1119: 1120: 1121: 1122: 1123: 1124: 1125: 1126: 1127: 1128: 1129: 1130: 1131: 1132: 1133: 1134: 1135: 1136: 1137: 1138: 1139: 1140: 1141: 1142: 1143: 1144: 1145: 1146: 1147: 1148: 1149: 1150: 1151: 1152: 1153: 1154: 1155: 1156: 1157: 1158: 
<?php

param_to_global('id', 'int', 'old_id', 'principal_id' );

$privilege_names = array( 'read', 'write-properties', 'write-content', 'unlock', 'read-acl', 'read-current-user-privilege-set',
                         'bind', 'unbind', 'write-acl', 'read-free-busy', 'schedule-deliver-invite', 'schedule-deliver-reply',
                         'schedule-query-freebusy', 'schedule-send-invite', 'schedule-send-reply', 'schedule-send-freebusy' );
$privilege_xlate = array(
  'all' => translate('All privileges'),
  'read' => translate('Read'),
  'write-properties' => translate('Write Metadata'),
  'write-content' => translate('Write Data'),
  'unlock' => translate('Override a Lock'),
  'read-acl' => translate('Read Access Controls'),
  'read-current-user-privilege-set' => translate('Read Current User\'s Access'),
  'bind' => translate('Create Events/Collections'),
  'unbind' => translate('Delete Events/Collections'),
  'write-acl' => translate('Write Access Controls'),
  'read-free-busy' => translate('Read Free/Busy Information'),
  'schedule-deliver-invite' => translate('Scheduling: Deliver an Invitation'),
  'schedule-deliver-reply' => translate('Scheduling: Deliver a Reply'),
  'schedule-query-freebusy' => translate('Scheduling: Query free/busy'),
  'schedule-send-invite' => translate('Scheduling: Send an Invitation'),
  'schedule-send-reply' => translate('Scheduling: Send a Reply'),
  'schedule-send-freebusy' => translate('Scheduling: Send free/busy'),
  'write' => translate('Write'),
  'schedule-deliver' => translate('Scheduling: Delivery'),
  'schedule-send' => translate('Scheduling: Sending')
);

$can_write_principal = ($session->AllowedTo('Admin') || ($session->principal_id == $id));
if ( !$can_write_principal && $id > 0 ) {
  $target_principal = new Principal('principal_id', $id);
  $can_write_principal = $session->HavePrivilegeTo('DAV::write', $target_principal->dav_name());
}


$delete_collection_confirmation_required = null;
$delete_principal_confirmation_required = null;
$delete_ticket_confirmation_required = null;
$delete_bind_in_confirmation_required = null;
$delete_binding_confirmation_required = null;

function handle_subaction( $subaction ) {
  global $session, $c, $id, $editor;
  global $delete_collection_confirmation_required;
  global $delete_principal_confirmation_required;
  global $delete_ticket_confirmation_required;
  global $delete_bind_in_confirmation_required;
  global $delete_binding_confirmation_required;
  global $can_write_principal;

  dbg_error_log('admin-principal-edit',':handle_action: Action %s', $subaction );

  switch( $subaction ) {
    case 'delete_collection':
      dbg_error_log('admin-principal-edit',':handle_action: Deleting collection %s for principal %d', $_GET['dav_name'], $id );
      if ($can_write_principal) {
        if ( $session->CheckConfirmationHash('GET', 'confirm') ) {
          dbg_error_log('admin-principal-edit',':handle_action: Allowed to delete collection %s for principal %d', $_GET['dav_name'], $id );
          $qry = new AwlQuery('DELETE FROM collection WHERE dav_name=?;', $_GET['dav_name'] );
          if ( $qry->Exec() ) {
            $c->messages[] = i18n('Collection deleted.');
            return true;
          }
          else {
            $c->messages[] = i18n('There was an error writing to the database.');
            return false;
          }
        }
        else {
          $c->messages[] = i18n('Please confirm deletion of collection - see below');
          $delete_collection_confirmation_required = $session->BuildConfirmationHash('GET', 'confirm');
          return false;
        }
      }
      else {
        $c->messages[] = i18n('You are not allowed to delete collections for this principal.');
      }
      break;

    case 'delete_principal':
      dbg_error_log('admin-principal-edit',':handle_action: Deleting principal %d', $id );
      if ( $session->AllowedTo('Admin') ) {
        if ( isset($id) && $id > 1 && $session->CheckConfirmationHash('GET', 'confirm') ) {
          dbg_error_log('admin-principal-edit',':handle_action: Allowed to delete principal %d -%s', $id );
          $qry = new AwlQuery('DELETE FROM dav_principal WHERE principal_id=?', $id );
          if ( $qry->Exec() ) {
            $c->messages[] = i18n('Principal deleted.');
            return true;
          }
          else {
            $c->messages[] = i18n('There was an error writing to the database.');
            return false;
          }
        }
        else {
          $c->messages[] = i18n('Please confirm deletion of the principal');
          $delete_principal_confirmation_required = $session->BuildConfirmationHash('GET', 'confirm');
          return false;
        }
      }
      else {
        $c->messages[] = i18n('You are not allowed to delete principals.');
      }
      break;

    case 'delete_ticket':
      dbg_error_log('admin-principal-edit',':handle_action: Deleting ticket "%s" for principal %d', $_GET['ticket_id'], $id );
      if ($can_write_principal) {
        if ( $session->CheckConfirmationHash('GET', 'confirm') ) {
          dbg_error_log('admin-principal-edit',':handle_action: Allowed to delete ticket "%s" for principal %d', $_GET['ticket_id'], $id );
          $qry = new AwlQuery('DELETE FROM access_ticket WHERE ticket_id=?;', $_GET['ticket_id'] );
          if ( $qry->Exec() ) {
            $c->messages[] = i18n('Access ticket deleted.');
            return true;
          }
          else {
            $c->messages[] = i18n('There was an error writing to the database.');
            return false;
          }
        }
        else {
          $c->messages[] = i18n('Please confirm deletion of access ticket - see below');
          $delete_ticket_confirmation_required = $session->BuildConfirmationHash('GET', 'confirm');
          return false;
        }
      }
      else {
        $c->messages[] = i18n('You are not allowed to delete tickets for this principal.');
      }
      break;

    case 'delete_bind_in':
    case 'delete_binding':
      dbg_error_log('admin-principal-edit',':handle_action: Deleting binding "%s" for principal %d', $_GET['bind_id'], $id );
      if ($can_write_principal) {
        if ( $session->CheckConfirmationHash('GET', 'confirm') ) {
          dbg_error_log('admin-principal-edit',':handle_action: Allowed to delete binding "%s" for principal %d', $_GET['bind_id'], $id );
          $qry = new AwlQuery('DELETE FROM dav_binding WHERE bind_id=?;', $_GET['bind_id'] );
          if ( $qry->Exec() ) {
            $c->messages[] = i18n('Binding deleted.');
            return true;
          }
          else {
            $c->messages[] = i18n('There was an error writing to the database.');
            return false;
          }
        }
        else {
          $c->messages[] = i18n('Please confirm deletion of binding - see below');
          if ( $subaction == 'delete_bind_in' ) {
            $delete_bind_in_confirmation_required = $session->BuildConfirmationHash('GET', 'confirm');
          }
          else {
            $delete_binding_confirmation_required = $session->BuildConfirmationHash('GET', 'confirm');
          }
          return false;
        }
      }
      else {
        $c->messages[] = i18n('You are not allowed to delete bindings for this principal.');
      }
      break;

      default:
      return false;
  }
  return false;
}

function principal_editor() {
  global $c, $id, $can_write_principal, $session;
  $editor = new Editor(translate('Principal'), 'dav_principal');

  $editor->SetLookup( 'date_format_type', "SELECT 'E', 'European' UNION SELECT 'U', 'US Format' UNION SELECT 'I', 'ISO Format'" );
  $editor->SetLookup( 'type_id', 'SELECT principal_type_id, principal_type_desc FROM principal_type ORDER BY principal_type_id' );
  $editor->SetLookup( 'locale', 'SELECT \'\', \''.translate("*** Default Locale ***").'\' UNION SELECT locale, locale_name_locale FROM supported_locales ORDER BY 1 ASC' );
  $editor->AddAttribute( 'locale', 'title', translate("The preferred language for this person.") );
  $editor->AddAttribute( 'fullname', 'title', translate("The full name for this person, group or other type of principal.") );
  $editor->AddAttribute( 'email', 'title', translate("The email address identifies principals when processing invitations and freebusy lookups. It should be set to a unique value.") );
  $editor->SetWhere( 'principal_id='.$id );

  $editor->AddField('is_admin', 'EXISTS( SELECT 1 FROM role_member WHERE role_no = 1 AND role_member.user_no = dav_principal.user_no )' );
  $editor->AddAttribute('is_admin', 'title', translate('An "Administrator" user has full rights to the whole DAViCal System'));

  $post_values = false;

  if ( isset($_POST['xxxxusername']) ) {
    $_POST['xxxxusername'] = trim(str_replace('/', '', $_POST['xxxxusername']));
    if ( $_POST['xxxxusername'] == '' ) {
      $c->messages[] = i18n("The username must not be blank, and may not contain a slash");
      $can_write_principal = false;
    }
  };
  if ( isset($_POST['fullname']) && trim($_POST['fullname']) == '' ) {
    $c->messages[] = i18n("The full name must not be blank.");
    $can_write_principal = false;
  };
  if ( isset($_POST['email']) && trim($_POST['email']) == '' ) {
    $c->messages[] = i18n("The email address really should not be blank.");
  }

  $pwstars = '@@@@@@@@@@';
  if ( $can_write_principal && $editor->IsSubmit() ) {
    $editor->WhereNewRecord( "principal_id=(SELECT CURRVAL('dav_id_seq'))" );
    if ( ! $session->AllowedTo('Admin') ) {
      unset($_POST['admin_role']);
      unset($_POST['user_active']);
    }
    unset($_POST['password']);
    if ( $_POST['newpass1'] != '' && $_POST['newpass1'] != $pwstars ) {
      if ( $_POST['newpass1'] == $_POST['newpass2'] ) {
        $_POST['password'] = $_POST['newpass1'];
      }
      else {
        $c->messages[] = "Password not updated. The supplied passwords do not match.";
      }
    }
    if ( isset($_POST['fullname']) && !isset($_POST['displayname']) ) {
      $_POST['displayname'] = $_POST['fullname'];
    }
    if ( isset($_POST['locale']) ) {
      awl_set_locale($_POST['locale']); // activate immediately
    }
    if ( isset($_POST['default_privileges']) ) {
      $privilege_bitpos = array_flip($privilege_names);
      $priv_names = array_keys($_POST['default_privileges']);
      $privs = privilege_to_bits($priv_names);
      $_POST['default_privileges'] = sprintf('%024s',decbin($privs));
      $editor->Assign('default_privileges', $privs_dec);
    }
    if ( $editor->IsCreate() ) {
      $c->messages[] = i18n("Creating new Principal record.");
    }
    else {
      $c->messages[] = i18n("Updating Principal record.");
    }
    $editor->Write();
    if ( $_POST['type_id'] != 3 && $editor->IsCreate() ) {
      /** We only add the default calendar if it isn't a group, and this is a create action */
      require_once('auth-functions.php');
      CreateHomeCollections($editor->Value('username'), $c->default_timezone);
      CreateDefaultRelationships($editor->Value('username'));
    }
    if ( $session->AllowedTo('Admin') ) {
      if ( $_POST['is_admin'] == 'on' ) {
        $sql = 'INSERT INTO role_member (role_no, user_no) SELECT 1, dav_principal.user_no FROM dav_principal WHERE user_no = :user_no AND NOT EXISTS(SELECT 1 FROM role_member rm WHERE rm.role_no = 1 AND rm.user_no = dav_principal.user_no )';
        $editor->Assign('is_admin', 't');
      }
      else {
        $sql = 'DELETE FROM role_member WHERE role_no = 1 AND user_no = :user_no';
        $editor->Assign('is_admin', 'f');
      }
      $params[':user_no'] = $editor->Value('user_no');
      $qry = new AwlQuery( $sql, $params );
      $qry->Exec('admin-principal-edit');
    }
  }
  else if ( isset($id) && $id > 0 ) {
    $editor->GetRecord();
    if ( $editor->IsSubmit() ) {
      $c->messages[] = i18n('You do not have permission to modify this record.');
    }
  }
  if ( $editor->Available() ) {
    $c->page_title = $editor->Title(translate('Principal').': '.$editor->Value('fullname'));
  }
  else {
    $c->page_title = $editor->Title(translate('Create New Principal'));
    $privs = decbin(privilege_to_bits($c->default_privileges));
    $editor->Assign('default_privileges', $privs);
    $editor->Assign('user_active', 't');
    foreach( $c->template_usr AS $k => $v ) {
      $editor->Assign($k, $v);
    }
  }
  if ( $post_values ) {
    $editor->PostToValues();
    if ( isset($_POST['default_privileges']) ) {
      $privilege_bitpos = array_flip($privilege_names);
      $priv_names = array_keys($_POST['default_privileges']);
      $privs = privilege_to_bits($priv_names);
      $_POST['default_privileges'] = sprintf('%024s',decbin($privs));
      $editor->Assign('default_privileges', $_POST['default_privileges']);
    }
  }


  $prompt_principal_id = translate('Principal ID');
  $value_id = ( $editor->Available() ? '##principal_id.hidden####principal_id.value##' : translate('New Principal'));
  $prompt_username = translate('Username');
  $prompt_password_1 = translate('Change Password');
  $prompt_password_2 = translate('Confirm Password');
  $prompt_fullname = translate('Fullname');
  $prompt_displayname = translate('Display Name');
  $prompt_email = translate('Email Address');
  $prompt_date_format = translate('Date Format Style');
  $prompt_admin = translate('Administrator');
  $prompt_active = translate('Active');
  $prompt_locale = translate('Locale');
  $prompt_type = translate('Principal Type');
  $prompt_privileges = translate('Privileges granted to All Users');

  if ($can_write_principal) {
    $privs_html = build_privileges_html( $editor, 'default_privileges');
    $submit_row = '<tr> <th class="right"></th>                   <td class="left" colspan="2">##submit##</td> </tr>';
  } else {
    $privs_html = principal_privilege_format_function( $editor->Value('default_privileges') );
    $submit_row = '';
  }

  $admin_row_entry = '';
  $delete_principal_button = '';
  if ( $session->AllowedTo('Admin') ) {
    $admin_row_entry = ' <tr> <th class="right">'.$prompt_admin.':</th><td class="left">##is_admin.checkbox##</td> </tr>';
    $admin_row_entry .= ' <tr> <th class="right">'.$prompt_active.':</th><td class="left">##user_active.checkbox##</td> </tr>';
    if ( isset($id) )
      $delete_principal_button = '<a href="'.$c->base_url . '/admin.php?action=edit&t=principal&subaction=delete_principal&id='.$id.'" class="submit">' . translate("Delete Principal") . '</a>';
  }

  $email_unique = '';
  $qry = new AwlQuery('SELECT user_no FROM usr WHERE lower(usr.email) = lower(:email)',
                        array( ':email' => $editor->Value('email') ));
  $qry->Exec('principal-edit', __LINE__, __FILE__);
  if ($qry->rows() > 1 ) {
      $email_unique = ' <b style="color:red;">' . translate('Attention: email address not unique, scheduling may not work!') . '</b>';
  }

  $id = $editor->Value('principal_id');
  $template = <<<EOTEMPLATE
##form##
<script language="javascript">
function toggle_privileges() {
  var argv = toggle_privileges.arguments;
  var argc = argv.length;

  if ( argc < 2 ) {
    return;
  }
  var match_me = argv[0];

  var set_to = -1;
  if ( argv[1] == 'all' ) {
    var form = document.getElementById(argv[2]);
    var fieldcount = form.elements.length;
    var matching = '/^' + match_me + '/';
    for (var i = 0; i < fieldcount; i++) {
      var fieldname = form.elements[i].name;
      if ( fieldname.match( match_me ) ) {
        if ( set_to == -1 ) {
          set_to = ( form.elements[i].checked ? 0 : 1 );
        }
        form.elements[i].checked = set_to;
      }
    }
  }
  else {
    for (var i = 1; i < argc; i++) {
      var f = document.getElementById( match_me + '_' + argv[i]);
      if ( set_to == -1 ) {
        set_to = ( f.checked ? 0 : 1 );
      }
      f.checked = set_to;
    }
  }
}
</script>
<style>
th.right, label.privilege {
  white-space:nowrap;
}
label.privilege {
  margin:0.2em 1em 0.2em 0.1em;
  padding:0 0.2em;
  line-height:1.6em;
  font-size:87%;
}
</style>
<table>
 <tr> <th class="right">$prompt_principal_id:</th><td class="left">
  <table width="100%" class="form_inner"><tr>
   <td>$value_id</td>
   <td align="right">$delete_principal_button</td>
  </tr></table>
 </td></tr>
 <tr> <th class="right">$prompt_username:</th>    <td class="left">##xxxxusername.input.50##</td> </tr>
 <tr> <th class="right">$prompt_password_1:</th>  <td class="left">##newpass1.password.$pwstars##</td> </tr>
 <tr> <th class="right">$prompt_password_2:</th>  <td class="left">##newpass2.password.$pwstars##</td> </tr>
 <tr> <th class="right">$prompt_fullname:</th>    <td class="left">##fullname.input.50##</td> </tr>
 <tr> <th class="right">$prompt_email:</th>       <td class="left">##email.input.50##$email_unique</td> </tr>
 <tr> <th class="right">$prompt_locale:</th>      <td class="left">##locale.select##</td> </tr>
 <tr> <th class="right">$prompt_date_format:</th> <td class="left">##date_format_type.select##</td> </tr>
 <tr> <th class="right">$prompt_type:</th>        <td class="left">##type_id.select##</td> </tr>
 $admin_row_entry
 <tr> <th class="right" style="white-space:normal;">$prompt_privileges:</th><td class="left">$privs_html</td> </tr>
 $submit_row
</table>
</form>
EOTEMPLATE;

  $editor->SetTemplate( $template );
  return $editor;
}


function build_privileges_html( $ed, $fname ) {
  global $privilege_xlate, $privilege_names;

  $btn_all = htmlspecialchars(translate('All'));             $btn_all_title = htmlspecialchars(translate('Toggle all privileges'));
  $btn_rw  = htmlspecialchars(translate('Read/Write'));      $btn_rw_title = htmlspecialchars(translate('Set read+write privileges'));
  $btn_read = htmlspecialchars(translate('Read'));           $btn_read_title = htmlspecialchars(translate('Set read privileges'));
  $btn_fb = htmlspecialchars(translate('Free/Busy'));        $btn_fb_title = htmlspecialchars(translate('Set free/busy privileges'));
  $btn_sd = htmlspecialchars(translate('Schedule Deliver')); $btn_sd_title = htmlspecialchars(translate('Privileges to allow delivery of scheduling messages'));
  $btn_ss = htmlspecialchars(translate('Schedule Send'));    $btn_ss_title = htmlspecialchars(translate('Privileges to delegate scheduling decisions'));

  $privs_dec = bindec($ed->Value($fname));
  $privileges_set = sprintf('<div id="privileges"><input type="hidden" name="%s[fake_privilege_for_input]" value="0">%s', $fname, "\n");
  for( $i=0; $i < count($privilege_names); $i++ ) {
    $privilege_set = ( (1 << $i) & $privs_dec ? ' CHECKED' : '');
    $privileges_set .= sprintf( '  <label class="privilege"><input name="%s[%s]" id="%s_%s" type="checkbox"%s>%s</label>'."\n",
                  $fname, $privilege_names[$i], $fname, $privilege_names[$i], $privilege_set,
                  $privilege_xlate[$privilege_names[$i]]);
  }
  $privileges_set .= '</div>'."\n";

  $form_id = $ed->Id();
  $html = <<<EOTEMPLATE
<input type="button" value="$btn_all" class="submit" title="$btn_all_title"
 onclick="toggle_privileges('$fname', 'all', 'form_$form_id');">
<input type="button" value="$btn_rw" class="submit" title="$btn_rw_title"
 onclick="toggle_privileges('$fname', 'read', 'write-properties', 'write-content', 'bind', 'unbind', 'read-free-busy', 'read-current-user-privilege-set', 'schedule-deliver-invite', 'schedule-deliver-reply', 'schedule-query-freebusy', 'schedule-send-invite', 'schedule-send-reply', 'schedule-send-freebusy' );">
<input type="button" value="$btn_read" class="submit" title="$btn_read_title"
 onclick="toggle_privileges('$fname', 'read', 'read-free-busy', 'schedule-query-freebusy', 'read-current-user-privilege-set' );">
<input type="button" value="$btn_fb" class="submit" title="$btn_fb_title"
 onclick="toggle_privileges('$fname', 'read-free-busy', 'schedule-query-freebusy' );">
<input type="button" value="$btn_sd" class="submit" title="$btn_sd_title"
 onclick="toggle_privileges('$fname', 'schedule-deliver-invite', 'schedule-deliver-reply', 'schedule-query-freebusy' );">
<input type="button" value="$btn_ss" class="submit" title="$btn_ss_title"
 onclick="toggle_privileges('$fname', 'schedule-send-invite', 'schedule-send-reply', 'schedule-send-freebusy' );">
<br>$privileges_set
EOTEMPLATE;

  return $html;
}


/**
* principal_privilege_format_function is for formatting the binary privileges from the
* database, including localising them.  This is a hook function for a browser
* column object, so it takes three parameters:
* @param mixed $value The value of the column.
* @param BrowserColumn $column The BrowserColumn object we are hooked into.
* @param dbrow $row The row object we read from the database.
* @return string The formatted privileges.
*/
function principal_privilege_format_function( $value, $column, $row ) {
  global $privilege_xlate;

  $privs = bits_to_privilege($value,'*');
  $formatted = '';
  foreach( $privs AS $k => $v ) {
    $formatted .= ($formatted == '' ? '' : ', ');
    $v = preg_replace( '{^.*:}', '', $v );
    $formatted .= (isset($privilege_xlate[$v]) ? $privilege_xlate[$v] : $v );
  }
  return $formatted;
}


function confirm_delete_principal($confirmation_hash, $displayname ) {
  $html = '<p class="error">';
  $html .= sprintf('<b>%s</b> \'%s\' <a class="error" href="%s&%s">%s</a> %s',
       translate('Deleting Principal:'), $displayname, $_SERVER['REQUEST_URI'],
        $confirmation_hash, translate('Confirm Deletion of the Principal'),
        translate('All of the principal\'s calendars and events will be unrecoverably deleted.') );
  $html .= "</p>\n";
  return $html;
}



function group_memberships_browser() {
  global $c, $id, $editor;
  $browser = new Browser(translate('Group Memberships'));

  $browser->AddColumn( 'group_id', translate('ID'), 'right', '##principal_link##' );
  $rowurl = $c->base_url . '/admin.php?action=edit&t=principal&id=';
  $browser->AddHidden( 'principal_link', "'<a href=\"$rowurl' || principal_id || '\">' || principal_id || '</a>'" );
  $browser->AddColumn( 'displayname', translate('Display Name') );
  $browser->AddColumn( 'member_of', translate('Is Member of'), '', '', 'is_member_of_list(principal_id)' );
  $browser->AddColumn( 'members', translate('Has Members'), '', '', 'has_members_list(principal_id)' );

  $browser->SetOrdering( 'displayname', 'A' );

  $browser->SetJoins( "group_member LEFT JOIN dav_principal ON (group_id = principal_id) " );
  $browser->SetWhere( 'user_active AND member_id = '.$id );

  if ( $c->enable_row_linking ) {
    $browser->RowFormat( '<tr onMouseover="LinkHref(this,1);" title="'.translate('Click to edit principal details').'" class="r%d">', '</tr>', '#even' );
  }
  else {
    $browser->RowFormat( '<tr class="r%d">', '</tr>', '#even' );
  }
  $browser->DoQuery();
  return $browser;
}


function group_row_editor() {
  global $c, $id, $editor, $can_write_principal;
  $grouprow = new Editor("Group Members", "group_member");
  $sql = 'SELECT principal_id, coalesce(displayname,fullname,username) FROM dav_principal ';
  $sql .= 'WHERE principal_id NOT IN (SELECT member_id FROM group_member WHERE group_id = '.$id.') ';
  $sql .= 'AND principal_id != '.$id;
  $sql .= 'ORDER BY 2';
  $grouprow->SetLookup( 'member_id', $sql);
  $grouprow->SetSubmitName( 'savegrouprow' );

  if ( $can_write_principal ) {
    if ( $grouprow->IsSubmit() ) {
      if ( $grouprow->IsUpdate() )
        $c->messages[] = translate('Updating Member of this Group Principal');
      else
        $c->messages[] = translate('Adding new member to this Group Principal');

      $_POST['group_id'] = $id;
      $member_id = intval($_POST['member_id']);
      $grouprow->SetWhere( 'group_id='.$id.' AND member_id='.$member_id);
      $grouprow->Write( );
      unset($_GET['member_id']);
    }
    elseif ( isset($_GET['delete_member']) ) {
      $qry = new AwlQuery('DELETE FROM group_member WHERE group_id=:group_id AND member_id = :member_id',
                            array( ':group_id' => $id, ':member_id' => intval($_GET['delete_member']) ));
      $qry->Exec('principal-edit');
      $c->messages[] = translate('Member deleted from this Group Principal');
    }
  }
  return $grouprow;
}


function edit_group_row( $row_data ) {
  global $id, $grouprow;

  $form_url = preg_replace( '#&(edit|delete)_group=\d+#', '', $_SERVER['REQUEST_URI'] );

  $template = <<<EOTEMPLATE
<form method="POST" enctype="multipart/form-data" id="add_group" action="$form_url">
  <td class="left"><input type="hidden" name="id" value="$id"></td>
  <td class="left" colspan="3">##member_id.select## &nbsp; ##Add.submit##</td>
  <td class="center"></td>
</form>

EOTEMPLATE;

  $grouprow->SetTemplate( $template );
  $grouprow->Title("");
  if ( $row_data->group_id > -1 ) $grouprow->SetRecord( $row_data );

  return $grouprow->Render();
}

function group_members_browser() {
  global $c, $id, $editor, $can_write_principal;
  $browser = new Browser(translate('Group Members'));

  $browser->AddColumn( 'group_id', translate('ID'), 'right', '##principal_link##' );
  $rowurl = $c->base_url . '/admin.php?action=edit&t=principal&id=';
  $browser->AddHidden( 'principal_id' );
  $browser->AddHidden( 'principal_link', "'<a href=\"$rowurl' || principal_id || '\">' || principal_id || '</a>'" );
  $browser->AddColumn( 'displayname', translate('Display Name') );
  $browser->AddColumn( 'member_of', translate('Is Member of'), '', '', 'is_member_of_list(principal_id)' );
  $browser->AddColumn( 'members', translate('Has Members'), '', '', 'has_members_list(principal_id)' );

  if ( $can_write_principal ) {
    $del_link  = '<a href="'.$c->base_url.'/admin.php?action=edit&t=principal&id='.$id.'&delete_member=##principal_id##" class="submit" title="">'.translate('Remove').'</a>';
    $browser->AddColumn( 'action', translate('Action'), 'center', '', "'$edit_link&nbsp;$del_link'" );
  }

  $browser->SetOrdering( 'displayname', 'A' );

  $browser->SetJoins( "group_member LEFT JOIN dav_principal ON (member_id = principal_id) " );
  $browser->SetWhere( 'user_active AND group_id = '.$id );

  if ( $c->enable_row_linking ) {
    $browser->RowFormat( '<tr onMouseover="LinkHref(this,1);" title="'.translate('Click to edit principal details').'" class="r%d">', '</tr>', '#even' );
  }
  else {
    $browser->RowFormat( '<tr class="r%d">', '</tr>', '#even' );
  }
  $browser->DoQuery();

  if ( $can_write_principal ) {
    $browser->ExtraRowFormat( '<tr class="r%d">', '</tr>', '#even' );
    $extra_row = array( 'group_id' => -1 );
    $browser->MatchedRow('group_id', -1, 'edit_group_row');
    $extra_row = (object) $extra_row;
    $browser->AddRow($extra_row);
  }
  return $browser;
}


function grant_row_editor() {
  global $c, $id, $editor, $can_write_principal, $privilege_names;

  $grantrow = new Editor("Grants", "grants");
  $grantrow->SetSubmitName( 'savegrantrow' );
  $edit_grant_clause = '';
  if ( isset($_GET['edit_grant']) ) {
    $edit_grant_clause = ' AND to_principal != '.intval($_GET['edit_grant']);
  }
  $grantrow->SetLookup( 'to_principal', 'SELECT principal_id, displayname FROM dav_principal WHERE user_active AND principal_id NOT IN (SELECT to_principal FROM grants WHERE by_principal = '.$id.$edit_grant_clause.') ORDER BY fullname' );
  if ( $can_write_principal ) {
    if ( $grantrow->IsSubmit() ) {
      if ( $grantrow->IsUpdate() )
        $c->messages[] = translate('Updating grants by this Principal');
      else
        $c->messages[] = translate('Granting new privileges from this Principal');
      $_POST['by_principal'] = $id;
      $to_principal = intval($_POST['to_principal']);
      $orig_to_id =  intval($_POST['orig_to_id']);
      $grantrow->SetWhere( 'by_principal='.$id.' AND to_principal='.$orig_to_id);
      if ( isset($_POST['grant_privileges']) ) {
        $privilege_bitpos = array_flip($privilege_names);
        $priv_names = array_keys($_POST['grant_privileges']);
        $privs_dec = privilege_to_bits($priv_names);
        $_POST['privileges'] = sprintf('%024s',decbin($privs_dec));
        $grantrow->Assign('privileges', $privs_dec);
      }
      $grantrow->Write( );
      unset($_GET['to_principal']);
    }
    elseif ( isset($_GET['delete_grant']) ) {
      $qry = new AwlQuery("DELETE FROM grants WHERE by_principal=:grantor_id AND to_principal = :to_principal",
                            array( ':grantor_id' => $id, ':to_principal' => intval($_GET['delete_grant']) ));
      $qry->Exec('principal-edit');
      $c->messages[] = translate('Deleted a grant from this Principal');
    }
  }
  return $grantrow;
}


function edit_grant_row_principal( $row_data ) {
  global $id, $grantrow;

  $orig_to_id = intval($row_data->to_principal);
  if ( $orig_to_id > -1 ) {
    $grantrow->SetRecord( $row_data );
  }
  else {
    $grantrow->Initialise( $row_data );
  }

  $privs_html = build_privileges_html( $grantrow, 'grant_privileges' );

  $form_id = $grantrow->Id();
  $form_url = preg_replace( '#&(edit|delete)_grant=\d+#', '', $_SERVER['REQUEST_URI'] );

  $template = <<<EOTEMPLATE
<form method="POST" enctype="multipart/form-data" id="form_$form_id" action="$form_url">
  <td class="left" colspan="2"><input type="hidden" name="id" value="$id"><input type="hidden" name="orig_to_id" value="$orig_to_id">##to_principal.select##</td>
  <td class="left" colspan="2">$privs_html</td>
  <td class="center">##submit##</td>
</form>

EOTEMPLATE;

  $grantrow->SetTemplate( $template );
  $grantrow->Title("");

  return $grantrow->Render();
}


function principal_grants_browser() {
  global $c, $id, $editor, $can_write_principal;
    $browser = new Browser(translate('Principal Grants'));

  $browser->AddColumn( 'to_principal', translate('To ID'), 'right', '##principal_link##' );
  $rowurl = $c->base_url . '/admin.php?action=edit&t=principal&id=';
  $browser->AddHidden( 'principal_link', "'<a href=\"$rowurl' || to_principal || '\">' || to_principal || '</a>'" );
  $browser->AddHidden( 'grant_privileges', 'privileges' );
  $browser->AddColumn( 'displayname', translate('Display Name') );
  $browser->AddColumn( 'privs', translate('Privileges'), '', '', 'privileges', '', '', 'principal_privilege_format_function' );
  $browser->AddColumn( 'members', translate('Has Members'), '', '', 'has_members_list(principal_id)' );

  if ( $can_write_principal ) {
    $del_link  = '<a href="'.$c->base_url.'/admin.php?action=edit&t=principal&id='.$id.'&delete_grant=##to_principal##" class="submit" title="">'.translate('Revoke').'</a>';
    $edit_link  = '<a href="'.$c->base_url.'/admin.php?action=edit&t=principal&id='.$id.'&edit_grant=##to_principal##" class="submit" title="">'.translate('Edit').'</a>';
    $browser->AddColumn( 'action', translate('Action'), 'center', '', "'$edit_link&nbsp;$del_link'" );
  }

  $browser->SetOrdering( 'displayname', 'A' );

  $browser->SetJoins( "grants LEFT JOIN dav_principal ON (to_principal = principal_id) " );
  $browser->SetWhere( 'by_principal = '.$id );

  if ( $c->enable_row_linking ) {
    $browser->RowFormat( '<tr onMouseover="LinkHref(this,1);" title="'.translate('Click to edit principal details').'" class="r%d">', '</tr>', '#even' );
  }
  else {
    $browser->RowFormat( '<tr class="r%d">', '</tr>', '#even' );
  }
  $browser->DoQuery();


  if ( $can_write_principal ) {
    if ( isset($_GET['edit_grant']) ) {
      $browser->MatchedRow('to_principal', $_GET['edit_grant'], 'edit_grant_row_principal');
    }
    else if ( isset($id ) ) {
      $browser->ExtraRowFormat( '<tr class="r%d">', '</tr>', '#even' );
      $extra_row = array( 'to_principal' => -1 );
      $browser->MatchedRow('to_principal', -1, 'edit_grant_row_principal');
      $extra_row = (object) $extra_row;
      $browser->AddRow($extra_row);
    }
  }
  return $browser;
}


function ticket_row_editor() {
  global $c, $id, $editor, $can_write_principal, $privilege_names;

  $ticketrow = new Editor("Tickets", "access_ticket");
  $ticketrow->SetSubmitName( 'ticketrow' );
  if ( $can_write_principal && $ticketrow->IsSubmit() ) {

    $username = $editor->Value('username');
    $ugly_path = $_POST['target'];
    if ( $ugly_path == '/'.$username || $ugly_path == '/'.$username.'/' ) {
      $target_collection = $id;
    }
    else {
      $username_len = strlen($username) + 2;
      $sql = "SELECT collection_id FROM collection WHERE dav_name = :exact_name";
      $sql .= " AND substring(dav_name FROM 1 FOR $username_len) = '/$username/'";
      $params = array( ':exact_name' => $ugly_path );
      if ( !preg_match( '#/$#', $ugly_path ) ) {
        $sql .= " OR dav_name = :truncated_name OR dav_name = :trailing_slash_name";
        $params[':truncated_name'] = preg_replace( '#[^/]*$#', '', $ugly_path);
        $params[':trailing_slash_name'] = $ugly_path."/";
      }
      $sql .= " ORDER BY LENGTH(dav_name) DESC LIMIT 1";
      $qry = new AwlQuery( $sql, $params );
      if ( $qry->Exec() && $qry->rows() > 0 ) {
        $row = $qry->Fetch();
        $target_collection = $row->collection_id;
      }
      else {
        $c->messages[] = translate('Can only add tickets for existing collection paths which you own');
        return $ticketrow;
      }
    }

    $_POST['dav_owner_id'] = $id;
    $_POST['target_collection_id'] = $target_collection;
    $ticket_id = check_by_regex($_POST['ticket_id'], '/[A-Za-z0-9]+/');
    $ticketrow->SetWhere( 'dav_owner_id='.$id.' AND ticket_id='.AwlQuery::quote($ticket_id));
    if ( isset($_POST['ticket_privileges']) ) {
      $privilege_bitpos = array_flip($privilege_names);
      $priv_names = array_keys($_POST['ticket_privileges']);
      $privs_dec = privilege_to_bits($priv_names);
      $_POST['privileges'] = sprintf('%024s',decbin($privs_dec));
      $ticketrow->Assign('privileges', $privs_dec);
    }
    $c->messages[] = translate('Creating new ticket granting privileges to this Principal');
    $ticketrow->Write( );
  }
  return $ticketrow;
}


function edit_ticket_row( $row_data ) {
  global $id, $ticketrow;

  if ( isset($row_data->ticket_id) ) {
    $ticketrow->Initialise( $row_data );
  }

  $privs_html = build_privileges_html( $ticketrow, 'ticket_privileges' );

  $form_id = $ticketrow->Id();
  $ticket_id = $row_data->ticket_id;
  $form_url = preg_replace( '#&(edit|delete)_[a-z]+=\d+#', '', $_SERVER['REQUEST_URI'] );

  $template = <<<EOTEMPLATE
<form method="POST" enctype="multipart/form-data" id="form_$form_id" action="$form_url">
  <td class="left">$ticket_id<input type="hidden" name="id" value="$id"><input type="hidden" name="ticket_id" value="$ticket_id"></td>
  <td class="left"><input type="text" name="target" value="$row_data->target"></td>
  <td class="left"><input type="text" name="expires" value="$row_data->expires" size="10"></td>
  <td class="left">$privs_html</td>
  <td class="center">##submit##</td>
</form>

EOTEMPLATE;

  $ticketrow->SetTemplate( $template );
  $ticketrow->Title("");

  return $ticketrow->Render();
}


function access_ticket_browser() {
  global $c, $id, $editor, $can_write_principal;

  $browser = new Browser(translate('Access Tickets'));

  if ( $can_write_principal ) {
    $browser->AddColumn( 'ticket_id', translate('Ticket ID'), '', '' );
  }
  $browser->AddColumn( 'target', translate('Target'), '', '<td style="white-space:nowrap;">%s</td>', "COALESCE(d.dav_name,c.dav_name)" );
  $browser->AddColumn( 'expires', translate('Expires'), '', '', 'TO_CHAR(expires,\'YYYY-MM-DD HH:MI:SS\')');
  $browser->AddColumn( 'privs', translate('Privileges'), '', '', 'privileges', '', '', 'principal_privilege_format_function' );
  if ($can_write_principal) {
    $delurl = $c->base_url . '/admin.php?action=edit&t=principal&id='.$id.'&ticket_id=##URL:ticket_id##&subaction=delete_ticket';
    $browser->AddColumn( 'delete', translate('Action'), 'center', '', "'<a class=\"submit\" href=\"$delurl\">".translate('Delete')."</a>'" );
  }

  $browser->SetOrdering( 'target', 'A' );

  $browser->SetJoins( 'access_ticket t LEFT JOIN collection c ON (target_collection_id=collection_id) LEFT JOIN caldav_data d ON (target_resource_id=dav_id)' );
  $browser->SetWhere( 'dav_owner_id = '.intval($editor->Value('principal_id')) );

  $browser->RowFormat( '<tr class="r%d">', '</tr>', '#even' );

  $browser->DoQuery();

  if ( $can_write_principal ) {
    $ticket_id = substr( str_replace('/', '', str_replace('+', '',base64_encode(sha1(date('r') .rand(0,2100000000) . microtime(true),true)))), 7, 8);
    $extra_row = array( 'ticket_id' => $ticket_id,
                        'expires' => date( 'Y-m-d', time() + (86400 * 31) ),
                        'target' => '/'.$editor->Value('username').'/'.$c->home_calendar_name.'/'
                      );
    $browser->MatchedRow('ticket_id', $ticket_id, 'edit_ticket_row');
    $browser->AddRow($extra_row);
  }
  return $browser;
}


function confirm_delete_ticket($confirmation_hash) {
  $html = '<table><tr><td class="error">';
  $html .= sprintf('<b>%s</b> "%s" <a class="error" href="%s&%s">%s</a> %s',
              translate('Deleting Ticket:'), $_GET['ticket_id'], $_SERVER['REQUEST_URI'],
              $confirmation_hash,
              translate('Confirm Deletion of the Ticket'),
              translate('The access ticket will be deleted.') );
  $html .= "</td></tr></table>\n";
  return $html;
}


function principal_collection_browser() {
  global $c, $page_elements, $id, $editor, $can_write_principal;

  $browser = new Browser(translate('Principal Collections'));

  $browser->AddColumn( 'collection_id', translate('ID'), 'right', '##collection_link##' );
  $rowurl = $c->base_url . '/admin.php?action=edit&t=collection&id=';
  $browser->AddHidden( 'collection_link', "'<a href=\"$rowurl' || collection_id || '\">' || collection_id || '</a>'" );
  $browser->AddColumn( 'dav_name', translate('Path') );
  $browser->AddColumn( 'dav_displayname', translate('Display Name') );
  $browser->AddColumn( 'publicly_readable', translate('Public'), 'centre', '', 'CASE WHEN publicly_readable THEN \''.translate('Yes').'\' ELSE \''.translate('No').'\' END' );
  $browser->AddColumn( 'privs', translate('Privileges'), '', '',
          "COALESCE( privileges_list(default_privileges), '[".translate('from principal')."]')" );
  if ($can_write_principal) {
    $delurl = $c->base_url . '/admin.php?action=edit&t=principal&id='.$id.'&dav_name=##URL:dav_name##&subaction=delete_collection';
    $browser->AddColumn( 'delete', translate('Action'), 'center', '', "'<a class=\"submit\" href=\"$delurl\" title=\"\">".translate('Delete')."</a>'" );
  }

  $browser->SetOrdering( 'dav_name', 'A' );

  $browser->SetJoins( "collection " );
  $browser->SetWhere( 'user_no = '.intval($editor->Value('user_no')) );

  if ($can_write_principal) {
    $browser->ExtraRowFormat( '<tr class="r%d">', '</tr>', '#even' );
    $browser->AddRow( array( 'dav_name' => '<a href="'.$rowurl.'&user_no='.intval($editor->Value('user_no')).'" class="submit">'.translate('Create Collection').'</a>' ));
  }

  if ( $c->enable_row_linking ) {
    $browser->RowFormat( '<tr onMouseover="LinkHref(this,1);" title="'.translate('Click to edit collection details').'" class="r%d">', '</tr>', '#even' );
  }
  else {
    $browser->RowFormat( '<tr class="r%d">', '</tr>', '#even' );
  }
  $browser->DoQuery();
  return $browser;
}

function confirm_delete_collection($confirmation_hash) {
  $html = '<table><tr><td class="error">';
  $html .= sprintf('<b>%s</b> "%s" <a class="error" href="%s&%s">%s</a> %s',
              translate('Deleting Collection:'), $_GET['dav_name'], $_SERVER['REQUEST_URI'],
              $confirmation_hash,
              translate('Confirm Deletion of the Collection'),
              translate('All collection data will be unrecoverably deleted.') );
  $html .= "</td></tr></table>\n";
  return $html;
}


function binding_row_editor() {
  global $c, $id, $editor, $can_write_principal;

  $bindingrow = new Editor("Bindings", "dav_binding");
  $bindingrow->SetSubmitName( 'bindingrow' );
  if ( $can_write_principal && $bindingrow->IsSubmit() ) {
    if ( substr($_POST['dav_name'], -1) != '/' ) {
      $_POST['dav_name'] .= '/';
    }

    $dav_name = $_POST['dav_name'];
    $parent = '/'.$editor->Value('username').'/';
    if ( strpos($dav_name, $parent) !== 0 ) {
      $c->messages[] = translate("Can only bind collections into the current principal's namespace");
      return $bindingrow;
    }
    if ( substr_count($dav_name, '/') != 3 || substr_count($dav_name, '\\') > 0 ) {
      $c->messages[] = translate("Bound As is invalid");
      return $bindingrow;
    }
    $qry = new AwlQuery('SELECT dav_name FROM collection where dav_name = :dav_name UNION SELECT dav_name FROM dav_binding WHERE dav_name = :dav_name', array( ':dav_name' => $dav_name) );
    if ( $qry->Exec('dav_name') && $qry->rows() > 0 ) {
      $c->messages[] = translate('A resource already exists at the destination.');
      return $bindingrow;
    }

    if ( empty($_POST['access_ticket_id']) )
      $_POST['access_ticket_id'] = null;
    $_POST['dav_owner_id'] = $id;
    $_POST['parent_container'] = $parent;

    //  external binds shouldn't ever point back to ourselves but they should be a valid http[s] url
    $href = $_POST['source'];
    if ( preg_match ( '{^(?:https?://|file:///)([^/]+)(:[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
    ) {
      $path = '/.external/' . md5($href);
      $qry->QDo('SELECT collection_id FROM collection WHERE dav_name = :dav_name ', array( ':dav_name' => $path ));
      if ( $qry->rows() == 1 && ($row = $qry->Fetch()) ) {
        $dav_id = $row->collection_id;
      }
      else {
        $qry->QDo( 'INSERT INTO collection ( user_no, parent_container, dav_name, dav_etag, dav_displayname,
                                              is_calendar, is_addressbook, resourcetypes, created )
                                VALUES( :user_no, :parent_container, :dav_name, :dav_etag, :dav_displayname,
                                        :is_calendar, :is_addressbook, :resourcetypes, current_timestamp )',
            array(
              ':user_no'          => $editor->Value('user_no'),
              ':parent_container' => '/.external/',
              ':dav_name'         => $path,
              ':dav_etag'         => md5( $editor->Value('user_no') . $path ),
              ':dav_displayname'  => $_POST['dav_displayname'],
              ':is_calendar'      => 't',
              ':is_addressbook'   => 'f',
              ':resourcetypes'    => '<DAV::collection/><urn:ietf:params:xml:ns:caldav:calendar/>'
            )
        );

        $qry->QDo('SELECT collection_id FROM collection WHERE dav_name = :dav_name ', array( ':dav_name' => $path ));
        if ( $qry->rows() != 1 || !($row = $qry->Fetch()) ) {
          $c->messages[] = translate('Database Error');
          return $bindingrow;
        }
        $dav_id = $row->collection_id;
      }

      $_POST['bound_source_id'] = $dav_id;
      $_POST['external_url'] = $href;
      $_POST['external_type'] = 'calendar';
    }
    else {
      // internal bind
      require_once('DAVResource.php');
      $source = new DAVResource( $href );
      if ( !$source->Exists() || $source->IsPrincipal() || !$source->IsCollection() || $source->dav_name() == '/' ) {
        $c->messages[] = translate('The BIND Request MUST identify an existing resource.');
        return $bindingrow;
      }
      if ( $source->IsBinding() )
        $source = new DAVResource( $source->bound_from() );
      $_POST['bound_source_id'] = $source->collection_id();
    }

    $c->messages[] = 'Creating new binding for this principal';
    $bindingrow->SetWhere( "dav_name = '$dav_name'" );
    $bindingrow->Write();
  }
  return $bindingrow;
}


function edit_binding_row( $row_data ) {
  global $id, $bindingrow;

  if ( isset($row_data->dav_name) ) {
    $bindingrow->Initialise( $row_data );
  }

  $form_id = $bindingrow->Id();
  $form_url = preg_replace( '#&(edit|delete)_[a-z]+=\d+#', '', $_SERVER['REQUEST_URI'] );
  $source_title = translate('Path to collection you wish to bind, like /user1/calendar/ or https://cal.example.com/user2/cal/');
  $access_title = translate('optional');

  $template = <<<EOTEMPLATE
<form method="POST" enctype="multipart/form-data" id="form_$form_id" action="$form_url">
  <td class="left">&nbsp;<input type="hidden" name="id" value="$id"></td>
  <td class="left"><input type="text" name="dav_name" value="$row_data->dav_name" size="25"></td>
  <td class="left"><input type="text" name="dav_displayname" size="20"></td>
  <td class="left"><input type="text" name="source" size="40" title="$source_title"></td>
  <td class="left"><input type="text" name="access_ticket_id" size="10" title="$access_title"></td>
  <td class="left">&nbsp;</td>
  <td class="center">##submit##</td>
</form>
EOTEMPLATE;

  $bindingrow->SetTemplate( $template );
  $bindingrow->Title("");

  return $bindingrow->Render();
}


function bindings_to_other_browser() {
  global $c, $editor, $can_write_principal;
  $browser = new Browser(translate('Bindings to other collections'));
  $browser->AddColumn( 'bind_id', translate('ID'), '', '' );
  $browser->AddHidden( 'b.dav_owner_id' );
  $browser->AddHidden( 'p.principal_id' );
  $browser->AddColumn( 'bound_as', translate('Bound As'), '', '<td style="white-space:nowrap;">%s</td>', 'b.dav_name' );
  $browser->AddColumn( 'dav_displayname', translate('Display Name'), '', '', 'b.dav_displayname' );
  $browser->AddColumn( 'dav_name', translate('To Collection'), '', '<td style="white-space:nowrap;">%s</td>', 'c.dav_name' );
  $browser->AddColumn( 'access_ticket_id', translate('Ticket ID'), '', '' );
  $browser->AddColumn( 'privs', translate('Privileges'), '', '', "privileges_list(privileges)" );
  if ($can_write_principal) {
    $delurl = $c->base_url . sprintf('/admin.php?action=edit&t=principal&id=%s&bind_id=##bind_id##&subaction=delete_bind_in', $editor->Value('principal_id'));
    $browser->AddColumn( 'delete', translate('Action'), 'center', '', "'<a class=\"submit\" href=\"$delurl\">".translate('Delete')."</a>'" );
  }

  $browser->SetOrdering( 'bound_as', 'A' );

  $browser->SetJoins( 'dav_binding b LEFT JOIN collection c ON (bound_source_id=collection_id) LEFT JOIN access_ticket t ON (ticket_id=access_ticket_id) LEFT JOIN principal p USING(user_no)' );
  $browser->SetWhere( 'b.dav_name ~ '.sprintf("'^/%s/'", $editor->Value('username')) );

  $browser->RowFormat( '<tr class="r%d">', '</tr>', '#even' );

  $browser->DoQuery();

  if ( $can_write_principal ) {
    $extra_row = (object) array( 'bind_id' => -1,
                                 'dav_name' => '/'.$editor->Value('username').'/boundcalendar/'
                               );
    $browser->MatchedRow('bind_id', -1, 'edit_binding_row');
    $browser->AddRow($extra_row);
  }
  return $browser;
}

function confirm_delete_bind_in($confirmation_hash) {
  $html = '<table><tr><td class="error">';
  $html .= sprintf('<b>%s</b> "%s" <a class="error" href="%s&%s">%s</a> %s',
              translate('Deleting Binding:'), $_GET['bind_id'], $_SERVER['REQUEST_URI'],
              $confirmation_hash,
              translate('Confirm Deletion of the Binding'),
              translate('The binding will be deleted.') );
  $html .= "</td></tr></table>\n";
  return $html;
}


function bindings_to_us_browser() {
  global $c, $editor, $session;
  $browser = new Browser(translate('Bindings to this Principal\'s Collections'));
  $browser->AddColumn( 'bind_id', translate('ID'), '', '' );
  $browser->AddHidden( 'b.dav_owner_id' );
  $browser->AddHidden( 'p.principal_id' );
  $browser->AddColumn( 'dav_name', translate('Collection'), '', '<td style="white-space:nowrap;">%s</td>', 'c.dav_name' );
  $browser->AddColumn( 'bound_as', translate('Bound As'), '', '<td style="white-space:nowrap;">%s</td>', 'b.dav_name' );
  $browser->AddColumn( 'access_ticket_id', translate('Ticket ID'), '', '' );
  $browser->AddColumn( 'privs', translate('Privileges'), '', '', "privileges_list(privileges)" );
  if ( $session->AllowedTo('Admin') ) {
    $delurl = $c->base_url . '/admin.php?action=edit&t=principal&id=##principal_id##&bind_id=##bind_id##&subaction=delete_binding';
    $browser->AddColumn( 'delete', translate('Action'), 'center', '', "'<a class=\"submit\" href=\"$delurl\">".translate('Delete')."</a>'" );
  }

  $browser->SetOrdering( 'dav_name', 'A' );

  $browser->SetJoins( 'dav_binding b LEFT JOIN collection c ON (bound_source_id=collection_id) LEFT JOIN access_ticket t ON (ticket_id=access_ticket_id) LEFT JOIN principal p USING(user_no)' );
  $browser->SetWhere( 'p.principal_id = '.intval($editor->Value('principal_id')) );

  $browser->RowFormat( '<tr class="r%d">', '</tr>', '#even' );

  $browser->DoQuery();
  return $browser;
}

function confirm_delete_binding( $confirmation_hash ) {
  $html = '<table><tr><td class="error">';
  $html .= sprintf('<b>%s</b> "%s" <a class="error" href="%s&%s">%s</a> %s',
              translate('Deleting Binding:'), $_GET['bind_id'], $_SERVER['REQUEST_URI'],
              $confirmation_hash,
              translate('Confirm Deletion of the Binding'),
              translate('The binding will be deleted.') );
  $html .= "</td></tr></table>\n";
  return $html;
}


if ( isset($_GET['subaction']) ) {
  if ( handle_subaction($_GET['subaction']) && 'delete_principal' == $_GET['subaction'] ) {
    return true;
  }
}

$editor = principal_editor();
$page_elements[] = $editor;

if ( isset($id) && $id > 0 ) {
  $c->stylesheets[] = 'css/browse.css';
  $c->scripts[] = 'js/browse.js';

  if ( isset($delete_principal_confirmation_required) )
    $page_elements[] = confirm_delete_principal($delete_principal_confirmation_required, $editor->Value('displayname'));


  $page_elements[] = group_memberships_browser();
  if ( $editor->Value('type_id') == 3 ) {
    $grouprow = group_row_editor();
    $page_elements[] = group_members_browser();
  }
  $grantrow = grant_row_editor();
  $page_elements[] = principal_grants_browser();
  if ( isset($delete_grant_confirmation_required) ) $page_elements[] = confirm_delete_grant($delete_grant_confirmation_required);

  $ticketrow = ticket_row_editor();
  $page_elements[] = access_ticket_browser();
  if ( isset($delete_ticket_confirmation_required) ) $page_elements[] = confirm_delete_ticket($delete_ticket_confirmation_required);

  $page_elements[] = principal_collection_browser();
  if ( isset($delete_collection_confirmation_required) ) $page_elements[] = confirm_delete_collection($delete_collection_confirmation_required);

  $bindingrow = binding_row_editor();
  $page_elements[] = bindings_to_other_browser();
  if ( isset($delete_bind_in_confirmation_required) ) $page_elements[] = confirm_delete_bind_in($delete_bind_in_confirmation_required);

  $page_elements[] = bindings_to_us_browser();
  if ( isset($delete_binding_confirmation_required) ) $page_elements[] = confirm_delete_binding($delete_binding_confirmation_required);
}
DAViCal API documentation generated by ApiGen