XMPPのIQまわりで

苦難の道のり

OpenFireをXMPPサーバにして,IQを使ったbotを作ろうとしています.なかなかうまくいきません.Net::XMPP2のIQを使ってるモジュールをまねしてみたりサンプルを使ってみたりしたんですが,ことごとく失敗.Psiを使いながら調べてみると,どうやらIQを処理してくれるはずのbotにまでパケットが届いていないことがわかりました.OpenFireが横取りして返事を返しているようです.そこでJavaDocをひもといてみると,以下のような文章を発見しました.
IQRouter (Core XMPP Server 4.3.2 API)

The typical packet will often be routed twice, once from the sender to some internal server component for handling or processing, and then back to the router to be delivered to it's final destination.

きっと"internal server component"ってのを作らないといけないんだな,ってわかったのでココロが折れました.

光明

でもここでふと気がついたことが."internel"ってわざわざ謳っているということは"external server component"ってのがあるのではないかと.調べてみると,OpenFireには"External Component Settings"って項目があって,それが標準ではService disabledになっていることがわかりました.enableにするにはポート番号と"shared secret"を指定する必要があります.ポート番号と共通鍵,このふたつをキーワードに記憶をひっくり返すと,思い出すものがありました.Jabber::RPC::Serverです.このモジュールは,サーバを作るときに

  # Server as Jabber component
  my $server = new Jabber::RPC::Server(
    server         => 'myserver.org:5702',
    identauth      => 'jrpc.myserver.org:secret',
    connectiontype => 'component',
    methods        => { 
                        'rpc.function1' => \&function1,
                        'rpc.function2' => \&function2,
                      } 
  );  

こんなコードを書いてます.ってことは,このモジュールのexampleに入ってるサンプルサーバが動くんじゃないかと思って試してみたらさくっと動きました.Psiから手動で

<iq type='set' 
    from='sample@xmpp.foo.co.jp' 
    to='jrpc.xmpp.foo.co.jp' 
    id='rpc1'>
  <query xmlns='jabber:iq:rpc'>
    <methodCall>
      <methodName>examples.getStateName</methodName>
      <params>
        <param>
          <value><i4>6</i4></value>
        </param>
      </params>
    </methodCall>
  </query>
</iq>

というxmlを投げた所,サーバから

<iq from="jrpc.xmpp.foo.co.jp"
 type="result"
 to="sample@xmpp.foo.co.jp/Psi"
 id="rpc1" >
  
<query xmlns="jabber:iq:rpc">
<methodResponse>
<params>
<param>
<value>
<string>Colorado</string>
</value>
</param>
</params>
</methodResponse>
</query>
</iq>

という,期待してた答えが返って来るのが確認できました.ただし,このスクリプトには問題があってdisco#infoを受け取ると異常終了してしまいます.

DEBUG: rpc_handler received:  
<iq to='jrpc.turk.nttr.co.jp'
 from='component.turk.nttr.co.jp'
 type='get'
 id='570-12'>
<query xmlns='http://jabber.org/protocol/disco#info'/>
</iq>
Can't call method "getTag" on an undefined value at /usr/local/lib/perl5/site_perl/5.8.8/Jabber/RPC/Server.pm line 156.

この問題を解決するためには,Net::XMPP2::Connectionを使ってDisco infoの処理も適切にできるよう組み直せばいいはずです.が,このモジュールを使ったサンプルがまだ動きません.

まとめ

今回気がついたのは,XEP-0114: Jabber Component Protocolを使ってXEP-0009: Jabber-RPCを実装する方法です.もうちょっと頑張れば,思いつくものが作れるようになるかな.ちなみに,Jabber::RPC::Serverの設定は上記の場合,

  server    => 'xmpp.foo.co.jp:5275',
  identauth => 'jrpc.xmpp.foo.co.jp:qwerty',
  connectiontype => 'component',

となります.