2009/07/15


amachangの記事glooxなんて物があるのを知った。
Gloox で XMPP を書いてみた - IT戦記

けっこうシンプルに書ける

http://d.hatena.ne.jp/amachang/20090601/1243852022

gloox - A portable high-level Jabber/XMPP library for C++

gloox is a rock-solid, full-featured Jabber/XMPP client library, written in C++. It makes writing spec-compliant clients easy and allows for hassle-free integration of Jabber/XMPP functionality into existing applications. gloox is released under the GNU GPL. Commercial licensing and support are available.

http://camaya.net/
確かに簡単そう。って事でWindowsだけれど試した。ソース見ると、なんと既にWindows対応出来ている。すばらしい。ただVisual Studio用のプロジェクトファイルが付いているだけだったので、mingw32からビルドする際には少し小細工が必要。
以下、その手順。
# wget http://camaya.net/download/gloox-1.0-beta7.tar.bz2
# tar xjvf gloox-1.0-beta7.tar.bz2
# cd gloox-1.0-beta7
# vim config.h.win

comment out HAVE_WINDNS_H
// #define HAVE_WINDNS_H 1

# cd src
# gcc -I. -c *.cpp
# ar cr libgloox.a *.o
これでmingw32用のlibgloox.aが出来上がる。そしてソース。glooxを使っておしゃべりするプログラムなので鶏っぽく"glookoo"(ぐるっくー)と名づけた。
必要な物はreadlineとpthread。readlineは出回っている物には--enable-multibyte付きでコンパイルされた物が見当たらなかったので、rubyのmingw32バイナリ等でも使われているココから使わせて頂く。
またpthreadはココからダウンロードして # make clean GCE-inlined
でビルドした物を使う。以下コード。
#include <gloox/client.h>
#include <gloox/connectionlistener.h>
#include <gloox/messagesessionhandler.h>
#include <gloox/messageeventhandler.h>
#include <gloox/messagehandler.h>
#include <gloox/message.h>
#include <gloox/messagesession.h>
#include <pthread.h>
#include <readline/readline.h>

#ifdef _WIN32
#include <windows.h>
#endif

static char* str_to_utf8_alloc(const char* str) {
#ifdef _WIN32
    size_t in_len = strlen(str);
    wchar_t* wcsdata;
    char* mbsdata;
    size_t mbssize, wcssize;

    wcssize = MultiByteToWideChar(GetACP(), 0, str, in_len,  NULL, 0);
    wcsdata = (wchar_t*) malloc((wcssize + 1) * sizeof(wchar_t));
    wcssize = MultiByteToWideChar(GetACP(), 0, str, in_len, wcsdata, wcssize + 1);
    wcsdata[wcssize] = 0;

    mbssize = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) wcsdata, -1, NULL, 0, NULL, NULL);
    mbsdata = (char*) malloc((mbssize + 1));
    mbssize = WideCharToMultiByte(CP_UTF8, 0, (LPCWSTR) wcsdata, -1, mbsdata, mbssize, NULL, NULL);
    mbsdata[mbssize] = 0;
    free(wcsdata);
    return mbsdata;
#else
    return strdup(str);
#endif
}

static char* utf8_to_str_alloc(const char* utf8) {
#ifdef _WIN32
    size_t in_len = strlen(utf8);
    wchar_t* wcsdata;
    char* mbsdata;
    size_t mbssize, wcssize;

    wcssize = MultiByteToWideChar(CP_UTF8, 0, utf8, in_len,  NULL, 0);
    wcsdata = (wchar_t*) malloc((wcssize + 1) * sizeof(wchar_t));
    wcssize = MultiByteToWideChar(CP_UTF8, 0, utf8, in_len, wcsdata, wcssize + 1);
    wcsdata[wcssize] = 0;

    mbssize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR) wcsdata, -1, NULL, 0, NULL, NULL);
    mbsdata = (char*) malloc((mbssize + 1));
    mbssize = WideCharToMultiByte(GetACP(), 0, (LPCWSTR) wcsdata, -1, mbsdata, mbssize, NULL, NULL);
    mbsdata[mbssize] = 0;
    free(wcsdata);
    return mbsdata;
#else
    return strdup(utf8);
#endif
}

class Glookoo : public gloox::ConnectionListener, gloox::MessageHandler
{
public:
    Glookoo(const char* server, const char* jid, const char* passwd, const char* user)
            : server_(server), jid_(jid), passwd_(passwd), user_(user) {
        client_ = NULL;
        session_ = NULL;
    }

    virtual ~Glookoo() {
        delete client_;
    }

    virtual void onConnect() {
        displayMessage("=== connected ===");
        session_ = new gloox::MessageSession(client_, gloox::JID(user_));
        session_->registerMessageHandler(this);
    }

    virtual void handleMessage(const gloox::Message& msg, gloox::MessageSession *session) {
        char* message = utf8_to_str_alloc(msg.body().c_str());
        displayMessage(message);
        free(message);
    }

    virtual void onDisconnect(gloox::ConnectionError reason) {
        displayMessage("=== disconnected ===");
        delete session_;
        session_ = NULL;
    }

    virtual bool onTLSConnect(const gloox::CertInfo &info) {
        return true;
    }

    void run() {
        client_ = new gloox::Client(gloox::JID(jid_), passwd_);
        client_->registerConnectionListener(this);
        client_->setServer(server_);
        client_->connect();
    }

    void sendMessage(const char* message) {
        if (session_) {
            char* send_message = str_to_utf8_alloc(message);
            session_->send(send_message, "");
            free(send_message);
        }
    }
    
    void displayMessage(const char* message) {
        int point = rl_point;
        rl_beg_of_line(0, 0);
        rl_redisplay();
        printf("%s\n", message);
        rl_point = point;
        rl_refresh_line(0, 0);
    }

private:
    gloox::Client* client_;
    gloox::MessageSession* session_;
    const char* server_;
    const char* jid_;
    const char* passwd_;
    const char* user_;
};

Glookoo* glookoo = NULL;

void* input_func(void* arg) {
    pthread_detach(pthread_self());
    while (true) {
        rl_callback_read_char();
    }
    return 0;
}

void* gloox_func(void* arg) {
    glookoo->run();
}

void input_handler(void) {
    char* message = rl_copy_text(0, rl_end);
    if (glookoo) glookoo->sendMessage(message);
    free(message);
}

int  opterr = 1;
int  optind = 1;
int  optopt;
char *optarg;
 
int getopt(int argc, char** argv, char* opts) {
    static int sp = 1;
    register int c;
    register char *cp;

    if(sp == 1) {
        if(optind >= argc ||
                argv[optind][0] != '-' || argv[optind][1] == '\0')
            return(EOF);
        else if(strcmp(argv[optind], "--") == 0) {
            optind++;
            return(EOF);
        }
    }
    optopt = c = argv[optind][sp];
    if(c == ':' || (cp=strchr(opts, c)) == NULL) {
        if(argv[optind][++sp] == '\0') {
            optind++;
            sp = 1;
        }
        return('?');
    }
    if(*++cp == ':') {
        if(argv[optind][sp+1] != '\0')
            optarg = &argv[optind++][sp+1];
        else if(++optind >= argc) {
            sp = 1;
            return('?');
        } else
            optarg = argv[optind++];
        sp = 1;
    } else {
        if(argv[optind][++sp] == '\0') {
            sp = 1;
            optind++;
        }
        optarg = NULL;
    }
    return(c);
}

int main(int argc, char* argv[]) {
    pthread_t pt_input, pt_gloox;
    char* server = "talk.google.com";
    char* jid = NULL;
    char* passwd = NULL;
    char* user = "wassr-bot@wassr.jp";
    int c;

    opterr = 0;
    while ((c = getopt(argc, argv, "j:p:s:u:") != -1)) {
        switch (optopt) {
        case 'j': jid = optarg; break;
        case 'p': passwd = optarg; break;
        case 's': server = optarg; break;
        case 'u': user = optarg; break;
        case '?': break;
        default:
            argc = 0;
            break;
        }
        optarg = NULL;
    }
    
    if (!jid || !passwd) {
        fprintf (stderr, "Usage: glookoo [-s server] [-j jid] [-p passwd] [-u user]\n");
        exit (1);
    }
    
    rl_callback_handler_install("#> ", (void(*)(char*))input_handler);

    glookoo = new Glookoo(server, jid, passwd, user);
    pthread_create(&pt_input, NULL, &input_func, glookoo);
    pthread_create(&pt_gloox, NULL, &gloox_func, glookoo);
    pthread_join(pt_gloox, NULL);
    pthread_join(pt_input, NULL);
    rl_deprep_terminal();
    return 0;
}
相変わらずコメントもなく適当ですね。
mingw32でのビルド手順は以下の通り。
gcc -Ic:/mingw/include/pthread -o glookoo.exe -I. glookoo.cxx libgloox.a c:/mingw/lib/readline.lib -lstdc++ -lws2_32 -lcrypt32 -lsecur32 -lpthreadGCE2
使い方はこんな感じ
Usage: glookoo [-s server] [-j jid] [-p passwd] [-u user]
デフォルトでサーバはGoogle Talk、ユーザはワッサーのボットになっています。
glookoo
マルチスレッドで入力とGlooxを処理していて入力途中の文字列を消されないようにしてあります。
今回はバイナリも置いておきます。
glookoo-0.0.1.tar.gz
よろしければどうぞ。ソースは後でgithubにでも上げておきます。
Posted at by




javascript:(function(e){e[0].value='your-name';e[1].value='#room-1,#room-2,#room-3';e[5].click()})(document.getElementsByTagName('form')[0].elements);
javascriptで動的にform/input要素を作っているのでname属性もid属性も無い。なのでこんなjs。
これでwebhatを開いてブックマークレット一発でログイン。
your-name, room-1, room-2, room-3 等を修正してお使い下さい。
Posted at by



2009/07/09


XML::Simpleだと格納される結果が決まっており、例えば <statuses>
    <status>
        <id>4773580</id>
        <text>kazuhoさんがやってくれました!</text>
        <user>
            <screen_name>mattn</screen_name>
        </user>
    </status>
    <status>
        <id>4773581</id>
        <text>今日のnickは○○提供です。</text>
        <user>
            <screen_name>kazuho</screen_name>
        </user>
    </status>
</statuses>
こんなXMLを以下の様な形にしたい場合がある場合に少し不便だったりします。 ---
id: 4773580
text: kazuhoさんがやってくれました!
screen_name: mattn
---
id: 4773581
text: 今日のnickは○○提供です。
screen_name: kazuho
XML::Simpleを使うと、arrayノード一つにID要素があると勝手にノード扱いになったり、不必要なノードへのアクセスが必要になったりします。以下XML::Simpleのパース結果
---
status:
  4773580:
    text: kazuhoさんがやってくれました!
    user:
      screen_name: mattn
  4773581:
    text: 今日のnickは○○提供です。
    user:
      screen_name: kazuho
こんな場合にはXML::CuteQueriesを使うと便利です。
Paul Miller / XML-CuteQueries - search.cpan.org

A cute little query language for converting XML to Perl

http://search.cpan.org/dist/XML-CuteQueries/
上の例であれば以下のコードで望み通りの形式でパース出来てしまいます。
use strict;
use warnings;
use LWP::Simple;
use XML::CuteQueries;

my $cq = XML::CuteQueries->new;
$cq->parse(get "http://api.wassr.jp/statuses/public_timeline.xml");
my @statuses   = $cq->cute_query("/statuses/*" => {'*' => '', 'user/*' => ''});
use YAML;
warn Dump @statuses;
PODを見ていただければ分かりますが、XPathでクエリ式を書きそれに対するデータシェイプを指定します。例であれば"/statuses/*"にあるノードすべて"*"はデータシェイプのルートに、また"/statuses/*"にある"user/*"(user内の全て)もデータシェイプ内のルートに置くという指定になります。
これ、XML版のWeb::Scraperって感じですかね。便利だわー。

モダンPerl入門: 業務で使う実用的なPerlの“すべて” モダンPerl入門: 業務で使う実用的なPerlの“すべて”
牧 大輔
翔泳社 単行本 / ¥1,004 (2009年02月01日)
 
発送可能時間:

Posted at by