月別アーカイブ: 4月 2007

Windows証明書サービスのバックアップと復元

Windows証明書サービスのバックアップと復元

32bit OS でバックアップしたのを 64bit OS で復元しようとしたら、「ディレクトリがありません」とか言って復元させてくれなかった。
64bit OS の奴は、%systemroot% が C:\Windows ではない状況で運用していたのでそいつが悪さをしていた。

  • C:\Windows\system32\CertLog ディレクトリを作成する
  • 証明書サービスを停止
  • certutil -restore -f CAバックアップディレクトリ で復元作業をする
  • C:\Windows\system32\CertLog\*.edb を %systemroot%\system3\Certlog に移動する
  • 証明書サービスを再起動

WSUS 3.0

WSUS 3.0

.NET Framework 2.0 + SQL Server 2005 ベースになった。

イベントリスナーの追加と削除

イベントリスナーの追加と削除

ブラウザ非互換性がー

AJAX.NET を利用していれば addEvent が使えるようなのだが、どうしたもんか。

デル太郎

デル太郎

オンラインでの
見積書を作成した時点では、それをそのまま使うとは思ってなかったので請求先・届け先を デル太郎 で住所もデルの見本通りにしていたのだが、
注文する時に失敗してそのままになってしまった。

確認用のメールには「デル太郎様」の表示がありメールには名前以外の請求先などの項目は含まれていなかった。
という状況でもちろん不安だったので振込前にどうなったのか確認したかったのだが、デルのオンライン注文システムは
「注文後から振込確認して2、3日後にならないとオーダーステータスが見れない」というシステムでどうにも確認が出来ず。
(今思うと電話して頑張って照合してもらえばよかったのだが)

で、振込後にデルから「コンピュータの注文をなさりましたか??担当者名が でるたろう 様で…」という電話が…
会社名と電話番号だけは本物を入れておいてよかった。あとはメールアドレスは本物。

注文する時に失敗した理由は、請求先の入力画面で「1つでもエラーがあるとエラー表示するだけではなく、全項目が元に戻ってしまう」という仕様だったため。
しかもメールアドレスについては、1つ目の入力項目は引き継がれている(ように見えた)のに確認用の2つ目の入力項目が空、というエラーだったので、
エラー部分だけ探して入力し直した際に全項目が元に戻っているという状況に気づかなかった。

こういうWeb入力画面で、入力した内容がエラーのせいで消える仕様は止めて欲しい。

AppleMail 落ちまくり

AppleMail 落ちまくり

何が悪いのか… 久々に以下のコマンドを実行してみた。

cd ~/Library/Mail
find  . \( -name Info.plist -o -name content_index -o -name .index.ready -o -name .lock -o -name table_of_contents \) -print0 | xargs -0 rm

3万とかの安いサーバ買う人ってなんなの?

3万とかの安いサーバ買う人ってなんなの?

Vista世代になってしまい、一般向けマシンにはそこそこのグラフィック性能が必要になってしまったけど、
開発者としてはそんなにグラフィック性能いらねえよという場面はあるわけで。

売る側としてもワークステーションは高性能マシンだし、かといって一般向けマシンではないし、
「サーバということにしとけば分かっている奴しか買わんでしょ」ということなのでは。

070-522

070-522

初めて不合格くらった。
Windowsアプリケーション開発のうち、見た目部分については今後もしやるなら WPF だよなあとほぼ捨ててかかってたので、
ToolStrip, NotifyIcon まわりがおそらく全滅。エラー表示だけはやってたからよかったけど。

試験を受けた印象としては、部分信頼を有効にしたClickOnceによる展開と、BackgroundWorker によるスレッド追加を推奨しているようだといったところ。
まあそうだよなあ。やっぱり部分信頼を有効にしたClickOnceアプリケーションを作らないとだめだな。
(その辺の勘所をつかんでないのに受かるんだったらその方が問題ある)

しかし試験内容は、MCPDに必要な試験3つ分をそのまま連結しただけ、試験レポートもそれぞれが1つのグラフにしかなってなくて、
どこが悪かったのかの指標としては全く使えない。
また、それぞれのパートの問題数は30問弱。通常は45問程度あったと思うので1問間違えた時のダメージが大きくなっていると思われる。
そんなこともあってアップグレードよりはそれぞれ別に受けた方がいいように感じた。

FreeBSD-SA-07:03.ipv6

FreeBSD-SA-07:03.ipv6

ひさびさにデカいネタきた。IPv6の外界につながってるマシンには早急に対応が必要。
丁度長期休暇前だというのに…(ていうか長期休暇だからシステム更新しろってか)

カーネルを更新して再起動。

livedoor Auth

livedoor Auth ASP.NET用認証ページ

Web.Config に authentication mode="Forms" を書いてやって、プログラムを Login.aspx として設置するだけというのを目指して作成した。
内部的には、livedoor Authが通ったら FormsAuthenticationTicket を作ってクッキーに書き込むことで Forms 認証を実現している。
通常のコンテンツページには全く影響がなく、Web.Configも至って普通なのでこれを用いたLivedoorID対応はかなり容易のはず。

要求するアクセス権が userhash の時は、ASP.NET的なユーザ名には userhash を設定し、
要求するアクセス権が id の時は、ASP.NET的なユーザ名は LivedoorID を設定する。

Web.Configの例。authdir/ 以下に認証したユーザしか利用できないページを置いておくというのを想定。

<?xml version="1.0"?>
<configuration>
 <appSettings/>
 <connectionStrings/>
 <system.web>
   <authentication mode="Forms" />
 </system.web>
 <location path="authdir">
   <system.web>
     <authorization>
       <deny users="?" />
     </authorization>
   </system.web>
 </location>
</configuration>

Login.aspx.cs:

using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Security.Cryptography;
using System.Text;
using System.Text.RegularExpressions;
using System.Web;
using System.Web.Security;
using System.Web.UI;
using System.Xml;

public partial class Login : Page
{
    // 環境毎に変更する部分ここから
    private string アプリケーションキー = "????????????????????????????????";
    private string 秘密鍵 = "????????????????";
    private string 要求するアクセス権 = "id"; // userhash または id
    // 環境毎に変更する部分ここまで

    private static readonly DateTime epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
    private const int userdata最大文字数 = 255;
    private const int 認証クッキータイムアウト分 = 30;

    private static readonly Regex reReturnUrl =
        new Regex(@"[A-Za-z0-9_\.\/]+$", RegexOptions.Compiled | RegexOptions.Singleline);

    protected void Page_Load(object sender, EventArgs e)
    {
        if (!IsPostBack)
        {
            if (Request.QueryString["app_key"] == アプリケーションキー)
            {
                string sig = GetSignature(Request.QueryString, 秘密鍵);
                if (sig == Request.QueryString["sig"])
                {
                    コールバック処理(Request.QueryString);
                }
            }
            else
            {
                ログインリダイレクト処理(); // コールバックでない場合はlivedoorAuthログインページに飛ぶ
            }
        }
    }

    private void ログインリダイレクト処理()
    {
        string userdata = Request.QueryString["ReturnURL"];
        if (!reReturnUrl.IsMatch(userdata)) userdata = ""; // 英数字アンダースコアピリオド以外の文字で構成された文字が含まれていたら DefaultUrl に変更する。

        // 文字数制限がエンコード後かどうかが確信できないので、エンコード後の長さで一応チェックしておく。
        string userdataUrlEncoded = HttpUtility.UrlEncode(userdata);
        if (userdataUrlEncoded.Length > userdata最大文字数) throw new ApplicationException("userdataの文字数がオーバーしています");

        NameValueCollection param = new NameValueCollection();
        param.Add("app_key", アプリケーションキー);
        param.Add("v", "1.0");
        param.Add("t", GetEpoch());
        param.Add("perms", 要求するアクセス権);
        param.Add("userdata", userdata);

        string redir = ログイン用URLの作成(param);
        Response.Redirect(redir);
    }

    private string ログイン用URLの作成(NameValueCollection param)
    {
        string sig = GetSignature(param, 秘密鍵);
        string t = param["t"];
        string userdata = param["userdata"];
        return
            String.Format("http://auth.livedoor.com/login/?app_key={0}&perms={1}&t={2}&v=1.0&userdata={3}&sig={4}",
                          アプリケーションキー, 要求するアクセス権, t, HttpUtility.UrlEncode(userdata), sig);
    }

    /// <summary>
    /// sigが正しいことを確認した後の処理
    /// </summary>
    private void コールバック処理(NameValueCollection param)
    {
        switch (要求するアクセス権)
        {
            case "userhash":
                // ユーザーを一意に識別する文字列である userhash をユーザ名として登録する
                RegisterPrincipalAndFormsCookie(param["userhash"]);
                break;
            case "id":
                Response.Cookies.Add(new HttpCookie("LivedoorUserhash", param["userhash"]));
                RegisterPrincipalAndFormsCookie(GetLivedoorID(param["token"]));
                break;
        }

        // userdata が利用可能であれば、そのURLをReturnURLとしてリダイレクトする
        string ReturnURL = param["userdata"];
        if (reReturnUrl.IsMatch(ReturnURL)) Response.Redirect(ReturnURL);
        Response.Redirect(FormsAuthentication.DefaultUrl);
    }

    /// <summary>
    /// LivedoorID をRPC経由で取得する
    /// </summary>
    /// <param name="token">トークン</param>
    /// <returns></returns>
    private string GetLivedoorID(string token)
    {
        string livedoorID;

        {
            NameValueCollection param = new NameValueCollection();
            param.Add("app_key", アプリケーションキー);
            param.Add("format", "xml");
            param.Add("v", "1.0");
            param.Add("t", GetEpoch());
            param.Add("token", token);
            param.Add("sig", GetSignature(param, 秘密鍵));

            string url = LivedoorID取得RPC用URLの作成();
            string errorcode;
            string message;
            HttpWebRequest wr = (HttpWebRequest) WebRequest.Create(url);
            wr.Method = "POST";
            wr.ContentType = "application/x-www-form-urlencoded";
            string postData = GetPostData(param);
            byte[] bytearr = Encoding.UTF8.GetBytes(postData);
            wr.ContentLength = bytearr.Length;
            Stream webRequestStream = wr.GetRequestStream();
            webRequestStream.Write(bytearr, 0, bytearr.Length);
            webRequestStream.Close();

            WebResponse res = wr.GetResponse();
            using (Stream st = res.GetResponseStream())
            {
                XmlReader xr = XmlTextReader.Create(st);
                xr.Read();
                xr.ReadStartElement("response");
                xr.ReadStartElement("error");
                errorcode = xr.ReadString();
                xr.ReadEndElement(); // error
                xr.ReadStartElement("message");
                message = xr.ReadString();
                xr.ReadEndElement(); // message
                xr.ReadStartElement("user");
                xr.ReadStartElement("livedoor_id");
                livedoorID = xr.ReadString();
                xr.ReadEndElement(); // livedoor_id
                xr.ReadEndElement(); // user
                xr.ReadEndElement(); // response
            }
            Trace.Write(
                String.Format("LivedoorID RPC result error:{0} message:{1} livedoor_id:{2}", errorcode, message,
                              livedoorID));
        }
        return livedoorID;
    }

    private static string GetPostData(NameValueCollection param)
    {
        StringBuilder s = new StringBuilder();
        foreach (string key in param.Keys)
        {
            s.Append(HttpUtility.UrlEncode(key));
            s.Append("=");
            s.Append(HttpUtility.UrlEncode(param[key]));
            s.Append("&");
        }
        if (s.Length > 0) s.Length = s.Length - 1; // 最後の&を除去
        return s.ToString();
    }

    /// <summary>
    /// 認証されたユーザIDとしてFormsクッキーに登録する
    /// 登録後ログイン処理前に利用していたURLにリダイレクトする
    /// </summary>
    /// <param name="username"></param>
    private void RegisterPrincipalAndFormsCookie(string username)
    {
        FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(
            1,
            username,
            DateTime.Now,
            DateTime.Now.AddMinutes(認証クッキータイムアウト分),
            false /* isPersistent*/,
            "" /* userData */,
            FormsAuthentication.FormsCookiePath);

        string encTicket = FormsAuthentication.Encrypt(ticket);
        Response.Cookies.Add(new HttpCookie(FormsAuthentication.FormsCookieName, encTicket));

        // LivedoorAuth だと一度 livedoor 側に移動するため、ReturnURL が保持されない
        // そのため一度 userdata に入れている。
        // Response.Redirect(FormsAuthentication.GetRedirectUrl(username, false /* createPersistentCookie */ ));
    }

    private static string LivedoorID取得RPC用URLの作成()
    {
        // RPC側はPOSTで送らなければならない
        return "http://auth.livedoor.com/rpc/auth";
    }

    private static string GetSignature(NameValueCollection パラメータ, string 秘密鍵)
    {
        HMACSHA1 sha1 = new HMACSHA1(Encoding.ASCII.GetBytes(秘密鍵));

        StringBuilder s = new StringBuilder();
        List<string> keylist = new List<string>();
        foreach (string key in パラメータ.Keys) keylist.Add(key);
        keylist.Sort();

        s.Length = 0; // StringBuilder初期化
        foreach (string key in keylist)
        {
            if (key == "sig")
                continue; // ここでは "sig" は無視する。コールバックURLで sig 付の NameValueCollection(QueryString) を渡すことを可能にするため。
            s.Append(key);
            s.Append(パラメータ[key]);
        }
        byte[] sha1bytes = sha1.ComputeHash(Encoding.ASCII.GetBytes(s.ToString()));

        s.Length = 0; // StringBuilder初期化
        foreach (byte b in sha1bytes)
        {
            s.Append(b.ToString("x2"));
        }
        return s.ToString();
    }

    /// <summary>
    /// エポック秒を文字列で返す
    /// </summary>
    /// <returns></returns>
    private static string GetEpoch()
    {
        return Convert.ToInt64(DateTime.Now.ToUniversalTime().Subtract(epoch).TotalSeconds).ToString();
    }
}

Login.aspx はLogin.aspx.csの中の処理が失敗した場合の表示を適当に書いておけばよい。処理がうまく動いている場合はリダイレクトされるので表示されない。

<%@ Page Language="C#" AutoEventWireup="true" CodeFile="Login.aspx.cs" Inherits="Login" %>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" >
<head runat="server">
    <title>livedoor Auth 認証処理ページ</title>
</head>
<body>
    <form id="form1" runat="server">
    <div>
    この表示が出る場合は何らかの問題があります。
    </div>
    </form>
</body>
</html>

702NKII ハードリセット

702NKII ハードリセット

システムを工場出荷時への初期化をするには
「発話ボタン、*、3を同時に押しながら、電源を投入。3つのボタンは地域選択画面が出るまで押し続ける」

先日とあるメールを読みたくて、IMAPメールボックスの接続先を一時的にPCで利用しているものに変更したのだが、
大量に保存してあった影響なのか「メモリが足りません。ファイルを削除して下さい」みたいなメッセージが出るようになった。
なのだが、メールボックスをほぼ空状態にし、普通の操作で削除できそうなところで探してみても対象のファイルはなかった。

なんでまあ FExplorer を使って system/mail 以下をざくざく消したらシステムが起動しなくなった。ということでハードリセットをした。

スキン等は初期状態になったけど、ウォレットの中身が残っていてくれたのが有り難かった。
RSMMC側に登録されているアプリケーションもそのまま利用可能で、大きなところでは連絡先ぐらいだ。