/* 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 #include #include #include #include #include #include #include #include #include #include #include #include #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)); }