Codin’ In The Free World

2010-02-19

[][] OAuth::LiteでxAuth

OAuth::Lite 1.25変更点

今までのget_access_token

my $access_token = $consumer->get_access_token(
  url => $access_token_url,
  token => $request_token,
  verifier => $verification_code,
);

tokenリクエストトークンを渡さなかったり

verifierを渡さなかったりしたら、エラーとして扱っていました。

これはOAuth 1.0 Rev a で必ず必要なパラメータだったためです。


またメソッドの返り値として得られるのはOAuth::Lite::Tokenオブジェクトでした。


しかしSession Extensionや、先日のxAuthなどでは、token以外の情報も

レスポンスに含まれるようになってきました。

また、xAuthでは、tokenやverifierなしでaccess_tokenを要求します


そんなわけで扱いづらくなってきたので, APIを変更したかったのですが、

後方互換の問題があるので、メソッド名を変えて別に用意しました。

get_access_tokenは今まで通り利用できます。

my $res = $consumer->obtain_access_token(
  url => $access_token_url,
  params => {
    x_auth_username => $username,
    x_auth_password => $password,
    x_auth_mode => "client_auth",
  }
);
  my $access_token = $res->token;
  say $access_token->token;
  say $access_token->secret;
  say $res->param('other_param');

戻り値に、ダイレクトにtokenのオブジェクトを返すのではなくて

OAuth::Lite::Responseを挟むようにしました。

$res->tokenで、OAuth::Lite::Tokenのオブジェクトが取れます。


あわせて、get_request_tokenに対してobtain_request_tokenも用意しました。

こちらも返り値がOAuth::Lite::Tokenでなく、OAuth::Lite::Responseのオブジェクトになりますが

それ以外は同じ動作をします。


twitterで実験

packageにexamples/twitter_xauth.plを足しておきました。

twitterでのxAuthを利用してみると、

access_tokenのエンドポイントは必ずhttpsのほうにアクセスすることになっています。

レスポンスを見るとxAuthで規定されているx_auth_expires以外にも

user_id, screen_nameが取れるようですね。



my $consumer = OAuth::Lite::Consumer->new(
    consumer_key    => $consumer_key,
    consumer_secret => $consumer_secret,
);

my $res = $consumer->obtain_access_token(
    url => q{https://twitter.com/oauth/access_token},
    params => {
        x_auth_username => $username,
        x_auth_password => $password,
        x_auth_mode     => "client_auth",
    },  
);

unless($res) {
    say "Failed to get access token";
    die dump($consumer->oauth_response);
}

say "[GOT ACCESS TOKEN]";
say "TOKEN:".$res->token->token;
say "TOKEN-SECRET:".$res->token->secret;
say "EXPIRES:".$res->param('x_auth_expires');
say $res->param('screen_name');
say $res->param('user_id');

特にコンシューマに対して制限なく、xAuthを利用できちゃうというところで、

ポリシーに関してはいろいろと思うところもありますが。

2010-02-12

[] OAuthでデスクトップアプリがブラウザを経由させたくないときのxAuth

最近twitter APIまわりで話題に出てきているようなので。


ちゃんと追いかけきれてないけど、恐らくこれのことですね。

http://tools.ietf.org/html/draft-dehora-farrell-oauth-accesstoken-creds-00


OAuth WRAPではUsername and Password Profileとして組み込まれてます。

http://d.hatena.ne.jp/lyokato/20091118/1258524429


WRAP/2.0が来るまでにOAuth1.0aで使いたい時に。


利用状況

ブラウザがない、あるいはブラウザを使うのが適切ではない状況での

いわゆるデスクトップアプリのためのもの。

組み込みだったり、搭載されているブラウザが貧弱だったりとか。

ブラウザと連携させたくない、あるいは出来ないとき。

仕様

まず始めにクライアントは、エンドユーザーにユーザー名とパスワードを入力させます。


クライアントは、いきなりアクセストークンを取得しにいく。

リクエストトークンを取得したり、ユーザーをプロバイダのページに飛ばして認可させる、という処理は省きます。

ユーザー名とパスワードを預かってる時点で、「ユーザーがクライアントを信頼してる」ということになるので。


アクセストークンURLにリクエストするときのパラメータは次のようになります。

  • x_auth_username
  • x_auth_password
  • x_auth_mode
  • oauth_consumer_key
  • oauth_signature_method
  • oauth_signature
  • oauth_timestamp
  • oauth_nonce
  • oauth_version

普通のOAuthのリクエストで使われるものと何が違うのか


まずoauth_tokenパラメータを省きます。 このやりとりでは、リクエストトークンを使わないので必要ありません。

x_authで始まる3つのパラメータが追加されています。(なのでxAuthと呼ばれてるみたい)


エンドユーザーから預かったユーザー名とパスワードを、

x_auth_username, x_auth_passwordの各パラメータの値として使うということですね。

x_auth_modeは必ず"client_auth"にしておきます。


このリクエストに対するレスポンスには次のパラメータが含まれます。

  • oauth_token
  • oauth_token_secret
  • x_auth_expires

tokenとtoken_secretはいつものですね。

x_auth_expiresが加わっています。unix timeの秒数が入ります。

その時間になったら、アクセストークンの有効期限が切れます。

ここが0になっていたときは無期限です。

所感

クライアントは、「ユーザー名とパスワード」というアカウント情報を、アクセストークン取得時にのみ使い、

その後は、アクセストークンのみ保存し、アカウント情報は記憶しないというのが行儀のよい使い方、ということになると思います。


とは言ってもエンドユーザーから見て、クライアントがそうしてくれる保証はないわけですし、フィッシングの危険性は伴います。

提供しているデータの性質を考えるとか、特定の信頼できるコンシューマのみに利用させるとかで、プロバイダ側はバランスを考える必要があるのではないでしょうか。


あと先日のGumblrのデスクトップ上に保存されたパスワード盗難の件もあることですし、トークンの保存時は気をつけましょう。

2010-01-21

[] ActionScriptでの日本語文字コード変換

ActionScriptのByteArrayクラスにはreadMultiByteとwriteMultiByteというメソッドがあり、

こいつを使えば、日本語の文字コードも変換できます。

ということになっているのですが、環境によって動作が不安定なようです。


実際に、自分もこれらのメソッドを利用していて、

Leopardで通ってたテストがSnow Leopard(64bitモード)で実行したらこけるという場面に遭遇しました。

原因(SDKのバージョンとか, JDKが64bitだとどうとか)はまだ探りきれてません。


さらに、半角カナの扱いなど、日本語独特の問題もありますし、今後も適切なメンテが行われるか分かりません。

(というかそもそも、バイト列を扱う機能と、文字コードの変換を扱う機能を同じクラスにしてしまうのはいかがなものか)

そんなわけで、as3jcodeを書いてみました。


http://github.com/lyokato/as3jcode


サポートする文字コード

  • UTF-8
  • UTF-16
  • EUC-JP
  • ISO-2022-JP
  • Shift_JIS

UTF-16はByteArray.(read|write)Multibyteに"UNICODE"を指定すれば動くとは思います。

日本語文字コードならともかく、さすがにコレをちゃんとサポートしてないということはないと思いますが、

今回は、UTF-16ベースで半角と全角のカタカナの変換処理などもしたかったので、

自分で書いてしまいました。

使い方

ByteArrayのオブジェクトからByteArrayのオブジェクトを生成します。

import org.coderepos.text.encoding.Jcode;

// utf-8のByteArrayオブジェクトからEUC-JPにコンバートしたByteArrayオブジェクトを取得
var eucBytes:ByteArray = Jcode.utf8_euc(utf8Bytes);

// 同様にISO-2022-JPに変換
var jisBytes:ByteArray = Jcode.utf8_jis(utf8Bytes);

// 同様にShift_JIS
var jisBytes:ByteArray = Jcode.utf8_sjis(utf8Bytes);

// 同様にUTF-16
var utf16Bytes:ByteArray = Jcode.utf8_utf16(utf8Bytes);

// その他も、メソッド名を見ればだいたい分かると思います。
tf8Bytes  = Jcode.euc_utf8(eucBytes);
utf8nBytes = Jcode.euc_utf8n(eucBytes);
utf8Bytes  = Jcode.jis_utf8(jisBytes);
utf8nBytes = Jcode.jis_utf8n(jisBytes);
utf8Bytes  = Jcode.sjis_utf8(sjisBytes);
utf8nBytes = Jcode.sjis_utf8n(sjisBytes);
utf8Bytes  = Jcode.utf16_utf8(sjisBytes);
utf8nBytes = Jcode.utf16_utf8n(sjisBytes);
jisBytes  = Jcode.euc_jis(eucBytes);
sjisBytes = Jcode.euc_sjis(eucBytes);
eucBytes  = Jcode.jis_euc(jisBytes);
sjisBytes = Jcode.jis_sjis(jisBytes);
eucBytes  = Jcode.sjis_euc(sjisBytes);
jisBytes  = Jcode.sjis_jis(sjisBytes);

ActionScriptではSystem.useCodePageなどを触らない限り、

基本的にはUTF-8が基準になります。

UTF-8のStringからByteArrayのオブジェクトを作りたいときは次のようにします。

var bytes:ByteArray = new ByteArray();
bytes.writeUTFBytes("日本語の文字列。これはUTF-8");

なので、例えば文字列をEUC-JPに変換したいときは次のようになります。

var bytes:ByteArray = new ByteArray();
bytes.writeUTFBytes("日本語の文字列。これはUTF-8");
var eucBytes:ByteArray = Jcode.utf8_euc(bytes);

UTF-8を軸にした変換はよくありますし、

上のように毎回ByteArrayを準備するのもめんどくさいので、

「UTF-8の文字列から他の文字コードのバイト列へ変換」,

「他の文字コードのバイト列からUTF-8の文字列へ変換」だけは

特別にメソッドを用意しています。

ar eucBytes:ByteArray  = Jcode.to_euc("日本語");
var jisBytes:ByteArray  = Jcode.to_jis("日本語");
var sjisBytes:ByteArray = Jcode.to_sjis("日本語");

var utf8String:String = Jcode.from_euc(eucBytes);
var utf8String:String = Jcode.from_jis(jisBytes);
var utf8String:String = Jcode.from_sjis(sjisBytes);


半角カタカナを使うとき。

h2z(hankaku to zenkaku)メソッドで半角カタカナを全角カタカナに変換できます

UTF-8の文字列で渡すと、変換されたUTF-8の文字列が返ります。

var utf8String:String = Jcode.h2z("アイウエオ");
// utf8String -> "アイウエオ".

あとはvalidationとか。

f (Jcode.is_hiragana(utf8string)) {
  ...
}

if (Jcode.is_zenkaku_katakana(utf8string)) {
  ...
}

if (Jcode.is_hankaku_katakana(utf8string)) {
  ...
}

// zenkaku and hankaku katakana
if (Jcode.is_katakana(utf8string)) {
  ...
}

とりあえず現状はこんな感じ。

2009-12-16

[] ActionScript XML SAX パーサ

ActionScriptはE4Xを実装してるので、メモリ食ってもいい環境で

DOM操作をしたいときは(名前空間が絡まなければ)比較的簡単なのですが、

ストリームの処理をしたいときには困ります。


最近のリアルタイム系コンテンツで使いたいということで、as3saxparserというのを書きました。

http://github.com/lyokato/as3saxparser


ハンドラの準備

ハンドラはIXMLSAXParserインタフェースを実装している必要があります。

IXMLSAXParserインタフェースを直接実装するか、

XMLSAXDefaultHandlerを継承するかします。


IXMLSAXParserインタフェースを直接実装する場合は次のようになります。

import org.coderepos.xml.sax.IXMLSAXParser;
import org.coderepos.xml.XMLAttributes;

public class MyHandler implements IXMLSAXParser
{
  ...

  public function startDocument():void { ... }
  public function endDocument():void { ... }
  public function startElement(ns:String, localName:String, attrs:XMLAttributes, depth:uint):void { ... }
  public function endElement(ns:String, localName:String, depth:uint):void { ... }
  public function cdata(str:String):void { ... }
  public function comment(str:String):void { ... }
  public function characters(str:String):void { ... }
}

インタフェースが要求する関数は上の7つです。

だいたい見れば分かると思います。

startElementについてだけ後で解説します。


IXMLSAXHandlerを直接実装するのではなく、

XMLSAXDefaultHandlerを継承して、必要なメソッドだけオーバーライドしてもよいです。

import org.coderepos.xml.sax.XMLSAXDefaultHandler;
import org.coderepos.xml.XMLAttributes;

public class MyHandler extends XMLSAXDefaultHandler
{
  ...
  override public function startElement(ns:String, localName:String, attrs:XMLAttributes, depth:uint):void {...}
  override public function endElement(ns:String, localName:String, depth:int):void {...}
  override public function characters(str:String):void {...}
}

こちらの方が楽ですが、ActionScriptは多重継承をサポートしていないので、

他に継承したいクラスが存在するときには使えません。

その場合はIXMLSAXParserを直接implementsしてください。


最終的に次のように使います。


import org.coderepos.xml.sax.XMLSAXParser;

var parser:XMLSAXParser = new XMLSAXParser();
parser.handler = new MyHandler();

try {
  parser.pushBytes(xmlFragmentBytes1);
  parser.pushBytes(xmlFragmentBytes2);
  parser.pushBytes(xmlFragmentBytes3);
  ...
} catch (e:*) {
  if (e is XMLSyntaxError) {

  } else if (e is XMLFragmentSizeOverError) {

  } else if (e is XMLElementDepthOverError) {

  } else {
  
  }
}


まず、XMLSAXParserのオブジェクトを生成し、そのhandlerに、

先ほど準備した、独自のハンドラクラスのオブジェクトをセットします。


後は、ソケットなりファイルなりから読み込んだByteArrayのオブジェクトをpushBytesに渡していきます。

SAXパーサーなので、当然、XMLを最後まで読まなくても、随時イベントが発生し、対応するハンドラのコールバックが

呼ばれますし、読み終わったフラグメントは捨てていきます。

不正なXMLだったり、XMLのタグやテキストのサイズが大きすぎたり、elementの階層が深すぎたりすると、

例外が投げられるのでtry catchしておきます。


タグやテキストのサイズの限界値、elementの階層の深さの限界値は、編集可能です。


import org.coderepos.xml.sax.XMLSAXParserConfig;
import org.coderepos.xml.sax.XMLSAXParser;

var config:XMLSAXParserConfig = new XMLSAXParserConfig();
config.MAX_FRAGMENT_SIZE = 1024;
config.MAX_ELEMENT_DEPTH = 20;

var parser:XMLSAXParser = new XMLSAXParser(config);


コールバック

startElementをもう一度みてみます。

public function startElement(ns:String, localName:String, attrs:XMLAttributes, depth:uint) {
  ...
}

attrsだけ説明が必要ですね。

<foo xmlns="http://example.com" xmlns:bar="http://example.com/bar" hoge="aaa" bar:huga="bbb"/>

例えばこのようなXML要素をパースしたときに呼ばれるstartElement内では、属性値を次のように取得できます。

attrs.getValue("hoge"); // aaaが返る
attrs.getValueWithPrefix("bar", "huga"); // bbbが返る

取得した属性を全部トレースしたければ次のように出来ます。

import org.coderepos.xml.XMLAttribute;

var attrArray:Array = attrs.toArray();
for each(var attr:XMLAttribute in attrArray) {
  trace(attr.uri);
  trace(attr.name);
  trace(attr.value);
}

2009-11-26

[][] Adobe alchemy tutorial2

前回(http://d.hatena.ne.jp/lyokato/20091124/1259029512)の続き

非同期処理

オブジェクトに関数を生やす(非同期じゃない場合)

AS3_Val obj = AS3_Object("");
AS3_Val func = AS3_Function(NULL, my_func);
AS3_SetS(obj, "myFunc", func);
AS3_Release(func);

ASからは次のような感じで呼べました。

var result:Object = obj.myFunc(arg1, arg2);

非同期処理にする場合はAS3_FunctionAsyncを使います。

AS3_Val obj = AS3_Object("");
AS3_Val func = AS3_FunctionAsync(NULL, my_func);
AS3_SetS(obj, "myFunc", func);
AS3_Release(func);

そうするとASからの呼び方が少し変わります。

第一引数には、関数の処理が完了したときに呼ばれるコールバックを渡すようにします。

関数自体に渡す引数は、一つずつずらします。


obj.myFunc( function(result:Object):void { ... }, arg1, arg2 );

関数my_funcの戻り値は、myFuncの第一引数として渡したコールバック関数の引数として渡されるようになります。

AS3_Val my_func(void *self, AS3_Val args)
{
  ...
  return result; // myFuncの戻り値ではなく、コールバック関数の引数として渡されるようになる
}

非同期処理を行うときは、IOなどの外部とのやりとりが発生して時間がかかる場合、

もしくは画像処理などの重い処理をするときにブロックしたくないときなどがあると思います。


重い処理を入れるときには適度にflyieldの呼び出しを入れてやるとよいようです。

flyieldは、alchemyによってタイムスライシングを利用して継続を行うようです。

[参考: http://d.hatena.ne.jp/nitoyon/20090115/alchemy_as_stack ]


alchemyに同梱されているlibpngのサンプルや、

こちら(http://segfaultlabs.com/blog/post/asynchronous-jpeg-encoding/)のjpegのサンプルなどでも

画像処理中、あるチャンクが処理されるごとにflyieldを実行してます。


for (i = 0; i < height; i++) {
  for (j = 0; j < width; j++) {
     ...
  }
  // 一行処理するごとに制御を一度戻す
  flyield();
}

ただ気になったのが、非同期設定されたコールバックの中で例外を投げたら、どこかで消されました。

obj.myFunc( function(res:Object):void { throw new Error(String(res)); }, arg1, arg2 );

例外が捕らえられない。

flyieldなどのように、例外を大域脱出に使ってるところで、コールバックの例外などが潰されるんでしょうか。

そもそもC側でActionScriptの例外を飛ばす方法が分からないので何とも言えないですが。