5月 20

興味本位でWindowsでOpenLDAPのデータでも操作してみようかな~とプログラミングを開始してみた。基のソースはMicrosoft MSDNのライブラリからパクってきたのだが。(興味がある方はリンクにアクセスしてね)

で、いきなりこのソースの注意点ですが、構造体:SEC_WINNT_AUTH_IDENTITYのPasswordは素のパスワードでは通用しないと思われ、HASH化した文字列が必要と思われます。パスワードのHASH化はslappasswdで得ることが可能ですので調べてくださいな。素のパスワードでアクセスしたい場合は、ldap_simple_bind_s()もしくはldap_simple_bind()を利用してくださいな。

さて、順調にこのソースを咀嚼していき、何とか情報を引き出し表示させるまでに。そこで、表示されているデータをジッと見つめていると、アトリビュート(属性)数は可変だし、配下のデータ数も可変。
さて、どうやってローカルに取り込もうか思いあぐねた結果、構造体+std::vectorにすることに。理由は、vectorを利用することで可変データに対応するため。(無駄なメモリ使いたくないし、解放操作も面倒だしw)
構造体は下記のような入れ子。(データ構造は本文最後の方で図を掲載してあります)

struct LDAP_ITEM
{
	string					attr;		// 属性名
	vector<string>		item;	// 属性配下のアイテム(vectorで可変に)
};

struct LDAP_DATA
{
	int						entrynum;	// エントリ番号
	vector<LDAP_ITEM>	data;		// エントリデータ内容(上記入れ子構造体をvectorで可変に)
};

で、次にどうやってこの構造体にデータをぶち込んでいるかは以下のソース。Microsoftのソースコードを基にしているので何を追加・変更しているかは大体察しが付くと思いますが、簡単にコメントも入れてみました。
ソースが長いので、「ソースを表示」で伸張してくださいな。

	//----------------------------------------------------------
	// Get the number of entries returned.
	//----------------------------------------------------------
	ULONG numberOfEntries;

	numberOfEntries = ldap_count_entries(pLdapConnection, pSearchResult);

	if(numberOfEntries == NULL)
	{
		printf("ldap_count_entries failed with 0x%0lx \n",errorCode);
		ldap_unbind_s(pLdapConnection);
		if(pSearchResult != NULL)
			ldap_msgfree(pSearchResult);
			return -1;
	}
	else
		printf("ldap_count_entries succeeded \n");

	printf("The number of entries is: %d \n", numberOfEntries);

	//----------------------------------------------------------
	// Loop through the search entries, get, and output the
	// requested list of attributes and values.
	//----------------------------------------------------------
	LDAPMessage* pEntry = NULL;
	PCHAR pEntryDN = NULL;
	ULONG iCnt = 0;
	char* sMsg;
	BerElement* pBer = NULL;
	PCHAR pAttribute = NULL;
	PCHAR* ppValue = NULL;
	ULONG iValue = 0;

	// コメント:入れ子構造体データの一時的な保管場所。
	// これをあとで下の元データの一時的な構造体にpush_back。
	LDAP_ITEM		tmp_item;
	// コメント:元データの一時的な保管場所。あとで実データにpash_back。
	LDAP_DATA		tmp_ldap;

	for( iCnt=0; iCnt < numberOfEntries; iCnt++ )
	{
		// Get the first/next entry.
		if( !iCnt )
			pEntry = ldap_first_entry(pLdapConnection, pSearchResult);
		else
			pEntry = ldap_next_entry(pLdapConnection, pEntry);

		// Output a status message.
		sMsg = (!iCnt ? "ldap_first_entry" : "ldap_next_entry");
		if( pEntry == NULL )
		{
			printf("%s failed with 0x%0lx \n", sMsg, LdapGetLastError());
			ldap_unbind_s(pLdapConnection);
			ldap_msgfree(pSearchResult);
			return -1;
		}

		tmp_ldap.entrynum = iCnt;    // コメント:エントリ番号を保存。

		// Get the first attribute name.
		pAttribute = ldap_first_attribute(pLdapConnection, pEntry, &pBer);

		// Output the attribute names for the current object
		// and output values.

		while(pAttribute != NULL)
		{

			// コメント:一時的なアイテム保管場所。
			//                   アイテムがいくつあるか分からないのでvector使用。
			vector<string>		tmpItem;
			// コメント:属性を保存。
			tmp_item.attr = pAttribute;    

			// Get the string values.
			ppValue = ldap_get_values(pLdapConnection, pEntry, pAttribute);

			// Print status if no values are returned (NULL ptr)
			if(ppValue == NULL)
			{
				printf(": [NO ATTRIBUTE VALUE RETURNED]");
			}

			// Output the attribute values
			else
			{
				iValue = ldap_count_values(ppValue);
				if(!iValue)
				{
					printf(": [BAD VALUE LIST]");
				}
				else
				{
					tmpItem.push_back(*ppValue);    // コメント:最初のアイテムを保存。

					// Output more values if available
					ULONG z;
					for(z=1; z<iValue; z++)
					{
						// コメント:残りのアイテムがあれば保存。
						tmpItem.push_back(ppValue[z]);    
					}
					// コメント:push_backされてきたアイテムを入れ子構造体の
					//                   一時保管場所構造体データに。
					tmp_item.item = tmpItem;    
					// コメント:一時的なアイテム保管場所のvector要素を消去。
					//                 (解放ではないですよ。かつ<vector>.clear()でもない)
					tmpItem.erase(tmpItem.begin(), tmpItem.end());    
				}
			}
			// コメント:直前で入れ子の一時データができているので、
			//                   一時的な本営データ保管場所にpush_back。
			//                   なぜここで本営データに入れないかは本文で。
			tmp_ldap.data.push_back(tmp_item);    

			// Free memory.
			if(ppValue != NULL)
				ldap_value_free(ppValue);
			ppValue = NULL;
			ldap_memfree(pAttribute);

			// Get next attribute name.
			pAttribute = ldap_next_attribute(pLdapConnection, pEntry, pBer);
		}
		// コメント:本営データにpush_back。
		ldap_data->push_back(tmp_ldap);    
		// コメント:本営データに入れたので一時データの要素を消去し、for続きへ。
		tmp_ldap.data.erase(tmp_ldap.data.begin(), tmp_ldap.data.end());    

		if( pBer != NULL )
			ber_free(pBer,0);
		pBer = NULL;
	}

	//----------------------------------------------------------
	// Normal cleanup and exit.
	//----------------------------------------------------------
	ldap_unbind(pLdapConnection);
	ldap_msgfree(pSearchResult);
	ldap_value_free(ppValue);
	return 0;

上のソースで、アイテムが全て揃ったところで、なぜ本営データにぶち込んでいないかと言えば、まだ属性データがあった場合、属性ごとに本営データができあがってしまい、意図する動作にならないためです。つまり、「1つのエントリに対し、複数の属性、1つの属性に対し複数のアイテム」といった可変データ(1xNxN)構造と微妙に異なるからなんです。

で、ここまできたところで、「ldap_data」ってどこで宣言してるんじゃ~ってお怒りかも知れませんが、本営データはこの関数に引数として渡ってきます。

int	GetLDAPData(const char* pHost, std::vector<LDAP_DATA> *ldap_data)

では、この関数の呼び元と出力事例のソースは以下の通りでやんす。
ソースが長いので、「ソースを表示」で伸張してくださいな。

	// 本営データ作成(ここではメモリ確保もせずほったらかし)
	vector<LDAP_DATA>	ldap;
	// 本営データのアドレスを引数で渡してあげます
	GetLDAPData(ldap_host.c_str(), &ldap);

	// データがあったら表示を開始します
	if(!ldap.empty())
	{
		// 本営データに対するイテレータを作成しポインターもどきにします
		for(vector<LDAP_DATA>::iterator iarray = ldap.begin(); iarray<ldap.end() ; iarray++)
		{
			cout << "ENTRY NUMBER: " << iarray->entrynum << endl;
			// 入れ子構造体データに対するイテレータを作成しポインターもどきにします
			for(vector<LDAP_ITEM>::iterator iattr = iarray->data.begin(); iattr<iarray->data.end() ; iattr++)
			{
				cout << "	ATTR: " << iattr->attr << ":";
				// アイテムデータに対しイテレータを作成しポインターもどきにします
				for(vector<string>::iterator iitem = iattr->item.begin(); iitem<iattr->item.end() ; iitem++)
				{
					cout << " " << *iitem;
					if(iitem<iattr->item.end()-1)
						cout << ",";
				}
				cout << endl;
			}
		}
	}

データ表示部分で、本営、入れ子構造体、アイテムに対しそれぞれイテレータを作成していますが、こうしておけば独立した形(表現は厳密ではないけれど)で各データにアクセス出来ます。配列カウンターで悩むことがないので便利なんです。まぁ、配列ポインタと変わりはないんですけど、プログラムを書く好みでdata[i][j][k]とか好きな方ではないので。OpenLDAPのデータ構造はこんな感じ。

OpenLDAPデータ構造

足早にざっと書いては見たけれど、OpenLDAPのアクセスなんて誰もやろうなんて考えないよね。と言うことで備忘録ということで。

written by hiro \\ tags: , , , ,


Leave a Reply

*