/*
Meanwhile - Unofficial Lotus Sametime Community Client Library
Copyright (C) 2004 Christopher (siege) O'Brien
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public
License along with this library; if not, write to the Free
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <netdb.h>
#include <glib.h>
#include <mw_common.h>
#include <mw_error.h>
#include <mw_service.h>
#include <mw_session.h>
#include <mw_srvc_im.h>
#define BUF_LEN 2048
/* client data should be put into a structure and associated with the
session. Then it will be available from the many call-backs
handling events from the session */
struct sample_client {
struct mwSession *session; /* the actual meanwhile session */
int sock; /* the socket connecting to the server */
int sock_event; /* glib event id polling the socket */
char *target;
char *message;
};
/* handler function for when the session wants to close IO */
static void mw_session_io_close(struct mwSession *session) {
struct sample_client *client;
client = mwSession_getClientData(session);
if(client->sock) {
g_source_remove(client->sock_event);
close(client->sock);
client->sock = 0;
client->sock_event = 0;
}
}
/* handler function for when the session wants to write data */
static int mw_session_io_write(struct mwSession *session,
const guchar *buf, gsize len) {
struct sample_client *client;
int ret = 0;
client = mwSession_getClientData(session);
/* socket was already closed. */
if(client->sock == 0)
return 1;
while(len) {
ret = write(client->sock, buf, len);
if(ret <= 0) break;
len -= ret;
}
if(len > 0) {
/* stop watching the socket */
g_source_remove(client->sock_event);
/* close the socket */
close(client->sock);
/* remove traces of socket from client */
client->sock = 0;
client->sock_event = 0;
/* return error code */
return -1;
}
return 0;
}
/* handles when a conversation has been fully established between
this client and another. */
static void mw_im_conversation_opened(struct mwConversation *conv) {
struct mwServiceIm *srvc;
struct mwSession *session;
struct sample_client *client;
struct mwIdBlock *idb;
/* get a reference to the client data */
srvc = mwConversation_getService(conv);
session = mwService_getSession(MW_SERVICE(srvc));
client = mwSession_getClientData(session);
/* make sure that it's not someone just randomly IM-ing us... */
idb = mwConversation_getTarget(conv);
g_return_if_fail(! strcmp(client->target, idb->user));
/* send the message and close the conversation */
mwConversation_send(conv, mwImSend_PLAIN, client->message);
mwConversation_close(conv, ERR_SUCCESS);
/* the polite way to close everything up. Will call
mw_session_stateChange after doing what needs to be done */
mwSession_stop(session, ERR_SUCCESS);
}
static struct mwImHandler mw_im_handler = {
.conversation_opened = mw_im_conversation_opened,
.conversation_closed = NULL,
.conversation_recv = NULL,
.clear = NULL,
};
static void mw_session_stateChange(struct mwSession *session,
enum mwSessionState state,
gpointer info) {
struct sample_client *client;
struct mwServiceIm *service;
struct mwConversation *conv;
struct mwIdBlock idb;
if(state == mwSession_STARTED) {
/* session is now fully started */
client = mwSession_getClientData(session);
g_return_if_fail(client != NULL);
/* create the im service, add it to the session, and start it up */
service = mwServiceIm_new(session,&mw_im_handler);
mwSession_addService(session, MW_SERVICE(service));
mwService_start(MW_SERVICE(service));
/* obtain a conversation with the specified user */
idb.user = client->target;
idb.community = NULL;
conv = mwServiceIm_getConversation(service, &idb);
/* and open it up. When it's finally opened, the
conversation_opened handler for the IM service will be
triggered */
mwConversation_open(conv);
} else if(state == mwSession_STOPPED) {
/* session has stopped */
if(info) {
/* stopped due to an error */
guint32 errcode;
char *err;
errcode = GPOINTER_TO_UINT(info);
err = mwError(errcode);
fprintf(stderr, "meanwhile error %s\n", err);
g_free(err);
exit(1);
} else {
exit(0);
}
}
}
/* the session handler structure is where you should indicate what
functions will perform many of the functions necessary for the
session to operate. Among these, only io_write and io_close are
absolutely required. */
static struct mwSessionHandler mw_session_handler = {
.io_write = mw_session_io_write,
.io_close = mw_session_io_close,
.clear = NULL,
.on_stateChange = mw_session_stateChange,
.on_setPrivacyInfo = NULL,
.on_setUserStatus = NULL,
.on_admin = NULL,
};
/** called from read_cb, attempts to read available data from sock and
pass it to the session, passing back the return code from the read
call for handling in read_cb */
static int read_recv(struct mwSession *session, int sock) {
guchar buf[BUF_LEN];
int len;
len = read(sock, buf, BUF_LEN);
if(len > 0) mwSession_recv(session, buf, len);
return len;
}
/** callback triggered from gaim_input_add, watches the socked for
available data to be processed by the session */
static gboolean read_cb(GIOChannel *chan,
GIOCondition cond,
gpointer data) {
struct sample_client *client = data;
int ret = 0;
int source = g_io_channel_unix_get_fd(chan);
if(cond & G_IO_IN) {
ret = read_recv(client->session, client->sock);
}
/* normal operation ends here */
if(ret > 0) return TRUE;
/* read problem occured if we're here, so we'll need to take care of
it and clean up internal state */
if(client->sock) {
g_source_remove(client->sock_event);
close(client->sock);
client->sock = 0;
client->sock_event = 0;
}
return FALSE;
}
/* address lookup */
static void init_sockaddr(struct sockaddr_in *addr,
const char *host, int port) {
struct hostent *hostinfo;
addr->sin_family = AF_INET;
addr->sin_port = htons (port);
hostinfo = gethostbyname(host);
if(hostinfo == NULL) {
fprintf(stderr, "Unknown host %s.\n", host);
exit(1);
}
addr->sin_addr = *(struct in_addr *) hostinfo->h_addr;
}
/* open a network socket to host:port */
static int init_sock(const char *host, int port) {
struct sockaddr_in srvrname;
int sock;
sock = socket(PF_INET, SOCK_STREAM, 0);
if(sock < 0) {
perror("socket failure");
exit(1);
}
init_sockaddr(&srvrname, host, port);
connect(sock, (struct sockaddr *)&srvrname, sizeof(srvrname));
return sock;
}
/* logging is redirected to here */
static void mw_log_handler(const gchar *domain, GLogLevelFlags flags,
const gchar *msg, gpointer data) {
#if DEBUG
/* ok, debugging is enabled, so let's print it like normal */
g_log_default_handler(domain, flags, msg, data);
#else
; /* nothing! very quiet */
#endif
}
int main(int argc, char *argv[]) {
char *server;
int portno;
char *sender;
char *password;
char *recipient;
char *message;
/* the meanwhile session itself */
struct mwSession *session;
/* client program data */
struct sample_client *client;
/* something glib uses to watch the socket for available data */
GIOChannel *io_chan;
if (argc < 7) {
fprintf(stderr,
"usage %s: server port sender password"
"recipient message\n", argv[0]);
exit(0);
}
server = argv[1];
portno = atoi(argv[2]);
sender = argv[3];
password = argv[4];
recipient = argv[5];
message = argv[6];
/* let's redirect the output of the glib logging facilities */
g_log_set_handler("meanwhile",
G_LOG_LEVEL_MASK | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION,
mw_log_handler, NULL);
/* set up the session stuff */
session = mwSession_new(&mw_session_handler);
mwSession_setProperty(session, mwSession_AUTH_USER_ID, sender, NULL);
mwSession_setProperty(session, mwSession_AUTH_PASSWORD, password, NULL);
mwSession_setProperty(session, mwSession_CLIENT_TYPE_ID,
GUINT_TO_POINTER(mwLogin_MEANWHILE), NULL);
/* set up the client data structure with the things we need it to
remember */
client = g_new0(struct sample_client, 1);
client->session = session;
client->sock = init_sock(server, portno);
client->target = recipient;
client->message = message;
/* associate the client data with the session */
mwSession_setClientData(session, client, g_free);
/* start the session up */
mwSession_start(session);
/* add a watch on the socket */
io_chan = g_io_channel_unix_new(client->sock);
client->sock_event = g_io_add_watch(io_chan, G_IO_IN | G_IO_ERR | G_IO_HUP,
read_cb, client);
/* ... and start the glib loop */
g_main_loop_run(g_main_loop_new(NULL, FALSE));
}