	Opening viewers on behalf of other viewers

	Applets may use the browser's services to open a sub-viewer. A
	sub-viewer is a viewer for which the browser does not provide
	the screen space and which is not closed automatically by the
	browser.

	However, the browser has to maintain a table of associations
	between viewer IDs and viewer descriptions, because it is the
	task of the browser to invoke the appropriate viewer
	functions.

	The functions involved are: [[W3AopenView]], [[W3AwriteView]],
	[[W3AcloseView]], and [[W3Asubprocess]].

	The implementation uses a linked list of [[ViewerAssoc]]
	structures, each with an ID and a pointer to the concrete
	viewer's functions.

<<*>>=
#include "config.h"
#include <Xm/Xm.h>
#include "w3a.h"
#include <errno.h>
#include <fcntl.h>
#include "str.h"
#include "url.h"
#include "w3alib.h"
#include "rcfile.e"
#include "globals.e"
#include "dynload.e"
#include "requests.e"
#include "input.e"
#include "cache.e"


/* check_url -- expand a relative URL, FALSE if it fails */
EXPORT Bool check_url(W3ADocumentInfo *doc, URI *uri)
{
    URI base;

    URL_clean(doc->url);
    if (! URL_parse(doc->url, uri)) return FALSE;
    if (uri->tp == URI_Rel) {
	if (! doc->referer || ! URL_parse(doc->referer, &base)) return FALSE;
	URL_expand(uri, base);
	dispose(doc->url);
	doc->url = uri2str(*uri);
    }
    return uri->tp == URI_URL;
}


EXPORT long W3AopenView(const W3ADocumentInfo doc, W3AWindow area)
{
    ViewerInfo *info;
    long id;
    int v;

    v = find_viewer(doc.mime_type);		/* Find and load a viewer */
    if (v == -1) {errno = ETYPE; return -1;}	/* No appropriate viewer */
    info = get_request_rec();			/* Alloc. admin. data */
    info->viewer = v;				/* Viewer class */
    if (! viewer_open(v, doc, area, (long) info)) {
	free_request_rec(info);
	return -1;
    }
    return (long) info;
}


EXPORT int W3AwriteView(long id, const char *buf, size_t nbytes)
{
    ViewerInfo *v = (ViewerInfo *) id;

    return viewer_write(v->viewer, (long) v, buf, nbytes);
}


EXPORT Bool W3AcloseView(long id)
{
    return free_request_rec((ViewerInfo *) id);
}


EXPORT long W3Asubprocess(W3ADocumentInfo *doc, int method, const
			  char *data, size_t nbytes, W3AWindow area)
{
    URI uri;
    int a = -1;
    Bool use_cache, ok = TRUE;
    int fd = -1, n;
    XtInputId input_id;
    ViewerInfo *info = NULL;
    long id = 0;
    const char *where;
    char *url;
    size_t howmuch;

    /* Check and maybe expand URL */
    if (! check_url(doc, &uri)) {errno = EURL; return FALSE;}
    url = newstring(doc->url);

    if (method != GET_METHOD) {
	a = find_agent(strip2str(uri.scheme));
	if (a == -1) {errno = ETYPE; goto fail;}
	if (method == DELETE_METHOD)
	    if (agent_delete(a, url)) goto success; else goto fail;
	if (method == PUT_METHOD || method == POST_METHOD) {
	    /*
	     * For POST, the connection is made immediately,
	     * without waiting for other connections to close.
	     * The request queue in `input.c' will notice
	     * that fd >= 0 and will use the already opened
	     * connection.
	     */
	    fd = agent_open(a, doc->url, method, O_NONBLOCK, doc->referer);
	    if (fd == -1) goto fail;
	    while (nbytes > 0) {
		int n;
		n = agent_write(a, fd, data, nbytes);
		if (n == -1 && errno != EAGAIN) goto fail;
		data += n;
		nbytes -= n;
	    }
	}
    }

    info = get_request_rec();
    info->doc = new_doc();
    info->method = method;
    copy_doc(info->doc, *doc);
    info->agent = a;
    info->fd = fd;
    info->viewer = -1;				/* No viewer yet */
    info->seqnr = seqnr++;			/* Unique request ID */
    info->total = 0;				/* Count of bytes read */
    info->high_prio = FALSE;
    info->w = area;

#if 0
    if (! use_cache) {
#endif
	/* Start request in the background */
	start_request(uri, info, FALSE);
#if 0
    } else {					/* Copy from the cache */
	info->viewer = find_viewer(doc->mime_type);
	if (! viewer_open(info->viewer, *doc, info->w, (long) info))
	    goto fail;
	if (viewer_write(info->viewer, (long) info, where, howmuch) != howmuch
	    || viewer_write(info->viewer, (long) info, NULL, 0) != 0)
	    goto fail;
    }
#endif

success:
    dispose(url);
    return (long) info;

fail:
    if (info) (void) free_request_rec(info);
    dispose(url);
    return -1;
}

