2007年10月27日土曜日

全角半角大文字小文字.

 今日は休日出勤.
 土曜・日曜と休みがあるだけ有り難いと(自らに)思い(込ませ)ながら仕事中.

 まったくどうでもいい話だけれども、他の社員が編集している文書ファイルを見てると、英語表記が半角だったり全角だったり大文字だったり小文字だったり(ある場所ではWindowsと書き、別の場所ではwindowsとかwindows[全角]とか…)します.
 あれってどうにかならないもんですかね?

 そういうファイルを開く度に、気になって気になって仕方がありません.
 いや、気になるというか「イライラ」します orz

 「そんな細かい事気にしてたら禿げるって!…あ…もうハゲ初期段階に突入してるか…」と思われるかもしれませんが、見た目も気になるのは勿論、データの再利用とかファイルに対して何らかの処理を行う時に面倒です.
 そんな風に書いてるから、データ処理のコードにUppercaseとかLowercaseとかはたまた半角全角変換と言う実に非効率でコスト高(実際の金額の問題は無視して、処理負荷の意味で)なコードを書く羽目になるんですよ.
 まったくもう!さっさと直しなさい!!

 …なーんて言えるのは社内だけの話で、お客さんに対しては言える訳も無く、当たり前に実装するんですけど.

 前に別のシステムで登録していたデータを移行する際、「姓と名のデータを分けて移行して」と言われたものの、どこぞの業者が作ったプログラムは『姓と名を分けずに一つの入力欄だけ設置』していた上に『姓と名の区切り文字のチェックも無し』と言う信じられない構造になっていて、データの中身を確認すると、案の定「姓と名の区切り文字が半角空白」「姓と名の区切り文字が全角空白」「姓と名の区切り文字が無い(絶望)」状態が混在.
 更にフリガナも姓と名が区切り無く保存されている上に、半角全角の区別(チェック)もなく登録されている始末.
 正にカオスなデータですw
 件数もかなりの数だったので、流石にこれは分割してDB格納するのは諦めてもらいました.
 半角全角の問題は、当然統一する様に直しましたけど.

 こんな変換作業(コード書き含む)なんて、本当に無駄な労力です.
 入力段階で(強制的に)統一しろって話ですよね.
 まぁ、プログラムはいいとして…Excelが存在し続ける限り、この変換問題は延々と続く悪寒がしますけど.
 人間諦めが肝心か…さて、さっさと帰りますか.

2007年10月23日火曜日

Windows版PHPのLDAP関数.

 一言メモ.

 以前書いたLDAP関数に関しての投稿は、Linux上で動作しているPHPで試していた訳ですが、マニュアルにある通り、Linux版ではOpenLDAPのクライアントがインストールされている必要があります.
 で、私はてっきりWindows版もOpenLDAPのクライアントが入ってないと駄目なんだろうと思っていたんですが、実は無くても全然問題ないんですね.
 これは楽だ!!

 …って、心底今更な話か orz

 マニュアルを見ると、LDAP関数の要件
LDAP サポートを有効にして PHP をコンパイルするには、 » OpenLDAP あるいは » Bind9.net から LDAP クライアント ライブラリを入手し、コンパイルしておく必要があります。

 と書いてあるから、完全に勘違いしてました.

2007年10月18日木曜日

ajaxWindows - AjaxがOSに成るのは何時の日か.

 Ajaxが今までに無いほどの繁栄を見せている昨今.
 タイトルにあるajaxWindowsの様な「AjaxなOS」がAjaxの行き着く究極なのでしょう.

 んが、正直使えたモンじゃありません orz



 確かに見た目は素晴らしいですが、動作が実用に耐えられるとは思えません.
 私がユーザー登録した9月の段階は「まぁ、ベータだから…(アルファではなかったはず)」と思っていたのですが、とても「どこでもOS」と言えるレベルではなさそうです.
 これは回線速度云々ではなく、現時点でのJavaScript自体の動作速度、言い換えればブラウザのJavaScript処理速度、ひいてはJavaScript自体の構造がOSなどのレスポンス重視の用途には「今の所」向いて居ないと言う事かもしれません.

 ajaxWindows以外にも「http://xcerion.com/」(3月にユーザー登録しましたが、まだ出来ない模様)なんかもありますが、今回のajaxWindowsを触って「あ~…根本的にAjaxでOS的な事を実用レベルで実装するのはまだまだ先の話だな」と実感した次第です.

 と言うか、Ajaxアプリ専用のブラウザとか作ったら、もう少し快適になりそうな気がしないでもないんですけどw
 まぁ、それじゃ本末転倒か.
 素直にiPod Touch触ってた方が余程先進的だと個人的には思いました.

2007年10月14日日曜日

電話番号の正規表現.

 今まで下らないと思って見てこなかったアルマゲドン.
 …不覚にも泣いてしまった orz
 あんな茶番劇に…自分で自分が情けない.

 と言うどーでもいい話に続いて、更にどーでもいい話をw
 電話番号の正規表現.
 検索かけると色々と出てきますが、心底意外にもその殆んどが怪しげな正規表現で「え〜…それじゃ抜け落ちるじゃん」と言う様な物でした.
 何で??
 みんな使う機会が多いと思うんだけどなぁ.

 勿論中には

 二度目の公開!電話番号の正規表現

 と言う様な素晴らしい正規表現も見付かりました.
 んが、そんなゴッツイ正規表現を求めるつもりは無い私の様な適当人間は、↓こんなんで十分だと思います.

public class Formatter {
    /**
     * Constructor.
     */
    public Formatter() {
    }

    /**
     * Constructor: 初期値として値を引数にとります.
     *
     * @param strValue
     */
    public Formatter(String strValue) {
        this.setValue(strValue);
    }

    /**
     * Property.
     */
    private String value;

    /**
     * Accessor: valueを取得します.
     *
     * @return String
     */
    public String getValue() {
        return (this.value != null) ? this.value : "";
    }

    /**
     * Accessor: valueをセットします.
     *
     * @param strValue
     */
    public void setValue(String strValue) {
        this.value = strValue;
    }

    /* --------------------------------------------------------------------- */

    /**
     * 電話番号として正しいフォーマットか確認します.<br>
     * 「-」(ハイフン)付きのフォーマットである必要があります.
     *
     * @return boolean
     */
    public boolean validatePhoneNumber() {
        // -> Local variable.
        String strPattern = "(?!^(090|080|070))(?=^\\d{2,5}?-\\d{1,4}?-\\d{4}$)[\\d-]{12}|" +
            "(?=^(090|080|070)-\\d{4}-\\d{4}$)[\\d-]{13}|" +
            "(?=^0120-\\d{2,3}-\\d{3,4})[\\d-]{12}|" +
            "^0800-\\d{3}-\\d{4}";

        //-> Check and return value.
        return this.checkMatch(this.getValue(), strPattern);
    }

    /**
     * 電話番号として正しいフォーマットか確認します.<br>
     * 「-」(ハイフン)無しのフォーマットである必要があります.
     *
     * @return boolean
     */
    public boolean validatePhoneNumberWithOutSeparator() {
        // -> Local variable.
        String strPattern = "(?!^(090|080|070))^[\\d]{10}|" +
            "^(090|080|070)[\\d]{8}";

        //-> Check and return value.
        return this.checkMatch(this.getValue(), strPattern);
    }

    /* --------------------------------------------------------------------- */

    /**
     * 正規表現でマッチするか確認します.
     *
     * @param strTarget
     * @param strPattern
     * @return boolean
     */
    protected boolean checkMatch(String strTarget, String strPattern) {
        // -> Local variable.
        java.util.regex.Pattern objPattern;
        java.util.regex.Matcher objMatcher;

        // -> 引数チェック.
        if ((strTarget == null) || (strPattern == null))
            return false;

        // -> Pattern/Matcherオブジェクト生成.
        objPattern = Pattern.compile(strPattern);
        objMatcher = objPattern.matcher(strTarget);

        // -> Set return value.
        return objMatcher.matches();
     }
}
 ※Java勉強中なのでコードはJava.

 "(?!^(090|080|070))(?=^\\d{2,5}?-\\d{1,4}?-\\d{4}$)[\\d-]{12}|" +

 これで固定電話番号確保(2007-10-20修正. 下部「追記 - 2」参照).
 先頭3桁が「090」「080(0800含む)」「070」以外であり且つ、市外局番は2〜5桁、市内局番は1桁(田舎の方. 北海道では普通)〜4桁(東京とか)、下4桁は固定で、ハイフン含めて合計12桁である場合、または

 "(?=^(090|080|070)-\\d{4}-\\d{4}$)[\\d-]{13}|"

 携帯の古い090と新しい080およびPHSの070から始まる電話番号の場合は、3-4-4桁フォーマットでハイフン含めて合計13桁の場合、または

 "(?=^0120-\\d{2,3}-\\d{3,4})[\\d-]{12}|^0800-\\d{3}-\\d{4}";

 フリーダイヤル「0120」から始まる電話番号の場合は、4-3-3桁および4-2-4桁フォーマットでハイフン含めて合計12桁、フリーダイヤル「0800」から始まる電話番号の場合は、4-3-4桁でハイフン含めて合計13桁(2007-10-20追記. 初回修整時に桁数ミスってました).
 これらに該当するものを電話番号として正しいものとする.

 …あれ?どこか固定で13桁になった所ってありましたっけ?
 何かニュースかWEBで見た記憶が…取り敢えず気にせずいきますw
 // TODO 後で調べる.
 ※追記 - 1参照.

 まぁ、これも正規表現かじった人に言わせれば「なんじゃそりゃw」なのかもしれませんw

 …と言うか正規表現自体が未だに良く分かりません orz

 何であんなにややっこしいんだろ.
 正規表現に触れる度に、もっと自然語(普通の言語. 要するに普段使っている言葉)と親和性の高い方法って無いの?と言いたくなります.
 アレですかね?
 頭のいい人には、正規表現も自然語と同レベルに見えるんでしょうかね?

/**
 * 2007-10-17 追記 - 1(追記の割りに長文です).
 * どうやら増えたのでは無く、今まで9桁だった固定電話番号が10桁に統合された
 * ニュースを見たのを記憶していた様です.
 * http://www.wdic.org/w/WDIC/9%E6%A1%81%E3%81%AE%E9%9B%BB%E8%A9%B1%E7%95%AA%E5%8F%B7
 * 正直今年まで9桁の電話番号があるなんて知りませんでした.
 * …正規表現で一致が抜け落ちるとか、人の正規表現についてアーダコーダ言っ
 * てる場合じゃなかったと言うオチですね orz
 * まぁ、それ以前に「0120」のケースを考えてなかった事にも気づいて、抜け落
 * ち過ぎじゃねーかよっ!と.
 * 「0120」(フリーダイヤル)の場合って、ハイフンの入れる位置が「極企業側
 * の事情」によって変わりますので、4-2-4と4-3-3を想定しないといけません.
 * と言う事で、週末0120について更に追記予定.
 * ん?ハイフン無しのケース?(と言う事を知人に言われましたw)
 * 正直それは「仕様を見直した方がいい」と言いたいのが本音です.
 * それって姓と名を区切り無しに管理しているのと同じです.
 * 印刷する時、市外局番・市内局番・下四桁を区切らず(区別せず)に数値だけ
 * で印刷するんですか?と.
 * 明確に区分されているものを「無かった事に」するんですか?と.
 * そう言う事です…とは言え、「前のシステムからデータ引き継ぐ段階で既にハ
 * イフン無かったんだから仕方ねーだろ!」とか言うケースも有り得ると言えば
 * 有り得なくも無いので、それも週末に.
 * って、別に改めて書く程の事でも無いんですけど.
 * 一応動く事を確認しないと気が済まないので、改めて出直します.
 */

/**
 * 2007-10-20 追記 - 2.
 * フリーダイヤル「0120」および「0800」を追加してみました.
 * ついでに固定電話番号判定部分で括弧が1つ多かったので除去 orz
 * 更に、固定電話番号12桁(ハイフン含む)の場合、先頭に「090」「080」
 * 「070」が先頭に来た場合は除外する記述「(?!〜)」を追加してみました.
 * …何か、やればやるほど抜けが見付かるダメっぷり orz
 */

2007年10月13日土曜日

Javaの勉強 - Post2.

 今日も事前知識0でJavaと対戦中.
 Javaにまったく相手にされてない感じですが orz

 私の様なずぶのJava素人が引っかかりそうな点をいくつか.

◇ServletContext


 ASP.NetなどでもHttpContextと言う(コード本体以外の)クラスで使うとコードが汚くなる上にそれって反則技じゃん!でお馴染みの存在がありますが、JavaのServletContextとは異なる様だ(何方かと言うとHttpServletRequestの方が近いか?).
※Javaのcom.sun.net.httpserver.HttpContextは全くの別物.
 ServletContextはアプリケーション全体で共有のObjectらしく、セットされた値はクライアントの別を問わず共通になる.
 ASP.NetではHttpContextをaspxから直接呼び出す事は殆んど無い(少なくとも私は使わない)けど、Java(Servlet+JSP)のServletContextではASP.Netのaspxに当たるJSPから呼び出されるケースもある様だ.
 Frameworkなどを使わず、純粋にServletからJSPに値を渡す際にもっともポピュラー(?)な方法が以下の様な方法だと思われます.

【Servlet Test.java】
[省略...]
protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
    //-> get servlet object.
    javax.servlet.ServletContext objServContext = getServletContext();

    //-> set attribute on servletcontext.
    objServContext.setAttribute("TestData", "This is a test.");

    //-> get dispatcher object, and forwarding.
    javax.servlet.RequestDispatcher objDisp = request.getRequestDispatcher("/test.jsp");
    objDisp.forward(request, response);
}
 ※何で名前空間フルで書いてるんだよwと思うかもしれないけど、私の様な素人にしてみればServletContextやRequestDispatcherがどの名前空間に属しているのか覚えるには、javax.servlet.*とimportするより、フルで書いた方が覚えると思う.

【JSP test.jsp】
<%
    javax.servlet.ServletContext objServContext = getServletContext();
    String strTest = (String)objServContext.getAttribute("TestData");
    println(strTest);
//-> 結果
This is a test.
%>


 非常に便利です.
 ServletからJSP、はたまたServletからServlet等、Dispatcherでforward/includeした場合や、response.sendRedirectした場合においても使用可能です(スコープがApplicationだから普通はHttpSession使いますけど).

 しかし、このServletContextが妙.
 Servletでは上記の様に「getServletContext」で取得しますが、何故かJSPでは「application」で代替する事が出来る.
 ↓こんな感じで.

【JSP test.jsp】
<%
    String strTest = (String)application.getAttribute("TestData");
    println(strTest);
%>


 確かにこっちの方がスマートだ.
 んが、だったらServletでも「application」って使えてもいいじゃない orz
 何でこんな事になってるんだろう?
 と言う事で調べてみると、「out」や「session」と同じ様に「暗黙的に宣言されているオブジェクト(暗黙オブジェクト)」と言う存在らしい.
 ふーん.
 慣れれば楽だけどなぁ…だから何でServletじゃ使えねーんだよ!と混乱しそうで恐い.

 その他にもServletに関する各種情報を取得・設定出来ます.
 例えばJSPで
<%
    out.println("Servlet Version : " + application.getMajorVersion() + "." + application.getMinorVersion());
;//-> 結果
Servlet Version : 2.4
%>

 なんて事を書くと、Servlet自体のバージョンも取得出来ました.
 …使いどころが思いつきませんがw


◇getAttribute / setAttribute


 上記にある様に、あるインターフェイスに「getAttribute」および「setAttribute」メソッドが存在する場合、各オブジェクトにAttribute(属性)をセットしたり取得したり出来ます.
 主にServletとJSPでの連系や、Application共通での値の保持、Session共通での値の保持なんかに使う事になりそうです.

 Servlet→JSPでこのgetAttribute/setAttributeを使用する場合は、Attributeをセットするスコープ(何ていうか…要するに適用範囲と言いますか境界と言いますか…スコープです)が重要らしい.
 まぁ、ServletContextで少し触れた様に、Application/Session/Requestで使い分けないと大変な事になるから、別にJavaに限らずWebアプリケーションでスコープを気にするのは当たり前ですねw

 Applicationスコープ … アプリケーション(自分で作ったアプリケーション)内で共通
 Sessionスコープ … クライアント別(1クライアントに対して共通)
 Requestスコープ … リクエスト別(1回リクエスト内でのみ共通)

    javax.servlet.ServletContext objServContext = getServletContext();
    objServContext.setAttribute("TestData", 1);

    javax.servlet.http.HttpSession objSession = request.getSession();
    objSession.setAttribute("TestData", 1);

    request.setAttribute("TestData", 1);



 はぁ…
 それにしても全然Java勉強が捗らない orz
 新鮮で楽しいと言えば楽しいんですけど、頭に入るのにここまで時間がかかると、普通に使えるようになる気が一切しませんから…

2007年10月11日木曜日

後で読む.

 マイコミジャーナル『実践サンプルで学ぶStruts 2 - 生まれ変わった定番フレームワークを徹底解説』
 http://journal.mycom.co.jp/special/2007/struts2/001.html

※この投稿は会社でチラ見して「後で自宅で詳しく見よう」と思っていたにも関わらず自宅に帰ったら見ようと思っていたページを見つけ出せなかったorz と言う間抜けな状況を解決する為の付箋紙的な投稿です.

2007年10月8日月曜日

OpenSourceのソースを見るのは何時の日か.

 休日出勤お疲れ様でした>自分.
 そんな事どーでもいいんですがw

 先日Microsoftから「.Net Frameworkのソースコードを公開する準備がある」と言う「今更かよ」なニュースが聞こえてきました.
 今の時代、開発言語をオープンにして(…ソースをオープンにしただけでオープンソースか?と言われると現在の感覚から言えば「オープンソースでは無い」でしょうね. まったく紛らわしい言葉です. オープンソースって)、開発者からのフィードバックでより良い開発環境を構築していくのは至極当然の成り行きか、と思いました.

 …んが.
 実際の所、私の様な低レベル人間はオープンソース(広い意味で)のプログラムでもソースコードなんて見ちゃいないじゃん、と言う実にネガティブな事実に気づいた今日この頃.
 そうだよね.
 数年前にWindows2000のコードが流出した事件がありましたが、私がそのコードを見た所でちんぷんかんぷん120%ですよ.
 何で100%を超えると行き成り120%に行く人が多いのかと言う疑問と同じくらいちんぷんかんぷんです.
 あのソースコード流出で狂喜乱舞したハッカーorクラッカーな人と言うのは、ある程度の知識を有した人だけの話な訳ですよ.
 だから、当時の私(今現在、何の進化もなし)は「ふーん、で?」な感想を持った事を覚えています.

 せっかくのオープンなソースをまったく把握する事が出来ない私.
 何時になったらそんなエクセレントなソースコードが読めるようになるんだろうか orz
 もったいないもったいない…

2007年10月7日日曜日

Javaの勉強 - Post1.

 ひっそりと長らく停止していたJavaの勉強.
 今日からまた週2時間くらいの勢いで再開予定 orz

 まぁ、アレです.
 仕事が忙しいので.
 明日も休日出勤だし.
 本当は毎晩ビール飲んだ暮れてるのが原因だけど orz

 と、それは置いておき、以前勉強した6時間分はすっかり頭の中から抜け落ちていたので、また第1歩からJavaを検証してみます.
 今回はStrutsをいきなり使ったりせず、Java(JSP/Servlet)そのものをいじくります.
 未だにTomcatとGeronimoの違いがよく分かりませんが(前者がJSP/Servletコンテナ・サーバ、後者がJ2EEコンテナ・サーバー(Application Server)…と言われてもねぇ)、それは今のところ無視して、普通にJSPとServletを動かして.

 で、再開1日目にして早速つまづきましたw

 Servletを触っていて、ふと「あれ?POSTされたデータのエスケープって、全部自分でやらないとダメなの?(自動で処理してくれるクラスって無いの?)」と言う疑問が沸き上がり、ちょろちょろと調べてみましたが、いまいち的を得た回答ページを見つける事が出来ず、『取り敢えず』以下の様なクラスを書いてみる事に.

package com.blogspot.lightmaterial.core.String;

import java.util.regex.*;

/**
* 文字列に対するセキュリティ処理を実装します.
*/
public class SecureString {
    /**
     * Property.
     */
    protected String value = null;


    /**
     * Constructor.
     */
    public SecureString() {
    }
    public SecureString(String strValue) {
        this.setValue(strValue);
    }


    /**
     * Accessor.
     */
    public String getValue() {
        return this.value;
    }

    public void setValue(String strValue) {
        this.value = strValue;
    }


    /**
     * Htmlタグをエスケープします.
     * @access public
     * @param void
     * @return String
     */
    public String toEscape() {
        //-> Local variable.
        int nIndex = 0;
        String strTarget = new String();
        String[] strPattern = {"<", ">", "'", "\""};
        String[] strReplace = {"&lt;", "&gt;", "&#039;", "&quot;"};

        //-> Check value property.
        if (this.value == null) return "";

        //-> Preserve value property.
        strTarget = this.value;

        //-> Replace value.
        for (nIndex=0; nIndex<strPattern.length; nIndex++)
            strTarget = this.doReplace(strTarget, strPattern[nIndex], strReplace[nIndex]);
        strTarget = Matcher.quoteReplacement(strTarget);

        //-> Set return value.
        return strTarget;
    }


    /**
     * 文字列をそのまま返します.
     * @access public
     * @param void
     * @return String
     */
    public String toString() {
        return this.value;
    }


    /**
     * 文字列を置換します.
     * @access protected
     * @param String Target
     * @param String Pattern
     * @param String Replace
     * @return String
     */
    protected String doReplace(String strTarget, String strPattern, String strReplace) {
        //-> Local variable.
        Pattern objPattern = null;
        Matcher objMatcher = null;

        //-> Replace target value.
        objPattern = Pattern.compile(strPattern);
        objMatcher = objPattern.matcher(strTarget);
        strTarget = (objMatcher.replaceAll(strReplace));

        //-> Set return value.
        return strTarget;
    }
}

 Stringを使いまわしてるので、メモリが無駄になってる上にエラー処理を一切していないと言う恐怖のコードですが、Java自体よく分かってない上に適当に書いてるので気にせず行きましょう.

 上記のコードを書き終わり、ServletのgetParameterの戻り値に適用.
 「…一応普通に動いてるな…」とは思ったのですが、何しろこんな書き方で正しい訳が無い.
 と言うか、書き方以前にきっとどこかにエスケープするクラスなりメソッドがあるはずなんですが、それが見つけられません.

 そう.
 それもこれもJavaのドキュメントが原因です.
 Javaのドキュメントって、親切そうでいて、以外と使い勝手が悪い様な気がしてなりません.
 それともPHPの親切なドキュメントに慣れすぎているのが悪いんでしょうか?
 正直、あの悪名高きMSDN(サブスクリプションじゃなくてヘルプの方)よりも『個人的には』使い勝手が低い様な気がします.
 いや、クラスの把握とかメソッドの把握にはいいんですが、何しろサンプルが異様に少ない.
 javadocで生成しているが故の制限なのかもしれませんが、正直私の様な初心者にはヘルプにサンプルが載ってないのは致命傷です orz

 こんな調子じゃ、これから先のJava勉強は茨の道確定ですね.
 明日も仕事だし、今日は諦めてさっさと寝よっと.

2007年10月4日木曜日

OpenLDAP for Win32 - その1.

 OpenLDAP for Win32の設定やらでひっかかった所を忘れない内に書きなぐる予定です.

 先日書いた様に、PHPのLDAP関数でActive Directoryには接続・認証出来たので、今度はOpenLDAPで試します.

 Linuxでやろうかとも思いましたが、面倒なので 先にWindows版からやった方が敷居が低いかな?と思い、以下のソフトをインストール.

 OpenLDAP for Win32
 ※2008-03-28追記 気づけば2回目書く前に「Project suspended」だそうです orz
  まだ http://download.bergmans.us/openldap/ ここからダウンロード可能ではありますが

 インストールは普通に終了.
 ※時間があれば手順追って書きますが、今は時間が無いのでパス.

 次に設定を.
 『取り敢えず』動かしてみる事が目標なので、WEB上にあった設定例通り、以下の様に設定.

suffix[tab]"dc=example,dc=com"
rootdn[tab]"cn=Manager,dc=example,dc=com"
rootpw[tab]secret
 ※『[tab]』はTAB文字(\t). その他は変更なし.

 そしてOpenLDAPサーバーを徐に開始(コントロールパネル→「管理ツール」→「サービス」→「OpenLDAP」を開始).
 問題なく起動した模様.

 次にTOPツリーと言う物を作成するらしい.
 Active Directoryで言う所の…何に当たるのかは不明w
 恐らく、Active Directoryを構築する際に「新しいドメインツリーを作成」とか「既存のドメインツリーに…」等々選択する項目の「最初のドメインツリーを作成」と言うものに当たるのでしょう.

 と言う事で、早速ツリーやOU(OrganizationUnit. 綴りが合っているかは未確認)、ユーザーなんかを作成する事が出来るコマンド「ldapadd」を実行する事に.

D:\OpenLDAP> ldapadd -x -D "cn=Manager,dc=example,dc=com" -w secret
ldap_bind: Invalid credentials (49)

 … orz
 なんじゃそりゃ.
 「ldap_bind: Invalid credentials (49)」で検索してみるも、結構色んな原因があるらしく、ページを見つける度に設定ファイルやら何やらいじくりまわす事数十回.
 これ↓が原因だった.

何故Appleのサイト?なMLから抜粋
> OK, I think I see the problem... it looks like it's not possible to do a simple bind against the rootdn if your password is specified in cleartext. The workaround would be to use slappasswd like this:
>
> % slappasswd -s secret
> {SSHA}ioGadl0574KxRPecJ7Pb5q33j2x/Fi3w
 ※注)ハッシュ結果はコマンド実行毎に異なります.

 …あ、そう.
 そんなに単純な事なのかよっ! orz
 確かに設定ファイルには

 Cleartext passwords, especially for the rootdn, should be avoid.

 とは書いてあるが、「駄目」とは書いてないじゃない orz
 と言う事で、先のAppleMLの続きを読むと…

> I believe this is due to Mac OS X's version of OpenLDAP being built with --disable-cleartext.

 アレか.
 OpenLDAP for Win32では「--disable-cleartext」なオプションが適用された状態でビルドされているってのか?
 …ダウンロードサイトなりREADME読めば、きっとちゃんと書いてあるんでしょうね orz
 はい、結局その辺すっ飛ばしてる私が悪いと言う事で.
 結局下記の通り実行.

D:\OpenLDAP> slappasswd -s secret
{SSHA}Fa6TV9xYXXh+iWrXqwRWj3fm1aTpylEt
 ※注)ハッシュ結果はコマンド実行毎に異なります.
 そして設定の「rootpw」を変更.

suffix[tab]"dc=example,dc=com"
rootdn[tab]"cn=Manager,dc=example,dc=com"
rootpw[tab]{SSHA}Fa6TV9xYXXh+iWrXqwRWj3fm1aTpylEt
 ※『{SSHA}』から記述するので注意.

 今日も今日とて時間の無駄!!!
 自分がアホ人間である事を再認識した今日この頃.
 続きはまた今度.

2007年10月2日火曜日

PHPのLDAP関数でActiveDirectoryに接続 - その2.

 メモ走り書き.

 ActiveDirectoryにはuidが存在しない様だ.
 へぇ~…って、じゃあログインユーザー自身の情報をどうやって取得すればいいんだよ!? orz

 普通に考えればldap_searchのフィルタに

 '(uid=' . $userid . ')'

 の様に指定するんだろうけど、uidが存在しないんじゃなぁ…
 と言う事でインターネットの海にどっぷりと沈みこみ、数時間後にようやく発見.
 何でこんなに時間がかかったかは秘密.
 秘密と言うか「cn」や「uid」をAttribute(属性)と呼ぶ事が分からなかったが為に、かなりのタイムロス orz

 で、結局ズバリのページを見つけて歓喜↓

 LDAP Attributes from Active Directory Users and Computers
 ※画面を少し下にスクロールしたら表があります.

 「uid」の代わりに「sAMAccountName」(または「userPrincipalName」)を使えそうです.
 これで心おきなく情報を取り放題ですよ.


$objConnection = ldap_connect('ldap://example.com', 389);
  or die('could not connect Active-Directory.');
ldap_set_option($objConnection, LDAP_OPT_PROTOCOL_VERSION, 3)
  or die('could not set protocol-version...');

if (! $objConnection) die('connection-object is empty.');
if (! @ldap_bind($objConnection, 'userid@example.com', 'password'))
  die("error.<br>reason... " . ldap_error($objConnection) . "<br>");

if ($objResult = @ldap_search($objConnection, 'OU=exampleou,DC=example,DC=com', '(sAMAccountName=userid)', array('cn', 'sAMAccountName', 'userPrincipalName', 'mail'))) {
  $aryResult = ldap_get_entries($objConnection, $objResult);
  print_r($aryResult);
} else {
  echo 'could not get entries.<br>';
}
ldap_unbind($objConnection);
echo 'finish.';
 ※タグべた書きなのでコードが読みにくいと言うか汚いと言うか投げやりなのは気にしない方向で.

PHPのLDAP関数でActiveDirectoryに接続.

 メモ走り書き.

 PHP4の環境からActiveDirectoryでの認証が必要になったのでテスト.

 ・ADサーバー : Windows Server 2003 Standard Edition

 マニュアルに従い、ldap_bindの第二引数にdnとして

  'cn=userid,ou=exampleou,o=excom,c=JP'
  'uid=userid,dc=example,dc=com'

 等々つっこんでみましたが、
Warning: ldap_bind(): Unable to bind to server: Invalid credentials in ....

 とエラーが出るばかりで認証出来ず.
 何でだ?何でだ?と頭を悩ませる事1時間.

 …ん?これって普通に「userid@example.com」形式じゃ駄目?
 と思い至り、早速やってみると……普通に接続しやがりました orz


$objConnection = ldap_connect('ldap://example.com', 389);
  or die('could not connect Active-Directory.');
//-> ↓2007-10-02追記(これもセットした方が良さそうです)
ldap_set_option($objConnection, LDAP_OPT_PROTOCOL_VERSION, 3)
  or die('could not set protocol-version...');

if ($objConnection) {
  if (@ldap_bind($objConnection, 'userid@example.com', 'password')) {
    echo "success.<br>";
  } else {
    echo "error.<br>reason... " . ldap_error($objConnection) . "<br>";
  }
  ldap_unbind($objConnection);
}


 まぁ、あれです.
 アホです.
 生粋のアホです orz
 また今日も時間を無駄にした...