/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*-
*
* Authors: Michael Zucchi <notzed@ximian.com>
*
* Copyright 2004 Ximian, Inc. (www.ximian.com)
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of version 2 of the GNU General Public
* License as published by the Free Software Foundation.
*
* 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 General Public
* License along with this program; if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*
* A GnomeVFS to CamelStream mapper.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <libgnomevfs/gnome-vfs.h>
#include "em-vfs-stream.h"
#ifndef EINPROGRESS
#define EINPROGRESS EAGAIN
#endif
#ifndef ELOOP
#define ELOOP EIO
#endif
#define LOG_STREAM
#define d(x)
#define EMVS_CLASS(x) ((EMVFSStreamClass *)(((CamelObject *)(x))->klass))
static CamelStreamClass *parent_class = NULL;
static void
em_vfs_stream_init (CamelObject *object)
{
/*EMVFSStream *emvfs = (EMVFSStream *)object;*/
}
static void
em_vfs_stream_finalize (CamelObject *object)
{
EMVFSStream *emvfs = (EMVFSStream *)object;
if (emvfs->handle)
gnome_vfs_close(emvfs->handle);
}
static void
emvfs_set_errno(GnomeVFSResult res)
{
switch(res) {
case GNOME_VFS_OK:
g_warning("em-vfs-stream: calling set_errno with no error");
break;
case GNOME_VFS_ERROR_NOT_FOUND:
case GNOME_VFS_ERROR_HOST_NOT_FOUND:
case GNOME_VFS_ERROR_INVALID_HOST_NAME:
case GNOME_VFS_ERROR_HOST_HAS_NO_ADDRESS:
case GNOME_VFS_ERROR_SERVICE_NOT_AVAILABLE:
errno = ENOENT;
break;
case GNOME_VFS_ERROR_GENERIC:
case GNOME_VFS_ERROR_INTERNAL:
case GNOME_VFS_ERROR_IO:
case GNOME_VFS_ERROR_EOF: /* will be caught by read before here anyway */
case GNOME_VFS_ERROR_SERVICE_OBSOLETE:
case GNOME_VFS_ERROR_PROTOCOL_ERROR:
default:
errno = EIO;
break;
case GNOME_VFS_ERROR_BAD_PARAMETERS:
case GNOME_VFS_ERROR_NOT_SUPPORTED:
case GNOME_VFS_ERROR_INVALID_URI:
case GNOME_VFS_ERROR_NOT_OPEN:
case GNOME_VFS_ERROR_INVALID_OPEN_MODE:
case GNOME_VFS_ERROR_NOT_SAME_FILE_SYSTEM:
errno = EINVAL;
break;
case GNOME_VFS_ERROR_CORRUPTED_DATA: /* not sure about these */
case GNOME_VFS_ERROR_WRONG_FORMAT:
case GNOME_VFS_ERROR_BAD_FILE:
errno = EBADF;
break;
case GNOME_VFS_ERROR_TOO_BIG:
errno = E2BIG;
break;
case GNOME_VFS_ERROR_NO_SPACE:
errno = ENOSPC;
break;
case GNOME_VFS_ERROR_READ_ONLY:
case GNOME_VFS_ERROR_READ_ONLY_FILE_SYSTEM:
errno = EROFS;
break;
case GNOME_VFS_ERROR_TOO_MANY_OPEN_FILES:
errno = EMFILE;
break;
case GNOME_VFS_ERROR_NOT_A_DIRECTORY:
errno = ENOTDIR;
break;
case GNOME_VFS_ERROR_IN_PROGRESS:
errno = EINPROGRESS;
break;
case GNOME_VFS_ERROR_INTERRUPTED:
errno = EINTR;
break;
case GNOME_VFS_ERROR_FILE_EXISTS:
errno = EEXIST;
case GNOME_VFS_ERROR_LOOP:
errno = ELOOP;
break;
case GNOME_VFS_ERROR_ACCESS_DENIED:
case GNOME_VFS_ERROR_NOT_PERMITTED:
case GNOME_VFS_ERROR_LOGIN_FAILED:
errno = EPERM;
break;
case GNOME_VFS_ERROR_IS_DIRECTORY:
case GNOME_VFS_ERROR_DIRECTORY_NOT_EMPTY: /* ?? */
errno = EISDIR;
break;
case GNOME_VFS_ERROR_NO_MEMORY:
errno = ENOMEM;
break;
case GNOME_VFS_ERROR_CANCELLED:
errno = EINTR;
break;
case GNOME_VFS_ERROR_DIRECTORY_BUSY:
errno = EBUSY;
break;
case GNOME_VFS_ERROR_TOO_MANY_LINKS:
errno = EMLINK;
break;
case GNOME_VFS_ERROR_NAME_TOO_LONG:
errno = ENAMETOOLONG;
break;
}
}
static ssize_t
emvfs_read(CamelStream *stream, char *buffer, size_t n)
{
EMVFSStream *emvfs = EM_VFS_STREAM (stream);
GnomeVFSFileSize count;
GnomeVFSResult res;
if (emvfs->handle == NULL) {
errno = EINVAL;
return -1;
}
/* TODO: handle camel cancellation? */
res = gnome_vfs_read(emvfs->handle, buffer, n, &count);
if (res == GNOME_VFS_OK)
return (ssize_t)count;
else if (res == GNOME_VFS_ERROR_EOF) {
stream->eos = TRUE;
return 0;
}
emvfs_set_errno(res);
return -1;
}
static ssize_t
emvfs_write(CamelStream *stream, const char *buffer, size_t n)
{
EMVFSStream *emvfs = EM_VFS_STREAM (stream);
GnomeVFSFileSize count;
GnomeVFSResult res;
if (emvfs->handle == NULL) {
errno = EINVAL;
return -1;
}
res = gnome_vfs_write(emvfs->handle, buffer, n, &count);
if (res == GNOME_VFS_OK)
return (ssize_t)count;
emvfs_set_errno(res);
return -1;
}
static int
emvfs_close(CamelStream *stream)
{
EMVFSStream *emvfs = EM_VFS_STREAM (stream);
GnomeVFSResult res;
if (emvfs->handle == NULL) {
errno = EINVAL;
return -1;
}
res = gnome_vfs_close(emvfs->handle);
emvfs->handle = NULL;
if (res == GNOME_VFS_OK)
return 0;
emvfs_set_errno(res);
return -1;
}
static off_t
emvfs_seek(CamelSeekableStream *stream, off_t offset, CamelStreamSeekPolicy policy)
{
EMVFSStream *emvfs = EM_VFS_STREAM (stream);
GnomeVFSSeekPosition vpolicy;
GnomeVFSFileSize pos;
GnomeVFSResult res;
if (emvfs->handle == NULL) {
errno = EINVAL;
return -1;
}
switch (policy) {
case CAMEL_STREAM_SET:
default:
vpolicy = GNOME_VFS_SEEK_START;
break;
case CAMEL_STREAM_CUR:
vpolicy = GNOME_VFS_SEEK_CURRENT;
break;
case CAMEL_STREAM_END:
vpolicy = GNOME_VFS_SEEK_END;
break;
}
if ( (res = gnome_vfs_seek(emvfs->handle, vpolicy, offset)) == GNOME_VFS_OK
&& (res = gnome_vfs_tell(emvfs->handle, &pos)) == GNOME_VFS_OK)
return pos;
emvfs_set_errno(res);
return -1;
}
static off_t
emvfs_tell(CamelSeekableStream *stream)
{
EMVFSStream *emvfs = EM_VFS_STREAM (stream);
GnomeVFSFileSize pos;
GnomeVFSResult res;
if (emvfs->handle == NULL) {
errno = EINVAL;
return -1;
}
if ((res = gnome_vfs_tell(emvfs->handle, &pos)) == GNOME_VFS_OK)
return pos;
emvfs_set_errno(res);
return -1;
}
static void
em_vfs_stream_class_init (EMVFSStreamClass *klass)
{
((CamelStreamClass *)klass)->read = emvfs_read;
((CamelStreamClass *)klass)->write = emvfs_write;
((CamelStreamClass *)klass)->close = emvfs_close;
((CamelSeekableStreamClass *)klass)->seek = emvfs_seek;
((CamelSeekableStreamClass *)klass)->tell = emvfs_tell;
/* set_bounds? */
}
CamelType
em_vfs_stream_get_type (void)
{
static CamelType type = CAMEL_INVALID_TYPE;
if (type == CAMEL_INVALID_TYPE) {
parent_class = (CamelStreamClass *)camel_seekable_stream_get_type();
type = camel_type_register ((CamelType)parent_class,
"EMVFSStream",
sizeof (EMVFSStream),
sizeof (EMVFSStreamClass),
(CamelObjectClassInitFunc) em_vfs_stream_class_init,
NULL,
(CamelObjectInitFunc) em_vfs_stream_init,
(CamelObjectFinalizeFunc) em_vfs_stream_finalize);
}
return type;
}
/**
* emvfs_stream_new:
* @handle:
*
* Create a new camel stream from a GnomeVFS handle. The camel stream
* will own the handle from now on.
*
* Return value: A CamelStream that will talk to @handle. This function cannot fail.
**/
EMVFSStream *
emvfs_stream_new(GnomeVFSHandle *handle)
{
EMVFSStream *emvfs;
emvfs = (EMVFSStream *)camel_object_new(em_vfs_stream_get_type());
emvfs->handle = handle;
return emvfs;
}