From dc7fed1157531b78da8b146d87c265ba22144517 Mon Sep 17 00:00:00 2001 From: Nikolai Kristiansen Date: Tue, 10 Apr 2012 21:53:53 +0200 Subject: [PATCH] Adds ldap as primary auth, lots of cleanups --- {inside => css}/handheld.css | 0 {inside => css}/print.css | 0 {inside => css}/print_prog.css | 0 css/rebuild.css | 137 +++++ css/reset.css | 63 +++ {inside => css}/screen.css | 6 +- css/style.css | 17 +- css/wp.css | 75 +++ includes/Ldap.class.php | 830 ++++++++++++++++++++++++++++ includes/Ldap_defines.php | 136 +++++ inside/ActionParser.php | 90 ++- inside/Navigation.php | 3 +- inside/Page.php | 181 +----- inside/Payment.php | 15 - inside/functions.php | 13 +- inside/graphics/bannerindex-400.png | Bin 0 -> 22934 bytes inside/graphics/header-210x.png | Bin 0 -> 2595 bytes inside/includes.php | 6 +- inside/ldap.php | 140 +++++ inside/menuExpandable3.js | 6 +- inside/screen.css.backup | 700 ----------------------- 21 files changed, 1475 insertions(+), 943 deletions(-) rename {inside => css}/handheld.css (100%) rename {inside => css}/print.css (100%) rename {inside => css}/print_prog.css (100%) create mode 100644 css/rebuild.css create mode 100644 css/reset.css rename {inside => css}/screen.css (98%) create mode 100644 css/wp.css create mode 100644 includes/Ldap.class.php create mode 100644 includes/Ldap_defines.php create mode 100644 inside/graphics/bannerindex-400.png create mode 100644 inside/graphics/header-210x.png create mode 100644 inside/ldap.php delete mode 100644 inside/screen.css.backup diff --git a/inside/handheld.css b/css/handheld.css similarity index 100% rename from inside/handheld.css rename to css/handheld.css diff --git a/inside/print.css b/css/print.css similarity index 100% rename from inside/print.css rename to css/print.css diff --git a/inside/print_prog.css b/css/print_prog.css similarity index 100% rename from inside/print_prog.css rename to css/print_prog.css diff --git a/css/rebuild.css b/css/rebuild.css new file mode 100644 index 0000000..08a16fe --- /dev/null +++ b/css/rebuild.css @@ -0,0 +1,137 @@ +/* -------------------------------------------------------------- + + Rebuild the default browser styles + + Based on work by Blueprint CSS + http://code.google.com/p/blueprintcss/ + +-------------------------------------------------------------- */ + + +body { + background: #fff; +} +body, input, textarea { + color: #111; + font: 12px Arial,sans-serif; + line-height: 1.5; +} + + +/* Headings +-------------------------------------------------------------- */ + +h1,h2,h3,h4,h5,h6 { + font-weight: normal; + clear: both; +} +hr { + background-color: #999; + border:0; + height: 1px; + margin-bottom: 1.5em; +} + + +/* Text elements +-------------------------------------------------------------- */ + +p { + margin-bottom: 1.5em; +} +ul { + margin: 0 0 1.5em 2.5em; +} +ol { + margin: 0 0 1.5em 2.5em; +} +ul { + list-style:disc; +} +ol { + list-style-type: decimal; +} +ol ol { + list-style:upper-alpha; +} +ol ol ol { + list-style:lower-roman; +} +ol ol ol ol { + list-style:lower-alpha; +} +ul ul, ol ol, ul ol, ol ul { + margin-bottom:0; +} +dl { + margin:0 1.5em; +} +dt { + font-weight: bold; +} +dd { + margin-bottom: 1.5em; +} +strong { + font-weight: bold; +} +cite, em, i { + font-style: italic; +} +blockquote { + margin: 0 3em; +} +blockquote em, blockquote i, blockquote cite { + font-style: normal; +} +pre { + font: 11px Monaco, monospace; + line-height: 1.5; + margin-bottom: 1.5em; +} +code { + font: 11px Monaco, monospace; +} +abbr, acronym { + border-bottom: 1px dotted #666; + cursor: help; +} +ins { + text-decoration: none; +} +sup, +sub { + height: 0; + line-height: 1; + vertical-align: baseline; + position: relative; + +} +sup { + bottom: 1ex; +} +sub { + top: .5ex; +} + + +/* + +Adapt the following for use in your Child Themes when using +different font-sizes and line-heights + +body, input, textarea { + font: --px Arial,sans-serif; + line-height:--; +} +p, ul, ol, dd, pre { + margin-bottom:--em; +} +pre, code { + font:--px Monaco, monospace; + line-height:--; +} + +All other styles can be overridden--or ignored!--in Child Theme stylesheets + +*/ \ No newline at end of file diff --git a/css/reset.css b/css/reset.css new file mode 100644 index 0000000..cbcd60f --- /dev/null +++ b/css/reset.css @@ -0,0 +1,63 @@ +/* -------------------------------------------------------------- + + Reset default browser CSS. + + Based on work by Eric Meyer: + http://meyerweb.com/eric/tools/css/reset/index.html + +-------------------------------------------------------------- */ + +/* v1.0 | 20080212 */ + +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, font, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td { + margin: 0; + padding: 0; + border: 0; + outline: 0; + font-size: 100%; + vertical-align: baseline; + background: transparent; +} +body { + line-height: 1; +} +ol, ul { + list-style: none; +} +blockquote, q { + quotes: none; +} +blockquote:before, blockquote:after, +q:before, q:after { + content: ''; + content: none; +} + +/* remember to define focus styles! */ +:focus { + outline: 0; +} + +/* remember to highlight inserts somehow! */ +ins { + text-decoration: none; +} +del { + text-decoration: line-through; +} + +/* tables still need 'cellspacing="0"' in the markup */ +table { + border-collapse: collapse; + border-spacing: 0; +} + +a img { border: none; } diff --git a/inside/screen.css b/css/screen.css similarity index 98% rename from inside/screen.css rename to css/screen.css index 68bbbcc..f1d452a 100644 --- a/inside/screen.css +++ b/css/screen.css @@ -107,7 +107,7 @@ a.skip-nav { } a.contact { - background: url("graphics/user_go.png") no-repeat right; + background: url("../inside/graphics/user_go.png") no-repeat right; padding-right: 20px; } /* =lists @@ -406,7 +406,7 @@ h4 { } li.menulist { - background: url(graphics/minus.png) no-repeat 3px 0.3em; + background: url(../inside/graphics/minus.png) no-repeat 3px 0.3em; line-height: 1.2em; list-style: none outside; margin: 0px 0 10px; @@ -415,7 +415,7 @@ li.menulist { } .jsenabled li.menulist { - background: url(graphics/plus.png) no-repeat 3px 0.3em; + background: url(../inside/graphics/plus.png) no-repeat 3px 0.3em; } .jsenabled .menu, .jsenabled .submenu { diff --git a/css/style.css b/css/style.css index 61b9619..bfc2aab 100644 --- a/css/style.css +++ b/css/style.css @@ -1,6 +1,6 @@ -@import 'css/reset.css'; -@import 'css/rebuild.css'; -@import 'css/wp-css'; +@import 'reset.css'; +@import 'rebuild.css'; +@import 'wp.css'; /** @@ -80,10 +80,10 @@ body.dns { } /*body.dns */ #header { - background:#191919 url('img/header-210x.png') repeat-x bottom; + background:#191919 url('../inside/graphics/header-210x.png') repeat-x bottom; } #header { - height:155px; + height:110px; margin-bottom:10px; overflow:hidden; position:relative; @@ -92,7 +92,7 @@ body.dns { z-index:999 } .site-title a { - background: url('img/bannerindex-400.png') no-repeat -20px 0; + background: url('../inside/graphics/bannerindex-400.png') no-repeat -20px 0; display:block; height:63px; margin-top:20px; @@ -569,3 +569,8 @@ h2.jobb { margin-top:0.5em; padding:10px 10px 5px; } +/* random styles */ +.home-link { + display: inline-block; + margin-bottom: 5px; +} diff --git a/css/wp.css b/css/wp.css new file mode 100644 index 0000000..7df75e3 --- /dev/null +++ b/css/wp.css @@ -0,0 +1,75 @@ +/* -------------------------------------------------------------- + + Basic WordPress Blog Styles + +-------------------------------------------------------------- */ + + +/* Images +-------------------------------------------------------------- */ + +.entry-content img { + margin: 0 0 1.5em 0; +} +.alignleft, +img.alignleft { + display: inline; + float: left; + margin-right: 1.5em; +} +.alignright, +img.alignright { + display: inline; + float: right; + margin-left: 1.5em; +} +.aligncenter, +img.aligncenter { + clear: both; + display: block; + margin-left: auto !important; + margin-right: auto !important; +} +.wp-caption { + text-align: center; + margin-bottom: 1.5em; +} +.wp-caption img { + border: 0 none; + margin: 0; + padding: 0; +} +.wp-caption p.wp-caption-text { + margin: 0; +} +.wp-smiley { + max-height: 1em; + margin:0 !important; +} +.gallery dl { + margin: 0; +} +.gallery-caption { + margin:-1.5em 0 0 0; +} + + +/* Pullquotes +-------------------------------------------------------------- */ + +blockquote.left { + float: left; + margin-left: 0; + margin-right: 20px; + text-align: right; + width: 33%; +} +blockquote.right { + float: right; + margin-left: 20px; + margin-right: 0; + text-align: left; + width: 33%; +} + + diff --git a/includes/Ldap.class.php b/includes/Ldap.class.php new file mode 100644 index 0000000..1b17d55 --- /dev/null +++ b/includes/Ldap.class.php @@ -0,0 +1,830 @@ +conn = ldap_connect($host, $port); + if( $this->conn == null ) { + throw new Exception("Could not connect to ldap server `$host:$port'"); + } + ldap_set_option($this->conn, LDAP_OPT_PROTOCOL_VERSION, 3); + } + + + public function __destruct() { + ldap_close($this->conn); + } + + public function getConn() { + return $this->conn; + } + + private function bind() { + if( !ldap_bind($this->conn, BIND_USER, BIND_PASS) ) { + throw new Exception("ERROR: Could not bind to ldap server."); + } + } + + private function unbind() { + /* Seems the ldap_unbind() function closes the connection, + * so we just drop the unbind process. Some security risk + * as the script continues bound to the already bound user, + * but what can one do?... */ + } + public function testBind($username, $password) { + $password = strtr($password, array("\'"=>"'")); + $baseDn = USUFFIX . "," . SUFFIX; + if ( ($isBound = @ldap_bind($this->conn, $username, $password)) === false ) { + // @see wpLDAP comment at http://ashay.org/?page_id=133#comment-558 + $isBound = @ldap_bind($this->conn, "uid=$username, $baseDn", $password); + } + return $isBound; + } + + public function search($baseDn, $filter, $attrs, $bind = false) { + if( $bind === true ) { + $this->bind(); + } + $res = ldap_search($this->conn, $baseDn, $filter, $attrs); + if(!$res) { + return false; + } + + $entries = ldap_get_entries($this->conn, $res); + + if( $bind === true ) { + $this->unbind(); + } + + return $entries; + } + + public function read($dn, $filter, $attrs, $bind = false) { + if( $bind === true ) { + $this->bind(); + } + $res = ldap_read($this->conn, $dn, $filter, $attrs); + if(!$res) { + return false; + } + + $entries = ldap_get_entries($this->conn, $res); + + if( $bind === true ) { + $this->unbind(); + } + + return $entries; + } + + private function userDn($username) { + return "uid=". $username . "," . USUFFIX . "," . SUFFIX; + } + + private function groupDn($groupname) { + return "cn=". $groupname . "," . GSUFFIX . "," . SUFFIX; + } + + private function homeAutomountDn($username) { + return "cn=". $username . ",ou=auto.home," . ASUFFIX . "," . SUFFIX; + } + + private function kerberosPrincipalDn($username) { + return "krbPrincipalName=". $username . "@". KERBEROS_DOMAIN .",cn=". KERBEROS_DOMAIN . "," + . KSUFFIX . "," . SUFFIX; + } + + private function ldapAdd($dn, $record) { + $this->bind(); + $res = ldap_add($this->conn, $dn, $record); +/* if ( !$res ) { + _log(ldap_error($this->conn)); + }*/ + $this->unbind(); + + if( $res ) { + debug("Added $dn"); + } else { + debug("Could not add $dn"); + } + + return $res; + } + + public function ldapDelete($dn) { + $this->bind(); + $res = ldap_delete($this->conn, $dn); + $this->unbind(); + + if( $res ) { + debug("Deleted $dn"); + } else { + debug("Could not delete $dn"); + } + return $res; + } + + public function getUser($username) { + + $filter = "(&(uid=$username)(objectClass=posixAccount))"; + $attrs = array(); + + $info = $this->search(USUFFIX .",". SUFFIX, $filter, $attrs); + + if( $info['count'] != 1 ) { + throw new Exception("Error retreiving user " . $username . "."); + } + + $fl = $this->flatten($info); + + if( count($fl) != 1 ) { + throw new Exception("Unexpected size of user array."); + } + + /* Removing objectclass... */ + unset($fl[0]['objectclass']); + + return $fl[0]; + + } + public function getUserWithMail($mail) { + + $filter = "(&(mail=$mail)(objectClass=posixAccount))"; + $attrs = array(); + + $info = $this->search(USUFFIX .",". SUFFIX, $filter, $attrs); + + if( $info['count'] != 1 ) { + throw new Exception("Error retreiving user " . $username . "."); + } + + $fl = $this->flatten($info); + + if( count($fl) != 1 ) { + throw new Exception("Unexpected size of user array."); + } + + /* Removing objectclass... */ + unset($fl[0]['objectclass']); + + return $fl[0]; + + } + + private function flatten($info) { + $res = array(); + + for($i=0;$i<$info['count'];$i++) { + $obj_i = $info[$i]; + for($j=0;$j<$obj_i['count'];$j++) { + if( $obj_i[$obj_i[$j]]['count'] == 1) { + $res[$i][$obj_i[$j]] = $obj_i[$obj_i[$j]][0]; + } else { + for($k=0;$k<$obj_i[$obj_i[$j]]['count'];$k++) { + $res[$i][$obj_i[$j]][] = $obj_i[$obj_i[$j]][$k]; + } + } + } + } + return $res; + } + + public function getAllUsers() { + $filter = "(uid=*)"; + $attrs = array(); + $res = ldap_search($this->conn, USUFFIX . "," . SUFFIX, $filter, $attrs); + if(! $res ) { + throw new Exception("Could not search ldap directory for users."); + } + + $users = array(); + + $info = ldap_get_entries($this->conn, $res); + for($i=0;$i<$info['count'];$i++) { + $users[] = $info[$i]; + } + return $users; + } + + public function getAllUsernames() { + $filter = "(&(uid=*))"; + $attrs = array("uid"); + $res = $this->search(USUFFIX .",". SUFFIX, $filter, $attrs); + if(!$res) { + throw new Exception("Could not search ldap directory for users."); + } + + $users = array(); + for($i=0;$i<$res['count'];$i++) { + $users[] = $res[$i]['uid'][0]; + } + return $users; + } + + public function getAllPrivateGroups() { + $filter = "(&(!(cn=dns-*))(objectclass=posixGroup))"; + $attrs = array(); + $res = $this->search(GSUFFIX . "," . SUFFIX, $filter, $attrs); + if(!$res) { + throw new Exception("Could not search ldap directory for private groups."); + } + + $groups = array(); + + for($i=0;$i<$res['count'];$i++) { + $group = array(); + $group['cn'] = $res[$i]['cn'][0]; + $group['description'] = $res[$i]['description'][0]; + $group['gidNumber'] = (int) $res[$i]['gidnumber'][0]; + $groups[] = $group; + } + + + return $groups; + } + + public function getAllDnsGroups() { + $filter = "(&(cn=dns-*)(objectclass=posixGroup))"; + $attrs = array(); + $res = $this->search(GSUFFIX . "," . SUFFIX, $filter, $attrs); + if(!$res) { + throw new Exception("Could not search ldap directory for dns groups."); + } + + $groups = array(); + + for($i=0;$i<$res['count'];$i++) { + $group = array(); + $group['cn'] = $res[$i]['cn'][0]; + $group['description'] = $res[$i]['description'][0]; + $group['gidNumber'] = (int) $res[$i]['gidnumber'][0]; + $groups[] = $group; + } + + return $groups; + } + + public function getGroupMembers($group) { + $filter = "(&(cn=".$group.")(objectclass=posixGroup))"; + $attrs = array("memberUid"); + $res = $this->read($this->groupDn($group), $filter, $attrs); + + $members = array(); + + if( ! $res ) { + return $members; + } + /* Return an empty array for the groups that do not + * contain any members */ + if( $res[0]['count'] == 0 ) return $members; + + for($i=0;$i<$res[0]['memberuid']['count'];$i++) { + $members[] = $res[0]['memberuid'][$i]; + } + + return $members; + } + + public function getUserGroups($username) { + $filter = "(&(memberUid=$username))"; + $attrs = array("cn"); + $attrsonly = 0; + $res = ldap_search($this->conn, GSUFFIX . "," . SUFFIX, + $filter, $attrs, $attrsonly); + if( !$res ) { + throw new Exception("ERROR. An error occured while searching for usergroups."); + } + $info = ldap_get_entries($this->conn, $res); + + $groups = array(); + for($i = 0; $i < $info['count']; $i++) { + array_push($groups, $info[$i]['cn'][0]); + } + + return $groups; + } + + public function userExists($username) { + if( empty($username) ) { + throw new Exception("<". __FUNCTION__ ."> ERROR: username is empty or null."); + } + $dn = $this->userDn($username); + $filter = "(&(uid=$username))"; + $attrs = array("dn"); + $attrsonly = 1; + + $res = ldap_search($this->conn, USUFFIX . "," . SUFFIX, + $filter, $attrs, $attrsonly); + + if( !$res ) { + throw new Exception("ERROR: An error occured while searching for user."); + } + + $info = ldap_get_entries($this->conn, $res); + + return ($info['count'] == 0? false : true); + } + public function mailExists($mail) { + if( empty($mail) ) { + throw new Exception("<". __FUNCTION__ ."> ERROR: mail is empty or null."); + } + $dn = USUFFIX . "," . SUFFIX; + $filter = "(mail=$mail)"; + $attrs = array("mail"); + $attrsonly = 1; + + $res = ldap_search($this->conn, USUFFIX . "," . SUFFIX, + $filter, $attrs, $attrsonly); + + if( !$res ) { + throw new Exception("ERROR: An error occured while searching for user."); + } + + $info = ldap_get_entries($this->conn, $res); + + return ($info['count'] == 0? false : true); + } + + public function groupExists($groupname) { + if( empty($groupname) ) { + throw new Exception("<". __FUNCTION__ ."> ERROR: groupname is empty or null."); + } + $filter = "(objectclass=*)"; + $attrs = array("cn"); + $res = ldap_read($this->conn, $this->groupDn($groupname), $filter, $attrs); + + if( !$res ) { + return false; +// throw new Exception("<". __FUNCTION__ ."> ERROR: could not read group"); + } + + $count = ldap_count_entries($this->conn, $res); + + return ($count == 1? true : false); + } + + public function homeDirectoryMountExists($username) { + if( empty($username) ) { + throw new Exception("<". __FUNCTION__ ."> ERROR: username is empty or null."); + } + $filter = "(objectclass=*)"; + $attrs = array("cn"); + $res = ldap_read($this->conn, $this->homeAutomountDn($username), $filter, $attrs); + + if( !$res ) { + return false; +// throw new Exception("<". __FUNCTION__ ."> ERROR: could not read automount"); + } + + $count = ldap_count_entries($this->conn, $res); + + return ($count == 1? true : false); + + } + private function ldapError() { + return ldap_error($this->conn); + } + + public function kerberosPrincipalExists($username) { + if( empty($username) ) { + throw new Exception("<". __FUNCTION__ ."> ERROR: username is empty or null."); + } + + $filter = "(objectclass=*)"; + $attrs = array("krbprincipalname"); + $res = ldap_read($this->conn, $this->kerberosPrincipalDn($username), $filter, $attrs); + + if( !$res ) { + return false; + } + + $count = ldap_count_entries($this->conn, $res); + + return ($count == 1? true : false); + + } + + private function getNextGid() { + $dn = GSUFFIX . "," . SUFFIX; + $filter = "(&(cn=dns-*))"; + $attrs = array("gidNumber"); + $res = ldap_search($this->conn, $dn, $filter, $attrs); + + if( !$res ) { + throw new Exception("ERROR: could not perform search for gid."); + } + + $info = ldap_get_entries($this->conn, $res); + + $num = (int) GID_MIN; + + for($i = 0; $i < $info['count']; $i++) { + if( $num < $info[$i]['gidnumber'][0] ) { + $num = (int) $info[$i]['gidnumber'][0]; + } + } + + if( $num >= (int) GID_MAX ) { + throw new Exception("ERROR: Gid number larger then GID_MAX"); + } + + return $num + 1; + } + + private function getNextUserGid() { + $dn = GSUFFIX . "," . SUFFIX; + $filter = "(&(!(cn=dns-*))(gidNumber=*))"; + $attrs = array("gidNumber"); + $res = ldap_search($this->conn, $dn, $filter, $attrs); + + if( !$res ) { + throw new Exception("ERROR: could not perform search for gid."); + } + + $info = ldap_get_entries($this->conn, $res); + + $num = (int) USER_GID_MIN; + for($i = 0; $i < $info['count']; $i++) { + if( $num < $info[$i]['gidnumber'][0] ) { + $num = (int) $info[$i]['gidnumber'][0]; + } + } + + if( $num >= (int) USER_GID_MAX ) { + throw new Exception("Error: Gid number larger then USER_GID_MAX"); + } + + return $num + 1; + } + + private function getNextUid() { + $dn = USUFFIX . "," . SUFFIX; + $filter = "(&(uid=*))"; + $attrs = array("uidNumber"); + $res = ldap_search($this->conn, $dn, $filter, $attrs); + if( !$res ) { + throw new Exception("ERROR: could not perform search for uid."); + } + $info = ldap_get_entries($this->conn, $res); + $num = (int) UID_MIN; + for($i = 0; $i < $info['count']; $i++) { + if( $num < $info[$i]['uidnumber'][0] ) { + $num = (int) $info[$i]['uidnumber'][0]; + } + } + + if( $num >= (int) UID_MAX ) { + throw new Exception("ERROR: UID number larger then UID_MAX"); + } + + return $num + 1; + } + + private function createUserHomeDirectory($user) { + $username = $user['username']; + $res = exec("sudo -u localedb /opt/edb/inside_ldap/create_user_home_directory.sh $username", + $output, $retval); + if( $retval != 0 ) { + throw new Exception("Could not create users home directory: $res"); + } + debug("Created home directory"); + return true; + } + + private function createUserHomeDirectoryMount($username) { + $info = array(); + + $info['objectClass'] = array("top", "automount"); + + $info['cn'] = $username; + $info['automountInformation'] = "-fstype=nfs4,rw,sec=krb5 " . FILESERVER . ":" . FILESERVER_HOMES . + "/" . $username; + + $dn = "cn=" . $username .",ou=auto.home,ou=Automount," . SUFFIX; + + return $this->ldapAdd($dn, $info); + } + + private function deleteUserHomeDirectoryMount($username) { + + $dn = "cn=" . $username .",ou=auto.home,ou=Automount," . SUFFIX; + + return $this->ldapDelete($dn); + } + + private function createUserKerberosCredentials($username, $password) { + $str = "sudo -u localedb /opt/edb/inside_ldap/create_user_kerberos_credentials.sh $username $password"; + $res = exec($str, $output, $retval); + if( $retval != 0 ) { + ldap_log("ERROR creating kerberos credentials: " . implode(",", $output)); + throw new Exception("Could not create users kerberos credentials"); + } + debug("Created users kerberos credentials"); + return true; + } + + private function deleteUserKerberosCredentials($username) { + $dn = "krbPrincipalName=$username@NEUF.NO,cn=NEUF.NO,cn=krbcontainer," . SUFFIX; + return $this->ldapDelete($dn); + } + + public function addGroup($name, $description, $gid = null) { + $dn = $this->groupDn($name); + + $info = array(); + $info['objectClass'] = array(); + $info['objectClass'][0] = "posixGroup"; + $info['description'] = $description; + $info['gidNumber'] = (int) ($gid == null? $this->getNextGid() : $gid); + + return $this->ldapAdd($dn, $info); + } + + + public function addUser($userinfo) { + if( !is_array($userinfo) ) { + throw new Exception("ERROR: Userinfo is not an array"); + } + + $info = array(); + /* Objectclass */ + $info['objectClass'] = array(); + $info['objectClass'][0] = "inetOrgPerson"; + $info['objectClass'][1] = "posixAccount"; + $info['objectClass'][2] = "shadowAccount"; + + /* User dn (identifies this record in LDAP) */ + $dn = $this->userDn($userinfo['username']); + + /* General user info */ + $info['uid'] = $userinfo['username']; + $info['givenname'] = $userinfo['firstname']; + $info['sn'] = $userinfo['lastname']; + $info['cn'] = $userinfo['firstname'] . " " . $userinfo['lastname']; + $info['displayname'] = $userinfo['firstname'] . " " . $userinfo['lastname']; + $info['gecos'] = $this->gecos($userinfo['username']); + $info['mail'] = $userinfo['email']; + + /* System specific user info */ + $info['userpassword'] = "{SSHA}". ssha($userinfo['password']); + $info['loginshell'] = LOGIN_SHELL; + $info['shadowexpire'] = (int) SHADOW_EXPIRE; + $info['shadowflag'] = (int) SHADOW_FLAG; + $info['shadowwarning'] = (int) SHADOW_WARNING; + $info['shadowmin'] = (int) SHADOW_MIN; + $info['shadowmax'] = (int) SHADOW_MAX; + $info['shadowlastchange'] = (int) SHADOW_LAST_CHANGE; + $info['homedirectory'] = HOME_DIRECTORY_PREFIX . "/" . $userinfo['username']; + $info['uidnumber'] = (int) $this->getNextUid(); + $info['gidnumber'] = (int) $this->getNextUserGid(); + + /* Check if the user is already in the LDAP directory */ + $user = $this->userExists($userinfo['username']); + if( $user == true ) { + throw new Exception("User ". $userinfo['username'] . " already exists."); + } + + if( $this->addGroup($userinfo['username'], "Usergroup", $info['gidnumber']) + === false ) { + throw new Exception("ERROR: ldap_add `group' failed."); + } + + if( $this->ldapAdd($dn, $info) === false ) { + throw new Exception("ERROR: ldap_add user: `". $info['uid']."' failed."); + } + + if( $this->createUserHomeDirectory($userinfo) === false ) { + throw new Exception("ERROR: could not create user home directory."); + } + + if( $this->createUserHomeDirectoryMount($userinfo['username']) === false ) { + throw new Exception("ERROR: could not create home directory mount in ldap"); + } + + if( $this->createUserKerberosCredentials($userinfo['username'], $userinfo['password']) === false) { + throw new Exception("ERROR: could not create user kerberos credentials."); + } + return true; + } + + public function addUserToGroup($username, $groupname) { + $dn = $this->groupDn($groupname); + $attr = array(); + $attr['memberUid'][] = $username; + + $this->bind(); + $res = ldap_mod_add($this->conn, $dn, $attr); + $this->unbind(); + + if( $res ) { + debug("Added $username to $dn"); + } else { + debug("Could not add $username to $dn"); + } + + return $res; + } + + public function removeUserFromGroup($username, $groupname) { + $dn = $this->groupDn($groupname); + $attr = array(); + $attr['memberUid'][] = $username; + + $this->bind(); + $res = ldap_mod_del($this->conn, $dn, $attr); + $this->unbind(); + + if( $res ) { + debug("Removed $username from $dn"); + } else { + debug("Could not remove $username from $dn"); + } + + return $res; + } + + public function deleteUser($username) { + /* Find all groups the user is a member of */ + $usergroups = $this->getUserGroups($username); + + foreach($usergroups as $group) { + $this->removeUserFromGroup($username, $group); + } + + $this->deleteUserHomeDirectoryMount($username); + $this->deleteUserKerberosCredentials($username); + + /* Delete the users usergroup */ + $this->ldapDelete($this->groupDn($username)); + + /* Delete the user */ + $this->ldapDelete($this->userDn($username)); + + return true; + } + + public function setUserPassword($username, $password) { + $dn = $this->userDn($username); + $this->bind(); + $attrs = array(); + $attrs['userPassword'] = ssha($password); + $res = ldap_mod_replace($this->conn, $dn, $attrs); + + if( $res ) { + debug("Password set/change for $dn"); + } else { + debug("Could not set password for $dn"); + } + + return $res; + } + + private function gecos($string) { + return $string; + } + + public function printIntegrityCheckStart($name) { +// debug("Running integrity check <" . $name . ">"); + _log_check("Running integrity check <" . $name . ">"); + } + + public function printIntegrityCheckStop($name) { +// debug("Completed integrity check <" . $name . ">"); + _log_check("Completed integrity check <" . $name . ">"); + } + + public function runAllIntegrityChecks() { + _log_check("------ Starting all integrity checks ------"); + $pass = true; + $methods = get_class_methods(__CLASS__); + foreach($methods as $method) { + if( strpos($method, "check") === 0 ) { + $this->printIntegrityCheckStart($method); + try { + if( $this->$method() === false ) { + $pass = false; + } + + } catch( Exception $e ) { + _log_check($e->getMessage()); + } + $this->printIntegrityCheckStop($method); + } + } + + if( $pass === false ) { + mail_admins("Integrity check failed, please review log."); + } + + _log_check("------ Completed all integrity checks (pass=". ($pass? "true":"false") .") ------"); + } + + public function checkUserPrimaryGroup() { + $filter = "(&(uid=*))"; + $attrs = array("uid"); + $res = $this->search(USUFFIX .",". SUFFIX, $filter, $attrs, true); + if(!$res) { + return false; + } + + $return_val = true; + + for($i=0;$i<$res['count'];$i++) { + $user = $res[$i]; + if( $this->groupExists($user['uid'][0]) === false ) { + _log_check_error("User `". $user['uid'][0] ."' does not have a primary group."); + $return_val = false; + } + } + + return $return_val; + } + + public function checkUserKerberosPrincipal() { + $users = $this->getAllUsernames(); + $return_val = true; + foreach($users as $user) { + if( $this->kerberosPrincipalExists($user) === false ) { + _log_check_error("User `". $user . "' does not have a kerberos principal."); + $return_val = false; + } + } + + return $return_val; + } + + public function checkUserAutomountHomeDirectory() { + $users = $this->getAllUsernames(); + $return_val = true; + foreach($users as $user) { + if( $this->homeDirectoryMountExists($user) === false ) { + _log_check_error("User `". $user . "' does not have a home directory mount record."); + $return_val = false; + } + } + + return $return_val; + } + + public function checkPrivateGroupUserExists() { + $private_groups = $this->getAllPrivateGroups(); + $return_val = true; + foreach($private_groups as $group) { + /* Special groups that don't require a user */ + if( $group['cn'] == "edb" ) continue; + if( $group['cn'] == "edbadmin" ) continue; + + if( $this->userExists($group['cn']) === false ) { + _log_check_error("Private Group `" . $group['cn'] . "' exists but user does not."); + $return_val = false; + } + } + + return $return_val; + } + + public function checkDnsGroupMembersExists() { + $dns_groups = $this->getAllDnsGroups(); + + $return_val = true; + + foreach($dns_groups as $group) { +// debug("Getting members for " . $group['cn']); + $members = $this->getGroupMembers($group['cn']); + foreach($members as $member) { + if( !$this->userExists($member) ) { + _log_check_error("Group `". $group['cn'] ."' contain user `". $member . + "', but the user does not exist."); + $return_val = false; + } + } + } + + /* These two are special groups */ + foreach(array("edb", "edbadmin") as $group) { + $members = $this->getGroupMembers($group); + foreach($members as $member) { + if( !$this->userExists($member) ) { + _log_check_error("Group `". $group ."' contain user `". $member . + "', but the user does not exist."); + $return_val = false; + } + } + } + + return $return_val; + } + + public function checkKerberosPrincipalUserExists() { + _log_check_error("Not implemented"); + return false; + } + + public function checkAutomountHomeDirectoryUserExists() { + _log_check_error("Not implemented"); + return false; + } + +} diff --git a/includes/Ldap_defines.php b/includes/Ldap_defines.php new file mode 100644 index 0000000..41414b3 --- /dev/null +++ b/includes/Ldap_defines.php @@ -0,0 +1,136 @@ +lmhash($password); +} + +function ntPassword($password) { + $hash = new SmbHash(); + return $hash->nthash($password); +} + +function ssha($password) { + return ssha_encode($password); +/* $salt = sha1(rand()); + $salt = substr($salt, 0, 4); + $hash = base64_encode( sha1 ($password . $salt, true) . $salt ); + return $hash;*/ +} + +function ssha_encode($password) { + $salt = ""; + for ( $i=1;$i<=10;$i++ ) { + $salt .= substr('0123456789abcdef', rand(0,15), 1); + } + $hash = base64_encode(pack("H*", sha1($password.$salt)).$salt); + + return $hash; +} + +function debug($str) { + $time = date("[Y-m-d H:i:s] "); + echo $time . $str . "\n"; +} + +function ldap_log($str) { + $time = date("[Y-m-d H:i:s] "); + //file_put_contents("/var/www/neuf.no/userinfo/log/user.log", $time . + // $str . "\n", FILE_APPEND); +} + +function _log_check($str) { + $time = date("[Y-m-d H:i:s] "); +// file_put_contents("/var/www/neuf.no/userinfo/log/integrity_check.log", $time . +// $str . "\n", FILE_APPEND); + debug($str); +} + +function _log_check_error($str) { + $time = date("[Y-m-d H:i:s] "); +// file_put_contents("/var/www/neuf.no/userinfo/log/integrity_check.log", $time . "[ERROR] " . +// $str . "\n", FILE_APPEND); + debug($str); + +} + +function mail_admins($str) { + _log_check("Mailing admins..."); +} + +function validate_user_info($user) { + if( !is_array($user) ) { + throw new Exception("ERROR: user array is not an array"); + } + + if( preg_match(USERNAME_REGEXP, $user['username']) == 0 ) { + throw new Exception("ERROR: username did not validate"); + } + + if( empty($user['firstname']) ) { + throw new Exception("ERROR: firstname missing"); + } + + if( empty($user['lastname']) ) { + throw new Exception("ERROR: lastname missing"); + } + + if( empty($user['email']) ) { + throw new Exception("ERROR: email missing"); + } + + if( empty($user['password']) ) { + throw new Exception("ERROR: password missing"); + } +} diff --git a/inside/ActionParser.php b/inside/ActionParser.php index 6131e7f..4efae0b 100644 --- a/inside/ActionParser.php +++ b/inside/ActionParser.php @@ -39,7 +39,7 @@ class ActionParser { if (checkAuth('perform-' . $this->action) || checkResponsible()) { switch ($this->action) { case 'log-in' : - $this->_logIn(); + new_login(scriptParam('username'),scriptParam('password')); break; case 'log-out' : @@ -589,60 +589,58 @@ private function logError($username, $error) $conn->query($sql); } - public function _logIn() { +public function _logIn() { $conn = db_connect(); + /* query db with form data (password, username) */ $sql = sprintf("SELECT id, passwordReset FROM din_user " . - "WHERE username = %s " . - "AND (password = PASSWORD(%s) or password = old_password(%s))", $conn->quoteSmart(scriptParam('username')), $conn->quoteSmart(scriptParam('password')), $conn->quoteSmart(scriptParam('password'))); + "WHERE username = %s " . + "AND (password = PASSWORD(%s) or password = old_password(%s))", $conn->quoteSmart(scriptParam('username')), $conn->quoteSmart(scriptParam('password')), $conn->quoteSmart(scriptParam('password'))); $result = $conn->query($sql); if (DB :: isError($result) != true) { - if ($result->numRows() > 0) { - $row = $result->fetchRow(DB_FETCHMODE_ASSOC); - $_SESSION['valid-user'] = $row['id']; - - // SHA1 - if ($_GET['debug']){ - echo "{SHA}" . base64_encode(pack("H", hash('sha1', $conn->quoteSmart(scriptParam('password'))))); - - die("Jeg skammer meg over at dette er skrevet i vim - Sjur"); - } + /* more than 1 row means valid user */ + if ($result->numRows() > 0) { + $row = $result->fetchRow(DB_FETCHMODE_ASSOC); + $_SESSION['valid-user'] = $row['id']; - if ($row['passwordReset'] == 1) { - $GLOBALS['extraScriptParams']['page'] = "reset-password"; + /* someone has requested a password reset, redirect to that page instead */ + if ($row['passwordReset'] == 1) { + $GLOBALS['extraScriptParams']['page'] = "reset-password"; + } + } else { + try + { + /* log auth errors with reason */ + $sql = sprintf("SELECT id FROM din_user WHERE username = %s", $conn->quoteSmart(scriptParam('username'))); + $result = $conn->query($sql); + $userExists = $result->numRows() > 0; + + $error = ''; + if(scriptParam('password') == '') + { + $error = 'no password supplied?'; + } + elseif($userExists) + { + $error = "user exists, wrong password?"; + } + else + { + $error = "user not found.."; + } + + $this->logError(scriptParam('username'), $error); + } + catch(Exception $e) + { + mail('komans@studentersamfundet.no','error with errorlog','error with errorlog'); + } + /* give user feedback */ + notify("Problemer med innlogging."); } - } else { - try - { - $sql = sprintf("SELECT id FROM din_user WHERE username = %s", $conn->quoteSmart(scriptParam('username'))); - $result = $conn->query($sql); - $userExists = $result->numRows() > 0; - - $error = ''; - if(scriptParam('password') == '') - { - $error = 'no password supplied?'; - } - elseif($userExists) - { - $error = "user exists, wrong password?"; - } - else - { - $error = "user not found.."; - } - - $this->logError(scriptParam('username'), $error); - } - catch(Exception $e) - { - mail('komans@studentersamfundet.no','error with errorlog','error with errorlog'); - } - notify("Problemer med innlogging."); - } } - } +} public function _logOut() { unset ($_SESSION['valid-user']); diff --git a/inside/Navigation.php b/inside/Navigation.php index a1fe835..5346e73 100644 --- a/inside/Navigation.php +++ b/inside/Navigation.php @@ -7,8 +7,9 @@ class Navigation { ?> + -_displayFooter(); -?> - - - -"; - $html .= "

Script execution: $time seconds || support"; - - $html .= "

upload_max_filesize = " . ini_get("upload_max_filesize") . "

"; - $html .= "\n"; - - print $html; - -/** - * Google Analytics, since 2010-11-19 15:05 - */ -?> - - @@ -620,10 +479,10 @@ possibly include it instead? --Thomas Misund, 26. Oct 2009 - - - + href="../css/theme; ?>" media="screen, projection" /> + + + diff --git a/inside/Payment.php b/inside/Payment.php index 3de855e..800004a 100644 --- a/inside/Payment.php +++ b/inside/Payment.php @@ -16,21 +16,6 @@ class Payment { } private function initPayex() { - //From setupexample - define('PAYEX_PXPARTNER_WSDL', 'https://external.payex.com/pxorder/pxpartner.asmx?WSDL'); - define('PAYEX_PXPRINT_WSDL', 'https://external.payex.com/pxorder/pxprint.asmx?WSDL'); - define('PAYEX_PXDEPOSIT_WSDL', 'https://external.payex.com/pxorder/pxdeposit.asmx?WSDL'); - define('PAYEX_PXACCOUNT_WSDL', 'https://external.payex.com/pxorder/pxaccount.asmx?WSDL'); - define('PAYEX_PXORDER_WSDL', 'https://external.payex.com/pxorder/pxorder.asmx?WSDL'); - define('PAYEX_PXCLIENT_WSDL', 'https://external.payex.com/pxclient/pxclient.asmx?WSDL'); - define('PAYEX_PXMERCHANT_WSDL', 'https://external.payex.com/pxmerchant/pxmerchant.asmx?WSDL'); - define('PAYEX_PXSUBSCRIPTION_WSDL', 'https://external.payex.com/pxsubscription/pxsubscription.asmx?WSDL'); - define('PAYEX_PXAGREEMENT_WSDL', 'https://external.payex.com/pxagreement/pxagreement.asmx?WSDL'); - - define('PAYEX_PXORDER_URL', 'https://account.payex.com/pxorder.asp'); // url to redirect user to - define('PAYEX_ACCOUNTNUMBER', 20180218); // payex merchant account number - define('PAYEX_ENCRYPTIONKEY', 'vy8wMAvJjvW4RaCEWJFf'); // payex encryption key - // end setup require_once ('../payex2/payex_pxorder.php'); // Include the Payex PxOrder implementation $this->payex = new PayexPxOrder(); // Make an instance of the PayexPxOrder class diff --git a/inside/functions.php b/inside/functions.php index a5b7964..1b54e0b 100644 --- a/inside/functions.php +++ b/inside/functions.php @@ -692,7 +692,7 @@ function createTransactionId() { function checkResponsible() { $subject = $_REQUEST; ob_start(); - var_dump($subject); + //var_dump($subject); $subject = ob_get_clean(); if (strstr($subject, "id") != false) { $pattern = "/\w+id/"; @@ -1030,7 +1030,7 @@ function displayLogin() { "brukernavn", + "label" => "Epost eller brukernavn", "type" => "text", "attributes" => Array ( "name" => "username", - "size" => 12, - "maxlength" => 12 + "size" => 25, + "maxlength" => 100 ) ); $fields[] = Array ( - "label" => "passord", + "label" => "Passord", "type" => "password", "attributes" => Array ( "name" => "password", @@ -1665,5 +1665,4 @@ function find_groups($uid) { return $arr; } - ?> diff --git a/inside/graphics/bannerindex-400.png b/inside/graphics/bannerindex-400.png new file mode 100644 index 0000000000000000000000000000000000000000..11e70c9ce5d78d3ed6aa9bd7b4e6ab4205400d16 GIT binary patch literal 22934 zcmbq)bx<5%800JvT$bRjfgr&GB)Gc-PPUS zw^h3{vs<+_^4{0|b@vNXl$St5CPW4R08L6#R0#k;vCw@1M0n`u-yc~b&<&iKkem}H_ASKQ_ecpU zZYvH5i?fUGp6<>X!ulwe+kL!VPhe30JB>GWiZ@+6e)n~-_x-C^=MIu-3@M+bkDA(? zW1GCbqPFt1%z2ZGd~*8e@Yrns2by>)WE4JiCcU5{eQ$#P$ZWxW+9GpeQf8W>a?{w>|;^pCHm2oI;6jMe+TI&?6!?|mxum6Fs@ zB83RVMVGnl)@|hLf4Ak8>Y;%PT;2tw`@VWO3M+MMB}!W$e#Q@~s$#8jaBv7ljterk zv_MCTiG0R3BChv(icWAkZH^e&`9o%DX-n|g^>{%+l=7RXukXJexu~eS7<`V)?@mS$ zeFyft`$l<0&J+}SQR3-aj$C1W&sTjHPNr)Yn%_6f1O1u797M}OpKQv*cuASRQ6@QK zXLD;NXX*p#Uu^GNOi2EFY;WJ5wTTBeaTq$y*$=PbUx<_nC_f1*E9$8tVT%b#3+D=! z&h9q1k|s@W?P_~@VyNpew-qBH#cglzdUIL#hAw(|G&wRQr^lwmg5$tML}_X9lvhvB z-+bv~hc+A<%!+&i)%y4>23{Jh8cpH8~AM5}g}Y{CJ4LpN)kb$oaSy6O$9i zEUj#VIrA`cbMv?k4i4bM3#o)%nnl|M8{NkLl6>lTb!}vzpm5lHK-tqwW=j0Cw4@al zdF>n<@nOQI#xk0O=k_MaW$k3O*=aB1=giDre+)@_3O*S#x7RCgEm2f#ay9~CR8mE8 z@q*vwOoC2daawP(@55l{J~x%XI9lwU(@Aan+73xq5m3bN;5+8-QSxbM`>pW$;` zFVL6k4M@vKRb>cxo#rXF@qeaZ%~D>QgP}=4GR)0gsMlk-e0X6i*UD?V`r8uCS8b_k zMGj7hVa27}Mqa=0FkrzK%v1EQnl@}z^0b+~$m@v7;UxN6|CJb@ z%lf0cj_v$kl9u+fs@p6&eC};MLP5>g^dhBFl^qcXdii3V)4>0AKe=OPB2mwkY76aU zH|}~MsAv{vEF#t_*p-F#bE=L_y*Omqzhc<&f3@bqSiAD%7v&48`j7+#6TX*-AtiCx zn0mKy?_fl0VrKdWhmGL8NhdTa5>E8}`jxu{F1VXu+Ke|TF|C3lm1li4Q6?5lg3^?# zIWaQ@1BoFK%i(UL@mq+MIuXxf6D<&zy0~B~>iI^LJ4^cX+H&@4>wy5RCWC&c4YL8g z1=Ioadx?<*e3qK7riFv14MLrJ5{rwPad4FRCMG7w_JJVE?w$=pDWr*%kjNSK;_?zP z!zM-$*2jh(Yr2Z!vTMs3!aCs`pk)L})upa;!~QG50l}hofTG6o6pUPXd2DJ1dWILv6*j2hvz`_$^Y}LsioiPbhwA5 zHLPb*u*StXu$5pFrl}Pb&}-gd6LMs5yTO~7 z8vk+DDp#S#rk|n5dGhpl=&MPVY2>er(N@)bCtkDYaoIamZo)C^%b$%mWm7F^BLaI5 z_7?yHqLT*fL=+U9xRj_l>y9n!>s=!0qqO-Wf(sS3Zjx%pR3!@!KqiEgd)Lr1y~+c!luy`(KP zxJwV-EOkmS8<>*2^w3gM;H{d296OX4Pg=Ud_4ZcSN7pA=iNa~yZ0{VCGPGwPCe82u zV!&bmgKQC-2uGRydv}lqg+gcP?-E0FX8i7029qEq8k_`F_=fvuE*4F0awdBCDJ7*w zd5CV0F^e$?-|A^|NTCRi+v#C(NyTEWQVF_?tLuZS%^I{=bN|m`ooBFeijpPZM%t=( zR#+HPh|;FJO4zaX7L=Vk`p@4Ft{=RAcu0vs%Dq5=i)>{`nYW@>sXZ}g%NQ;vYh3H5 z@ew`Q?%Daq@)+s9pk4`K$5k`v8 zd9#;SRiAZ8$YY5A{GJ0(Ci+H}u2=muesz<#kg%2|vVDr|xgzKnIHWpFS`Yw}k&J}K z#LPOoYE@h`>8wFdM~K1`*4EDE_5Xm!%Iz5Tbo|u1<)Uhe(;eScNDOI{>+wG8kH69A zVB{cjF!Hb3-!Shf^CG09`raYaJ6nA@jtoga=JwF*34B(6S=l;s8fn`cgf)N78 zCZr-I&W{{1CH<6LcW81MNn!#4{$yWlLIlal%gRflZXa7C!^D*3K=l4{G*}s2PPo22 z&MB538EVK$v%8rF)_wSHV*LE4ciu~cNSK3#$WienX<%uoXQ{|4n?oClat-Mz)QS z$p1N1rq3=78deoI ztoc6eR*=_RU2Z!#us&GU(To^`cIxWt78ev$EuPxHx^8Zla0Uwtsp_;q`DfLE#l+H_ zIDegGB0UdPg3IYO0aq|rS3tU~$UrDYYlU+h*SRdre|LFj=n3Q(SyRIY(eqMHd5 zrNkj#u4D!xXCahJX4KV{_=z06H8Rd0@I}1?g+DunJ&1r^ok<@FOQv`A^J#|as>Zw^ zbE_Z4wHD*{~wEa_uE%C1K#;^N}o!34yb+t^mr@MJ!f8CFsnpbcc>T0Ap3NB2yG_lf(78qT6WLBfv~m2vURciL z!|`$hMulFJ<(B=|`lRNNu3Py`9t3V~?!kwF~SOGqT?B!%)I9T23QmZpKm zQTP1k{G0|i!o;jfgBsHFEt}U@IB(9Jm=iMg5sY{e^GSPdzKAPzV(Fhe|K+)3;_3>{ z?DvD%M^=|+^GEixzM8!$cit>!UivA*^Ls?X=}v%GG*l4L0e*1{q ze0#bQ74!D$Riwgnr<#-m19V36?EM&;5{mrzF_an@S1FzOku--3Y07gN=$A`BGBPyW zPrIxZxY?7?_kK6Y%G%rY@|2K)4rw;npSaSi9IrPV>ei0-x-Cfl)t(dJ^7ibtQ~s&^ z$4Iga`+??SRF;27X5q)fi-HI{uGfNj{oub9OyA^Pqj{-kz+gv@`^`FrnV6$_?cLD~ zByUoI{IY9LcSEK4U}5|xA72_eU-O z7$oC?mo>%mSZlj0Ni+=mQ(u^*ucx|*jcK|cFIAA+xUV7RBU3|8t#TF~9_>IBCGOmQ zPfX13onmQgS#AGS=lAl8Ym40Tr4u zHtW-uv_+`8i+-xAJuHu!&SwQec$-QN$`qqQ?AYr?MBVX&if73ahJXF8z^zD56cN|b zQZchTv$`A2?r=Zum5S~`@O@XVhJp?;nrypUeS(STDO{8-OfNJKogyM%0!N47p*nD% zM32qORF=OT=fw~jzTZ4Z3zvr0ame3_ieM~Sd2e~@_wQH?IuZSc3>4wWQ4ujwf0);? z*E2@N#5`FZFLay7cbz|$0I)`pU}1ldzcE{hO~><8=lZ27qPzFB|4M!iw+4$9r}b)s znT4g}Fc!fO9b?S=v=fw$6p?AS8!r39a``@gotc@D^>&>7t&r&0^rxb^d8aoFOS#vu z3KKB6&EVM>-eGvgiQ!|DD_86InUs;A5ZWiqnU3}<2oN(RSJsu6dtBxZ5i#qw)|y`S zh85nacfcCP;g_|w(U)u0Fz|d<&QAL|`ilh(MBi!1LJ+8gqNEMV_Y;IJ7W&c$>_8~(@9;VR%w(XFl%(V82tDc8RUty_wMXM^i%~Ih6OJFpd z=yy~>ib(%lMED`Z>)Ttl2Ak#ALTUQ9^UCY4UA8-YalI-Od)T6RP8it$#6yMg5re?}>@?oK0t3dZ4|8QNhey}f zwHp*$vy&$@c{gn_0(zJ@1P%ZoSfCUzu%#-O&3a{)s6)79Tg9$dTAV_O8Ad_NmMrdw40#X;Bzkx)YHN!7)O&$-v^Splq@t->2c?TA;W1GWK;nU&iVay&j^`s;K0^{Bz%v8fj_^G zxS%rj;_U26i<;W4gSXBKiDqglE4$qHND$%=;0N^!HRYd?14BKAme<08lh+ArHfCa0 z$6hDLu%Ee@>kdZ!_9=`>11GN26po2@Q$a{Aax~M+vNY5A^ZV-+JZ3#PR1&9(AAj0e zVTaqp2MW2r@HbbLcpNM^Y-RZvygrv#wm;yu-pB0O9w=8S0w(hn@;Z&@0n8q}?XACm zJMIxxcCF2ALmU}_UfsB=SDRrk|dS!o@8 zBAb`HN-H9av!sX*?A896gf33%;>Cl=m3~>Hug_Y_WL}}iifxwS$L6GrSoYe~Rq2^J zjzCcw68^KlJRhVJhaK)aE6H(@LEjnCC(ZM{`7yvzAQnIvF}H`3oSBEn+Vu$~49Nkd zyIUmN=demudrzPd=%r+JZ_|qtFHw;!P!a_EF@aoGazOyGbJ7Ye)E%Z?g-ifjmxb{upUvw6-Dl-gQwDKP{8}D;M z?B;yY)Jk!zIkjQ3$72vD&40Dz_4I6ANCpT37>s%_HYYZR18RI18#6-*5yG{E{DDHk z{<)odH!v~dumws(;VeO7wEcY@_ynX`cdrs;-lzZq1-LV`KR$iJ7Hl@y?y$WZv5Sh;K#%!+M?r?FI-CgkhGEi0Q^nv@ zJ9dL#vs#kj3P9yN(bVE1*V3=~GbRRRzvq$4xfEu-4s_IbYBAv)PSJ9uNqk_VOBn?A zpFVmAz18mk>CwFe$|{YXy{jF3qhC|gX8iVRKH58ueWz#w-Wa@`2#vD1bh{|*8#$g~ zvE#7($+TeP;9v6x!7%(C3IYglGS{o0`vHFnB}Q;bw}>-aTH(_U$lqZtn==cG5Dtbl zfjXdPBNPWYSSe2tFaUUe@!*TpQlOT(KIP%4CQ>ZB9&1z`#z`=eu7z+7A%$iC{z=af zY;peNL%uhNR4$HHGHK4Q-EvA)Bul^S=ad~ypp1xe)|TAia8>vLDm%cVLtq8pnQrgw z*sunLoVGt`WRD~8XA2|^Yy}^%(|6)`Za7P$Qcz%2c)Krd^yV9zc`UjBKnj}$+h_)l z_SN99#4gcw4t)@`ts=)nwBSq-LvG?#lq@*3_bJ8)B9MY?!$9$@&do$pW)fU3Hdpea` z#`wTy@q5^6ztQ>N(&?yOOI_$(6sL9se$8eXo;uLlnv;O+k4KxG_uZ=7GB3X!Z+hQ@ z%fl3;BRn#U^u*E%K6x4KeHdKh3cpC8#flj=E4-{xu}yKq?Ck8~y*K~IMCmxSJWQ!R zLqkbly?h<|hnd&kfu1|Tcnxp0H+cJAtpFlWTwYmpczF29-ybFtgc-`>sv*0hio+jI zE?Zt+P94|<-rY40gZ>%*@^Z=LrDH^Px4}^rDat);8!@hzqATAg8v{;le?i1Ner*tH ze9-Y|Sw5X|(Z9R>@I>Z$&KcN3bAT`*<1io?{LBRJ7_NcT@zm3IWv)4nb z4p~PYPp3_7+`!frBv(qfs+zDTTA$$ zr?Rl|qd*4r$|cVeHKK^VpyI)gah#$I$S5d@sAQbiO(rAi8DvT<)KuJThv=j+2xw?% z_rrBoUYOw6xGa7S%29u%goJ)HGFHPZ=^XF*vnx=yPqhd>OH5DS=OX#QTh6W4jN@Yh zUUUS)v^3)6N*#~Q(s2Qv=7YUYo^U?hr`1{|4C)nH&w3c|;E><~I!$XnLuDkJ%(*Oq zgYV+v@R9{ntW8Z^V$w}8p3N-|Q112$$f@^$DQEkh1UV zRpK!v=~QzO%M%GBf?>`do+F`Z4^}@uQPc?{2p$ffrI13jbYpL4=aa`yqWU=h)A3c( z)x0z?&5qivhbs795dqRPRT9PD3OP*7h{A)h|I5;S53q2m?py&KAj0Y*8A$V)^>Q7b_ZrS(*0o&%H)^O@`~~tLPEk%#GL8KD68%ajxF_Z)r@ne~^UKk^n?l-GOm|t% ze@$k03iB2Ee%U>H?S9Yaar!~RUFMJxy6+AP*DF$P=evJ1u2{0iv96cO?w<=HmSpzE z{iwIEUI?R1`_4CdwvceCUPl)rKmmU^!3k-%p|Jxyo_u=TZAO&$7L^N60o>wz5Fw>gB> zAKfOArxY-2JFxdRTj0;e09pH2sOQBRC4N-=yV!8Fbt@VJc>tFwmEB_MMX5xE%b`1H zA?vhaKKYYgwImj92; z7>Q5X?cSRr5x5=ie@|EOG&ip&XY$;(o?ps%d3t#XRiXuej1HZ-KbehZr?=akwR2X9 z%96tbtnEP_Lp+IMMxGK#Eo%wSAG}lP6T>m!e#DP_*u@Zd+z;E!@~CcZyszb>V}1RA z8-w7T!{Tw)uAj}&;b@at)sw6LIykI5S;eXa8G+qn*3-K?nYUWoK@M_%^|^C24=2=- z3}U5W^_iA^2O@_NTHCKXgMAT?1pK3;(eE!uIF6ca+Df-5bKNN@D5#3YQ~1BIu{q{S zQVS0a(K!{$rYgPO=f$+~+T0!s1#bLV-hes@N4F&0jaoE`Y)C(Vr9FuMyxGX8Y~cC1 z{W=JZeL@g~CX0)8?fJ4jnI|Fey#0GXpOp1GuOd4ddz4*P8*7%&<&X;&t-_Mt@&x$u zxMuuRI#gkTTSvapxyg7HFWcv~LM^Wgrs^lG|Cu5eM>>Ep_C~+1c3%SCjj3R7H{>-b>2M67%?IO)lLp zXfRPCvS`Uh+h{lN-JUepX}>;PG9+ULKFiX-cgkQ=-;E^roDUPwi3G%^XCt7Hd4qi) zO-5Q{7285i{am?tNkt_jc^UV8QQrFtLY@}H_^7w`0bT%_CV*+QKW*K~y!)4)m65ch6`S}WGBPqFIVr1N zr4(&)|Ju{V((+LIjxj|)clzK^t>fkIn;rrs_}rfvGs3)s`5Q$-R})I|3W=_+c(BfC ztB&Kt!@@!e1Q{85_l(;;R&z};27{De7H2GlmzSnnQ8bo%Fn_AQ!d3Y$pDqEHrnhg1 zgp=o*``CW3EOpgoHWBh-`y@gdLmQ6_7p=$b_F8m(taE<GzOp!nbzvOQ+%z;)KVnk7UP$|~mKoD#`PTJ&-!(!l-W@Mu12ocB(l z*v|Y9Mx+W-{4W*DL{?V1s*@5`h1Y^-#W!;}AaMLFa4P(_ptmeG0Uj0>PVQuDwe`@{ zhte#M{-Ld_D5z`zi~kis1b21T?Ea&BJ%4c{Q!n}XzH$wPqmHA4l*{!x;F&_?6Opfu z0LvxfHqQ3^>X)}Hv--1tTZEj>`!dKS_#tQ{+>bl+`qOPsT~B`lVu=h;W#KLSoQHaL zdl1mOE(=C{n>|k~n+3q`AtO7&P?Eqa_>JiYIe-ZVg^>G$OULe=T>E zV1Hiuy{eQHe3tziK_g^ypc8S!h&I~lO!tq0hoN`I@X?)!nB2`A)j%|VyZ2&G^mdRa z)D9LT&lPYEEWR$2-J00wS(3m*eXzTUHDK{KW(C~|n7l8+hH|=d2nHFN+h~&$TEv(G zISpMd=&uE8$RTM9PD5%WHJ}I!@84y*nC-V?f(7&b*tO+#T$jTj9@d`%C>H1s!i4!j z-ih}M-sK$&3^J9MxMkr(kK0;+UB_G1MrYUBZ43d&Vav~-KZgjT;HRcO?~P?}c$Jeq zU#~}kCOg+zPB55tt@XU$0vp)LIcZ3t?H#!+5FiN+L3Dxq00}Yi_dVk1*&Ul*sFR=666S@VlU@YtRrZ9>~;tzmONx zUhR9edzd2vsX#-SMTw7(&lQg*=8`E@{Oyu_Se^h^F9r>$|6QvGV{a3 ziW@1cF@F}1y;5Xpl7{jvognX=z7k`k_l0EFzedu^5kQ)V(f?>Lt~-9sm(JtbcWjQA z!yQp65rx^4Cl3Cj2s^*ySm^BLc4l0&XvM?B!`B0aF>Q5P+^VMc1sXqzun5HL8?DW4 zZ6H*%@yYfYLF3+_L90A{rbhdyDE^sC}LL?mBjZxqnH*wpO+{SyS9g2;t~y| ze5Dd{R@UzeK5l=Xwy*5wzR%eGL=2LQ63;VphE)Ki=5+PWwv;(Hay z*Jm|P`*C?$-Lr5OH8S)!Y;bHWv+8=oL_0;c-?nI^1nT=iTY(@%XgI@tvOD$ZiEpt}vhY#V$qaVvTMf0W)!ZYUn zq3svaOtYim8x>nwzy*Q*_9}FdkdVUG@18o(uA1aCxoqU*r2#Yr*zY+ZbQmjsZ*CCZ zrx#(n%A2X3E4(#yq{-$O5+0bNFSbDGEcaI}w`LvTC!7MI39jN{ku5I!~x7*Qbp;C8*g(>|Rv ztr4=bvkRLwbvhKTeg63q`)1W`we15JoyYt`L~`_vtDA35Qi*Q1&m{o>7-JB?i25zV zx17*$t*5`58Nf%pMzo8;N}+gPJ@l@-khZ?*-S4)Xed-|Le@+xGAtt~yU6%+74lb=r zn@p7BXjq9{+r#tixaD#AHYscBj|`RWpj_NK5ht;}xcDRf!~nXCnYxS1jYpfF038KC zzvV(Kh0N|)>Xod9@XDB}%^#or%2UuCM2w{!)2GQ{t6S#7heHdo&nZwn$AWNvX670? zYUkHu$sUEh1N_NgQ~+$`ca1By1~r=gtZ1uztNXndTcAwNUINRBKpiGL*)*1mydH_@ zU3pT0(}f|dfw{M8H+_uGD4{QappXy7aAf_ zQdL7wh$s{?gP8D9YNB|Sw`Cmdm`rW-gjh5YGwHPGA&-6fbUOjol~C2>8cpF4(b3c_ zaa;CWZqh-oZ*Frfm)Cz9q+%c={_?%M^p4N{Es&L!_4xNCGRlO8j;faD_n~l{DRh~E zpAvY?i?=fZSp@?)xYE^1)7L8GKr)klh^rHVq>0j>CF|YkJ~O?AYKJ^2@`M+^cE~vY zBbd|r-*Kz^MfcA3{XJ!{FuFG>vvS2ZDR98kp(;yASsCN()wn=MgdlY=D?kZ`2A2wO z>A@*ED)e&pHA0VMKNR&TKeMu493Nl}C;Ojc0##*zESg8x>$b`s=}=!!#cZ zatH8>0Wr;Ho(OlHiVy%!RaKSm1N23oc*tD&wrtnY^)TJ%V#chqaC-HSy=l*-sHoV9 za7d#o26*%94{lt|C>P3Nj;C>02}w&|n!^WzNHR%Yp%B2Cn4Em}U&fCCJDes$jFNi& zs%UU`xCoWuqdg}(`zceC<3sj9G>Jv+;t9LZdmm*xLGtwr>I7POLZ&#$oT`F8_v8I4 zGky2hre?6neL32RT}14F@{Y0TskDrIp}8zb7_p9E`9$0A!K6g3l9O}NU zIa>#rTiDP^$x5AuA$~sh&>>}j&Vxf~lY@-_BT->YSz>|jlf@K0ySD@^K>F(6=bL$b zb?E%_1``k<;loa_!{;dOS35hGRg_l7NJ+~^N=eCj+8}dby33M978mpN$*OPx?w$Hs+nd=Qp$ci7@ zhUpLhpc#qej_2o^%ar|;Y!T-}cLCI;ZGHWzwWZ~2eO;YPq4bA&QxlUTv%eXsM7d~- zZ5I%-w|s%6Sg^9H>g>?JfB(E{DYT;0NusmQ2rrRDUox$O5Qq#`Rz^BsZa;b`x6 zv0^zo(el!a%gv1&R%onW!|M~8JcNYKSn2%Tv!bTzJ7Rte@hIq(#*lu==JWB~VNRd3 zBjK>6Bz`*S?678*VuLw{U@BP25V3Q7379{6hYGx}LaeT40~ry*7v5lQY#58W2jRm1 zz(efZx|)I(M^mU&@b-D*MnSUqPm*Q7n>e@5g98h2{{{GJ7Z(&GVQC`PetW#r`qmEnQp{* zOc{O|==T(SRC$u+Y8M|2ktBN!11M%#RH@^(!;PncEe;O6^nGq_+G7XwF-{-jXI}hh z5n&|b?O9WNV$exZ>({;#V8E?Sc0TxVelo$?QPfsPfJRUOSQY}*Sm^@6$(eOlXjl#T z@=A=F{F`46d-c*LP+J zM3A>WwC?P^;V)#vJC5JCp-DZ$KZy3(?QR;3ijGa9wC|C+cI{FB`C3Yc)AO=B<4MH^ z1m%lX>EvKnX<9?=r5dQF-SK#y5Ti@xb3dtWjgA!sbyeG?OQONZaB#^znDY2vQ_)lksVBFqqu4MKs}nF{Wh!QvYredAJ@`27{;inW9f4bk=OY=g?f(}i@ zJ7~1D9Df=Q5ZN`Tq`=SE_})nlMjXr$39$?dI0FktOtK>b)QA+kV9uWVkgR`iQQTbj>ffRS4pS7# z0hpW`YN6{X{|~&R80>^-gFB9mnb4G<;n_iO8O6Jaxs}VI#-W6U?E*7zYW3fREE?7m zgC>H?pkD_-Ha8phHK$bT$RBfo9w*DI#89pp4FVp6?aQ@w0(zQaE2^SWG zxcT0Y%Efav$Ti`CyUTdR<*Zw*by_0YFn;LA@S~@q6~`}itDLgxZtv^Cjl>RXtqu+< z=CclGQhcM(n6Q#=e4e^51lo1dNKiS=+d&r(+P-_Q&W4+}F~A;AW* z*io$A_zC~%3*?By5@`4VZ;RM)6fj^)5CseXF8-PJFMR4qj?C|M@##AzriE6NR%}w% z;{w#DmzL`H2jzi4KTc0c&pmwrd_f9zL3v+(^BKbXm-QTh7kOJf5cc*?SIOoyFMQxT z_Ta7q=NtXXGyHgJa&~qVIK;3=!!B&7WjIM29sv#7AMCm9{qs>6fW2FRW$t*@x~L~v zKCAdGMerd$G`Uw?98E@67L4NVolE}ZOI=gJPF*kxE~C~}pE~}u59L9fX4kH>za9~c z1>KOx+w*`^!da8mKd1$YvN5=^1_H0%1+n@eed3he2F3bY-$|JWbCP8tdcL;Nj0UctjgJPRpqw!vYxM$ zQ#&^|cl#kQM?j75{HKUM54<>3J7?&`J8u>h1_1yVYHti3mj-vU8-&9j7v(Vj^~-xh z=njAt4Uqr>sfB0~LGb>77?ia$$9_q4eJTNJQ$KMM|FS8rL??!ZK2y{nW4qUlkCDp; zN{K+7R+OL4ThF7S?qC~~nS>3GkI-ds`Sb^+Kd&CFeJ3y@@BRMm!B2lteI8&{(e)+_%?Rv=yN};MxH~M4@-t0Qil&wVW zQ~Ci^N*0^lNCb%(q825_WDAz4fRXP)&MGS3Cu?)N|NQy0s+L^Z0hf*s=I_Enj$Y8I zSkr{#qGIFay?emZo2vH6D8a2Oud*k9#>g>5%L|@=oT-{L!m5n;d5utP<>AEa3uepNs+?}@y z>Z0y$vFu+yYK2Zh0_00TrL)LTq9@?{xFMmffP)vgy$^0t4?64zuQ%FXz8si{a9NvIcV__oB&q0mu7V_e1h4MvDBijl>UEoHe+m+%0QZH*< zNKe4&KfRAkOz78dU&#UJRVwOgBOoDNbedM>wCDV*^Le^uR=~-AHGIqBi_&hehQ@xA z-yUE9e<}_JD?2l@y$ehcNdAi3S=)FtG&)3>+!Ih7I=F#?%M`B|=u!c=LVD_r_M$Hz z+}09GQIIfu`+9w^C*u3m%GFxl2Fbd=;RB<%5k5WrV=@r*5iZnfRz7qoJYa*gr&zpy_9* zZuaVo76c~$fj^4hsKXS}V~YW$i0za8>F?91rGWIYR*slUysA-5kq{v#Cnr;BrFiVe zOX1hY=0{v~$Y=LIW0t|?%MIlb$T2!{%IsKiaE-c5EZU5mZSoknG6-}DQj>x_umOO> z<4j3N4FALZyM&=ewemSc<>~3HS{`CY)IhpuA%P2hOWO9y=f8jD@Am^^=HxOsP3q=% zOsf~Iy&tbFgk0q6`oK62BW-hYv#`k{Al*=0Vpjy~wYI&fNh%3_euw)zSfaT~h;9q~ z8nX14dcq^dr0Dtn7_z&*O)-pU1v$AVHcDO{Xy_!j*KmlDDf#VmOM#E%rV|!pbn`BG zV;~43Ly4Ay1#b$^2#;kRDO5RqRV16ndwl9~dhF2-I3<=N)4mKh{g*Q5HGK!_H^62Rg zVV?7TP!ksJHjBf=?0$ZJ7ITG&$cl7i-;4O#3bw&7Apz&tpU1lrVg%4T$apG}Li?bA z&(6uYba(u3D<(Yr$MTSk8#z>0#?`RKU0j~d^V}-HJ5GIgH(P~-lnEX9j;6vem9yqB zG4lwv*?68dY5=g%=0jBWva7vx;)>^}VSNI1OWJ)GCUR=R3*~n(ewI#QpPU`;Q!l4! zt2@9w5AW{oo<8BOFz7b#Q{X3R1ARpiHe=AQ7vd2}Li`{I$5kQwKqy-kF`J6rzkfhu~r}K78*9sX1%CYu`zwRY%jaJ7Qj@`1~2|Z)Qs)ITI5n zG6j$~wBQ8o_Kb+DQh@!>jJ6F#t|$Lo0><}(Ma9Gpphk-VBkt$hv&HFZ=Lg5Mk)4S5 z@3C;{RHRu8KMpk2>~R*)S{mue&mGLo)tm8=DlnywsWBL{4N5B@h+bO=6tbRsco(gn zx>r=^I;xtQ_Rr+!Q_lus@Haj`opp5FpCX)T+9m?<=*dQ(h?U*r2et?yyCxXVJ^Hr? z#goAki;Hw=pUwW}j*iO4_3Tjp&Ej!0eul0=f#a3;@z6ZDdRR>D!Iloj1;x8MJEO)) zekhWRZ~gY@fn{1l;OXhvmG~150HE~F*pX}SQ3O2ZF@3v^9nbXat|)l3SjSTL1DaN5`|kx8pP730-f zV{DhHGWI(X+=!R!Dy-dxf30=MR!gGURC6=*e;Fp|wHOiFT!Dc5LU+i=t(^*cN;V(z$hwA0W?fd-0&&CNgB z-9{dq{~I7k{} zBm0Zem%sxEfkWWj^loK*BlnxkhMeZ($E4cY+5;{wuIZ5NT@!RvHQS)wSN0s@jBn9t zEFiUxE8O$oA6!>w?N56A*%(mxTen~EtEeph%rNVhiT5I~16`z8TkE`kauS8lWw>1* z8992C-tg@0KHVSH#+_L~$;xS3TRy)#{hPzPVIBcJ+3dU(76IeO+L?e5rD6K&Rz|z? zQ2c{6Zi~VqD?Bs@EsGm5{cov0kZwIIg~KFm^i@I?M2{9DKH`JoaPoUh~YpsESY`=@& zL&mRKWbY}V+4F4PqiD@+oNS(_R?$4p5>-nt)dP;r0XZV771Hr%DJ)vXIOzg|2A=hm zMJI%IotoWn>GF7V**Ivk4~pqH8=QnT2m0sDTIC^h%7rU<_t!O_p^1#wD*=69v7fQA z=kAV5x&IFAe=KiouQ}bdCSwZ!@!5-3iKl)*r1*1t+kG>26q}f^+TfTi*lEC4+!ukT z-uB#$lI_%#v2uOwtZHq2yj?b07W2>NY_$)XDRLg%32%qIqxi)_6O+MRxOzUK;5-~l z0cQY+8Z9kMO^x9p@~ zZt@6@At9{LdtGSSdVr1_V3+IZOu<>3K|??%_74dO5mYk#@%aNCCbR`aCF5o^yWuHS zH7b&==WTud{+hn+pqZ_6Q;SlPGy7+_@j$MY`!^9tPI?rms$y zKR;j;6`9CxJ8~Hu?)#6ZS6DAKn>a1!Yfj&MSw={nXYjr_ahol9Cju45$?1?;;N0KH(83ars5~D-L4jh+k+k+H8)>i0uY?Kxk52}ak;+co+ z?)G_-{B_InI$gv8Ca_3x^->ZNe8{rLAy;_Jb8;O%a!3kS0c18ox2jUO5S zJ|ZikK$NUfsz@ZPRgrgWw*OnEc@=_A(5zB9T>@ooDk>^7y3OtuB3><++@DC^AW1Kk zO(*F2B~3_aY3cpAsGlUEGN$wtk+7N1(^x|vdZ~*tFin}LGL`?0{uCQY>{slny3^A^$?!C8}mxFaS2KI=(#BmRdiI~+;y0IHBtf= z7Zlg58kS@9c-{Uzo)1fyDi8{MuTY|Zd|IyOGQ-0u$@Vt(c=`tm*&iVFaAq0zX9WU{ zfru`hTHny-c>ucOnJGd-x-=EJZ65>fLk zJd8@#Ulof*{q0wZoZHs3;@Z~g&7D2+8Ac2Q{vu3-e^)NwpYobq-IH?oGy#Nm8gLR7 zAQoUDi+p_f`*gOkH4x)hG;4l3EGZL1h*&V`yv9}8@ql~Gb$+B5Cs}KDxIJ`b{2mE) zXe9kZoU}M~UteZISRbAgyhtdbqp7#GPQD8Le~OFByvp0$^y*9RW-vxGlyiI_Vqt&z zAElgERFhv6Q;{C$?;MZT=DztY4V#ASTBZ&!dFa19 zJcQX5BefM5L}Zpdnl|d}hg#Xw)Pj9iikJlNZ0+nseB2~m0?bS!w}1g8N_>n`pq>7Ov7fQMzJ}esQ97S>Q%UlMNOl0H@aH0}zgQzX__`E{ zJmoIc!&gczR*!ifeUaX0m3MHCYjP|twTZyzkjT+=mzb4%Ytq(ncy{m2rpHYTd4w4e z1wVYBgB0!axQLoqoL&){rT%lpy#wo|ps&<5PH;O2SKC}mokT$}TE!V4%hsL9)bL19 z&}~;!8Hy<@qv(laX}H{#UIT&J zTW{U5TF5830>&MzI{zpxHpXL=pa5E627%Kwuv?xMki0}qFNrbe4eGN6n;>Ar~M zCd!rN6?vjw{U@(wK@NWGd+{B2TmBKvfSRUC94IdTTM@?Z`gn?3ID_K7urOYM8y_CK z0~xb?JAO!oX+uYo=UPyP>PnmQ*d{0Io^l?=sdOM&AcF!U!Do9&iLCD)%KW)3LS+5; zvq7P@>Q8VAD5N+Ya)fc7$TO{jXqX2CTffdoa1~adVA4X8F8fPN_a8=v`%Zqd$^Mf|A$KJqDDDIC4rEbL z1b=$WNX$b340lz5L^af?CsH+v9ph@*;%iUtCFoXC0c%J}gCNx@h+A=O;l3Z63Y7xLS{ZaX!>RyI=w zQD;BKi(a!4(Z*sBggd(ZKPQbuICu>Fk7Sjed3%dSJy5MtA4~Ri2ppT$Wl%UkkLjuqsSi~AQ^jhW?+NVP;<>C%wn;BjlQlNHuAr&jnN%kF2 z`h>73av4of`{a)woIsvnSY1^c$@72)$hC9wgyH2}qY9hh6^#Y8hrK=v0k}v5CNOW+ z7f?zEp(15iIgv`r16Jejt@h|{4Mh2eU)%q#A`nP%$JP;Qa&)w5f4!#ASwTVc+_BV= z7jWU;CnbDC0CI0+Ce{))dyH}dB$=OvYyrnLfODi@r91wo2vG<@6h&vt9;$6^L%;ka zj!7D35iOtya5*kV7gzY;ckbM&0usRSF9fKQb$b%f^YmWU>(yTJrZ$1$2IVNFaJnHx zL=Pf>T1rZK{OGLp{9bor;_rxX_98Rc2vRLwiRY|V?IY8_oy4*S1h*HO&>!-0fx>mG z;mdb}laJU>CDl<}iyyQ8f%r+pt_j9~eKZ3%_CFC7Yc0=0(EJ z%mf`mV3%nQO;353ef5sj^x1o`JUh`xB1QK73!K)%2yvRez9}CcpTWGaP2~CSuKsp* zhOjc zC(y=XTe=jm&r9_b;`o@;uz%rCC78(p&WUm=$;s}N=kcKU0yN+T=R?tF3zhAruQW%E z!~e8bt-bu49~M~Whl8J8NBte@&rl0eeWQ_EGFoD6k_%HD9bhPYEnt3#?}z?=k2ti` z^>*s$*BP*|95Bij1t=|5gBSiAf4AD~Z6Tl+NnzYAWQrglcVa7aK?^2-a>GM%R+z#p z~s6S9GBlZkO)a4?>a&7$6AE8DH@){XDU_~2u=+kllQ=>Z8D5rLUNMr6;@ zXiyuQI^^l+A|xHqSGJzE??v(Vz3#|MJXYPq-1%oDTzZ~5c}c>DVwPAo|S;K zL~lh~y&J?5md^z#An)9rD0_A}?~l{x0<{}1+&n6Gb~#kFrH)rGHPI3EE=w3*a%^ew z4|$Q~Uh&n--q`Q_=%f-turYn!DQDdEZyDgG#f9KjxM9MX_%Gz4-F>?G7L( zG_RZ;+%x@dd}L>=?8*vU*lyXbuD$ErfEtL4g5!J8owg!@2F_KP1rG0eu)aWKv#M)y z_YfT1j`aqjnYR$b_FFa6Qdv7Z$}OYI`)5~p$bOvQ$^;rspqbG>3K5kueq!*cx!Hse z#2lW4EH0qV3tBY>>gFZ1H!1(ULADKT?BL?7Pguf9pi$%mF~tNj zsPXyq6PSm;bN0xtyA(GyiHp%q=k>&Vz?N>@2>N0~D?`2=k(rXx>VDiiS{^)AAa1``{8B0zn`2IH{5dNn{N8<|b#yj4T)Sw@nkT zZMF=ZCpyFzf^WNsNsr87cAQ+CU?w|a(@x){ZdW#d&qdCHa73PcY2|#*TbuYEH8n~m z@sY!jnvNCNOP@E16$NF`g{D}#>vOKOha}Qh_G}2X$RYagX!oM;+$=*p3Jayl$^{2NB8sa%T|eivH35?JF{YsI6{# zt_<`bzaZbeczT`ovM;4~|FAj(W0b{jOoOnNx%>1+xT`sz1%>m!wapU?0nTB=Z%~vh zEOXW(4CjDE-e)U<=OC9Pf2&za+C_uEg1lue0Y3SP(wU4#n6@V8>kO+`fqvt4&AU8Y;lqkubG4v;TTV3iR_h>O|WteI|9PVK@TY2!7_ z+yQ~~F=^HI6T3L)@gN*`_F$mG6WUAZW+?{N#2(1EsrIcCiFlq=)C5H7T0Vq>>b(n% z(rvg@OZBKN)a%4+PsV-yPjakb)s>V_$GhOzBhZ<6D~0XyPZIa5pCs3Bn8azj6qSPk zoA%h;oViuqlo?N576SwAD138!O0Zd4noBT8`Y2B2LH!K8xx=*~Rz&1!Mm(Fi^-x^F zhH?75Yf`i#Wih%lB9^YS5E)Z$%sf=|dQC8(0==ajt+IS zZ-9YsW++vGUMGgnuz~d&cbdkV_Ur2Ronealy&?ZqK-b5ZNImgfAys+?Z~%aq{PO7~ z&XHk*4(7J-UeL1HmWnvyLq&`8ersvnDo8bjBd9Id$yU$SSj|bNClq&igbIo&{J>`d z$ef}yKhjaA_1ib@b0%i+?}Px0MhO@!VgOmZgfW}33251FP_+dq*UMq2*E+C%Hh(e+ z=7b+k-->{Uh>yL!eR+tW!}%fl4N;=spZqTNbd|5bkm>0y{O?T-NT$nx?=rv{44%2+ z1A&Aj%!qwwmwSKI#4Lv|1BEulj+g?;BJi-;0zSC$b;@UVrUj{8IC|miT#g4~=NG2~ zn)zs8YEkrlm)pix$@ z3MptQLBSx|q-AOvRa^V?VMG)o@w*Q9)8k`39U4siWw9YgBAXnEl(aOVwy>}bcnMqw z0Ma~6o%t}+zu}we9tROgNt8XI1eew;T!|gq40R(dY=J_OATP4)!QJKRDc4fHaY?7` z%xTo+Nq*t0#A!nO(2C8&w2rOeEPpzOWvJfFZ;@nn2JL9}R#B}#N9W7n_l>k@(as&O zjZQLaCMtz)Kx0c?8%9uUZEw?Lq@}5LzsYoOt*=iH!WGd0zYL!s8-QIfV4@cBglmr; z0K&KP%=9!b@jHj|va*niO-AtS=l7k*7eaX5AxDR`nw*Lf_)P2>q^2q7*ZCb<8p~$iWLdvO!V#L>PH-KmDp9A)H)9{qrdEL>Gjg$PjgL7W zm|vXZWaczYw=!)) zEExqwc2;^`H-MwjvNYTS60NfbfbntS)vH%%JHx8>jUzhX@}|R^`Z)OP`Z5~G8g@h{ za|KsbR20eGl(-S}{UEv5vGsRz@gDW!18_~xhSwsGjj3dmtx2i8JW`K$X-5G0bL%fD zw6?ZNQBiSn+SCMh89et!A2VhI5R(2H?&&xEGp^u+svY`7d>0XKI;RuceAyrKuiPPz z9dvYco%j8AJbi$D8O0`hHIpw;W7OKc>VEYDQd6Iik@oK#I&`2;Wre*DmoF6+l0O%e zZJOfe%d3GGS)%(!fR6c~4^i(rLwTQS*XXjZr+Rh9&2o2rGiL6vKOIOhBcttU;jdedNB4Y4EUy=R2d#TI;0>l2zXU8PDnhnEPuW@1e zxZz!t|9ntN$84cixlR{28O1Jm`X`mo5Cjq0TlDlhl5staK?I-GD7q21jgQSTFO1I* ziDwPWWIb^FBxGJX3jDEhWpV!pAaX$d`r!BLKd0wN8)_UEuWSd1Oh@0))TasU5My1J zsgd72G9j$7_bfYJuq>^tQJ~K5ady9O+tDghgPA?Rh0%gne7Kp;u5vqsqmURUj5JGn zLX&9s@PwXzn}vm`E;Y;jz=ouUkM^zAIJJ9M@ET6R?K_f;tPpx`+Sv39Tu@Ko=`8St zB>#h;Ej8{uP1@#o`3L!T3%lKP6bn)3WAxtn^-Z&|i<3AuZY&})5{iC^E>nG zY9?j4iza40NDhY4APtA}R_AB+D2<*dV2DAkd1>n*u- zpH-a=HT5ErqHlN@kQiXd8H_CS*nthq37-DS`OSz_3NU|Rr!xR)xUx=_q_3wN;p;=j5J?MX3WgfWN_ptJnQkVRjH>Wqa!CU`f zP+z!SD*T|VpfysD1cp})7On2udC4CSQ%Wo5@<&Q`OV`(pYtZ(Ks6w1K|qQJ|u&Ezm@76L^r2TtpT_zGVXy7&pDLl@ zFOAMrB2tPk)J;Hcw<$mWYdWK67}3e6#J_Fzy_zZHg4zLYjLOW)LTm^NBTvBZMn zi5OFJa|=@eCzm7uAAR+UkCzv4hYlM{Nl$)z_ADxpCJlZ#z)-JN#Cb0zRT3qZjVi5q zzwmo@pzyJr2G~KX?|%9A$vlO6MK{6ANb;IvFYmma?e)p(;Dx+$Vd0vQ+1ca&&E7j} zcrbcHT4X{2Jl2r0Z-ZUGIV$+d9#^uStLD z2*64rwhXHR(`CTDq>TB=`HZ z?xj2a2PBm1xLIKC9URCE7Bs3buyzfq9AEPq>ZD)DP0Q`9%TzCT_vOiVo&4hx4Da`u zrO-Qum0tmr?oB!nf*gGAbCT1E^{Q1-)I0FI@;a8w+0f%*6bAsi(e6gtg3ihAU+Z-RS&tjtGYx#%~$TmefSw}uSdqY)cby^ySiHF?#`)nwzHGm z^Y;hF88bVbgw54!?RYJ~JVoE~A5d2CzvGEmamxz5;+yw9HJ1k|(J!4<%mkg=9rtSA zj6M1gmFIU+iU8Z(`_wE&pGW#16PN$03d$~$#`ww2;HNUNB$Yw{Xdhqx6WaEwC7bRa zUGV>p-TVJ6Up_g!AxIxv!1%G%asgr!x0jNUmkrd;+RKjL7I*`R2nk8@3rX+`i|7f7 zONok0iHPzEiAV_v(XM~Q{jUXY-E18pe*gCZAyGXcAt@0tDN*77xxiCq7-+UYAXOzz K#YzRMcmD&=Mm_rg literal 0 HcmV?d00001 diff --git a/inside/graphics/header-210x.png b/inside/graphics/header-210x.png new file mode 100644 index 0000000000000000000000000000000000000000..de59222f7111c5672f79a789089b501f3e7e6dfc GIT binary patch literal 2595 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 FV1iT=?&|;m literal 0 HcmV?d00001 diff --git a/inside/includes.php b/inside/includes.php index 86b9d58..8b68a23 100644 --- a/inside/includes.php +++ b/inside/includes.php @@ -21,7 +21,11 @@ set_include_path("../includes"); $include_path = "../includes/"; //PEAR-class -require_once $include_path."DB.php"; +require_once "DB.php"; + +/* LDAP auth*/ +require_once "Ldap_defines.php"; +require_once "ldap.php"; //Local classes require_once "Action.php"; diff --git a/inside/ldap.php b/inside/ldap.php new file mode 100644 index 0000000..321dbe4 --- /dev/null +++ b/inside/ldap.php @@ -0,0 +1,140 @@ +mailExists($username_or_email) ) { + return $db->getUserWithMail($username_or_email); + } + if($db->userExists($username_or_email)) { + return $db->getUser($username_or_email); + } + return false; +} + +function ldap_authenticate($username, $password, $db) { + return $db->testBind($username, $password); +} + +function legacy_user_exists($username) { + $conn = db_connect(); + + /* get legacy_user_id */ + $sql = sprintf("SELECT id FROM din_user WHERE username=%s", $conn->quoteSmart($username)); + $result = $conn->query($sql); + if (DB :: isError($result) == true) { + /* dberror */ + notify("Databasen sliter, prøv igjen senere."); + return false; + } + if ($row = & $result->fetchRow(DB_FETCHMODE_ASSOC)) { + return $row['id']; + } + return false; +} + +function legacy_get_user_from_mail($mail) { + $conn = db_connect(); + + $sql = sprintf("SELECT username FROM din_user WHERE email = %s", $conn->quoteSmart($mail)); + $result = $conn->query($sql); + if (DB :: isError($result) == true) { + /* dberror */ + notify("Databasen sliter, prøv igjen senere."); + return false; + } + if ($row = & $result->fetchRow(DB_FETCHMODE_ASSOC)) { + return $row['username']; + } + return false; +} + +function legacy_authenticate($username, $password) { + $conn = db_connect(); + /* query db with form data (password, username) */ + $sql = sprintf("SELECT id FROM din_user WHERE username = %s AND (password = PASSWORD(%s) or password = OLD_PASSWORD(%s))", + $conn->quoteSmart($username), + $conn->quoteSmart($password), + $conn->quoteSmart($password)); + + $result = $conn->query($sql); + if (DB :: isError($result) != true) { + /* more than 1 row means valid user */ + if ($result->numRows() > 0) { + $row = $result->fetchRow(DB_FETCHMODE_ASSOC); + return $row['id']; + } + } + return false; +} + +function is_mail($subject) { + $subject = trim($subject); + /* from: http://www.regular-expressions.info/email.html */ + $pattern = "/^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i"; + $match = preg_match($pattern, $subject); + + return $match; +} + +?> diff --git a/inside/menuExpandable3.js b/inside/menuExpandable3.js index 236ad35..ca76409 100644 --- a/inside/menuExpandable3.js +++ b/inside/menuExpandable3.js @@ -17,11 +17,11 @@ function initializeMenu(menuId, actuatorId) { return; } - actuator.parentNode.style.backgroundImage = "url(graphics/plus.png)"; + actuator.parentNode.style.backgroundImage = "url(../inside/graphics/plus.png)"; actuator.onclick = function() { var display = menu.style.display; this.parentNode.style.backgroundImage = - (display == "block") ? "url(graphics/plus.png)" : "url(graphics/minus.png)"; + (display == "block") ? "url(../inside/graphics/plus.png)" : "url(../inside/graphics/minus.png)"; menu.style.display = (display == "block") ? "none" : "block"; return false; @@ -33,7 +33,7 @@ function menuExpandCurrentSection(menuId){ if (menu == null){ return; } - menu.parentNode.style.backgroundImage = "url(graphics/minus.png)"; + menu.parentNode.style.backgroundImage = "url(../inside/graphics/minus.png)"; menu.style.display = "block"; } diff --git a/inside/screen.css.backup b/inside/screen.css.backup deleted file mode 100644 index f6fada4..0000000 --- a/inside/screen.css.backup +++ /dev/null @@ -1,700 +0,0 @@ -/** - * text: #000 (black) - * background: #FFF (white) - * light-fill: #EEE - * conrast-fill: #DDD - * passive-cell: #AAA - * border: #CCC - * link: #00D - * - **/ -/* =general -*********************************************************************/ - -html, body { - margin: 0; - padding: 5px; - font-size: .905em; - line-height: 1.5em; - font-family: arial, sans-serif; -} - -img { - border: none; - display: block; -} - -td img { - display: inline; -} - -.mceEditor img { - display: inline; -} - -p { - margin: 0 0 5px 0; - padding: 0; -} - -em { - font-style: italic; -} - -strong, p.strong { - font-weight: bolder; -} - - -/* =links -*********************************************************************/ - -a { - margin: 0; - padding: 0; - text-decoration: none; - color: #00D; -} - -a:hover { - text-decoration: underline; -} - -a:visited { - color: #00D; -} - -th a { - color: #000; -} - -th a:visited { - color: #000; -} - -.optionsMenu a, a.button, span.button, label.button { - border: 1px solid #CCC; - background: #DDD; - padding: 1px 5px; - margin: 3px 0; - letter-spacing: 1px; - color: #000; - font-weight: bold; - white-space: nowrap; -} - -a.read { - font-weight: normal; -} - -a.unread { - font-weight: bold; -} - -a.skip-nav { - display: none; -} - -a.contact { - background: url("graphics/user_go.png") no-repeat right; - padding-right: 20px; -} -/* =lists -*********************************************************************/ - -ul { - -} - -li { - -} - -ul.linker, .linker li { - margin: 0; - padding: 0; - list-style: none; -} - -/* =forms -*********************************************************************/ - -form { - margin: 0; - padding: 0; -} - -textarea, input { - font-family: arial, sans-serif; - line-height: 1.2em; - font-size: 1em; - margin: 2px 0; -} - -form .title { - font-size: 16px; - font-weight: bold; -} - -textarea:focus, input:focus { - background: #FFD; -} - -.readonly { - background: inherit; - border: none; -} - -fieldset { - width: 90%; - max-width: 600px; - padding: 15px; - border: 3px solid #CCC; -} - -fieldset label { - font-weight: bold; -} - -legend { - font-weight: bold; - padding: 0 5px; - color: black; -} - -fieldset input, fieldset select, fieldset textarea { - margin: 0 15px; -} - -input.submit { - margin: 0px; - width: 100px; -} - -div.horizontal-form { - padding: 3px; - margin: 5px 0; - background: #EEE; - border: 1px solid #CCC; - font-weight: bold; -} - -.delete { - text-align: right; - color: red; - font-weight: bold; -} - -.formHelp { - cursor: help; -} - -.formHelpMessage { - background: #FFD; - border: 1px solid black; - font-size: 11px; - padding: 3px 3px 3px 20px; - max-width: 300px; -} - -.helpMessageClose { - border: 1px solid black; - position: absolute; - top: 2px; - left: 2px; - padding: 0 2px; - line-height: 1em; - cursor: pointer; - background: red; - color: white; - font-weight: bold; -} -/* =tables -*********************************************************************/ - -table { - margin: 5px 0; - padding: 0; - border: 1px solid #CCC; -} - -th { - padding: 0 5px; - text-align: center; - font-weight: bold; - letter-spacing: 2px; - background: #DDD; -} - -tr { - padding: 0px; - margin: 0px; -} - -td { - vertical-align: top; - padding: 0px 5px; - background: #EEE; -} - -td.label { - font-weight: bold; - text-align: right; -} - -td.number { - text-align: right; -} - -td.phone { - white-space: nowrap; -} - -td.date { - white-space: nowrap; -} - -td.is_member{ - text-align: center; -} - -tr.table-footer td { - font-weight: bold; - text-align: center; -} - -table.calendar { - width: 98%; - margin: 0 auto; - font-size: .85em; -} - -.calendar td { - padding: 0; - margin: 0; -} - -td.passive { - background: #AAA; -} - -td.weekNumber { - background: #DDD; - text-align: center; - vertical-align: middle; - font-weight: bold; -} - -div.calendar-event, label.calendar-event.button { - background: #CCC; -} - -div.calendar-concert, label.calendar-concert.button { - background: #DDD; -} - -.calendar td div { - padding: 2px 1px; - margin: 1px; - line-height: 1em; - border-bottom: 1px dotted #000; -} - -td h4 { - text-align: right; -} - -th a { - display: block; -} - -table.sortable tr:hover td { - background: #DDD; -} - -td.unread { - text-align: center; - font-size: 1.8em; - line-height: 0.1em; - padding: 15px 5px 0px 3px; -} - - -/* =headings -*********************************************************************/ - -h1, h2, h3, h4, h5, h6 { - margin: 0; - padding: 0; - line-height: 1.2em; -} - -h1 { - font-size: 2em; -} - -h2 { - font-size: 1.5em; - text-align: center; -} - -h3 { - font-size:1.2em; -} - -h4 { - font-size: 1em; - font-weight: bold; -} -/* =header -*********************************************************************/ -#header { - background-color: #EEE; - border: 1px solid #CCC; - -moz-border-radius: 8px; - color: #000; - text-align: center; - letter-spacing: 3px; - padding: 5px; -} - -#header h1 a { - text-decoration: none; - color: #000; - display: block; -} - - - -/* =navigation -*********************************************************************/ - - -#nav { - float: left; - background-color: #EEE; - border: 1px solid #CCC; - -moz-border-radius: 8px; - color: #000; - width: 170px; - margin: 5px 0 15px 0; - padding: 15px; -} - -#navList { - margin: 0px; - padding: 0px; -} - -li.menulist { - background: url(graphics/minus.png) no-repeat 3px 0.3em; - line-height: 1.2em; - list-style: none outside; - margin: 0px 0 10px; - padding: 2px 10px; - border: 1px solid black; -} - -.jsenabled li.menulist { - background: url(graphics/plus.png) no-repeat 3px 0.3em; -} - -.jsenabled .menu, .jsenabled .submenu { - display: none; -} - -.menu, .submenu { - margin: 0px; - padding: 0px; -} - -.menu li, .submenu li { - background: #EEE; - list-style: none outside; - margin: 3px 5px; - padding: 0px 3px; - border: 1px dotted #CCC; -} - -.menu li:hover, .submenu li:hover { - background: #DDD; -} - -#nav li a { - color: black; -} - -a.switch { - background-color: transparent; - color: #000; - font-size: .9em; - line-height: 1em; - padding-left: 5px; - text-decoration: none; - font-weight: bold; - display: block; -} - -a.switch:hover { - text-decoration: underline; -} - -.menu li a, .submenu li a { - display: block; - background-color: transparent; - color: #000; - text-decoration: none; -} - -.menu li a:hover, submenu li a:hover { - text-decoration: underline; -} - -span.key { - text-decoration: underline; -} - -#calHeader { - width: 400px; - margin: 0 auto; -} - -#previous { - float: left; - margin: 0px; -} - -#next { - float: right; - margin: 0px; -} - -#previous a, #next a { - border: 1px solid #CCC; - font-weight: bold; - padding: 0 5px; - margin: 0px; - line-height: 1em; - background: #DDD; - color: #000; -} - -/* =content -*********************************************************************/ -#content { - padding: 0px; - margin: 5px 0 15px 207px; -} - -div.text-column { - max-width: 600px; -} - -* html div.text-column { - width: 600px; -} - -div.article_images { - float: right; - margin: 0px 0px 3px 3px; - width: 200px; -} -div.article_images div { - border: 1px dotted #ccc; -} -/*div.primary_image { - float: right; - margin: 0px 0px 3px 3px; - width: 200px; - border: 1px dotted #ccc; -} -*/ -span.caption { - font-size: 11px; - font-style: italic; -} - -/*div.extra_image { - float: left; - margin: 0px 3px 3px 0px; - width: 200px; - border: 1px dotted #ccc; -}*/ - -p.date { - font-style: italic; -} - -p.date:first-letter { - text-transform: uppercase; -} - -p.subtitle { - font-style: italic; -} - -p.intro { - font-weight: bold; - font-style: italic; -} - -div.messages, div.errors { - margin: 0 0 5px 0; - padding: 3px; - background: #000; - color: #FFF; - border: 1px solid #CCC; -} - -div.messages ul, div.erros ul { - margin: 0 0 0 35px; - padding: 0; - font-weight: bold; -} - -div.timeData { - border: 1px solid #CCC; - background: #EEE; - padding: 1px; -} - -div.optionsMenu { - margin: 5px 0; -} - -div.product { - border: 1px solid black; - margin: 5px; - padding: 5px; - cursor: pointer; -} - -div.product_images { - float: right; -} - -tr.barshift-full td { - background: #AAA; - font-style: italic; -} - -td.worker-status { - text-align: center; - font-weight: bold; -} - -div.calendar-barshift { - border: 1px solid #CCC; -} - -div.calendar-barshift.full { - border: 1px solid #CCC; - background: #DDD; -} - -div.calendar-barshift h4 { - text-align: left; - white-space: nowrap; -} - -div.calendar-barshift ul { - margin: 0; - padding: 0; - list-style: none; -} - -.calendar-register-me { - border: 1px solid black; - padding: 1px; - width: 50%; - color: black; - margin: 0px; - display: block; - text-align: center; - background: #DDD; -} - -div.calendar-barshift li { - white-space: nowrap; - margin: 0; - padding: 0; -} - -div.comments { - border-top: 2px solid #CCC; - -} - -div.comment { - border-top: 2px solid #CCC; - padding: 5px 0; - position: relative; -} - -div.comment-meta { - position: absolute; - -} - -div.comment-main { - margin-left: 150px; - min-height: 60px; - border-left: 2px dotted #CCC; - padding-left: 5px; -} - -* html div.comment-main { - height: 60px; -} - -.clear-right { - clear: right; -} -/* =footer -*********************************************************************/ -#footer { - clear: both; - text-align: center; - background: #EEE; - border: 1px solid #CCC; - -moz-border-radius: 8px; - color: #000; - margin-top: 0px; -} - - - -/* =extras -*********************************************************************/ -.domenabled ul#finder, .domenabled ul#finder li, .domenabled ul#finder ul { - list-style-type:none; - margin: 5px 0; - padding:0; - line-height: 1em; -} - -.domenabled ul#finder ul.hidden { - display: none; -} -.domenabled ul#finder ul.shown { - margin: 3px 25px; - display: block; -} - -.domenabled ul#finder li a { -} - -.domenabled #finder a.open { - font-weight: bold; - display: block; - color: black; -} - -.domenabled #finder a.parent { - color: black; -} - - -.domenabled div.disabled { - display: none; -}