Blame samples/socket.c

Packit 16808d
Packit 16808d
/*
Packit 16808d
  Meanwhile - Unofficial Lotus Sametime Community Client Library
Packit 16808d
  Copyright (C) 2004  Christopher (siege) O'Brien
Packit 16808d
  
Packit 16808d
  This library is free software; you can redistribute it and/or
Packit 16808d
  modify it under the terms of the GNU Library General Public
Packit 16808d
  License as published by the Free Software Foundation; either
Packit 16808d
  version 2 of the License, or (at your option) any later version.
Packit 16808d
  
Packit 16808d
  This library is distributed in the hope that it will be useful,
Packit 16808d
  but WITHOUT ANY WARRANTY; without even the implied warranty of
Packit 16808d
  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit 16808d
  Library General Public License for more details.
Packit 16808d
  
Packit 16808d
  You should have received a copy of the GNU Library General Public
Packit 16808d
  License along with this library; if not, write to the Free
Packit 16808d
  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
Packit 16808d
*/
Packit 16808d
Packit 16808d
Packit 16808d
Packit 16808d
/**
Packit 16808d
   @file socket.c
Packit 16808d
   
Packit 16808d
   This file is a simple demonstration of using unix socket code to
Packit 16808d
   connect a mwSession to a sametime server and get it fully logged
Packit 16808d
   in. It doesn't do anything after logging in.
Packit 16808d
   
Packit 16808d
   Here you'll find examples of:
Packit 16808d
    - opening a socket to the host
Packit 16808d
    - using the socket to feed data to the session
Packit 16808d
    - using a session handler to allow the session to write data to the
Packit 16808d
      socket
Packit 16808d
    - using a session handler to allow the session to close the socket
Packit 16808d
    - watching for error conditions on read/write
Packit 16808d
*/
Packit 16808d
Packit 16808d
Packit 16808d
Packit 16808d
#include <netdb.h>
Packit 16808d
#include <netinet/in.h>
Packit 16808d
#include <stdio.h>
Packit 16808d
#include <stdlib.h>
Packit 16808d
#include <string.h>
Packit 16808d
#include <sys/socket.h>
Packit 16808d
#include <sys/types.h>
Packit 16808d
#include <unistd.h>
Packit 16808d
Packit 16808d
#include <glib.h>
Packit 16808d
Packit 16808d
#include <mw_common.h>
Packit 16808d
#include <mw_session.h>
Packit 16808d
Packit 16808d
Packit 16808d
Packit 16808d
/** help text if you don't give the right number of arguments */
Packit 16808d
#define HELP \
Packit 16808d
"Meanwhile sample socket client\n" \
Packit 16808d
"Usage: %s server userid password\n" \
Packit 16808d
"\n" \
Packit 16808d
"Connects to a sametime server and logs in with the supplied user ID\n" \
Packit 16808d
"and password. Doesn't actually do anything useful after that.\n\n"
Packit 16808d
Packit 16808d
Packit 16808d
Packit 16808d
/** how much to read from the socket in a single call */
Packit 16808d
#define BUF_LEN 2048
Packit 16808d
Packit 16808d
Packit 16808d
Packit 16808d
/* client data should be put into a structure and associated with the
Packit 16808d
   session. Then it will be available from the many call-backs
Packit 16808d
   handling events from the session */
Packit 16808d
struct sample_client {
Packit 16808d
  struct mwSession *session;  /* the actual meanwhile session */
Packit 16808d
  int sock;                   /* the socket connecting to the server */
Packit 16808d
  int sock_event;             /* glib event id polling the socket */
Packit 16808d
};
Packit 16808d
Packit 16808d
Packit 16808d
Packit 16808d
/* the io_close function from the session handler */
Packit 16808d
static void mw_session_io_close(struct mwSession *session) {
Packit 16808d
  struct sample_client *client;
Packit 16808d
Packit 16808d
  /* get the client data from the session */
Packit 16808d
  client = mwSession_getClientData(session);
Packit 16808d
Packit 16808d
  /* safety check */
Packit 16808d
  g_return_if_fail(client != NULL);
Packit 16808d
Packit 16808d
  /* if the client still has a socket to be closed, close it */
Packit 16808d
  if(client->sock) {    
Packit 16808d
    g_source_remove(client->sock_event);
Packit 16808d
    close(client->sock);
Packit 16808d
    client->sock = 0;
Packit 16808d
    client->sock_event = 0;
Packit 16808d
  }
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
Packit 16808d
/* the io_write function from the session handler */
Packit 16808d
static int mw_session_io_write(struct mwSession *session,
Packit 16808d
			       const guchar *buf, gsize len) {
Packit 16808d
Packit 16808d
  struct sample_client *client;
Packit 16808d
  int ret = 0;
Packit 16808d
Packit 16808d
  /* get the client data from the session */
Packit 16808d
  client = mwSession_getClientData(session);
Packit 16808d
Packit 16808d
  /* safety check */
Packit 16808d
  g_return_val_if_fail(client != NULL, -1);
Packit 16808d
Packit 16808d
  /* socket was already closed, so we can't possibly write to it */
Packit 16808d
  if(client->sock == 0) return -1;
Packit 16808d
Packit 16808d
  /* write out the data to the socket until it's all gone */
Packit 16808d
  while(len) {
Packit 16808d
    ret = write(client->sock, buf, len);
Packit 16808d
    if(ret <= 0) break;
Packit 16808d
    len -= ret;
Packit 16808d
  }
Packit 16808d
Packit 16808d
  /* if for some reason we couldn't finish writing all the data, there
Packit 16808d
     must have been a problem */
Packit 16808d
  if(len > 0) {
Packit 16808d
    /* stop watching the socket */
Packit 16808d
    g_source_remove(client->sock_event);
Packit 16808d
Packit 16808d
    /* close the socket */
Packit 16808d
    close(client->sock);
Packit 16808d
Packit 16808d
    /* remove traces of socket from client */
Packit 16808d
    client->sock = 0;
Packit 16808d
    client->sock_event = 0;
Packit 16808d
Packit 16808d
    /* return error code */
Packit 16808d
    return -1;
Packit 16808d
  }
Packit 16808d
Packit 16808d
  /* return success code */
Packit 16808d
  return 0;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
Packit 16808d
/* the on_stateChange function from the session handler */
Packit 16808d
static void mw_session_stateChange(struct mwSession *session,
Packit 16808d
				   enum mwSessionState state, 
Packit 16808d
				   gpointer info) {
Packit 16808d
Packit 16808d
  if(state == mwSession_STARTED) {
Packit 16808d
    /* at this point the session is all ready to go. */
Packit 16808d
    printf("session fully started\n");
Packit 16808d
  }
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
Packit 16808d
/* the session handler structure is where you should indicate what
Packit 16808d
   functions will perform many of the functions necessary for the
Packit 16808d
   session to operate. Among these, only io_write and io_close are
Packit 16808d
   absolutely required. */
Packit 16808d
static struct mwSessionHandler mw_session_handler = {
Packit 16808d
  .io_write = mw_session_io_write,  /**< handle session to socket */
Packit 16808d
  .io_close = mw_session_io_close,  /**< handle session closing socket */
Packit 16808d
  .clear = NULL,                    /**< cleanup function */
Packit 16808d
  .on_stateChange = mw_session_stateChange,  /**< session status changed */
Packit 16808d
  .on_setPrivacyInfo = NULL,        /**< received privacy information */
Packit 16808d
  .on_setUserStatus = NULL,         /**< received status information */
Packit 16808d
  .on_admin = NULL,                 /**< received an admin message */
Packit 16808d
};
Packit 16808d
Packit 16808d
Packit 16808d
Packit 16808d
/** called from read_cb, attempts to read available data from sock and
Packit 16808d
    pass it to the session. Returns zero when the socket has been
Packit 16808d
    closed, less-than zero in the event of an error, and greater than
Packit 16808d
    zero for success */
Packit 16808d
static int read_recv(struct mwSession *session, int sock) {
Packit 16808d
  guchar buf[BUF_LEN];
Packit 16808d
  int len;
Packit 16808d
Packit 16808d
  len = read(sock, buf, BUF_LEN);
Packit 16808d
  if(len > 0) mwSession_recv(session, buf, len);
Packit 16808d
Packit 16808d
  return len;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
Packit 16808d
/** callback registerd via g_io_add_watch in main, watches the socket
Packit 16808d
    for available data to be processed by the session */
Packit 16808d
static gboolean read_cb(GIOChannel *chan,
Packit 16808d
			GIOCondition cond,
Packit 16808d
			gpointer data) {
Packit 16808d
Packit 16808d
  struct sample_client *client = data;
Packit 16808d
  int ret = 0;
Packit 16808d
Packit 16808d
  if(cond & G_IO_IN) {
Packit 16808d
    ret = read_recv(client->session, client->sock);
Packit 16808d
Packit 16808d
    /* successful operation ends here, as the read_recv function
Packit 16808d
       should only return sero or lower in the event of a disconnect
Packit 16808d
       or error */
Packit 16808d
    if(ret > 0) return TRUE;
Packit 16808d
  }
Packit 16808d
Packit 16808d
  /* read problem occured if we're here, so we'll need to take care of
Packit 16808d
     it and clean up internal state */
Packit 16808d
  if(client->sock) {
Packit 16808d
Packit 16808d
    /* stop watching the socket */
Packit 16808d
    g_source_remove(client->sock_event);
Packit 16808d
Packit 16808d
    /* close it */
Packit 16808d
    close(client->sock);
Packit 16808d
Packit 16808d
    /* don't reference the socket or its event now that they're gone */
Packit 16808d
    client->sock = 0;
Packit 16808d
    client->sock_event = 0;
Packit 16808d
  }
Packit 16808d
Packit 16808d
  return FALSE;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
Packit 16808d
/* address lookup used by init_sock */
Packit 16808d
static void init_sockaddr(struct sockaddr_in *addr,
Packit 16808d
			  const char *host, int port) {
Packit 16808d
Packit 16808d
  struct hostent *hostinfo;
Packit 16808d
Packit 16808d
  addr->sin_family = AF_INET;
Packit 16808d
  addr->sin_port = htons (port);
Packit 16808d
  hostinfo = gethostbyname(host);
Packit 16808d
  if(hostinfo == NULL) {
Packit 16808d
    fprintf(stderr, "Unknown host %s.\n", host);
Packit 16808d
    exit(1);
Packit 16808d
  }
Packit 16808d
  addr->sin_addr = *(struct in_addr *) hostinfo->h_addr;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
Packit 16808d
/* open and return a network socket fd connected to host:port */
Packit 16808d
static int init_sock(const char *host, int port) {
Packit 16808d
  struct sockaddr_in srvrname;
Packit 16808d
  int sock;
Packit 16808d
Packit 16808d
  sock = socket(PF_INET, SOCK_STREAM, 0);
Packit 16808d
  if(sock < 0) {
Packit 16808d
    fprintf(stderr, "socket failure");
Packit 16808d
    exit(1);
Packit 16808d
  }
Packit 16808d
  
Packit 16808d
  init_sockaddr(&srvrname, host, port);
Packit 16808d
  connect(sock, (struct sockaddr *)&srvrname, sizeof(srvrname));
Packit 16808d
Packit 16808d
  return sock;
Packit 16808d
}
Packit 16808d
Packit 16808d
Packit 16808d
Packit 16808d
int main(int argc, char *argv[]) {
Packit 16808d
Packit 16808d
  /* the meanwhile session itself */
Packit 16808d
  struct mwSession *session;
Packit 16808d
Packit 16808d
  /* client program data */
Packit 16808d
  struct sample_client *client;
Packit 16808d
Packit 16808d
  /* something glib uses to watch the socket for available data */
Packit 16808d
  GIOChannel *io_chan;
Packit 16808d
Packit 16808d
  /* specify host, user, pass on the command line */
Packit 16808d
  if(argc != 4) {
Packit 16808d
    fprintf(stderr, HELP, *argv);
Packit 16808d
    return 1;
Packit 16808d
  }
Packit 16808d
Packit 16808d
  /* create the session and set the user and password */
Packit 16808d
  session = mwSession_new(&mw_session_handler);
Packit 16808d
  mwSession_setProperty(session, mwSession_AUTH_USER_ID, argv[2], NULL);
Packit 16808d
  mwSession_setProperty(session, mwSession_AUTH_PASSWORD, argv[3], NULL);
Packit 16808d
Packit 16808d
Packit 16808d
  /* create the client data. This is arbitrary data that a client will
Packit 16808d
     want to store along with the session for its own use */
Packit 16808d
  client = g_new0(struct sample_client, 1);
Packit 16808d
  client->session = session;
Packit 16808d
Packit 16808d
  /* associate the client data with the session, specifying an
Packit 16808d
     optional cleanup function which will be called upon the data when
Packit 16808d
     the session is free'd via mwSession_free */
Packit 16808d
  mwSession_setClientData(session, client, g_free);
Packit 16808d
Packit 16808d
  /* set up a connection to the host */
Packit 16808d
  client->sock = init_sock(argv[1], 1533);
Packit 16808d
Packit 16808d
  /* start the session. This will cause the session to send the
Packit 16808d
     handshake message (using the io_write function specified in the
Packit 16808d
     session handler). */
Packit 16808d
  mwSession_start(session);
Packit 16808d
Packit 16808d
  /* add a watch on the socket. Any data returning from the server
Packit 16808d
     will trigger read_cb, which will in turn read the data and pass
Packit 16808d
     it to the session for interpretation */
Packit 16808d
  io_chan = g_io_channel_unix_new(client->sock);
Packit 16808d
  client->sock_event = g_io_add_watch(io_chan, G_IO_IN | G_IO_ERR | G_IO_HUP,
Packit 16808d
				      read_cb, client);
Packit 16808d
Packit 16808d
  /* Create a new main loop and start it. This will cause the above
Packit 16808d
     watch to begin actually polling the socket. Use g_idle_add,
Packit 16808d
     g_timeout_add to insert events into the event loop */
Packit 16808d
  g_main_loop_run(g_main_loop_new(NULL, FALSE));
Packit 16808d
Packit 16808d
  /* this won't happen until after the main loop finally terminates */
Packit 16808d
  return 0;
Packit 16808d
}
Packit 16808d
Packit 16808d