morphbr@534: /*
morphbr@534:  * @author Artur Duque de Souza <souza.artur@indt.org.br>
morphbr@534:  *
morphbr@534:  * This program is free software; you can redistribute it and/or modify
morphbr@534:  * it under the terms of the GNU Lesser General Public License as published by
morphbr@534:  * the Free Software Foundation; either version 2 of the License, or
morphbr@534:  * (at your option) any later version.
morphbr@534:  *
morphbr@534:  * This program is distributed in the hope that it will be useful,
morphbr@534:  * but WITHOUT ANY WARRANTY; without even the implied warranty of
morphbr@534:  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
morphbr@534:  * GNU General Public License for more details.
morphbr@534:  *
morphbr@534:  m * You should have received a copy of the GNU Lesser General Public License
morphbr@534:  * along with this program; if not, write to the Free Software
morphbr@534:  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
morphbr@534:  */
morphbr@534: 
morphbr@534: #ifdef HAVE_CONFIG_H
morphbr@534: #include <config.h>
morphbr@534: #endif
morphbr@534: 
morphbr@534: #include <sys/socket.h> /* for socket(), connect(), send(), and recv() */
morphbr@534: #include <arpa/inet.h>  /* for sockaddr_in and inet_addr() */
morphbr@534: #include <stdlib.h>     /* for atoi() and exit() */
morphbr@534: #include <string.h>     /* for memset() */
morphbr@534: #include <unistd.h>     /* for close() */
morphbr@534: #include <errno.h>
morphbr@534: 
morphbr@534: #include <glib.h>
morphbr@534: #include <glib/gprintf.h>
morphbr@534: #include <glib/gstdio.h>
morphbr@534: #include <gmyth-stream-client.h>
morphbr@534: 
morphbr@534: #include <libgnomevfs/gnome-vfs-module.h>
morphbr@534: #include <libgnomevfs/gnome-vfs-utils.h>
morphbr@534: 
morphbr@534: #define BUFFER_SIZE 4096
morphbr@534: 
morphbr@534: typedef struct {
morphbr@534:     gint port;
morphbr@534:     gchar* hostname;
morphbr@534: 
morphbr@534:     GMythStreamClient *stream;
morphbr@534:     gint fd;
morphbr@534: } gmsHandle;
morphbr@534: 
morphbr@534: typedef struct {
morphbr@534:     gchar *file_name;
morphbr@534:     gchar *mux;
morphbr@534:     gchar *vcodec;
morphbr@534:     guint vbitrate;
morphbr@534:     gdouble fps;
morphbr@534:     gchar *acodec;
morphbr@534:     guint abitrate;
morphbr@534:     guint width;
morphbr@534:     guint height;
morphbr@534:     guint port;
morphbr@534:     gchar *opt;
morphbr@534: } UriArgs;
morphbr@534: 
morphbr@534: 
morphbr@534: static gmsHandle* gmsHandle_new(GnomeVFSURI *uri);
morphbr@534: 
morphbr@534: static GnomeVFSResult
morphbr@534: do_open (GnomeVFSMethod        *method,
morphbr@534: 	 GnomeVFSMethodHandle **method_handle,
morphbr@534: 	 GnomeVFSURI           *uri,
morphbr@534: 	 GnomeVFSOpenMode       mode,
morphbr@534: 	 GnomeVFSContext       *context);
morphbr@534: 
morphbr@534: static GnomeVFSResult
morphbr@534: do_read (GnomeVFSMethod       *method,
morphbr@534: 	 GnomeVFSMethodHandle *method_handle,
morphbr@534: 	 gpointer              buffer,
morphbr@534: 	 GnomeVFSFileSize      bytes,
morphbr@534: 	 GnomeVFSFileSize     *bytes_read,
morphbr@534: 	 GnomeVFSContext      *context);
morphbr@534: 
morphbr@534: static GnomeVFSResult
morphbr@534: do_close (GnomeVFSMethod * method,
morphbr@534:           GnomeVFSMethodHandle * method_handle,
morphbr@534:           GnomeVFSContext * context);
morphbr@534: 
morphbr@534: static GnomeVFSResult
morphbr@534: do_get_file_info (GnomeVFSMethod * method,
morphbr@534: 		  GnomeVFSURI * uri,
morphbr@534: 		  GnomeVFSFileInfo * file_info,
morphbr@534: 		  GnomeVFSFileInfoOptions options,
morphbr@534: 		  GnomeVFSContext * context);
morphbr@534: 
morphbr@534: 
morphbr@534: static GnomeVFSResult
morphbr@534: do_get_file_info_from_handle (GnomeVFSMethod 		*method,
morphbr@534:                               GnomeVFSMethodHandle 	*method_handle,
morphbr@534:                               GnomeVFSFileInfo 		*file_info,
morphbr@534:                               GnomeVFSFileInfoOptions    options,
morphbr@534:                               GnomeVFSContext 		*context);
morphbr@534: 
morphbr@534: 
morphbr@534: static gboolean
morphbr@534: do_is_local (GnomeVFSMethod * method, const GnomeVFSURI * uri);
morphbr@534: 
morphbr@534: 
morphbr@534: static gmsHandle* gmsHandle_new(GnomeVFSURI *uri)
morphbr@534: {
morphbr@534:     gmsHandle* handler = (gmsHandle*)g_malloc0(sizeof(gmsHandle));
morphbr@534: 
morphbr@534:     handler->hostname = (gchar*)gnome_vfs_uri_get_host_name(uri);
morphbr@534:     handler->port = gnome_vfs_uri_get_host_port(uri);
morphbr@534:     handler->stream = gmyth_stream_client_new ();
morphbr@534: 
morphbr@534:     return handler;
morphbr@534: }
morphbr@534: 
morphbr@534: static GnomeVFSMethod method = {
morphbr@534:     sizeof (GnomeVFSMethod),
morphbr@534:     do_open,                       /* open */
morphbr@534:     NULL,                          /* create */
morphbr@534:     do_close,                      /* close */
morphbr@534:     do_read,                       /* read */
morphbr@534:     NULL,                          /* write */
morphbr@534:     NULL,                          /* seek */
morphbr@534:     NULL,                          /* tell */
morphbr@534:     NULL,                          /* truncate_handle */
morphbr@534:     NULL,                          /* open_directory */
morphbr@534:     NULL,                          /* close_directory */
morphbr@534:     NULL,                          /* read_directory */
morphbr@534:     do_get_file_info,              /* get_file_info */
morphbr@534:     do_get_file_info_from_handle,  /* get_file_info_from_handle */
morphbr@534:     do_is_local,                   /* is_local */
morphbr@534:     NULL,                          /* make_directory */
morphbr@534:     NULL,                          /* remove_directory */
morphbr@534:     NULL,                          /* move */
morphbr@534:     NULL,                          /* unlink */
morphbr@534:     NULL,                          /* check_same_fs */
morphbr@534:     NULL,                          /* set_file_info */
morphbr@534:     NULL,                          /* truncate */
morphbr@534:     NULL,                          /* find_directory */
morphbr@534:     NULL,                          /* create_symbolic_link */
morphbr@534:     NULL,                          /* monitor_add */
morphbr@534:     NULL,                          /* monitor_cancel */
morphbr@534:     NULL                           /* file_control */
morphbr@534: };
morphbr@534: 
morphbr@534: GnomeVFSMethod *
morphbr@534: vfs_module_init (const char *method_name, const char *args)
morphbr@534: {
morphbr@534:     return &method;
morphbr@534: }
morphbr@534: 
morphbr@534: void
morphbr@534: vfs_module_shutdown (GnomeVFSMethod* method)
morphbr@534: {
morphbr@534:     return;
morphbr@534: }
morphbr@534: 
morphbr@544: char* _parse_opt(char* opt)
morphbr@544: {
morphbr@544:     char** list = g_strsplit(opt, "opt=", 2);
morphbr@544:     char** opts = g_strsplit(list[1], "+", 32);
morphbr@544:     char* value = "";
morphbr@544:     char* aux;
morphbr@544:     gint i = 0;
morphbr@544: 
morphbr@544:     for (aux = opts[0]; aux != NULL; aux = opts[++i])
morphbr@544:         value = g_strdup_printf("%s %s", value, aux);
morphbr@544: 
morphbr@544:     g_free(aux);
morphbr@544:     g_strfreev(list);
morphbr@544:     g_strfreev(opts);
morphbr@544:     return value;
morphbr@544: }
morphbr@544: 
morphbr@534: static UriArgs *
morphbr@534: _uri_parse_args (const GnomeVFSURI *uri)
morphbr@534: {
morphbr@544:     gchar *file = gnome_vfs_unescape_string (uri->text, NULL);
renatofilho@538: 
morphbr@544:     gchar **list = g_strsplit(file, "\'", 3);
morphbr@544:     gchar **prefix = g_strsplit_set(list[0], "/=", 3);
morphbr@544:     gchar **lst = g_strsplit (list[2], "?", 0);
morphbr@544: 
morphbr@534:     UriArgs *info = g_new0 (UriArgs, 1);
morphbr@544:     gint i = 1;
morphbr@534: 
morphbr@544:     gchar** prop = NULL;
morphbr@544:     prop = g_strsplit_set(lst[0], "/=", 3);
morphbr@534: 
morphbr@544:     info->file_name = g_strdup_printf ("%s://%s",\
morphbr@544:                                        prefix[1],list[1]);
morphbr@534: 
morphbr@544:     //g_debug("FILENAME: [%s]", info->file_name);
renatofilho@538: 
morphbr@544:     g_strfreev(prop);
morphbr@544: 
morphbr@544:     gchar* walk;
morphbr@544:     for (walk = lst[1]; walk != NULL; walk = lst[++i])
morphbr@534:     {
morphbr@544:         prop = g_strsplit(walk, "=", 2);
morphbr@534: 
morphbr@534:         if (g_strv_length (prop) == 2) {
morphbr@544:             if (strcmp (prop[0], "mux") == 0) {
morphbr@534:                 info->mux = g_strdup (prop[1]);
morphbr@534:             } else if (strcmp (prop[0], "vcodec") == 0) {
morphbr@534:                 info->vcodec = g_strdup (prop[1]);
morphbr@534:             } else if (strcmp (prop[0], "vbitrate") == 0) {
morphbr@534:                 info->vbitrate = atoi (prop[1]);
morphbr@534:             } else if (strcmp (prop[0], "fps") == 0) {
morphbr@534:                 info->fps = g_strtod (prop[1], NULL);
morphbr@534:             } else if (strcmp (prop[0], "acodec") == 0) {
morphbr@534:                 info->acodec = g_strdup (prop[1]);
morphbr@534:             } else if (strcmp (prop[0], "abitrate") == 0) {
morphbr@534:                 info->abitrate = atoi (prop[1]);
morphbr@534:             } else if (strcmp (prop[0], "width") == 0) {
morphbr@534:                 info->width = atoi (prop[1]);
morphbr@534:             } else if (strcmp (prop[0], "height") == 0) {
morphbr@534:                 info->height = atoi (prop[1]);
morphbr@534:             } else if (strcmp (prop[0], "opt") == 0) {
morphbr@544:                 info->opt = g_strdup (_parse_opt(walk));
morphbr@534:             }
renatofilho@538:         }
morphbr@534:         g_strfreev (prop);
morphbr@534:     }
morphbr@544: 
morphbr@534:     g_free (file);
morphbr@544:     g_strfreev (list);
morphbr@544:     g_strfreev (prefix);
morphbr@544:     g_strfreev (lst);
morphbr@534:     return info;
morphbr@534: }
morphbr@534: 
morphbr@534: static GnomeVFSResult
morphbr@534: do_open (GnomeVFSMethod        *method,
morphbr@534: 	 GnomeVFSMethodHandle **method_handle,
morphbr@534: 	 GnomeVFSURI           *uri,
morphbr@534: 	 GnomeVFSOpenMode       mode,
morphbr@534: 	 GnomeVFSContext       *context)
morphbr@534: {
morphbr@534:     gmsHandle *handle = gmsHandle_new(uri);
morphbr@534:     UriArgs *args;
morphbr@534: 
morphbr@534:     if (!gmyth_stream_client_connect (handle->stream,
morphbr@534:                                       gnome_vfs_uri_get_host_name  (uri),
morphbr@534:                                       gnome_vfs_uri_get_host_port  (uri))) {
morphbr@534: 
renatofilho@556:         return GNOME_VFS_ERROR_HOST_NOT_FOUND;
morphbr@534:     }
morphbr@534: 
morphbr@534:     args = _uri_parse_args (uri);
morphbr@534: 
morphbr@534:     gint ret = gmyth_stream_client_open_stream (handle->stream,
morphbr@534:                                                 args->file_name,
morphbr@534:                                                 args->mux,
morphbr@534:                                                 args->vcodec,
morphbr@534:                                                 args->vbitrate,
morphbr@534:                                                 args->fps,
morphbr@534:                                                 args->acodec,
morphbr@534:                                                 args->abitrate,
morphbr@534:                                                 args->width,
morphbr@534:                                                 args->height,
morphbr@534:                                                 args->opt);
morphbr@534: 
morphbr@534:     g_free (args);
morphbr@534: 
morphbr@534:     if (ret == -1) {
morphbr@534:         gmyth_stream_client_disconnect (handle->stream);
renatofilho@556:         return GNOME_VFS_ERROR_HOST_NOT_FOUND;
morphbr@534:     }
morphbr@534: 
morphbr@534:     handle->fd = gmyth_stream_client_play_stream (handle->stream);
morphbr@534: 
morphbr@534:     if (handle->fd == -1) {
morphbr@534:         gmyth_stream_client_disconnect (handle->stream);
renatofilho@556:         return GNOME_VFS_ERROR_HOST_NOT_FOUND;
morphbr@534:     }
morphbr@534: 
morphbr@534:     *method_handle = (GnomeVFSMethodHandle *) handle;
morphbr@534:     return GNOME_VFS_OK;
morphbr@534: }
morphbr@534: 
morphbr@534: static GnomeVFSResult
morphbr@534: do_read (GnomeVFSMethod       *method,
morphbr@534: 	 GnomeVFSMethodHandle *method_handle,
morphbr@534: 	 gpointer              buffer,
morphbr@534: 	 GnomeVFSFileSize      bytes,
morphbr@534: 	 GnomeVFSFileSize     *bytes_read,
morphbr@534: 	 GnomeVFSContext      *context)
morphbr@534: {
morphbr@534: 
morphbr@534:     gint64 total_read = 0;
morphbr@534:     gmsHandle *handle = (gmsHandle *) method_handle;
morphbr@534: 
morphbr@544:     //g_debug("waiting something");
morphbr@544: 
morphbr@534:     total_read = recv(handle->fd, buffer, BUFFER_SIZE, 0);
morphbr@544:     //g_debug("COULD READ: %d bytes", total_read);
morphbr@534:     *bytes_read = (GnomeVFSFileSize) total_read;
morphbr@534: 
morphbr@544:     //if (total_read < 0) g_debug("ERROR!!!!!!!!!!!!!!!!");
morphbr@544: 
morphbr@534:     if (total_read < 0) return GNOME_VFS_ERROR_INTERNAL;
morphbr@534:     else return GNOME_VFS_OK;
morphbr@534: 
morphbr@534: }
morphbr@534: 
morphbr@534: static GnomeVFSResult
morphbr@534: do_close (GnomeVFSMethod * method,
morphbr@534:           GnomeVFSMethodHandle * method_handle,
morphbr@534:           GnomeVFSContext * context)
morphbr@534: {
morphbr@534:     gmsHandle *handle = (gmsHandle *) method_handle;
morphbr@534: 
morphbr@544:     g_debug("close close close");
morphbr@544: 
morphbr@534:     gmyth_stream_client_close_stream (handle->stream);
morphbr@534:     gmyth_stream_client_disconnect (handle->stream);
morphbr@534: 
morphbr@534:     g_free(handle);
morphbr@534:     return GNOME_VFS_OK;
morphbr@534: }
morphbr@534: 
morphbr@534: 
morphbr@534: static GnomeVFSResult
morphbr@534: do_get_file_info (GnomeVFSMethod * method,
morphbr@534: 		  GnomeVFSURI * uri,
morphbr@534: 		  GnomeVFSFileInfo * file_info,
morphbr@534: 		  GnomeVFSFileInfoOptions options,
morphbr@534: 		  GnomeVFSContext * context)
morphbr@534: {
morphbr@534:     file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_TYPE |
morphbr@534:         GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS;
morphbr@534: 
morphbr@534:     file_info->type = GNOME_VFS_FILE_TYPE_SOCKET;
morphbr@534: 
morphbr@534:     file_info->permissions = GNOME_VFS_PERM_USER_READ |
morphbr@534:         GNOME_VFS_PERM_OTHER_READ |
morphbr@534:         GNOME_VFS_PERM_GROUP_READ;
morphbr@534: 
morphbr@534:     return GNOME_VFS_OK;
morphbr@534: }
morphbr@534: 
morphbr@534: 
morphbr@534: static GnomeVFSResult
morphbr@534: do_get_file_info_from_handle (GnomeVFSMethod 		*method,
morphbr@534:                               GnomeVFSMethodHandle 	*method_handle,
morphbr@534:                               GnomeVFSFileInfo 		*file_info,
morphbr@534:                               GnomeVFSFileInfoOptions    options,
morphbr@534:                               GnomeVFSContext 		*context)
morphbr@534: {
morphbr@534:     file_info->valid_fields = GNOME_VFS_FILE_INFO_FIELDS_TYPE |
morphbr@534:         GNOME_VFS_FILE_INFO_FIELDS_PERMISSIONS;
morphbr@534: 
morphbr@534:     file_info->type = GNOME_VFS_FILE_TYPE_SOCKET;
morphbr@534: 
morphbr@534:     file_info->permissions = GNOME_VFS_PERM_USER_READ |
morphbr@534:         GNOME_VFS_PERM_OTHER_READ |
morphbr@534:         GNOME_VFS_PERM_GROUP_READ;
morphbr@534: 
morphbr@534:     return GNOME_VFS_OK;
morphbr@534: }
morphbr@534: 
morphbr@534: 
morphbr@534: static gboolean
morphbr@534: do_is_local (GnomeVFSMethod * method, const GnomeVFSURI * uri)
morphbr@534: {
morphbr@534:     return FALSE;
morphbr@534: }