Logo Search packages:      
Sourcecode: farsight version File versions

yahoowebcam.c

/*
 * Farsight Voice+Video library
 * Copyright (c) 2005 Philippe Khalaf <burger@speedy.org>
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 *
 * This program 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 General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */

// Derived from gaim-vv's implementation
// * Some code copyright (C) 2003, Timothy T Ringenbach <omarvo@hotmail.com>
// * Some code copyright (C) 2002, Philip S Tellis <philip . tellis AT gmx . net>
// * Some code copyright (C) 2002, Michaƫl Kamp <miksun AT users . sourceforge . net>

// This plugin uses a different method than the msnwebcam protocol
// it actually does all the header parsing in a separate element,
// this element sends signals when events happen (new user, disconnected user,
// etc)

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#include <farsight/farsight-plugin.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <fcntl.h>
#include <netdb.h>
#include <netinet/in.h>

#include "yahoowebcam.h"
#include "yahoowebcam-marshal.h"

// TODO use webcam.yahoo.com and resolve
#define YAHOO_WEBCAM_SERVER_HOST "68.142.233.23"
#define YAHOO_WEBCAM_SERVER_PORT 5100

enum {
   ARG_0,
   ARG_SESSION_TYPE,
   ARG_REMOTE_USER,
   ARG_LOCAL_USER,
   ARG_SERVER_KEY,
   ARG_LOCAL_IP,
   ARG_CONN_TYPE
};

// signals
enum {
    VIEWER_REQUEST,
    VIEWER_JOINED,
    VIEWER_COUNT,
    VIEWER_IPS,
    LAST_SIGNAL
};

static void     farsight_yahoowebcam_class_init    (FarsightYahooWebcamClass *klass);
static void     farsight_yahoowebcam_init          (FarsightYahooWebcam      *yahoowebcam);
static void     farsight_yahoowebcam_dispose       (GObject * object);

static void     farsight_yahoowebcam_connect       (FarsightProtocol *protocol);
static void     farsight_yahoowebcam_disconnect    (FarsightProtocol *protocol);
static GstElement *
                farsight_yahoowebcam_create_bin    (FarsightProtocol *protocol);

static gboolean farsight_yahoowebcam_get_server_info    (FarsightYahooWebcam *yahoowebcam);
static gboolean connected_to_master_server_cb           (GIOChannel *ch, GIOCondition cond, gpointer data);
static void     farsight_yahoowebcam_connect_to_master_server (FarsightYahooWebcam *yahoowebcam);
static gboolean connected_to_server_cb                  (GIOChannel *ch, GIOCondition cond, gpointer data);
static void     farsight_yahoowebcam_connect_to_server  (FarsightYahooWebcam *yahoowebcam);

static void     farsight_yahoowebcam_finalize      (GObject      *object);
static void     farsight_yahoowebcam_set_property  (GObject      *object, 
                                            guint             prop_id,
                                            const GValue     *value, 
                                            GParamSpec       *pspec);
static void     farsight_yahoowebcam_get_property  (GObject          *object, 
                                            guint             prop_id, 
                                            GValue           *value,
                                            GParamSpec       *pspec);

static GObjectClass *parent_class = NULL;
static guint yahoowebcam_signals[LAST_SIGNAL] = { 0 };

GType
farsight_yahoowebcam_get_type (void)
{
    static GType type = 0;

    if (type == 0) {
        static const GTypeInfo info = {
            sizeof (FarsightYahooWebcamClass),
            NULL,
            NULL,
            (GClassInitFunc) farsight_yahoowebcam_class_init,
            NULL,
            NULL,
            sizeof (FarsightYahooWebcam),
            0,
            (GInstanceInitFunc) farsight_yahoowebcam_init
        };

        type = g_type_register_static (FARSIGHT_PROTOCOL_TYPE,
                                       "FarsightYahooWebcamType",
                                       &info, 0);
    }

    return type;
}

static void
farsight_yahoowebcam_class_init (FarsightYahooWebcamClass *klass)
{
    GObjectClass *gobject_class;
    FarsightProtocolClass *farsight_protocol_class;

    gobject_class = (GObjectClass *) klass;
    farsight_protocol_class = (FarsightProtocolClass*) klass;
    parent_class = g_type_class_peek_parent (klass);

    farsight_protocol_class->connect = farsight_yahoowebcam_connect;
    farsight_protocol_class->disconnect = farsight_yahoowebcam_disconnect;
    farsight_protocol_class->create_bin = farsight_yahoowebcam_create_bin;

    gobject_class->dispose = farsight_yahoowebcam_dispose;
//    gobject_class->finalize = farsight_yahoowebcam_finalize;

    // signals
    yahoowebcam_signals[VIEWER_REQUEST] = g_signal_new ("viewer-request",
            G_TYPE_FROM_CLASS (klass),
            G_SIGNAL_RUN_LAST,
            G_STRUCT_OFFSET (FarsightYahooWebcamClass, viewer_request),
            NULL,
            NULL,
            g_cclosure_marshal_VOID__STRING,
            G_TYPE_NONE,
            1,
            G_TYPE_STRING);

    yahoowebcam_signals[VIEWER_JOINED] = g_signal_new ("viewer-joined",
            G_TYPE_FROM_CLASS (klass),
            G_SIGNAL_RUN_LAST,
            G_STRUCT_OFFSET (FarsightYahooWebcamClass, viewer_joined),
            NULL,
            NULL,
            g_cclosure_marshal_VOID__STRING,
            G_TYPE_NONE,
            1,
            G_TYPE_STRING);

    yahoowebcam_signals[VIEWER_COUNT] = g_signal_new ("viewer-count",
            G_TYPE_FROM_CLASS (klass),
            G_SIGNAL_RUN_LAST,
            G_STRUCT_OFFSET (FarsightYahooWebcamClass, viewer_count),
            NULL,
            NULL,
            g_cclosure_marshal_VOID__INT,
            G_TYPE_NONE,
            1,
            G_TYPE_INT);

    yahoowebcam_signals[VIEWER_IPS] = g_signal_new ("viewer-ips",
            G_TYPE_FROM_CLASS (klass),
            G_SIGNAL_RUN_LAST,
            G_STRUCT_OFFSET (FarsightYahooWebcamClass, viewer_ips),
            NULL,
            NULL,
            yahoowebcam_marshal_VOID__STRING_STRING,
            G_TYPE_NONE,
            2,
            G_TYPE_STRING,
            G_TYPE_STRING);

    gobject_class->set_property = farsight_yahoowebcam_set_property;
    gobject_class->get_property = farsight_yahoowebcam_get_property;

    g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SESSION_TYPE,
            g_param_spec_uint ("session-type", "The type of session", 
                "Set to 0 if downloading, set to 1 if uploading",
                0, G_MAXUINT16, 0, G_PARAM_READWRITE));

    g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_REMOTE_USER,
        g_param_spec_string ("remote-user", "Remote User",
            "The user from which we are downloading his webcam feed, only set if downloading",
            "", G_PARAM_READWRITE));

    g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOCAL_USER,
        g_param_spec_string ("local-user", "Local User",
            "Our local screen name",
            "", G_PARAM_READWRITE));

    g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_SERVER_KEY,
        g_param_spec_string ("server-key", "Server Key",
            "The key to use when connecting to the server",
            "", G_PARAM_READWRITE));

    g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_LOCAL_IP,
        g_param_spec_string ("local-ip", "Local IP",
            "Our local ip",
            "", G_PARAM_READWRITE));

    g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_CONN_TYPE,
            g_param_spec_uint ("conn-type", "The type of connection", 
                "Set to 0 for dialup, set to 1 for DSL/Cable, set 2 for T1/LAN",
                0, G_MAXUINT16, 0, G_PARAM_READWRITE));
}

static void
farsight_yahoowebcam_init (FarsightYahooWebcam *yahoowebcam)
{
    yahoowebcam->connection = NULL;
    yahoowebcam->remote_user = NULL;
    yahoowebcam->server_key = NULL;
    yahoowebcam->local_ip = NULL;

    yahoowebcam->server = NULL;

    // let's default to DSL
    yahoowebcam->conn_type = 1;
}

static void 
farsight_yahoowebcam_dispose (GObject * object)
{
    FarsightYahooWebcam *yahoowebcam = FARSIGHT_YAHOOWEBCAM (object);

    if (yahoowebcam->local_ip)
        g_free (yahoowebcam->local_ip);
    if (yahoowebcam->remote_user)
        g_free (yahoowebcam->remote_user);
    if (yahoowebcam->server_key)
        g_free (yahoowebcam->server_key);
    if (yahoowebcam->server)
        g_free (yahoowebcam->server);

    if (yahoowebcam->connection)
    {
        g_io_channel_shutdown (yahoowebcam->connection, TRUE, NULL);
        g_io_channel_unref (yahoowebcam->connection);
    }

    gst_adapter_clear (yahoowebcam->adapter);
    g_object_unref (yahoowebcam->adapter);
}

static void
farsight_yahoowebcam_connect (FarsightProtocol *protocol)
{
    FarsightYahooWebcam *yahoowebcam;

    g_return_if_fail (protocol != NULL);

    yahoowebcam = FARSIGHT_YAHOOWEBCAM (protocol);

    farsight_yahoowebcam_connect_to_master_server (yahoowebcam);
}

// get the server info from master server
static gboolean
farsight_yahoowebcam_get_server_info (FarsightYahooWebcam *yahoowebcam)
{
    gint fd = g_io_channel_unix_get_fd (yahoowebcam->connection);

    gchar *data = NULL;
    gchar *packet = NULL;
    guchar magic_nr[] = {0, 1, 0};
    guchar header_len = 8;
    guint len = 0;
    guint pos = 0;
    gchar str[1024];
    guint status = 0;

    /* send initial packet */
    if (yahoowebcam->session_type == DOWNLOADER)
        data = "<RVWCFG>";
    else
        data = "<RUPCFG>";
    g_message ("Writing to server");
    gst_util_dump_mem( data, strlen(data));
    if (write(fd, data, strlen(data)) == -1)
    {
        return FALSE;
    }

    /* send data */
    if (yahoowebcam->session_type == DOWNLOADER) {
        data = g_strdup_printf("g=%s\r\n", yahoowebcam->remote_user);
    } else {
        data = g_strdup("f=1\r\n");
    }

    // create packet and write
    len = strlen(data);
    g_message("size %d", header_len + len);
    packet = g_new0(char, header_len + len);
    packet[pos++] = header_len;
    memcpy(packet + pos, magic_nr, sizeof(magic_nr));
    pos += sizeof(magic_nr);
    pos += yahoo_put32(packet + pos, len);
    memcpy(packet + pos, data, len);
    pos += len;
    gst_util_dump_mem( packet, header_len + len);
    g_message ("Writing to server");
    gst_util_dump_mem( data, pos);
    if (write(fd, packet, pos) == -1)
    {
        g_free(packet);
        g_free(data);
        return FALSE;
    }
    g_free(packet);
    g_free(data);

    // now we block and wait for reply
    gint got = recv(fd, str, sizeof(str), 0);
    if (got != -1)
    {
        g_message ("Got response %d bytes, extracting info", got);
        gst_util_dump_mem(str, got);
        // skip size
        pos = 0;
        guint len = str[pos++];
        g_message ("Packet says %d bytes", len);
        if (len != got)
        {
            return FALSE;
        }

        /* extract status (0 = ok, 6 = webcam not online) */
        status = str[pos++];
        g_message ("Packet says status is %d", status);

        if (status == 0) {
            pos += 2; /* skip next 2 bytes */
            yahoowebcam->server =  g_memdup(str+pos, 16);
            pos += 16;
        } else if (status == 6) {
            farsight_protocol_disconnect (FARSIGHT_PROTOCOL(yahoowebcam));
            return FALSE;
        }
        return TRUE;
    }

    return FALSE;
}

static void
set_local_ip(FarsightYahooWebcam *yahoowebcam)
{
    struct sockaddr addr;
    socklen_t namelen = sizeof(addr);
    gint fd = g_io_channel_unix_get_fd (yahoowebcam->connection);

    if (getsockname(fd, &addr, &namelen))
        return;

    yahoowebcam->local_ip = g_strdup((gchar *)inet_ntoa(((struct sockaddr_in *)&addr)->sin_addr));
}

// Called when connection to master server established
static gboolean 
connected_to_master_server_cb (GIOChannel *ch, GIOCondition cond, gpointer data)
{
    FarsightYahooWebcam *yahoowebcam = FARSIGHT_YAHOOWEBCAM (data);
    gint error, len;
    gint fd = g_io_channel_unix_get_fd (ch);

    g_message ("handler called on fd %d", fd);

    // first we get our own ip
    set_local_ip (yahoowebcam);

    errno = 0;
    if (!((cond & G_IO_IN) || (cond & G_IO_OUT)))
    {
        g_message ("Condition received is %d", cond);
        goto error;
    }

    len = sizeof(error);

    /* Get the error option */
    if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*) &error, &len) < 0)
    {
        g_warning ("getsockopt() failed");
        goto error;
    }

    /* Check if there is an error */
    if (error)
    {
        g_message ("getsockopt gave an error : %d", error);
        goto error;
    }

    /* Remove NON BLOCKING MODE */
    if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK) != 0)
    {
        g_warning ("fcntl() failed");
        goto error;
    }

    g_message ("Got connection to master server on fd %d", fd);

    // let's get server from master server
    if (farsight_yahoowebcam_get_server_info (yahoowebcam))
    {
        // we shutdown our current connection to master server
        g_source_remove (yahoowebcam->connect_watch);
        g_io_channel_shutdown (yahoowebcam->connection, TRUE, NULL);
        g_io_channel_unref (yahoowebcam->connection);

        // ok let's connect to new server
        farsight_yahoowebcam_connect_to_server (yahoowebcam);
        return FALSE;
    }
    else
    {
        g_message ("Couldn't get info from master server on fd %d", fd);
        goto error;
    }

    /* Error */
error:
    g_message ("Got error on master server from fd %d, closing", fd);
    // find, shutdown and remove channel from fdlist
    g_io_channel_shutdown (yahoowebcam->connection, TRUE, NULL);
    g_io_channel_unref (yahoowebcam->connection);
    farsight_protocol_disconnect 
        (FARSIGHT_PROTOCOL(yahoowebcam));

    return FALSE;
}

static void
farsight_yahoowebcam_connect_to_master_server (FarsightYahooWebcam *yahoowebcam)
{
    gint fd = -1;
    struct sockaddr_in theiraddr;
    memset(&theiraddr, 0, sizeof(theiraddr));

    if ( (fd = socket(PF_INET, SOCK_STREAM, 0)) == -1 )
    {
        // show error
        g_message ("could not create socket!");
        return;
    }

    yahoowebcam->connection = g_io_channel_unix_new (fd);
    g_io_channel_set_close_on_unref (yahoowebcam->connection, TRUE);

    // set non-blocking mode
    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);

    theiraddr.sin_family = AF_INET;
    theiraddr.sin_addr.s_addr = inet_addr (YAHOO_WEBCAM_SERVER_HOST);
    theiraddr.sin_port = htons (YAHOO_WEBCAM_SERVER_PORT);

    g_message ("Attempting connection to master yahoo webcam server");
    // this is non blocking, the return value isn't too usefull
    gint ret = connect (fd, (struct sockaddr *) &theiraddr, sizeof (theiraddr));
    if (ret < 0)
    {
        if (errno != EINPROGRESS)
        {
            g_io_channel_shutdown (yahoowebcam->connection, TRUE, NULL);
            g_io_channel_unref (yahoowebcam->connection);
            return;
        }
    }

    // add a watch on that io for when it connects
    yahoowebcam->connect_watch = g_io_add_watch (yahoowebcam->connection, 
            (G_IO_IN|G_IO_OUT|G_IO_PRI|G_IO_ERR|G_IO_HUP|G_IO_NVAL), 
            connected_to_master_server_cb, yahoowebcam);
}

// Called when incoming messages are received from the server
// during broadcast
// The only things i understand righ now are :
// - Receiving and accepting requests to view the broadcast
// - Receiving notifications when viewer leaves
// In between that there seems to be packets for :
// - Informing us how many viewers are viewing
// - Informing us of the internet/external ips of the viewers when they join
static gboolean 
handle_server_messages_cb (GIOChannel *ch, GIOCondition cond, gpointer data)
{
    FarsightYahooWebcam *yahoowebcam = FARSIGHT_YAHOOWEBCAM (data);
    gint fd = g_io_channel_unix_get_fd (yahoowebcam->connection);

    gint error;
    guint len=0;

    errno = 0;
    if (!((cond & G_IO_IN) || (cond & G_IO_OUT)))
    {
        g_message ("Condition received is %d", cond);
        goto error;
    }

    len = sizeof(error);

    /* Get the error option */
    if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*) &error, &len) < 0)
    {
        g_warning ("getsockopt() failed");
        goto error;
    }

    /* Check if there is an error */
    if (error)
    {
        g_message ("getsockopt gave an error : %d", error);
        goto error;
    }

    gchar str[80192];
    GstBuffer *buf;
    guchar *header;
    guint8 *peek_at_header_len;
    guint8 header_len;
    guchar *payload;
    gint got = -1;

    got = recv(fd, str, sizeof(str), 0);
    //gst_util_dump_mem (str, sizeof(str));
    g_message ("calling handle_server_messages_cb %d", got);
    if (got > 0)
    {
        buf = gst_buffer_new ();
        GST_BUFFER_DATA (buf) = g_memdup (str, got);
        GST_BUFFER_SIZE (buf) = got;
        gst_adapter_push (yahoowebcam->adapter, buf);

        // should check magic here
        peek_at_header_len = (guint8 *) gst_adapter_peek (yahoowebcam->adapter, 1);

        // do we have enough bytes to read a header
        // read headers as long as there is no payload to get
        while (gst_adapter_available (yahoowebcam->adapter) >= 
                (yahoowebcam->have_header?yahoowebcam->payload_size:*peek_at_header_len))
        {
            if (!yahoowebcam->have_header)
            {
                header = (guchar *) gst_adapter_peek 
                    (yahoowebcam->adapter, *peek_at_header_len);
                header_len = header[0];

                if (header_len >= 8) {
                    yahoowebcam->reason = header[1];
                    /* next 2 bytes should always be 05 00 */
                    yahoowebcam->payload_size = GUINT32_FROM_BE (*((guint32 *) (header + 4)));
                }
                if (header_len >= 13) {
                    yahoowebcam->packet_type = header[8];
                    yahoowebcam->timestamp = GUINT32_FROM_BE (*((guint32 *) (header + 9)));
                }

                gst_util_dump_mem (header, header_len);
                g_printf ("Got header info length %d packet_type %.2X reason %d payload_size %d timestamp %d\n",
                        header_len, 
                        yahoowebcam->packet_type, 
                        yahoowebcam->reason, 
                        yahoowebcam->payload_size, 
                        yahoowebcam->timestamp);

                gst_adapter_flush (yahoowebcam->adapter, header_len);
                yahoowebcam->have_header = TRUE;
            }

            if (yahoowebcam->payload_size > 0)
            {
                if (gst_adapter_available (yahoowebcam->adapter) < yahoowebcam->payload_size)
                    break;
                else
                {
                    payload = (guchar *)gst_adapter_peek(yahoowebcam->adapter, yahoowebcam->payload_size);
                    yahoowebcam->have_header = FALSE;
                }
            }

            // so now we can check for our various messages and act upon them
            // 0x00 means a new viewer is requesting to view
            if (yahoowebcam->packet_type == 0x00)
            {
                // We need to send a signal now
                // a signal viewer-request that sends the viewers email addr
                g_message("Got viewer request %s", payload);
                //g_signal_emit (yahoowebcam, yahoowebcam_signals[VIEWER_REQUEST], 0,
                //        (gchar *)payload);
            }
            // 0x0c means viewer joined successfully
            if (yahoowebcam->packet_type == 0x0c)
            {
                // we send a signal now
                // a signal viewer-joined that sends the viewers email addr
                //g_signal_emit_by_name ();
            }
            // 0x05 is an info header about the # of viewers
            if (yahoowebcam->packet_type == 0x05)
            {
                // we send a signal now
                // a signal viewer-count that sends the current # of viewers
                // # of viewers = timestamp
                //g_signal_emit_by_name ();
            }
            // 0x13 is an info packet about the int/ext ips of the viewer
            if (yahoowebcam->packet_type == 0x13)
            {
                // we send a signal now
                // a signal viewer-ips that sends the internal and ext IPs of
                // the last viewer that joined
                // need to parse the string into 2 ip addresses
                //g_signal_emit_by_name ();
            }
            gst_adapter_flush (yahoowebcam->adapter, yahoowebcam->payload_size);
        }
        return TRUE;
    }

    /* Error */
error:
    g_message ("Got error on broadcast socket fd %d, closing", fd);
    // find, shutdown and remove channel from fdlist
    g_io_channel_shutdown (yahoowebcam->connection, TRUE, NULL);
    g_io_channel_unref (yahoowebcam->connection);
    farsight_protocol_disconnect 
        (FARSIGHT_PROTOCOL(yahoowebcam));

    return FALSE;
}

// Called when reply to init has been received from server
static gboolean
verify_reply_from_server (GIOChannel *ch, GIOCondition cond, gpointer cb_data)
{
    FarsightYahooWebcam *yahoowebcam = FARSIGHT_YAHOOWEBCAM (cb_data);
    gint fd = g_io_channel_unix_get_fd (yahoowebcam->connection);

    gchar str[1024];
    GstBuffer *buf;
    guchar *header;
    guchar reason;
    guint8 *peek_at_header_len;
    guint8 header_len;
    guchar packet_type;
    guint timestamp, payload_size;

    gint got = recv(fd, str, sizeof(str), 0);
    if (got != -1)
    {

        buf = gst_buffer_new ();
        GST_BUFFER_DATA (buf) = g_memdup (str, got);
        GST_BUFFER_SIZE (buf) = got;
        gst_adapter_push (yahoowebcam->adapter, buf);

        peek_at_header_len = (guint8 *) gst_adapter_peek (yahoowebcam->adapter, 1);

        // do we have enough bytes to read a header
        while (gst_adapter_available (yahoowebcam->adapter) >= 
                *peek_at_header_len)
        {
            header = (guchar *) gst_adapter_peek 
                (yahoowebcam->adapter, *peek_at_header_len);
            header_len = header[0];

            // ok if we are broadcasting, we receive a wierd packet here, i have no
            // clue what it is for, it has 4 bytes and looks something like this
            // 04 00 00 00
            // i'm guessing the first 04 is the packet size
            if (yahoowebcam->session_type == UPLOADER)
            {
                if (header_len == 0x04 && header[1] == 0x00)
                {
                    gst_util_dump_mem (header, header_len);
                    // ok we assume this is enough...
                    FARSIGHT_PROTOCOL_CLASS (parent_class)->connected 
                        (FARSIGHT_PROTOCOL (yahoowebcam));
                    // if broadcasting, we need to listen for incoming requests
                    // and status messages on the viewers
                    yahoowebcam->connect_watch = g_io_add_watch (yahoowebcam->connection, 
                            (G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL), 
                            handle_server_messages_cb, yahoowebcam);
                }
                else
                {
                    // hrrm we did not get that mystery packet
                    // disconnect
                    farsight_protocol_disconnect 
                        (FARSIGHT_PROTOCOL(yahoowebcam));
                }
                gst_adapter_flush (yahoowebcam->adapter, header_len);
                return FALSE;
            }

            if (header_len >= 8) {
                reason = header[1];
                /* next 2 bytes should always be 05 00 */
                payload_size = GUINT32_FROM_BE (*((guint32 *) (header + 4)));
            }
            if (header_len >= 13) {
                packet_type = header[8];
                timestamp = GUINT32_FROM_BE (*((guint32 *) (header + 9)));
            }

            gst_util_dump_mem (header, header_len);
            g_message("Got header info length %d packet_type %.2X reason %d payload_size %d timestamp %d\n",
                    header_len, packet_type, reason, payload_size, timestamp);

            gst_adapter_flush (yahoowebcam->adapter, header_len);
            if (payload_size > 0)
            {
                gst_adapter_flush (yahoowebcam->adapter, payload_size);
            }

            // TODO also get the remote ips from packet 0x13

            if (yahoowebcam->first)
            {
                // ignore first header.. not sure what it's for
                yahoowebcam->first = FALSE;
                continue;
            }

            if (packet_type == 0x00)
            {
                if (timestamp == 1)
                {
                    // ok we got an accept
                    g_message ("Got accept!");
                    FARSIGHT_PROTOCOL_CLASS (parent_class)->connected 
                        (FARSIGHT_PROTOCOL (yahoowebcam));

                    return FALSE;
                }
                else
                {
                    // refused
                    g_message ("Refused!");
                    farsight_protocol_disconnect 
                        (FARSIGHT_PROTOCOL(yahoowebcam));
                    return FALSE;
                }
            }
        }
        return TRUE;
    }
    return FALSE;
}

// Called when connection to server established
static gboolean 
connected_to_server_cb (GIOChannel *ch, GIOCondition cond, gpointer cb_data)
{
    FarsightYahooWebcam *yahoowebcam = FARSIGHT_YAHOOWEBCAM (cb_data);
    gint error;
    gint fd = g_io_channel_unix_get_fd (ch);
    gchar *data = NULL;
    gchar *packet = NULL;
    guchar magic_nr[] = {1, 0, 0, 0, 1};
    unsigned header_len = 0;
    guint len = 0;
    guint pos = 0;

    g_message ("handler called on fd %d", fd);

    errno = 0;
    if (!((cond & G_IO_IN) || (cond & G_IO_OUT)))
    {
        g_message ("Condition received is %d", cond);
        goto error;
    }

    len = sizeof(error);

    /* Get the error option */
    if (getsockopt(fd, SOL_SOCKET, SO_ERROR, (void*) &error, &len) < 0)
    {
        g_warning ("getsockopt() failed");
        goto error;
    }

    /* Check if there is an error */
    if (error)
    {
        g_message ("getsockopt gave an error : %d", error);
        goto error;
    }

    /* Remove NON BLOCKING MODE */
    if (fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) & ~O_NONBLOCK) != 0)
    {
        g_warning ("fcntl() failed");
        goto error;
    }

    g_message ("Got connection to server on fd %d", fd);
    // let's initiate this session now
    /* send initial packet */
    if (yahoowebcam->session_type == DOWNLOADER)
    {
        data = g_strdup("<REQIMG>");
    } else {
        data = g_strdup("<SNDIMG>");
    }
    if (write(fd, data, strlen(data)) == -1)
    {
        g_free(data);
        goto error;
    }
    g_free(data);

    /* send data */
    if (yahoowebcam->session_type == DOWNLOADER)
    {
        header_len = 8;
        data = g_strdup_printf(
                "a=2\r\nc=us\r\ne=21\r\nu=%s\r\nt=%s\r\ni=%s\r\ng=%s\r\no=w-2-5-1\r\np=%d\r\n",
                yahoowebcam->local_user, 
                yahoowebcam->server_key, 
                yahoowebcam->local_ip, 
                yahoowebcam->remote_user, 
                yahoowebcam->conn_type);
    } else {
        header_len = 13;
        data = g_strdup_printf("a=2\r\nc=us\r\nu=%s\r\nt=%s\r\ni=%s\r\no=w-2-5-1\r\np=%d\r\nb=%s\r\nd=\r\n",
                yahoowebcam->local_user, 
                yahoowebcam->server_key, 
                yahoowebcam->local_ip, 
                yahoowebcam->conn_type,
                "Farsight");
    }

    len = strlen(data);
    packet = g_new0(char, header_len + len);
    packet[pos++] = header_len;
    packet[pos++] = 0;
    if (yahoowebcam->session_type == DOWNLOADER)
    {
        packet[pos++] = 1;
        packet[pos++] = 0;
    } else {
        packet[pos++] = 5;
        packet[pos++] = 0;
    }

    pos += yahoo_put32(packet + pos, len);
    if (yahoowebcam->session_type == UPLOADER)
    {
        memcpy(packet + pos, magic_nr, sizeof(magic_nr));
        pos += sizeof(magic_nr);
    }
    memcpy(packet + pos, data, len);
    if (write(fd, packet, header_len + len) == -1)
    {

        g_free(packet);
        g_free(data);
        goto error;
    }
    g_free(packet);
    g_free(data);

    yahoowebcam->adapter = gst_adapter_new();
    yahoowebcam->first = TRUE;

    yahoowebcam->have_header = FALSE;

    // add a watch on that io for when it connects
    yahoowebcam->connect_watch = g_io_add_watch (yahoowebcam->connection, 
            (G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL), 
            verify_reply_from_server, yahoowebcam);

    return FALSE;

    /* Error */
error:
    g_message ("Got error on server from fd %d, closing", fd);
    // find, shutdown and remove channel from fdlist
    g_source_remove (yahoowebcam->connect_watch);
    g_io_channel_shutdown (yahoowebcam->connection, TRUE, NULL);
    g_io_channel_unref (yahoowebcam->connection);
    farsight_protocol_disconnect 
        (FARSIGHT_PROTOCOL(yahoowebcam));

    return FALSE;
}

static void
farsight_yahoowebcam_connect_to_server (FarsightYahooWebcam *yahoowebcam)
{
    gint fd = -1;
    struct sockaddr_in theiraddr;
    memset(&theiraddr, 0, sizeof(theiraddr));

    if ( (fd = socket(PF_INET, SOCK_STREAM, 0)) == -1 )
    {
        // show error
        g_message ("could not create socket!");
        return;
    }

    yahoowebcam->connection = g_io_channel_unix_new (fd);
    g_io_channel_set_close_on_unref (yahoowebcam->connection, TRUE);

    // set non-blocking mode
    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);

    theiraddr.sin_family = AF_INET;
    theiraddr.sin_addr.s_addr = inet_addr (yahoowebcam->server);
    theiraddr.sin_port = htons (YAHOO_WEBCAM_SERVER_PORT);

    g_message ("Attempting connection to yahoo webcam server : %s", yahoowebcam->server);
    // this is non blocking, the return value isn't too usefull
    gint ret = connect (fd, (struct sockaddr *) &theiraddr, sizeof (theiraddr));
    if (ret < 0)
    {
        if (errno != EINPROGRESS)
        {
            g_io_channel_shutdown (yahoowebcam->connection, TRUE, NULL);
            g_io_channel_unref (yahoowebcam->connection);
            return;
        }
    }

    // add a watch on that io for when it connects
    yahoowebcam->connect_watch = g_io_add_watch (yahoowebcam->connection, 
            (G_IO_IN|G_IO_OUT|G_IO_PRI|G_IO_ERR|G_IO_HUP|G_IO_NVAL), 
            connected_to_server_cb, yahoowebcam);
}

static void
farsight_yahoowebcam_disconnect (FarsightProtocol *protocol)
{
    FarsightYahooWebcam *yahoowebcam;

    g_return_if_fail (protocol != NULL);

    yahoowebcam = FARSIGHT_YAHOOWEBCAM (protocol);

    g_message ("Disconnecting");
    // should probably send a bye or something first
    // will know after some sniffing
    if (yahoowebcam->connection)
    {
        g_io_channel_shutdown (yahoowebcam->connection, TRUE, NULL);
        g_io_channel_unref (yahoowebcam->connection);
    }
}

static GstElement*
farsight_yahoowebcam_create_bin (FarsightProtocol *protocol)
{
    FarsightYahooWebcam *yahoowebcam;
    GstElement *bin;

    g_return_val_if_fail (protocol != NULL, NULL);

    yahoowebcam = FARSIGHT_YAHOOWEBCAM (protocol);

    gint fd = g_io_channel_unix_get_fd (yahoowebcam->connection);

    fcntl(fd, F_SETFL, fcntl(fd, F_GETFL) | O_NONBLOCK);

    /* verify the caps and create the bin */
    if (yahoowebcam->session_type == DOWNLOADER)
    {
        g_message ("Creating downloader bin on fd %d", fd);
        bin = gst_bin_new ("yahoowebcam_receive");
        GstElement *fdsrc = gst_element_factory_make ("fdsrc", "fdsrc");
        GstElement *jpeg2kdec = gst_element_factory_make ("jpeg2000dec", "jpeg2000dec");
        GstElement *yahooparse = gst_element_factory_make ("yahooparse", "yahooparse");

        g_object_set (G_OBJECT(fdsrc), "fd", fd, NULL);
        //g_object_set (G_OBJECT(fdsrc), "blocksize", 1024, NULL);

        gst_bin_add_many (GST_BIN(bin), fdsrc, jpeg2kdec, yahooparse, NULL);
        gst_element_link_pads (fdsrc, "src", yahooparse, "sink");
        gst_element_link_pads (yahooparse, "src", jpeg2kdec, "sink");

        gst_element_add_pad (bin,
            gst_ghost_pad_new("videosrc",
                gst_element_get_pad (jpeg2kdec, "src")));
    }
    else
    {
        bin = gst_bin_new ("yahoowebcam_send");
        g_message ("Creating uploader bin on fd %d", fd);
        GstElement *fdsink = gst_element_factory_make ("multifdsink", "fdsinkelement");
        GstElement *jpeg2kenc = gst_element_factory_make ("jpeg2000enc", "jpeg2000enc");
        GstElement *yahooenc = gst_element_factory_make ("yahooenc", "yahooenc");

        gst_bin_add_many (GST_BIN(bin), fdsink, jpeg2kenc, yahooenc, NULL);
        gst_element_link_pads (jpeg2kenc, "src", yahooenc, "sink");
        gst_element_link_pads (yahooenc, "src", fdsink, "sink");

        gst_element_add_pad (bin,
            gst_ghost_pad_new("videosink",
                gst_element_get_pad (jpeg2kenc, "sink")));

        g_signal_emit_by_name(G_OBJECT(fdsink), "add", fd);
    }

    return bin;
}

static void
farsight_yahoowebcam_set_property (GObject      *object, 
                           guint         prop_id,
                           const GValue *value, 
                           GParamSpec   *pspec)
{
    FarsightYahooWebcam *yahoowebcam;

    g_return_if_fail (FARSIGHT_IS_YAHOOWEBCAM (object));

    yahoowebcam = FARSIGHT_YAHOOWEBCAM (object);

    switch (prop_id) {
        case ARG_SESSION_TYPE:
            yahoowebcam->session_type = g_value_get_uint (value);
            break;
        case ARG_REMOTE_USER:
            if (yahoowebcam->remote_user)
                g_free (yahoowebcam->remote_user);
            yahoowebcam->remote_user = g_value_dup_string (value);
            break;
        case ARG_LOCAL_USER:
            if (yahoowebcam->local_user)
                g_free (yahoowebcam->local_user);
            yahoowebcam->local_user = g_value_dup_string (value);
            break;
         case ARG_SERVER_KEY:
            if (yahoowebcam->server_key)
                g_free (yahoowebcam->server_key);
            yahoowebcam->server_key = g_value_dup_string (value);
            break;
        case ARG_LOCAL_IP:
            if (yahoowebcam->local_ip)
                g_free (yahoowebcam->local_ip);
            yahoowebcam->local_ip = g_value_dup_string (value);
            break;
        case ARG_CONN_TYPE:
            yahoowebcam->conn_type = g_value_get_uint (value);
            break;
    }
}

static void
farsight_yahoowebcam_get_property (GObject    *object, 
                           guint       prop_id, 
                           GValue     *value,
                           GParamSpec *pspec)
{
    FarsightYahooWebcam *yahoowebcam;

    g_return_if_fail (FARSIGHT_IS_YAHOOWEBCAM (object));

    yahoowebcam = FARSIGHT_YAHOOWEBCAM (object);

    switch (prop_id) {
        case ARG_SESSION_TYPE:
            g_value_set_uint (value, yahoowebcam->session_type);
            break;
        case ARG_REMOTE_USER:
            g_value_set_string (value, yahoowebcam->remote_user);
            break;
        case ARG_LOCAL_USER:
            g_value_set_string (value, yahoowebcam->local_user);
            break;
        case ARG_SERVER_KEY:
            g_value_set_string (value, yahoowebcam->server_key);
            break;
        case ARG_LOCAL_IP:
            g_value_set_string (value, yahoowebcam->local_ip);
            break;
        case ARG_CONN_TYPE:
            g_value_set_uint (value, yahoowebcam->conn_type);
            break;
    }
}

static gboolean 
init_plugin (FarsightPlugin *plugin)
{
    if (!farsight_protocol_register (plugin, FARSIGHT_TYPE_YAHOOWEBCAM))
        return FALSE;
    return TRUE;
}

static FarsightPluginInfo plugin_info = {
    FARSIGHT_MAJOR_VERSION,
    FARSIGHT_MINOR_VERSION,

    "Yahoo Webcam Protocol",                                /* description */
    "0.1.0",                                            /* version */
    "Farsight Project",  /* author */
    "http://farsight.sf.net/",                          /* homepage */
    NULL,                                               /* load */
    NULL                                                /* unload */
};

FARSIGHT_INIT_PLUGIN (init_plugin, plugin_info);

Generated by  Doxygen 1.6.0   Back to index