Category Archives: コンピューター関連

awstatsでサブページがリンク切れ~

当方のサイトはawstatsで簡単なアクセス解析をしているのですが、生成される解析結果のトップページからのサブページへのリンクと実際にawstatsがはき出すhtmlファイル名が異なっており、リンク切れを起こしておりました。まぁ、外部からはアクセスできないので、放置しておいても問題ないのだが・・・
トップページ(index.html)からサブページのリンクは、「ホスト名.yyyymm.カテゴリ.html」なのだが、実際に作成されるファイル名は、「awstats.ホスト名.yyyymm.カテゴリ.html」。つまり、実際に作成されるファイルに「awstats」が付帯している。
調べてみると、バージョン6.9.5以降のバグらしく、当方が使用しているバージョンは7.0.3 in Fedora15。ビンゴでした。
さて、どうしたものかと思案した結果、awstatsのソースを弄っても、バージョンアップごとに手入れ部分を修正するのも面倒だったので、外的処置で逃げることに。
そうです、生成されたindex.htmlのリンクを強制的に書き換える方法です。
何らかのスクリプトで解析ページ生成をしていると思うので、そのスクリプトの最後に下記を挿入。
(解析ページ保存先やホスト名は適当に書き換えてください)

pushd <解析ページ生成ディレクトリ>
sed -i "s/href=\"witkitty\.com\./href=\"awstats\.witkitty\.com\./g" index.html
popd

言わなくてもお分かりかと思いますが、注意点は「置換文字列中の特殊文字にご注意を!」です。
上記sedコマンドでは、ダブルクォーテーション(”)とピリオド(.)は特殊文字になるので、特殊文字として認識しないようバックスラッシュ(\)で意図的に指定しています。

とまぁ、これで一時的に回避しておいて、また問題が出たらその時にでも。

XPERIAとGoogleカレンダーの同期が・・・

XPERIAのアプリ:ジョルテを使ってGoogleカレンダーと同期しているのだが、5月末あたりに何故かカレンダーが更新されない事象が発生。
なんで?といろいろアプリを変更して見るも同期せず。
しかし、Googleカレンダー側で新たに予定を入れるとそこだけ更新される・・・ということは、Googleカレンダーの過去データに何か問題が生じていると思われ、以下の手順で復旧を試みることに。(Googleカレンダー側のデータは表示されているのでどういう壊れ方をしたのかさっぱり検討がつかんw)

Googleカレンダー設定画面から、

  • カレンダーをエクスポート(カレンダーデータをファイルとして保存)
  • カレンダーを削除
  • エクスポートしたデータをインポート(保存されたカレンダーデータを登録)

ここで、おもむろにXPERIAから「カレンダー同期」を実行~。
「むふふ、同期したぉ」

さて、では何故Googleカレンダー側のデータに問題が生じたかについては・・・・・「不明」です。
もし再発するようであれば、その時の当方の操作も含め再現できたらなぁ~と。

同じ現象が出ている人はお試しあれ。

WP-Weatherプラグインの日本語化

当ブログのテーマを変えたついでに、ウェジットプラグインで使っているお天気情報表示プラグイン「WP-Weather」を日本語化してみました。このプラグインは国際化に対応しているので、ある程度はリソースだけを弄るだけで日本語化できます。

大雑把な手順は以下の通り。(ちなみに訳等の作業はWindowsマシンで行っています)

  1. wp-weather-en_US.poをwp-weather-ja.poにコピーし、作業マシンに引っ張ってきます。
  2. POEdit(作者さんのサイト)でこのファイルを開き、対訳を変更していきます。
  3. 対訳の変更が終わったら、カタログを作成します。(wp-weather-ja.moができあがります)
  4. wp-weather-ja.moファイルをlocalizationディレクトリにコピーします。

上記の方法で固定文字列が日本語化されます。しかし、私は欲張って日付の書式を日本式にしたり、動的に変わるお天気なども日本語化したくなり・・・「はまりました・・・orz icon_cry.gif

このプラグインの流れは、「更新→weather.comに問い合わせ→wordpress DBのデータ更新→データ表示」となるわけです。当方は、「DBはUTF-8だし、wordpressやプラグインで使用する文字列もUTF-8なので問題ないだろう」と高をくくり、がしがしと動的に変わる文字列をpoファイルに登録・対訳し、ソースを変更していきました。そして、更新~。
「・・・、対訳が出てこねぇ~ icon_evil.gif
ソース文字列をHexコードでチェックして見るも、特に変なコードが入っているわけでもなし。「う~ん、う~ん」と呻きながら小一時間。phpの文字列操作関数群を眺めながら、「おやっ、mb_~()?、もしやマルチバイト・・・」。
ここまで足突っ込んじゃっているし、とりあえずマルチバイト系文字列に変換してみるか~とゴソゴソソースを修正&更新~。
「うぉ、対訳出てきやがった icon_eek.gif
う~ん、文字列はかなり癖あるなぁ~と。(当方の知識不足は棚上げしておきます、ぉぃ)

DBのデータを一旦マルチバイト文字列に変換し、その文字列を変換用検索文字列として渡します。
変更例は以下のよう感じ。

$htmlstring .= '<br />'.__(mb_substr($xml->cc->t, 0, mb_strlen($xml->cc->t) ,'UTF-8'), 'wp-weather').'<br/>';

また、日付を海外書式(Monday, May 23)から日本書式(5月23日 月曜日)へ変換したい場合の例はこんな感じ。

$htmlstring .= date_i18n('n月j日 l', strtotime($day_forecast->lsup));

上記の例の基のデータ($day_forecast->lsup)はデータベースを覗くと[ 5/23/11 2:13 PM Local Time ]な感じなんですが、date_i18n()とstrtotime()関数の組み合わせは結構融通が利くみたい。例えば[ May 24 ]をstrtotime()に通した後、date_i18n()に通すと思い通りの書式で出力してくれる。もし、ソースを見る機会があったら覗いて見ようかと。ここまで融通が利くと結構バグがあるかもしれんしね。(ぉぃ、疑う前に知識身につけろ・・・ごもっとも)
注:date_i18n()関数はwordpress固有の関数ですのでご注意を。

さて最後に
まだ日本語化終わっていません、キリッ

と言うのも、お天気の種類がいくつあるのか分からないので、出たとこ勝負で訳しています。
作者に聞いてみようと思うので、進捗があったら更新します。
それと、この変更はまだ作者に了解を取って行っているわけではないのでファイルは公開できません。上記の問い合わせとあわせて聞いてみますので、これも進捗があったら更新します、あしからず。

可変構造データの扱い

興味本位で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のアクセスなんて誰もやろうなんて考えないよね。と言うことで備忘録ということで。

Page 1 of 1212345...10...Last »