Blame samples/socket.c

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