Codin’ In The Free World

2011-12-29

[][] C/Objective-C GeoHash Library

GeoHashの勉強がてら、Cでライブラリを書いて、

さらにそのObjective-C wrapperも用意してみた。

C版

https://github.com/lyokato/libgeohash

Objective-C Wrapper

https://github.com/lyokato/objc-geohash


GeoHashについてはこのあたりを参照のこと。

http://blog.masuidrive.jp/index.php/2010/01/13/geohash/

使い方

緯度と軽度と求めたいハッシュの長さを指定すると

ハッシュ値を取得できる。

C版

#include <geohash.h>

char* hash = GEOHASH_encode(35.6894875, 139.6917064, 13);
...
free(hash);

Objective-C版

#import "GeoHash.h"

NSString *hash = [GeoHash hashForLatitude:35.6894875                                               
                               longtitude:139.6917064                                              
                                   length:13];                                                     

/* hash equals to @"xn774c06kdtve" */

GEOHASHの値から、それが表す緯度経度の範囲を取得する

C版

GEOHASH *area;
area = GEOHASH_decode("c216ne");

/*
次のように、指定されたハッシュが表す範囲の、緯度経度の最大値、最小値をdoubleで取得できる

area->latitude.max;
area->latitude.min;
area->longtitude.max;
area->longtitude.min;
*/

GEOHASH_free_area(area);

Objective-C版

GHArea *area = [GeoHash areaForHash:@"c216ne"]; 

/* 
次のように、指定されたハッシュが表す範囲の、緯度経度の最大値、最小値をNSNumberのオブジェクトとして取得できる

area.latitude.max          
area.latitude.min                                                                                  
area.longtitude.max
area.longtitude.min 
*/

あるハッシュがあらわすブロックに隣接するブロックのハッシュ値を取得する。

隣接する方角を指定する。


C版

char *adjacent;

/*
次の四つから、隣接する方角を指定する
- GEOHASH_NORTH
- GEOHASH_WEST
- GEOHASH_EAST
- GEOHASH_SOUTH
*/

adjacent = GEOHASH_get_adjacent("dqcjq", GEOHASH_NORTH);
free(adjacent);

Objective-C版

/*
次の四つから、隣接する方角を指定する
- GHDirectionNorth
- GHDirectionWest
- GHDirectionEast
- GHDirectionSouth
*/

NSString *adjacentHash = [GeoHash adjacentForHash:@"dqcjq"                                         
                                        direction:GHDirectionNorth]; 

周囲8ブロックのハッシュ値をまとめて取得

C版

GEOHASH *neighbors;
neighbors = GEOHASH_get_neighbors("dqcw5");

/*

次のようにそれぞれの方角のブロックのハッシュ値が取れる。

neighbors->north;
neighbors->south;
neighbors->east;
neighbors->west;
neighbors->north_east;
neighbors->north_west;
neighbors->south_east;
neighbors->south_west;
*/

GEOHASH_free_neighbors(neighbors);

Objective-C版

NSNeighbors *neighbors = [GeoHash neighborsForHash:@"dqcw5"];                                      

/*

次のようにそれぞれの方角のブロックのハッシュ値が取れる。

neighbors.north                
neighbors.south                 
neighbors.east                  
neighbors.west             
neighbors.northEast        
neighbors.northWest        
neighbors.southEast        
neighbors.southWest   
*/

上記の各関数は、不正なハッシュ値をうけると、CならNULL, Objective-Cならnilを返すようになってる。

ハッシュ値の検証は次のように出来る


C版

#include <stdbool.h>
bool result = GEOHASH_verify_hash("abcde");

Objective-C版

BOOL result = [GeoHash verifyHash:@"abcde"];

とりあえずこんな感じ

2010-10-17

[] YAPC::Asia2010

YAPC::Asia2010に参加し、 「DataPortability ans SocialWeb Protocols」という

タイトルで発表させて頂きました。

発表資料


Protocol紹介

まず始めに、ざっくりプロトコル群を紹介していきましたが、

ここに名前を上げた以外にもどんどん新しいプロトコルが出てきてますね。


Perlのカンファレンスなので触れませんでしたが、このブログに書いてきたとおり、

他の言語でAtompubのライブラリなんかも書いていたりします。

ruby製のatomutilなんかは比較的よく使って頂いているようです。

正規表現しくってて1.9で動かないとか、余計なwarningが出るとか、

libxml_rubyじゃなくてREXML使ってるので、実務で使えるスピードじゃないとかいう

意見があがってるのは承知しているのですが、rubyに触る機会も減り、時間とモチベーションも確保できないので、

こちらも我こそはという方がいればメンテナを委譲したいところであります。


XMPPもActionScriptでas3xmppclientというのを作ってgithubに置いてあります。

他にもAS製XMPPクライアントライブラリはあるのですが, GPLだったので個人的に使いにくかったり、

自分が使いたい拡張があったりして自作しました。

その中で使ってるas3saxparserやas3saslclientも分離してgithubに置いてあります。

XMPPやリアルタイム系は色々と面白いので、別の機会にまた何かしらアウトプットしたいと思っています。


認証認可について

OpenID, OAuth, OpenSocialに絞ってプロトコルのフローのざっくりとした紹介を行い、

それらのライブラリについても紹介を行いました。

いくつかの紹介記事で「OpenIDのライブラリがなかったのでOpenID::Liteを作った」というように書かれてたのを見ましたが、そこは誤解ですね。

正確には、「JanRainに相当する、OpenID2.0のフルスペックサポートのライブラリがなかったから」です。

とは言っても実際はXRIなどの機能はなかなか使われてなかったりします。


OAuth2.0

OAuth2.0のおおざっぱな紹介をしつつ、

OAuth::Lite2というOAuth2.0対応のライブラリの説明をしました。

OAuth2.0は未だdraftでありながら、既にfacebookやmixiで使われ始めています。


OAuth on Native Apps

カスタムURIを利用したリダイレクトのパターンや

トークンの保存方法を各プラットフォーム毎に軽く紹介。

こちらも機会があればもっと細かいコードを出せればいいですね。


あとネイティブアプリでのOAuth利用時に問題とされている点や

Twitterでの事例などを紹介しておきました。


まとめ

毎年のことながら、他社の方とのコミュニケーションが楽しかったですね。

特に今年は転職などで環境を大きく変えた方がたくさんいて刺激になりました。

運営をはじめとする関係者の皆様、おつかれさまでした!

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)) {
  ...
}

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