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、ユーザは
ワッサーのボットになっています。
マルチスレッドで入力とGlooxを処理していて入力途中の文字列を消されないようにしてあります。
今回はバイナリも置いておきます。
glookoo-0.0.1.tar.gz
よろしければどうぞ。ソースは後でgithubにでも上げておきます。