/* 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 */ /** @file socket.c This file is a simple demonstration of using unix socket code to connect a mwSession to a sametime server and get it fully logged in. It doesn't do anything after logging in. Here you'll find examples of: - opening a socket to the host - using the socket to feed data to the session - using a session handler to allow the session to write data to the socket - using a session handler to allow the session to close the socket - watching for error conditions on read/write */ #include #include #include #include #include #include #include #include #include #include #include /** help text if you don't give the right number of arguments */ #define HELP \ "Meanwhile sample socket client\n" \ "Usage: %s server userid password\n" \ "\n" \ "Connects to a sametime server and logs in with the supplied user ID\n" \ "and password. Doesn't actually do anything useful after that.\n\n" /** how much to read from the socket in a single call */ #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 */ }; /* the io_close function from the session handler */ static void mw_session_io_close(struct mwSession *session) { struct sample_client *client; /* get the client data from the session */ client = mwSession_getClientData(session); /* safety check */ g_return_if_fail(client != NULL); /* if the client still has a socket to be closed, close it */ if(client->sock) { g_source_remove(client->sock_event); close(client->sock); client->sock = 0; client->sock_event = 0; } } /* the io_write function from the session handler */ static int mw_session_io_write(struct mwSession *session, const guchar *buf, gsize len) { struct sample_client *client; int ret = 0; /* get the client data from the session */ client = mwSession_getClientData(session); /* safety check */ g_return_val_if_fail(client != NULL, -1); /* socket was already closed, so we can't possibly write to it */ if(client->sock == 0) return -1; /* write out the data to the socket until it's all gone */ while(len) { ret = write(client->sock, buf, len); if(ret <= 0) break; len -= ret; } /* if for some reason we couldn't finish writing all the data, there must have been a problem */ 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 success code */ return 0; } /* the on_stateChange function from the session handler */ static void mw_session_stateChange(struct mwSession *session, enum mwSessionState state, gpointer info) { if(state == mwSession_STARTED) { /* at this point the session is all ready to go. */ printf("session fully started\n"); } } /* 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, /**< handle session to socket */ .io_close = mw_session_io_close, /**< handle session closing socket */ .clear = NULL, /**< cleanup function */ .on_stateChange = mw_session_stateChange, /**< session status changed */ .on_setPrivacyInfo = NULL, /**< received privacy information */ .on_setUserStatus = NULL, /**< received status information */ .on_admin = NULL, /**< received an admin message */ }; /** called from read_cb, attempts to read available data from sock and pass it to the session. Returns zero when the socket has been closed, less-than zero in the event of an error, and greater than zero for success */ 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 registerd via g_io_add_watch in main, watches the socket 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; if(cond & G_IO_IN) { ret = read_recv(client->session, client->sock); /* successful operation ends here, as the read_recv function should only return sero or lower in the event of a disconnect or error */ 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) { /* stop watching the socket */ g_source_remove(client->sock_event); /* close it */ close(client->sock); /* don't reference the socket or its event now that they're gone */ client->sock = 0; client->sock_event = 0; } return FALSE; } /* address lookup used by init_sock */ 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 and return a network socket fd connected 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) { fprintf(stderr, "socket failure"); exit(1); } init_sockaddr(&srvrname, host, port); connect(sock, (struct sockaddr *)&srvrname, sizeof(srvrname)); return sock; } int main(int argc, char *argv[]) { /* 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; /* specify host, user, pass on the command line */ if(argc != 4) { fprintf(stderr, HELP, *argv); return 1; } /* create the session and set the user and password */ session = mwSession_new(&mw_session_handler); mwSession_setProperty(session, mwSession_AUTH_USER_ID, argv[2], NULL); mwSession_setProperty(session, mwSession_AUTH_PASSWORD, argv[3], NULL); /* create the client data. This is arbitrary data that a client will want to store along with the session for its own use */ client = g_new0(struct sample_client, 1); client->session = session; /* associate the client data with the session, specifying an optional cleanup function which will be called upon the data when the session is free'd via mwSession_free */ mwSession_setClientData(session, client, g_free); /* set up a connection to the host */ client->sock = init_sock(argv[1], 1533); /* start the session. This will cause the session to send the handshake message (using the io_write function specified in the session handler). */ mwSession_start(session); /* add a watch on the socket. Any data returning from the server will trigger read_cb, which will in turn read the data and pass it to the session for interpretation */ 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); /* Create a new main loop and start it. This will cause the above watch to begin actually polling the socket. Use g_idle_add, g_timeout_add to insert events into the event loop */ g_main_loop_run(g_main_loop_new(NULL, FALSE)); /* this won't happen until after the main loop finally terminates */ return 0; }