function ldap_get_ad_pwdexpire($pwdlastset, $ldapconn, $user_dn){
        global $CFG;

        if (!function_exists('bcsub')) {
            error_log($this->errorlogtag.get_string ('needbcmath', 'auth_ldap'));
            return 0;
        }

        // If UF_DONT_EXPIRE_PASSWD flag is set in user's
        // userAccountControl attribute, the password doesn't expire.
        $sr = ldap_read($ldapconn, $user_dn, '(objectClass=*)',
                        array('userAccountControl'));
        if (!$sr) {
            error_log($this->errorlogtag.get_string ('useracctctrlerror', 'auth_ldap', $user_dn));
            // Don't expire password, as we are not sure if it has to be
            // expired or not.
            return 0;
        }

        $entry = ldap_get_entries_moodle($ldapconn, $sr);
        $info = $entry[0];
        $useraccountcontrol = $info['useraccountcontrol'][0];
        if ($useraccountcontrol & UF_DONT_EXPIRE_PASSWD) {
            // Password doesn't expire.
            return 0;
        }

        // If pwdLastSet is zero, the user must change his/her password now
        // (unless UF_DONT_EXPIRE_PASSWD flag is set, but we already
        // tested this above)
        if ($pwdlastset === '0') {
            // Password has expired
            return -1;
        }

        // ----------------------------------------------------------------
        // Password expiration time in Active Directory is the composition of
        // two values:
        //
        //   - User's pwdLastSet attribute, that stores the last time
        //     the password was changed.
        //
        //   - Domain's maxPwdAge attribute, that sets how long
        //     passwords last in this domain.
        //
        // We already have the first value (passed in as a parameter). We
        // need to get the second one. As we don't know the domain DN, we
        // have to query rootDSE's defaultNamingContext attribute to get
        // it. Then we have to query that DN's maxPwdAge attribute to get
        // the real value.
        //
        // Once we have both values, we just need to combine them. But MS
        // chose to use a different base and unit for time measurements.
        // So we need to convert the values to Unix timestamps (see
        // details below).
        // ----------------------------------------------------------------

        $sr = ldap_read($ldapconn, ROOTDSE, '(objectClass=*)',
                        array('defaultNamingContext'));
        if (!$sr) {
            error_log($this->errorlogtag.get_string ('rootdseerror', 'auth_ldap'));
            return 0;
        }

        $entry = ldap_get_entries_moodle($ldapconn, $sr);
        $info = $entry[0];
        $domaindn = $info['defaultnamingcontext'][0];

        $sr = ldap_read ($ldapconn, $domaindn, '(objectClass=*)',
                         array('maxPwdAge'));
        $entry = ldap_get_entries_moodle($ldapconn, $sr);
        $info = $entry[0];
        $maxpwdage = $info['maxpwdage'][0];
        if ($sr = ldap_read($ldapconn, $user_dn, '(objectClass=*)', array('msDS-ResultantPSO'))) {
            if ($entry = ldap_get_entries_moodle($ldapconn, $sr)) {
                $info = $entry[0];
                $userpso = $info['msds-resultantpso'][0];

                // If a PSO exists, FGPP is being utilized.
                // Grab the new maxpwdage from the msDS-MaximumPasswordAge attribute of the PSO.
                if (!empty($userpso)) {
                    $sr = ldap_read($ldapconn, $userpso, '(objectClass=*)', array('msDS-MaximumPasswordAge'));
                    if ($entry = ldap_get_entries_moodle($ldapconn, $sr)) {
                        $info = $entry[0];
                        // Default value of msds-maximumpasswordage is 42 and is always set.
                        $maxpwdage = $info['msds-maximumpasswordage'][0];
                    }
                }
            }
        }
        // ----------------------------------------------------------------
        // MSDN says that "pwdLastSet contains the number of 100 nanosecond
        // intervals since January 1, 1601 (UTC), stored in a 64 bit integer".
        //
        // According to Perl's Date::Manip, the number of seconds between
        // this date and Unix epoch is 11644473600. So we have to
        // substract this value to calculate a Unix time, once we have
        // scaled pwdLastSet to seconds. This is the script used to
        // calculate the value shown above:
        //
        //    #!/usr/bin/perl -w
        //
        //    use Date::Manip;
        //
        //    $date1 = ParseDate ("160101010000 UTC");
        //    $date2 = ParseDate ("197001010000 UTC");
        //    $delta = DateCalc($date1, $date2, \$err);
        //    $secs = Delta_Format($delta, 0, "%st");
        //    print "$secs \n";
        //
        // MSDN also says that "maxPwdAge is stored as a large integer that
        // represents the number of 100 nanosecond intervals from the time
        // the password was set before the password expires." We also need
        // to scale this to seconds. Bear in mind that this value is stored
        // as a _negative_ quantity (at least in my AD domain).
        //
        // As a last remark, if the low 32 bits of maxPwdAge are equal to 0,
        // the maximum password age in the domain is set to 0, which means
        // passwords do not expire (see
        // http://msdn2.microsoft.com/en-us/library/ms974598.aspx)
        //
        // As the quantities involved are too big for PHP integers, we
        // need to use BCMath functions to work with arbitrary precision
        // numbers.
        // ----------------------------------------------------------------

        // If the low order 32 bits are 0, then passwords do not expire in
        // the domain. Just do '$maxpwdage mod 2^32' and check the result
        // (2^32 = 4294967296)
        if (bcmod ($maxpwdage, 4294967296) === '0') {
            return 0;
        }

        // Add up pwdLastSet and maxPwdAge to get password expiration
        // time, in MS time units. Remember maxPwdAge is stored as a
        // _negative_ quantity, so we need to substract it in fact.
        $pwdexpire = bcsub ($pwdlastset, $maxpwdage);

        // Scale the result to convert it to Unix time units and return
        // that value.
        return bcsub( bcdiv($pwdexpire, '10000000'), '11644473600');
    }