高見知英のかいはつにっし(β)

高見知英のアプリケーション開発日誌 のほか、地域活動などの活動報告ブログ。

Atom API

 そういえばブックマーク関連で。
はてなブックマークでは、Atomフィードを配布しています。そして、これを応用してブラウザ外からファイルを送受信したり、リストを得たりする――いわゆる「はてなブックマークAtomAPI」が使用できます。これを使えば、学校と家で同じブックマークを共有出来る。プライベートなブックマークについても、サブアカウントを作ってプライベートモードのブックマークを使えばいいことだし。
ということで、AtomAPIについて調査。日本にはAtomAPIの情報は少ないです、探し方が悪いのか。Perlなどのコードは見つかりましたが、XML::Atom::Feedという、未知のライブラリを使われてるので移植は難しそう。

 その後、英語のページでは一応見つかりました。とはいえ、AtomAPI?という、これまた未知のクラスが使われており、サンプルとするのは難しいです(ソースを見たところusingにはフレームワーク名前空間しか使われていないし、独自クラスというわけではないようだし…)。
 ただ、とりあえずこれだけでも、表示するまでには至りました。HttpWebRequest関連も全くだったし、結構ややこしいです(>_<)
WCCS認証周りは怪しいなあ、とりあえず見よう見まねでやってみたものの、これでうまくいくかどうか不安。上のサンプルコードではこれまた未知のクラスが使われていたようですし。認証しようとして失敗したら、403でも返ってくるのかと思ったんですが、そうではないみたい。
でもちらっと見たときのAjaxのリクエストコードもこんな感じだったし。このあたりから慣れておかないといけないんだろうなあ。

// 認証用クラス(参照変数にnullを指定したいがためにクラスにした)
	public class AuthorizeKey
	{
	  public string Username;
	  public string Password;
	  public AuthorizeKey(string username, string password)
	  {
	    this.Username = username;
	    this.Password = password;
	  }
	  public string GetWSSEString()
	  {
	    System.Security.Cryptography.SHA1 sha1 = 
	      new System.Security.Cryptography.SHA1CryptoServiceProvider();
	    string nonce   = 
	      Convert.ToBase64String(
	        sha1.ComputeHash(Encoding.Default.GetBytes
	          (DateTime.Now.ToString())));
	    string created = DateTime.Now.ToString("g");
	    byte[] pDigest = sha1.ComputeHash(Encoding.Default.GetBytes(nonce + created + Password));
	    return string.Format("UsernameToken Username=\"{0}\", " +
	                         "PasswordDigest=\"{1}\", " +
	                         "Nonce=\"{2}\", Created=\"{3}\"",
	                         Username,
	                         Convert.ToBase64String(pDigest),
	                         nonce,
	                         created);
	  }
	}

中略

    public static string LoadFromURL(string url, AuthorizeKey authorize, IBookmarkList feedList)
    {
      string nextURL = url;
      string title   = "";
      do
      {
        // HTTP GET
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(nextURL);
        request.ContentType = "application/atom+xml";
        if(authorize != null)
          request.Headers.Add("X-WSSE", authorize.GetWSSEString());
        request.Headers.Add("UserAgent", typeof(AtomFeed).FullName);
        request.Method = "GET";
        HttpWebResponse responce = (HttpWebResponse)request.GetResponse();
        if*1;
        nextURL = "";

        // ドキュメント解析
        System.IO.StreamReader reader = new System.IO.StreamReader(responce.GetResponseStream());
        try
        {
          XmlDocument doc = new XmlDocument();
          doc.LoadXml(reader.ReadToEnd());
          foreach(XmlElement elm in doc["feed"])
          {
            if(elm.Name == "title")
              title = elm.InnerText;
            if(elm.Name == "link" && elm.Attributes["rel"].Value == "next")
              nextURL = elm.Attributes["href"].Value;
            if(elm.Name == "entry")
            { // entryの取得
              IBookmark bookmark = feedList.AddBookmark();
              foreach(XmlElement entryNode in elm.ChildNodes)
              {
                if(entryNode.Name == "title") 
                  bookmark.SiteName  = entryNode.InnerText;
                if(entryNode.Name == "link" && entryNode.Attributes["rel"].Value == "related")
                  bookmark.SiteURL   = entryNode.Attributes["href"].Value;
                if(entryNode.Name == "summary")
                  bookmark.Summary   = entryNode.InnerText;
                if(entryNode.Name == "issued")
                  bookmark.Created   = DateTime.Parse(entryNode.InnerText);
                if(entryNode.Name == "dc:subject")
                  bookmark.Directory += "/" + entryNode.InnerText;                
              }
            } 
          } 
        }finally{
          reader.Close();
        }
      }while(nextURL != "");
      return title;
	  }

 さて、タグの処理もしてやらなくちゃなあ、タグまわりはAPIが提供されてないことですし、全部自分でやらなくちゃいけないですね。

*1:int)responce.StatusCode >= 400) throw new WebException(string.Format("{0} - {1}", (int)responce.StatusCode, responce.StatusCode