2009/07/15

Recent entries from same category

  1. RapidJSON や simdjson よりも速いC言語から使えるJSONライブラリ「yyjson」
  2. コメントも扱える高機能な C++ 向け JSON パーサ「jsoncpp」
  3. C++ で flask ライクなウェブサーバ「clask」書いた。
  4. C++ 用 SQLite3 ORM 「sqlite_orm」が便利。
  5. zsh で PATH に相対パスを含んだ場合にコマンドが補完できないのは意図的かどうか。

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