<?php
/**
 *  Lism_AppObject.php
 *
 *  @author     {$author}
 *  @package    LISM
 *  @version    $Id: skel.app_object.php,v 1.3 2006/11/06 14:31:24 cocoitiban Exp $
 */

/**
 *  Lism_AppObjectManager
 *
 *  @author     {$author}
 *  @access     public
 *  @package    LISM
 */
class Lism_AppObjectManager extends Ethna_AppManager
{
}

/**
 *  Lism_AppObject
 *
 *  @author     {$author}
 *  @access     public
 *  @package    Lism
 */
class Lism_AppObject extends Ethna_AppObject
{
    /** @var    array   オブジェクト定義 */
    var $object_def = null;

    var $my_ldap = null;

    var $uri = 'ldap://localhost';

    var $binddn = '';

    var $bindpw = '';

    // {{{ Lism_AppObject
    /**
     *  Lism_AppObjectクラスのコンストラクタ
     *
     *  @access public
     *  @param  object  Ethna_Backend   &$backend   Ethna_Backendオブジェクト
     *  @param  mixed   $key_type   検索キー名
     *  @param  mixed   $key        検索キー
     *  @param  array   $prop       プロパティ一覧
     *  @return mixed   0:正常終了 -1:キー/プロパティ未指定 Ethna_Error:エラー
     */
    function Lism_AppObject(&$backend, $key_type = null, $key = null, $prop = null)
    {
        $this->backend =& $backend;
        $this->config =& $backend->getConfig();
        $this->action_form =& $backend->getActionForm();
        $this->af =& $this->action_form;
        $this->session =& $backend->getSession();
        $ctl =& $backend->getController();

        // プロパティ定義自動取得    
        if (is_null($this->object_def)) {
            $this->object_def = $this->_getObjectDef();
        }

        $this->binddn = $this->session->get('binddn');
        $this->bindpw = $this->session->get('bindpw');

        $conf = $this->config->get('ldap');
        $this->uri = $conf['uri'];
        if (!$this->binddn) {
            $this->binddn = $conf['binddn'];
            $this->bindpw = $conf['bindpw'];
        }

        $ldap = $this->connect();
        if (Ethna::isError($ldap)) {
            return $ldap;
        }
        $this->my_ldap = $ldap;

        register_shutdown_function(array(&$this, 'disconnect'));

        if (is_null($this->prop_def)) {
            $this->prop_def = $this->_getPropDef();
        }

        // キー妥当性チェック
        if (is_null($key_type) && is_null($key) && is_null($prop)) {
            // perhaps for adding object
            return 0;
        }

        // プロパティ設定
        if (is_null($prop)) {
            $this->_setPropByDB($key_type, $key);
        } else {
            $this->_setPropByValue($prop);
        }

        $this->prop_backup = $this->prop;

        if (is_null($this->id)) {
            $this->id = $this->_getDn();
        }

        return 0;
    }

    // {{{ get
    /**
     *  オブジェクトプロパティへのアクセサ(R)
     *
     *  @access public
     *  @param  string  $key    プロパティ名
     *  @return mixed   プロパティ
     */
    function get($key)
    {
        if (isset($this->prop[$key]) == false) {
            return null;
        }

        if ($key == 'area' || $key == 'category' || $key == 'system') {
            return $this->prop[$key];
        }

        if (is_null($this->prop_def) || (preg_match('/^([^;]*);/', $key, $match) && isset($this->prop_def[$match[1]]))) {
            return $this->prop[$key];
        }

        return parent::get($key);
    }
    // }}}

    // {{{ set
    /**
     *  オブジェクトプロパティへのアクセサ(W)
     *
     *  @access public
     *  @param  string  $key    プロパティ名
     *  @param  string  $value  プロパティ値
     */
    function set($key, $value)
    {
        if ($key == 'area' || $key == 'unit' || $key == 'category' || $key == 'system' || $key == 'parent') {
            $this->prop[$key] = $value;
            return null;
        }

        if (is_null($this->prop_def) || (preg_match('/^([^;]*);/', $key, $match) && isset($this->prop_def[$match[1]]))) {
            $this->prop[$key] = $value;
            return null;
        }

        parent::set($key, $value);
    }
    // }}}

    // {{{ add
    /**
     *  オブジェクトを追加する
     *
     *  @access public
     *  @return mixed   0:正常終了 Ethna_Error:エラー
     */
    function add()
    {
        $info = array();
        foreach ($this->object_def as $object_name => $object_attr) {
            // use 1st one
            break;
        }
        $info['objectClass'] = $object_attr['objectclass'];
        foreach ($this->prop as $key => $value) {
            if ($key == 'area' || $key == 'unit' || $key == 'category' || $key == 'system' || $key == 'parent' || $key == 'basedn') {
                continue;
            }
            $info[$key] = $value;
        }

        $r = @ldap_add($this->my_ldap, $this->id, $info);
        if (!$r) {
            return Ethna::raiseError(ldap_error($this->my_ldap));
        }

        // バックアップ/キャッシュ更新
        $this->prop_backup = $this->prop;
        $this->_clearPropCache();

        return 0;
    }
    // }}}

    // {{{ update
    /**
     *  オブジェクトを更新する
     *
     *  @access public
     *  @return mixed   0:正常終了 Ethna_Error:エラー
     */
    function update()
    {
        $info = array();
        foreach ($this->prop as $key => $value) {
            if ($key == 'area' || $key == 'unit' || $key == 'category' || $key == 'system' || $key == 'parent' || $key == 'basedn') {
                continue;
            }
            $info[$key] = $value;
        }

        $r = @ldap_modify($this->my_ldap, $this->id, $info);
        if (!$r) {
            return Ethna::raiseError(ldap_error($this->my_ldap));
        }

        // バックアップ/キャッシュ更新
        $this->prop_backup = $this->prop;
        $this->_clearPropCache();

        return 0;
    }
    // }}}

    // {{{ replace
    /**
     *  オブジェクトを置換する
     *
     *  MySQLのREPLACE文に相当する動作を行う(add()で重複エラーが発生したら
     *  update()を行う)
     *
     *  @access public
     *  @return mixed   0:正常終了 >0:オブジェクトID(追加時) Ethna_Error:エラー
     */
    function replace()
    {
        return $this->update();
    }
    // }}}

    // {{{ remove
    /**
     *  オブジェクトを削除する
     *
     *  @access public
     *  @return mixed   0:正常終了 Ethna_Error:エラー
     */
    function remove()
    {
        $r = @ldap_delete($this->my_ldap, $this->id);
        if (!$r) {
            return Ethna::raiseError(ldap_error($this->my_ldap));
        }

        // プロパティ/バックアップ/キャッシュクリア
        $this->id = $this->prop = $this->prop_backup = null;
        $this->_clearPropCache();

        return 0;
    }
    // }}}

    // {{{ searchId
    /**
     *  オブジェクトIDを検索する
     *
     *  @access public
     *  @param  array   $filter     検索条件
     *  @param  array   $order      検索結果ソート条件
     *  @param  int     $offset     検索結果取得オフセット
     *  @param  int     $count      検索結果取得数
     *  @return mixed   array(0 => 検索条件にマッチした件数,
     *                  1 => $offset, $countにより指定された件数のオブジェクトID一覧)
     *                  Ethna_Error:エラー
     */
    function searchId($filter = null, $order = null, $offset = null, $count = null)
    {
        $r = $this->_getLDAP_Search($filter, $order, $offset, $count);
        if (Ethna::isError($r)) {
            return $r;
        }

        $id_list = array();
        foreach ($r[1] as $entry) {
            array_push($id_list, strtolower($entry['dn']));
        }

        return array($r[0], $id_list);
    }
    // }}}

    // {{{ searchProp
    /**
     *  オブジェクトプロパティを検索する
     *
     *  @access public
     *  @param  array   $keys       取得するプロパティ
     *  @param  array   $filter     検索条件
     *  @param  array   $order      検索結果ソート条件
     *  @param  int     $offset     検索結果取得オフセット
     *  @param  int     $count      検索結果取得数
     *  @return mixed   array(0 => 検索条件にマッチした件数,
     *                  1 => $offset, $countにより指定された件数のオブジェクトプロパティ一覧)
     *                  Ethna_Error:エラー
     */
    function searchProp($keys = null, $filter = null, $order = null,
                        $offset = null, $count = null)
    {
        $r = $this->_getLDAP_Search($filter, $order, $offset, $count);
        if (Ethna::isError($r)) {
            return $r;
        }

        $prop_list = array();
        foreach ($r[1] as $entry) {
            $tmp = $this->_fetchRow($entry);
            if ($keys === null) {
                $key_type = array_merge(array('id', 'area', 'unit', 'category', 'system'), array_keys($tmp));
            } else {
                $key_type = $keys;
            }

            $row = array();
            foreach ($key_type as $key) {
                if ($key == 'id') {
                    $row[$key] = strtolower($entry['dn']);
                } else if (isset($tmp[$key]) == false) {
                    $row[$key] = '';
                } else {
      	            $row[$key] = $tmp[$key];
                }
            }            

            $prop_list[] = $row;
        }

        return array($r[0], $prop_list);
    }
    // }}}

    // {{{ connect
    /**
     * LDAPサーバへの接続
     *
     * @access private
     * @return mixed
     * @throws Ethna_Error
     */
    function connect()
    {
        $ldap = ldap_connect($this->uri);
        if ($ldap) {
            $r = @ldap_bind($ldap, $this->binddn, $this->bindpw);

            if (!$r) {
                return Ethna::raiseError("bind failed.", E_DB_CONNECT);
            }
        } else {
                return Ethna::raiseError("Could not connect to LDAP server.", E_DB_CONNECT);
        }

        return $ldap;
    }
    // }}}

    // {{{ disconnect
    /**
     * LDAPサーバへの接続を切断
     *
     * @access private
     * @return void
     */
    function disconnect()
    {
         @ldap_unbind($this->my_ldap);
    }
    // }}}

    // {{{ _getOjbectDef
    /**
     *  オブジェクト定義を取得する
     *
     *  (クラス名→オブジェクト名のルールを変えたい場合は
     *  このメソッドをオーバーライドします)
     *
     *  @access protected
     *  @return array   オブジェクト定義
     */
    function _getObjectDef()
    {
        $class_name = get_class($this);
        if (preg_match('/(\w+)_(.*)/', $class_name, $match) == 0) {
            return null;
        }
        $object = $match[2];

        // PHP 4は常に小文字を返す...のでPHP 5専用
        $object = preg_replace('/([A-Z])/e', "strtolower('\$1')", $object);

        $conf = $this->config->get('secioss');

        return array($object => array('objectclass' => $conf[$object]['objectclass']));
    }
    // }}}

    // {{{ _getPropDef
    /**
     *  プロパティ定義を取得する
     *
     *  @access protected
     *  @return array   プロパティ定義
     */
    function _getPropDef()
    {
        if (is_null($this->object_def)) {
            return null;
        }
        foreach ($this->object_def as $object_name => $object_attr) {
            // use 1st one
            break;
        }

        $conf = $this->config->get('ldap');

        $cache_manager =& Ethna_CacheManager::getInstance('localfile');
        $cache_manager->setNamespace('ethna_app_object');
        $cache_key = md5($conf['uri'] . '-' . $object_name);

        if ($cache_manager->isCached($cache_key, $this->prop_def_cache_lifetime)) {
            $prop_def = $cache_manager->get($cache_key,
                                            $this->prop_def_cache_lifetime);
            if (Ethna::isError($prop_def) == false) {
                return $prop_def;
            }
        }

        $r = $this->_getMetaData($object_name);
        if(Ethna::isError($r)){
            return null;
        }

        $prop_def = array();
        for ($i = 0; $i < count($r); $i++) {
            if (preg_match('/MUST \(? *([^)]*) *\)? MAY/', $r[$i], $match)) {
                foreach (preg_split('/ *\$ */', preg_replace('/ */', '', $match[1])) as $k) {
                    switch($k) {
                      case 'userid':
                        $k = 'uid';
                        break;
                      case 'organizationName':
                        $k = 'o';
                        break;
                      case 'organizationalUnitName':
                        $k = 'ou';
                        break;
                      default:
                    }

                    $prop_def[strtolower($k)] = array(
                        'primary' => true,
                        'form_name' => $k
                    );
                }
            }

            if (preg_match('/MAY \(? *([^)]*) \)?\)/', $r[$i], $match)) {
                foreach (preg_split('/ *\$ */', preg_replace('/ */', '', $match[1])) as $k) {
                    switch($k) {
                      case 'userid':
                        $k = 'uid';
                        break;
                      case 'organizationName':
                        $k = 'o';
                        break;
                      case 'organizationalUnitName':
                        $k = 'ou';
                        break;
                      default:
                    }

                    $prop_def[strtolower($k)] = array(
                        'primary' => false,
                        'form_name' => $k
                    );
                }
            }
        }

        $cache_manager->set($cache_key, $prop_def);

        return $prop_def;
    }
    // }}}

    // {{{ _setPropByDB
    /**
     *  オブジェクトプロパティをDBから取得する
     *
     *  @access private
     *  @param  mixed   $key_type   検索キー名
     *  @param  mixed   $key        検索キー
     */
    function _setPropByDB($key_type, $key)
    {
        global $_ETHNA_APP_OBJECT_CACHE;

        $key_type = to_array($key_type);
        $key = to_array($key);
        if (count($key_type) != count($key)) {
            trigger_error(sprintf("Unmatched key_type & key length [%d-%d]",
                          count($key_type), count($key)), E_USER_ERROR);
            return;
        }

        $filter = array();
        for ($i = 0; $i < count($key_type); $i++) {
            if ($key_type[$i] == 'id') {
                $this->id = $key[$i];
                $filter['id'] = $key[$i];
                continue;
            }

            $type = preg_replace('/;.*$/', '', $key_type[$i]);
            if (!is_null($this->prop_def[$type]) && isset($this->prop_def[$type]) == false) {
                trigger_error("Invalid key_type [$type]", E_USER_ERROR);
                return;
            }
            $filter[$key_type[$i]] = $key[$i];
        }

        // キャッシュチェック
        $class_name = strtolower(get_class($this));
        if (is_array($_ETHNA_APP_OBJECT_CACHE) == false
            || array_key_exists($class_name, $_ETHNA_APP_OBJECT_CACHE) == false) {
            $_ETHNA_APP_OBJECT_CACHE[$class_name] = array();
        }
        $cache_key = serialize(array($key_type, $key));
        if (array_key_exists($cache_key, $_ETHNA_APP_OBJECT_CACHE[$class_name])) {
            $this->prop = $_ETHNA_APP_OBJECT_CACHE[$class_name][$cache_key];
            return;
        }

        // プロパティ取得
        $r = $this->_getLDAP_Search($filter, null, null, null);
        if (Ethna::isError($r)) {
            return;
        }
        $n = $r[0];
        if ($n == 0) {
            // try default
            if ($this->_setDefault($key_type, $key) == false) {
                // nop
            }
            return;
        } else if ($n > 1) {
            trigger_error("Invalid key (multiple rows found) [$key]", E_USER_ERROR);
            return;
        }

        $this->prop = $this->_fetchRow($r[1][0]);

        // キャッシュアップデート
        $_ETHNA_APP_OBJECT_CACHE[$class_name][$cache_key] = $this->prop;
    }
    // }}}

    // {{{ _setPropByValue
    /**
     *  コンストラクタで指定されたプロパティを設定する
     *
     *  @access private
     *  @param  array   $prop   プロパティ一覧
     */
    function _setPropByValue($prop)
    {
        $def = $this->getDef();
        if (is_null($def)) {
            $def = $prop;
        }

        foreach ($def as $key => $value) {
            if ($key != 'id' && isset($prop[$key])) {
                $this->prop[$key] = $prop[$key];
            }
        }

        foreach ($prop as $key => $value) {
            if ($key == 'id') {
                $this->id = $value;
            } else if (is_null($this->prop_def) || (preg_match('/^([^;]*);/', $key, $match) && isset($this->prop_def[$match[1]]))) {
                $this->prop[$key] = $value;
            }

            if ($key == 'area' || $key == 'unit' || $key == 'category' || $key == 'system' || $key == 'parent' || $key == 'basedn') {
                $this->prop[$key] = $value;
            }
        }

        if (is_array($this->prop)) {
            foreach ($this->prop as $key => $value) {
                if ($this->prop[$key] === '') {
                    $this->prop[$key] = ' ';
                }
            }
        }
    }
    // }}}

    // {{{ _getMetaData
    /**
     *  オブジェクト定義情報を取得する
     *
     *  @access public
     *  @return mixed   array: エントリデータ Ethna_Error::エラー
     */
    function _getMetaData($object)
    {
        if (isset($this->object_def[$object]) == false ||
          isset($this->object_def[$object]['objectclass']) == false) {
            return Ethna::raiseError("ObjectClass not defined");
        }

        $r = @ldap_read($this->my_ldap, '', 'objectClass=*', array('subschemaSubEntry'));
        if (ldap_errno($this->my_ldap) == 32) {
            // for PHP 4.x
            $r = @ldap_read($this->my_ldap, 'cn=Subschema', 'objectClass=*', array('subschemaSubEntry'));
        }

        if (!$r) {
            return null;
        }

        $entries = ldap_get_entries($this->my_ldap, $r);
        if ($entries['count'] != 1) {
            return Ethna::raiseError("Can't get subschemaSubEntry");
        }

        $r = @ldap_read($this->my_ldap, $entries[0]['subschemasubentry'][0], 'objectClass=*', array('objectClasses'));
        if (!$r) {
            return Ethna::raiseError(ldap_error($this->my_ldap));
        }

        $entries = ldap_get_entries($this->my_ldap, $r);
        if ($entries['count'] != 1) {
            return Ethna::raiseError("Can't get objectClasses");
        }

        $def = array();
        foreach ($this->object_def[$object]['objectclass'] as $objectclass) {
            foreach ($entries[0]['objectclasses'] as $oc_def) {
                if (preg_match("/NAME '$objectclass'/i", $oc_def)) {
                    $def[] = $oc_def;
                    break;
                }
            }
        }

        return $def;
    }
    // }}}

    // {{{ _getLDAP_Search
    /**
     *  オブジェクト検索を行う
     *
     *  @access private
     *  @param  array   $filter     検索条件
     *  @param  array   $order      検索結果ソート条件
     *  @param  int     $offset     検索結果取得オフセット
     *  @param  int     $count      検索結果取得数
     *  @return mixed   array(0 => 検索条件にマッチした件数,
     *                  1 => $offset, $countにより指定された件数のオブジェクト一覧)
     *                  Ethna_Error:エラー
     */
    function _getLDAP_Search($filter, $order, $offset, $count)
    {
        $basedn = $this->_getLDAP_SearchBase($filter);

        //LDAP用のフィルタに変換
        $ldap_filter = $this->_getLDAP_SearchFilter($filter);

        if (!$this->my_ldap) {
            return Ethna::raiseError("Could not connect to LDAP server.", E_DB_CONNECT);
        }

        if (isset($filter['id'])) {
            $r = @ldap_read($this->my_ldap, $basedn, $ldap_filter);
        } else {
            $r = @ldap_search($this->my_ldap, $basedn, $ldap_filter);
        }

        if ($r) {
            $entries = ldap_get_entries($this->my_ldap, $r);
        } else if (ldap_errno($this->my_ldap) == 32) {
            return array(0, array());
        } else {
            return Ethna::raiseError(ldap_error($this->my_ldap));
        }
        ldap_free_result($r);

        if (is_array($order)) {
            foreach ($order as $k => $v) {
                break;
            }
        } else {
            $k = null;
            $v = -1;
        }

        list($object_name) = array_keys($this->object_def);
        $conf = $this->config->get('secioss');

        $n = 0;
        $index = array();
        for ($i = 0; $i < $entries['count']; $i++) {
            if (isset($conf[$object_name]['objectdn'])) {
                if (!preg_match('/.*,'.$conf[$object_name]['objectdn'].',/i', $entries[$i]['dn'])) {
                    continue;
                }
            }

            if ($k && isset($entries[$i][$k])) {
                $index[$entries[$i][$k][0].'_'.$i] = $i;
            } else {
                $index[$entries[$i]['dn']] = $i;
            }
            $n++;
        }

        if ($v == OBJECT_SORT_ASC) {
            ksort($index);
        } else if ($v == OBJECT_SORT_DESC) {
            krsort($index);
        }

        if ($offset && $count) {
            $index = array_splice($index, $offset - 1, $count);
        }


        $list = array();
        foreach($index as $k => $v) {
            $list[] = $entries[$v];
        }

        return array($n, $list);
    }
    // }}}

    // {{{ _getLDAP_SearchBase
    /**
     *  オブジェクト検索LDAPのBase DNを構築する
     *
     *  @access private
     *  @param  array   $filter     検索条件
     *  @return string  オブジェクト検索のBase DN(エラーならnull)
     */
    function _getLDAP_SearchBase($filter)
    {
        if (isset($filter['id'])) {
            return $filter['id'];
        }

        if (is_null($this->object_def)) {
            return null;
        }
        foreach ($this->object_def as $object_name => $object_attr) {
            // use 1st one
            break;
        }

        $conf = $this->config->get('secioss');

        if (isset($filter['basedn'])) {
            $basedn = $filter['basedn'];
        } else {
            $basedn = $conf['basedn'];
        }

        if (isset($conf['system']) && isset($filter['system'])) {
            $basedn = 'ou=' . $filter['system'] . ',' . $basedn;
        }

        if (isset($conf['area']['id']) && isset($filter['area'])) {
            $basedn = $conf['area']['id'] . '=' . $filter['area'] . ',' . $basedn;
        }

        if (isset($conf['unit']['id']) && isset($this->prop['unit'])) {
            foreach (split('/', $this->prop['unit']) as $unit) {
                $basedn = $conf['unit']['id'] . '=' . $unit . ',' . $basedn;
            }
        }

        if (isset($conf['category']['id']) && isset($filter['category'])) {
            if (isset($conf[$object_name]['objectdn'])) {
                $basedn = $conf[$object_name]['objectdn'] . ',' . $basedn;
            }

            if (is_array($filter['category'])) {
                foreach ($filter['category'] as $category) {
                    $basedn = $conf['category']['id'] . '=' . $category . ',' . $basedn;
                }
            } else {
                $basedn = $conf['category']['id'] . '=' . $filter['category'] . ',' . $basedn;
            }
        }

        return $basedn;
    }
    // }}}

    // {{{ _getLDAP_SearchFilter
    /**
     *  オブジェクト検索LDAPの条件文を構築する
     *
     *  @access private
     *  @param  array   $filter     検索条件
     *  @return string  オブジェクト検索の条件文(エラーならnull)
     */
    function _getLDAP_SearchFilter($filter)
    {
        if (is_null($this->object_def)) {
            return null;
        }
        foreach ($this->object_def as $object_name => $object_attr) {
            // use 1st one
            break;
        }
        if (isset($object_attr['objectclass']) == false) {
            return null;
        }

        $ldap_filter = '';
        foreach ($object_attr['objectclass'] as $objectclass) {
            if ($ldap_filter) {
                $ldap_filter = "(&$ldap_filter(objectClass=$objectclass))";
            } else {
                $ldap_filter = "(objectClass=$objectclass)";
            }
        }

        if (is_array($filter) == false) {
            return $ldap_filter;
        }

        foreach ($filter as $key => $values){
            $key = strtolower($key);

            if ($key == 'id' || (!is_null($this->prop_def) && isset($this->prop_def[$key]) == false)) {
                continue;
            }

            if (is_array($values)) {
                foreach ($values as $value) {
                    $value = addcslashes($value,'()');
                    $ldap_filter = "(&$ldap_filter($key=$value))";
                }
            } else {
                $values = addcslashes($values,'()');
                $ldap_filter = "(&$ldap_filter($key=$values))";
            }
        }

        return $ldap_filter;
    }
    // }}}

    // {{{ _getDn
    /**
     *  オブジェクトDNを返す
     *
     *  @access public
     *  @return mixed   オブジェクトDN
     */
    function _getDn()
    {
        if (is_null($this->object_def)) {
            return null;
        }
        foreach ($this->object_def as $object_name => $object_attr) {
            // use 1st one
            break;
        }

        $conf = $this->config->get('secioss');

        // オブジェクトのプライマリキー定義構築
        if (is_null($this->id_def)) {
            $this->id_def = $conf[$object_name]['id'];
        }
        if (!isset($this->prop[$this->id_def])) {
            return null;
        }

        if (isset($this->prop['parent'])) {
            return $this->id_def . '=' . $this->prop[$this->id_def] . ',' . $this->prop['parent'];
        }

        if (isset($this->prop['basedn'])) {
            $basedn = $this->prop['basedn'];
        } else {
            $basedn = $conf['basedn'];
        }

        if (isset($conf['system']) && isset($this->prop['system'])) {
            $basedn = 'ou=' . $this->prop['system'] . ',' . $basedn;
        }

        if (isset($conf['area']['id']) && isset($this->prop['area'])) {
            $basedn = $conf['area']['id'] . '=' . $this->prop['area'] . ',' . $basedn;
        }

        if (isset($conf['unit']['id']) && isset($this->prop['unit'])) {
            foreach (split(':', $this->prop['unit']) as $unit) {
                $basedn = $conf['unit']['id'] . '=' . $unit . ',' . $basedn;
            }
        }

        if (isset($conf[$object_name]['objectdn'])) {
            $basedn = $conf[$object_name]['objectdn'] . ',' . $basedn;
        }

        if (isset($conf['category']['id']) && isset($this->prop['category'])) {
            $basedn = $conf['category']['id'] . '=' . $this->prop['category'] . ',' . $basedn;
        }

        return $this->id_def . '=' . $this->prop[$this->id_def] . ',' . $basedn;
    }
    // }}}

    // {{{ _fetchRow
    /**
     *  ldap_get_entries()の結果を整形して返す
     *
     *  @access public
     *  @return mixed
     */
    function _fetchRow($entry)
    {
        $row = array();
        for ($i = 0; $i < $entry['count']; $i++) {
            $key = $entry[$i];

            if ($entry[$key]['count'] == 1) {
                if ($entry[$key][0] && !preg_match('/^ *$/', $entry[$key][0])) {
                    $row[$key] = $entry[$key][0];
                }
            } else {
                $v = array();
                for ($j = 0; $j < $entry[$key]['count']; $j++) {
                    if ($entry[$key][$j] !== '' && $entry[$key][$j] !== ' ') {
                        $v[] = $entry[$key][$j];
                    }
                }
                $row[$key] = $v;
            }
        }

        list($object_name) = array_keys($this->object_def);

        $conf = $this->config->get('secioss');

        $basedn = $conf['basedn'];
        $dn = strtolower($entry['dn']);
        if (isset($conf['system']) &&
          preg_match('/ou='. '([^,]*),' .$basedn . '$/i', $dn, $match)) {
            $row['system'] = $match[1];
            $basedn = 'ou=' . $match[1] . ',' . $basedn;
        }

        if (isset($conf['area']['id']) && 
          preg_match('/' . $conf['area']['id'] . '=([^,]*),' . $basedn . '$/i', $dn, $match)) {
            $row['area'] = $match[1];
            $basedn = $conf['area']['id'] . '=' . $match[1] . ',' . $basedn;
        }

        if (isset($conf[$object_name]['objectdn'])) {
            $basedn = $conf[$object_name]['objectdn'] . ',' . $basedn;
        }

        if (isset($conf[$object_name]['categoryid']) &&
           preg_match('/' . $conf[$object_name]['categoryid'] . '=([^,]*),' . $basedn . '$/i', $dn, $match)) {
            $row['category'] = $match[1];
        }

        return $row;
    }
    // }}}
}
?>
