/*
*
* (c) COPYRIGHT MIT and INRIA, 1996.
* Please first read the full copyright statement in file COPYRIGHT.
*
*/
/*
* query.c : contains all the functions for requesting and publishing
* URLs via libwww. It handles any eventual HTTP error code
* (redirection, authentication needed, not found, etc.)
*
* Author: J. Kahan
* J. Kahan/R. Guetari Windows 95/NT routines
*/
#ifndef AMAYA_JAVA
/* defines to include elsewhere
*********************************/
#ifndef _WINDOWS
#endif /* !_WINDOWS */
#define AMAYA_WWW_CACHE
#define AMAYA_LOST_UPDATE
#define CACHE_DIR_NAME DIR_STR"libwww-cache"
#define DEFAULT_CACHE_SIZE 5
#define DEFAULT_MAX_SOCKET 64
#define DEFAULT_DNS_TIMEOUT 60
#define DEFAULT_PERSIST_TIMEOUT 60L
#define DEFAULT_NET_EVENT_TIMEOUT 60000
/* Amaya includes */
#define THOT_EXPORT extern
#include "amaya.h"
#include <sys/types.h>
#include <fcntl.h>
#include "HTEvtLst.h"
#include "HTAABrow.h"
#if defined(__svr4__) || defined (_AIX)
#define CATCH_SIG
#endif
/* local structures coming from libwww and which are
not found in any .h file
*/
struct _HTStream
{
const HTStreamClass *isa;
FILE *fp;
BOOL leave_open; /* Close file when TtaFreeMemory? */
char *end_command; /* Command to execute */
BOOL remove_on_close; /* Remove file? */
char *filename; /* Name of file */
HTRequest *request; /* saved for callback */
HTRequestCallback *callback;
};
struct _HTError
{
HTErrorElement element; /* Index number into HTError */
HTSeverity severity; /* A la VMS */
BOOL ignore; /* YES if msg should not go to user */
void *par; /* Explanation, e.g. filename */
int length; /* For copying by generic routine */
char *where; /* Which function */
};
/* Type definitions and global variables etc. local to this module */
/*----------------------------------------------------------------------*/
/*** private variables ***/
static HTList *converters = NULL; /* List of global converters */
static HTList *acceptTypes = NULL; /* List of types for the Accept header */
static HTList *transfer_encodings = NULL;
static HTList *content_encodings = NULL;
static int object_counter = 0; /* loaded objects counter */
static boolean AmayaAlive_flag; /* set to 1 if the application is active;
0 if we have killed */
static boolean CanDoStop_flag; /* set to 1 if we can do a stop, 0
if we're inside a critical section */
#ifdef AMAYA_WWW_CACHE
static int fd_cachelock; /* open handle to the .lock cache file */
#endif /* AMAYA_WWW_CACHE */
#include "answer_f.h"
#include "query_f.h"
#include "AHTURLTools_f.h"
#include "AHTBridge_f.h"
#include "AHTMemConv_f.h"
#include "AHTFWrite_f.h"
/* prototypes */
#ifdef __STDC__
static void RecCleanCache (char *dirname);
#ifdef _WINDOWS
int WIN_Activate_Request (HTRequest* , HTAlertOpcode, int, const char*, void*, HTAlertPar*);
#endif /* _WINDOWS */
#else
static void RecCleanCache (/* char *dirname */);
#ifdef _WINDOWS
int WIN_Activate_Request (/* HTRequest* , HTAlertOpcode, int, const char*, void*, HTAlertPar* */);
#endif /* _WINDOWS */
#endif /* __STDC__ */
#ifdef AMAYA_WWW_CACHE
/***************************************************************
lock functions, used to avoid concurrent use of the cache
Mostly based on W. Richard Stevens' APUE book.
***************************************************************/
/*----------------------------------------------------------------------
set_cachelock
sets a write lock on filename.
Returns -1 in case of failure, otherwise, it'll open a handle on
filename and fd_cachelock will take its value.
----------------------------------------------------------------------*/
#ifdef __STDC__
static int set_cachelock (char *filename)
#else
static int set_cachelock (filename)
char *filename;
#endif /* __STDC__ */
{
#ifdef _WINDOWS
return 0;
#else
int status;
struct flock lock;
lock.l_type = F_WRLCK;
lock.l_start = 0;
lock.l_whence = SEEK_SET;
lock.l_len = 0;
fd_cachelock = open (filename, O_WRONLY);
if (fd_cachelock == -1)
status = -1;
else
status = fcntl(fd_cachelock, F_SETLK, &lock);
if (status == -1)
fd_cachelock = 0;
return (status);
#endif /* _WINDOWS */
}
/*----------------------------------------------------------------------
clear_cachelock
remove the write lock set on a filename.
It'll close the fd handle on filename and reset *fd to 0.
----------------------------------------------------------------------*/
#ifdef __STDC__
static int clear_cachelock (void)
#else
static int clear_cachelock ()
int *fd;
#endif /* __STDC__ */
{
#ifdef _WINDOWS
return 0;
#else
int status;
struct flock lock;
if (fd_cachelock)
return (-1);
lock.l_type = F_UNLCK;
lock.l_start = 0;
lock.l_whence = SEEK_SET;
lock.l_len = 0;
status = fcntl(fd_cachelock, F_SETLK, &lock);
close (fd_cachelock);
fd_cachelock = 0;
return (status);
#endif /* _WINDOWS */
}
/*----------------------------------------------------------------------
test_cachelock
returns 0 if a fd is not locked by other process, otherwise
returns the pid of the process who has the lock
----------------------------------------------------------------------*/
#ifdef __STDC__
static int test_cachelock (char *filename)
#else
static int test_cachelock (filename)
char *filename;
#endif __STDC__
{
#ifdef _WINDOWS
int fd;
fd = _open (filename, _O_WRONLY | _O_BINARY);
if (fd != -1)
{
_close (fd);
return 0;
}
else
return -1;
#else
struct flock lock;
int fd, status;
lock.l_type = F_WRLCK;
lock.l_start = 0;
lock.l_whence = SEEK_SET;
lock.l_len = 0;
fd = open (filename, O_WRONLY);
if (fd < 0)
return (-1);
/* is this file locked ? */
status = fcntl (fd, F_GETLK, &lock);
close (fd);
if (status < 0)
return (-1);
if (lock.l_type == F_UNLCK)
return (0); /* false, region is not locked by another proc */
return (lock.l_pid); /* true, return pid of lock owner */
#endif /* _WINDOWS */
}
#endif /* AMAYA_WWW_CACHE */
/*----------------------------------------------------------------------
GetDocIdStatus
gets the status associated to a docid
----------------------------------------------------------------------*/
#ifdef __STDC__
AHTDocId_Status *GetDocIdStatus (int docid, HTList * documents)
#else
AHTDocID_Status *GetDocIdStatus (docid, documents)
int docid;
HTList *documents;
#endif
{
AHTDocId_Status *me;
HTList *cur;
if (documents)
{
cur = documents;
while ((me = (AHTDocId_Status *) HTList_nextObject (cur)))
{
if (me->docid == docid)
return (me);
}
}
return (AHTDocId_Status *) NULL;
}
/*----------------------------------------------------------------------
AHTGuessAtom_for
Converts an Amaya type descriptor into the equivalent MIME type.
----------------------------------------------------------------------*/
#ifdef __STDC__
static HTAtom *AHTGuessAtom_for (char *urlName, PicType contentType)
#else
static HTAtom *AHTGuessAtom_for (urlName, contentType)
char *urlName;
PicType contentType;
#endif
{
HTAtom *atom;
char *filename;
HTEncoding enc = NULL;
HTEncoding cte = NULL;
HTLanguage lang = NULL;
double quality = 1.0;
switch (contentType)
{
case xbm_type:
atom = HTAtom_for("image/xbm");
break;
case eps_type:
atom = HTAtom_for("application/postscript");
break;
case xpm_type:
atom = HTAtom_for("image/xpm");
break;
case gif_type:
atom = HTAtom_for("image/gif");
break;
case jpeg_type:
atom = HTAtom_for("image/jpeg");
break;
case png_type:
atom = HTAtom_for("image/png");
break;
case unknown_type:
default:
/*
** Amaya could not detect the type, so
** we try to use the filename's suffix to do so.
*/
filename = AmayaParseUrl (urlName, "", AMAYA_PARSE_PATH | AMAYA_PARSE_PUNCTUATION);
HTBind_getFormat (filename, &atom, &enc, &cte, &lang, &quality);
TtaFreeMemory (filename);
if (atom == WWW_UNKNOWN)
/*
** we could not identify the suffix, so we assign it
** a default type
*/
atom = HTAtom_for ("text/html");
break;
}
return atom;
}
/*----------------------------------------------------------------------
AHTReqContext_new
create a new Amaya Context Object and update the global Amaya
request status.
----------------------------------------------------------------------*/
#ifdef __STDC__
static AHTReqContext *AHTReqContext_new (int docid)
#else
static AHTReqContext *AHTReqContext_new (docid)
int docid;
#endif
{
AHTReqContext *me;
AHTDocId_Status *docid_status;
if ((me = (AHTReqContext *) TtaGetMemory (sizeof (AHTReqContext))) == NULL)
outofmem (__FILE__, "AHTReqContext_new");
/* clear the structure */
memset ((void *) me, 0, sizeof (AHTReqContext));
/* Bind the Context object together with the Request Object */
me->request = HTRequest_new ();
/* clean the associated file structure) */
HTRequest_setOutputStream (me->request, NULL);
/* Initialize the other members of the structure */
me->reqStatus = HT_NEW; /* initial status of a request */
me->docid = docid;
HTRequest_setMethod (me->request, METHOD_GET);
HTRequest_setOutputFormat (me->request, WWW_SOURCE);
HTRequest_setContext (me->request, me);
/* experimental */
me->read_sock = INVSOC;
me->write_sock = INVSOC;
me->except_sock = INVSOC;
/* Update the global context */
HTList_appendObject (Amaya->reqlist, (void *) me);
docid_status = GetDocIdStatus (docid, Amaya->docid_status);
if (docid_status == NULL)
{
docid_status = (AHTDocId_Status *)
TtaGetMemory (sizeof (AHTDocId_Status));
docid_status->docid = docid;
docid_status->counter = 1;
HTList_addObject (Amaya->docid_status, (void *) docid_status);
}
else
docid_status->counter++;
Amaya->open_requests++;
#ifdef DEBUG_LIBWWW
fprintf (stderr, "AHTReqContext_new: Created object %p\n", me);
#endif
return me;
}
/*----------------------------------------------------------------------
AHTReqContext_delete
Delete an Amaya Context Object and update the global Amaya request
status.
----------------------------------------------------------------------*/
#ifdef __STDC__
boolean AHTReqContext_delete (AHTReqContext * me)
#else
boolean AHTReqContext_delete (me)
AHTReqContext *me;
#endif
{
AHTDocId_Status *docid_status;
if (me)
{
#ifdef DEBUG_LIBWWW
fprintf (stderr, "AHTReqContext_delete: Deleting object %p\n", me);
#endif
if (Amaya->reqlist)
HTList_removeObject (Amaya->reqlist, (void *) me);
docid_status = GetDocIdStatus (me->docid, Amaya->docid_status);
if (docid_status)
{
docid_status->counter--;
if (docid_status->counter == 0)
{
HTList_removeObject (Amaya->docid_status, (void *) docid_status);
TtaFreeMemory ((void *) docid_status);
}
}
if (me->method != METHOD_PUT && HTRequest_outputStream (me->request))
AHTFWriter_FREE (HTRequest_outputStream (me->request));
HTRequest_delete (me->request);
if (me->output && me->output != stdout)
{
#ifdef DEBUG_LIBWWW
fprintf (stderr, "AHTReqContext_delete: URL is %s, closing "
"FILE %p\n", me->urlName, me->output);
#endif
fclose (me->output);
me->output = NULL;
}
if (me->error_stream != (char *) NULL)
HT_FREE (me->error_stream);
#ifndef _WINDOWS
#ifdef WWW_XWINDOWS
if (me->read_xtinput_id || me->write_xtinput_id ||
me->except_xtinput_id)
RequestKillAllXtevents(me);
#endif /* WWW_XWINDOWS */
#endif /* !_WINDOWS */
if (me->reqStatus == HT_ABORT)
{
if (me->outputfile && me->outputfile[0] != EOS)
{
TtaFileUnlink (me->outputfile);
me->outputfile[0] = EOS;
}
}
if ((me->mode & AMAYA_ASYNC) || (me->mode & AMAYA_IASYNC))
/* for the ASYNC mode, free the memory we allocated in GetObjectWWW
or in PutObjectWWW */
{
if (me->urlName)
TtaFreeMemory (me->urlName);
if (me->outputfile)
TtaFreeMemory (me->outputfile);
}
if (me->content_type)
TtaFreeMemory (me->content_type);
if (me->formdata)
HTAssocList_delete (me->formdata);
/* to trace bugs */
memset ((void *) me, 0, sizeof (AHTReqContext));
TtaFreeMemory ((void *) me);
Amaya->open_requests--;
return TRUE;
}
return FALSE;
}
/*----------------------------------------------------------------------
Thread_deleteAll
this function deletes the whole list of active threads.
----------------------------------------------------------------------*/
#ifdef __STDC__
static void Thread_deleteAll (void)
#else
static void Thread_deleteAll ()
#endif
{
HTList *cur;
AHTReqContext *me;
AHTDocId_Status *docid_status;
if (Amaya && Amaya->reqlist)
{
if (Amaya->open_requests > 0)
#ifdef DEBUG_LIBWWW
fprintf (stderr, "Thread_deleteAll: Killing %d outstanding "
"requests\n", Amaya->open_requests);
#endif
{
cur = Amaya->reqlist;
/* erase the requests */
while ((me = (AHTReqContext *) HTList_removeLastObject (cur)))
{
if (me->request)
{
#ifndef _WINDOWS
#ifdef WWW_XWINDOWS
RequestKillAllXtevents (me);
#endif /* WWW_XWINDOWS */
#endif /* !_WINDOWS */
if (!HTRequest_kill (me->request))
AHTReqContext_delete (me);
}
} /* while */
/* erase the docid_status entities */
while ((docid_status = (AHTDocId_Status *) HTList_removeLastObject ((void *) Amaya->docid_status)))
TtaFreeMemory ((void *) docid_status);
} /* if */
}
}
/*----------------------------------------------------------------------
AHTOpen_file
----------------------------------------------------------------------*/
#ifdef __STDC__
int AHTOpen_file (HTRequest * request)
#else
int AHTOpen_file (request)
HTRequest *request;
#endif /* __STDC__ */
{
AHTReqContext *me; /* current request */
me = HTRequest_context (request);
if (!me)
return HT_ERROR;
#ifdef DEBUG_LIBWWW
fprintf(stderr, "AHTOpen_file: start for object : %p\n", me);
#endif /* DEBUG_LIBWWW */
if (me->reqStatus == HT_ABORT)
{
#ifdef DEBUG_LIBWWW
fprintf(stderr, "AHTOpen_file: caught an abort request, skipping it\n");
#endif /* DEBUG_LIBWWW */
return HT_OK;
}
if (me->method == METHOD_PUT)
{
me->reqStatus = HT_WAITING;
return HT_OK;
}
if (HTRequest_outputStream (me->request))
{
#ifdef DEBUG_LIBWWW
fprintf(stderr, "AHTOpen_file: output stream already existed for url %s\n", me->urlName);
#endif /* DEBUG_LIBWWW */
return HT_OK;
}
#ifdef DEBUG_LIBWWW
fprintf(stderr, "AHTOpen_file: opening output stream for url %s\n", me->urlName);
#endif /* DEBUG_LIBWWW */
if (!(me->output) &&
(me->output != stdout) &&
#ifndef _WINDOWS
(me->output = fopen (me->outputfile, "w")) == NULL)
{
#else /* !_WINDOWS */
(me->output = fopen (me->outputfile, "wb")) == NULL)
{
#endif /* !_WINDOWS */
me->outputfile[0] = EOS; /* file could not be opened */
#ifdef DEBUG_LIBWWW
fprintf(stderr, "AHTOpen_file: couldn't open output stream for url %s\n", me->urlName);
#endif
TtaSetStatus (me->docid, 1,
TtaGetMessage (AMAYA, AM_CANNOT_CREATE_FILE),
me->outputfile);
me->reqStatus = HT_ERR;
return (HT_ERROR);
}
HTRequest_setOutputStream (me->request,
AHTFWriter_new (me->request,
me->output, YES));
me->reqStatus = HT_WAITING;
return HT_OK;
}
/*----------------------------------------------------------------------
redirection_handler
this function is registered to handle permanent and temporary
redirections.
----------------------------------------------------------------------*/
#ifdef __STDC__
static int redirection_handler (HTRequest * request, HTResponse * response, void *param, int status)
#else
static int redirection_handler (request, context, status)
HTRequest *request;
HTResponse *response;
void *param;
int status;
#endif
{
HTAnchor *new_anchor = HTResponse_redirection (response);
AHTReqContext *me = HTRequest_context (request);
HTMethod method = HTRequest_method (request);
char *ref;
char *tmp;
if (!me)
/* if the redirect doesn't come from Amaya, we call libwww's standard
redirect filter */
return (HTRedirectFilter (request, response, param, status));
if (!new_anchor)
{
if (PROT_TRACE)
HTTrace ("Redirection. No destination\n");
return HT_OK;
}
/*
** Only do redirect on GET and HEAD
*/
if (!HTMethod_isSafe (method))
{
/*
** If we got a 303 See Other then change the method to GET.
** Otherwise ask the user whether we should continue.
*/
if (status == HT_SEE_OTHER)
{
if (PROT_TRACE)
HTTrace("Redirection. Changing method from %s to GET\n",
HTMethod_name(method));
HTRequest_setMethod(request, METHOD_GET);
}
else
{
HTAlertCallback *prompt = HTAlert_find (HT_A_CONFIRM);
if (prompt)
{
if ((*prompt) (request, HT_A_CONFIRM, HT_MSG_REDIRECTION,
NULL, NULL, NULL) != YES)
return HT_OK;
}
}
}
/*
** Start new request with the redirect anchor found in the headers.
** Note that we reuse the same request object which means that we must
** keep this around until the redirected request has terminated. It also
** allows us in an easy way to keep track of the number of redirections
** so that we can detect endless loops.
*/
if (HTRequest_doRetry (request))
{
/* only do a redirect using a network protocol understood by Amaya */
if (IsValidProtocol (new_anchor->parent->address))
{
/* if it's a valid URL, we try to normalize it */
/* We use the pre-redirection anchor as a base name */
ref = AmayaParseUrl (new_anchor->parent->address,
me->urlName, AMAYA_PARSE_ALL);
if (ref)
{
HT_FREE (new_anchor->parent->address);
tmp = NULL;
HTSACopy (&tmp, ref);
new_anchor->parent->address = tmp;
TtaFreeMemory (ref);
}
else
return HT_OK; /* We can't redirect anymore */
}
else
return HT_OK; /* We can't redirect anymore */
/* update the current file name */
if ((me->mode & AMAYA_ASYNC) || (me->mode & AMAYA_IASYNC))
{
TtaFreeMemory (me->urlName);
me->urlName = TtaStrdup (new_anchor->parent->address);
}
else
/* it's a SYNC mode, so we should keep the urlName */
{
strncpy (me->urlName, new_anchor->parent->address,
MAX_LENGTH - 1);
me->urlName[MAX_LENGTH - 1] = EOS;
}
ChopURL (me->status_urlName, me->urlName);
TtaSetStatus (me->docid, 1, TtaGetMessage (AMAYA, AM_RED_FETCHING),
me->status_urlName);
/* Start request with new credentials */
if (HTRequest_outputStream (me->request) != NULL) {
AHTFWriter_FREE (HTRequest_outputStream (me->request));
if (me->output != stdout) { /* Are we writing to a file? */
#ifdef DEBUG_LIBWWW
fprintf (stderr, "redirection_handler: New URL is %s, closing "
"FILE %p\n", me->urlName, me->output);
#endif
fclose (me->output);
me->output = NULL;
}
}
/* reset the status */
me->reqStatus = HT_NEW;
/* clear the errors */
HTError_deleteAll (HTRequest_error (request));
HTRequest_setError (request, NULL);
if (me->method == METHOD_PUT
|| me->method == METHOD_POST) /* PUT, POST etc. */
status = HTLoadAbsolute (me->urlName, request);
else
HTLoadAnchor (new_anchor, request);
}
else
{
HTRequest_addError (request, ERR_FATAL, NO, HTERR_MAX_REDIRECT,
NULL, 0, "HTRedirectFilter");
TtaSetStatus (me->docid, 1, TtaGetMessage (AMAYA, AM_REDIRECTIONS_LIMIT),
NULL);
if (me->error_html)
DocNetworkStatus[me->docid] |= AMAYA_NET_ERROR;
/* so that we can show the error message */
}
/*
** By returning HT_ERROR we make sure that this is the last handler to be
** called. We do this as we don't want any other filter to delete the
** request object now when we have just started a new one ourselves
*/
return HT_ERROR;
}
#ifdef AMAYA_LOST_UPDATE
/*----------------------------------------------------------------------
precondition_handler
412 "Precondition failed" handler
----------------------------------------------------------------------*/
#if __STDC__
static int precondition_handler (HTRequest * request, HTResponse * response, void *context, int status)
#else
static int precondition_handler (request, response, context, status)
HTRequest *request;
HTResponse *response;
void *context;
int status;
#endif /* __STDC__ */
{
AHTReqContext *me = (AHTReqContext *) HTRequest_context (request);
HTAlertCallback *prompt = HTAlert_find (HT_A_CONFIRM);
boolean force_put;
if (!me)
return HT_OK; /* not an Amaya request */
if (prompt)
force_put = (*prompt) (request, HT_A_CONFIRM, HT_MSG_RULES,
NULL, NULL, NULL);
else
force_put = NO;
if (force_put)
{
/* start a new PUT request without preconditions */
/* @@ do we need to kill the request? */
if (me->output && me->output != stdout)
{
fclose (me->output);
me->output = NULL;
}
/* turn off preconditions */
HTRequest_setPreconditions(me->request, HT_NO_MATCH);
/*
** reset the Amaya and libwww request status
*/
me->reqStatus = HT_NEW;
/* clear the errors */
HTError_deleteAll (HTRequest_error (request));
HTRequest_setError (request, NULL);
/* make the request */
status = HTPutDocumentAnchor (HTAnchor_parent (me->source), me->dest, me->request);
/* stop here */
return HT_ERROR;
}
else
{
/* abort the request */
/* @@ should we call StopRequest here? */
me->reqStatus = HT_ABORT;
/* stop here */
return HT_ERROR;
}
}
/*----------------------------------------------------------------------
check_handler
Request HEAD checker filter
----------------------------------------------------------------------*/
static int check_handler (HTRequest * request, HTResponse * response,
void * param, int status)
{
AHTReqContext *me = (AHTReqContext *) HTRequest_context (request);
HTAlertCallback *prompt = HTAlert_find (HT_A_CONFIRM);
boolean force_put;
if (!me)
return HT_OK; /* not an Amaya request */
HTRequest_deleteAfter(me->request, check_handler);
/*
** If head request showed that the document doesn't exist
** then just go ahead and PUT it. Otherwise ask for help
*/
if (status == HT_ERROR || status == HT_INTERRUPTED || status == HT_TIMEOUT)
{
/* we'd need to call terminate_handler, to free the resources */
/* abort the request */
/* @@ should we call StopRequest here? */
me->reqStatus = HT_ABORT;
/* stop here */
return HT_ERROR;
}
else if (status == HT_NO_ACCESS || status == HT_NO_PROXY_ACCESS)
{
/* we'd need to call terminate_handler, to free the resources */
/* abort the request */
/* @@ should we call StopRequest here? */
me->reqStatus = HT_ABORT;
/* stop here */
return HT_ERROR;
}
else if ( (abs(status)/100) != 2)
{
/* it's a new ressource, so we start a new PUT request with a
"if-none-match *" precondition */
HTRequest_setPreconditions(me->request, HT_DONT_MATCH_ANY);
status = HTPutDocumentAnchor (HTAnchor_parent (me->source), me->dest, me->request);
return HT_ERROR; /* stop here */
}
else
{
if (prompt)
force_put = (*prompt) (request, HT_A_CONFIRM, HT_MSG_FILE_REPLACE,
NULL, NULL, NULL);
else
force_put = FALSE;
if (force_put)
{
/* Start a new PUT request without preconditions */
HTRequest_setPreconditions(me->request, HT_NO_MATCH);
status = HTPutDocumentAnchor (HTAnchor_parent (me->source), me->dest, me->request);
return HT_ERROR; /* stop here */
}
else
{
/* we'd need to call terminate_handler, to free the resources */
/* abort the request */
/* @@ should we call StopRequest here? */
me->reqStatus = HT_ABORT;
/* stop here */
return HT_ERROR;
}
}
return HT_ERROR;
}
#endif /* AMAYA_LOST_UPDATE */
/*----------------------------------------------------------------------
terminate_handler
this function is registered to handle the result of the request
----------------------------------------------------------------------*/
#if __STDC__
static int terminate_handler (HTRequest * request, HTResponse * response, void *context, int status)
#else
static int terminate_handler (request, response, context, status)
HTRequest *request;
HTResponse *response;
void *context;
int status;
#endif /* __STDC__ */
{
AHTReqContext *me = (AHTReqContext *) HTRequest_context (request);
boolean error_flag;
char *content_type;
if (!me)
return HT_ERROR; /* not an Amaya request */
if (me->reqStatus == HT_END)
/* we have already processed this request! */
return HT_OK;
/* if Amaya was killed, treat with this request as if it were
issued by a Stop button event */
if (!AmayaIsAlive ())
me->reqStatus = HT_ABORT;
if (status == HT_LOADED ||
status == HT_CREATED ||
status == HT_NO_DATA ||
/* kludge for work around libwww problem */
(status == HT_INTERRUPTED && me->method == METHOD_PUT) ||
#ifdef AMAYA_WWW_CACHE
/* what status to use to know we're downloading from a cache? */
status == HT_NOT_MODIFIED ||
#endif /* AMAYA_WWW_CACHE */
me->reqStatus == HT_ABORT)
error_flag = FALSE;
else
error_flag = TRUE;
/* output any errors from the server */
/*
** me->output = output file which will receive an html file
** me->error_html = yes, output HTML errors in the screen
** request->error_stack == if there are any errors, they will be here
** me->error_stream_size If it's != 0 means an error message has already
** been written to the stack
*/
/* First, we verify if there are any errors and if they are not
** yet written to the error stack. If no, then let's try to write them
** ourselves
*/
#ifdef DEBUG_LIBWWW
fprintf (stderr, "terminate_handler: URL is "
"%s, closing FILE %p status is %d\n", me->urlName, me->output,
status);
#endif
if (me->output && me->output != stdout)
{
/* we are writing to a file */
if (me->reqStatus != HT_ABORT)
{ /* if the request was not aborted and */
if (error_flag &&
me->error_html == TRUE)
/* there were some errors and we want to print them */
{
if (me->error_stream_size == 0)/* and the stream is empty */
AHTError_MemPrint (request); /* copy errors from
**the error stack
** into the error stream */
if (me->error_stream)
{ /* if the stream is non-empty */
fprintf (me->output, me->error_stream);/* output the errors */
/* Clear the error context, so that we can deal with
this answer as if it were a normal reply */
HTError_deleteAll( HTRequest_error (request));
HTRequest_setError (request, NULL);
error_flag = 0;
}
} /* if error_stack */
}
/* if != HT_ABORT */
#ifdef DEBUG_LIBWWW
fprintf (stderr, "terminate_handler: URL is %s, closing "
"FILE %p\n", me->urlName, me->output);
#endif
fclose (me->output);
me->output = NULL;
}
if (error_flag)
me->reqStatus = HT_ERR;
else if (me->reqStatus != HT_ABORT)
me->reqStatus = HT_END;
/* copy the content_type */
if (!me->content_type)
{
/* @@ should get this info from response */
if (request->anchor && request->anchor->content_type)
content_type = request->anchor->content_type->name;
else
content_type = "www/unknown";
if (content_type && content_type [0] != EOS)
{
/* libwww gives www/unknown when it gets an error. As this is
an HTML test, we force the type to text/html */
if (!strcmp (content_type, "www/unknown"))
me->content_type = TtaStrdup ("text/html");
else
me->content_type = TtaStrdup (content_type);
/* Content-Type can be specified by an httpd server's admin.
To be on the safe side, we normalize its case */
ConvertToLowerCase (me->content_type);
#ifdef DEBUG_LIBWWW
fprintf (stderr, "content type is: %s\n", me->content_type);
#endif /* DEBUG_LIBWWW */
}
}
/* to avoid a hangup while downloading css files */
if (AmayaAlive_flag && (me->mode & AMAYA_LOAD_CSS))
TtaSetStatus (me->docid, 1,
TtaGetMessage (AMAYA, AM_ELEMENT_LOADED),
me->status_urlName);
/* don't remove or Xt will hang up during the PUT */
if (AmayaIsAlive () && ((me->method == METHOD_POST) ||
(me->method == METHOD_PUT)))
{
PrintTerminateStatus (me, status);
}
ProcessTerminateRequest (request, response, context, status);
/* stop here */
return HT_ERROR;
}
/*----------------------------------------------------------------------
AHTLoadTerminate_handler
this is an application "AFTER" Callback. It's called by the library
when a request has ended, so that we can setup the correct status.
----------------------------------------------------------------------*/
#ifdef __STDC__
int AHTLoadTerminate_handler (HTRequest * request, HTResponse * response, void *param, int status)
#else
int AHTLoadTerminate_handler (request, response, param, status)
HTRequest *request;
HTResponse *response;
void *param;
int status;
#endif
{
/** @@@@ use this with printstatus ?? */
AHTReqContext *me = HTRequest_context (request);
HTAlertCallback *cbf;
AHTDocId_Status *docid_status;
switch (status)
{
case HT_LOADED:
if (PROT_TRACE)
HTTrace ("Load End.... OK: `%s\' has been accessed\n",
me->status_urlName);
docid_status = GetDocIdStatus (me->docid,
Amaya->docid_status);
if (docid_status != NULL && docid_status->counter > 1)
TtaSetStatus (me->docid, 1,
TtaGetMessage (AMAYA, AM_ELEMENT_LOADED),
me->status_urlName);
break;
case HT_NO_DATA:
if (PROT_TRACE)
HTTrace ("Load End.... OK BUT NO DATA: `%s\'\n",
me->status_urlName);
TtaSetStatus (me->docid, 1,
TtaGetMessage (AMAYA, AM_LOADED_NO_DATA),
me->status_urlName);
break;
case HT_INTERRUPTED:
if (PROT_TRACE)
HTTrace ("Load End.... INTERRUPTED: `%s\'\n",
me->status_urlName);
TtaSetStatus (me->docid, 1,
TtaGetMessage (AMAYA, AM_LOAD_ABORT),
NULL);
break;
case HT_RETRY:
if (PROT_TRACE)
HTTrace ("Load End.... NOT AVAILABLE, RETRY AT %ld\n",
HTResponse_retryTime (response));
TtaSetStatus (me->docid, 1,
TtaGetMessage (AMAYA, AM_NOT_AVAILABLE_RETRY),
me->status_urlName);
break;
case HT_ERROR:
cbf = HTAlert_find (HT_A_MESSAGE);
if (cbf)
(*cbf) (request, HT_A_MESSAGE, HT_MSG_NULL, NULL,
HTRequest_error (request), NULL);
break;
if (PROT_TRACE)
HTTrace ("Load End.... ERROR: Can't access `%s\'\n",
me->status_urlName ? me->status_urlName :"<UNKNOWN>");
TtaSetStatus (me->docid, 1,
TtaGetMessage (AMAYA, AM_CANNOT_LOAD),
me->status_urlName ? me->status_urlName : "<UNKNOWN>");
break;
default:
if (PROT_TRACE)
HTTrace ("Load End.... UNKNOWN RETURN CODE %d\n", status);
break;
}
return HT_OK;
}
#ifdef DEBUG_LIBWWW
static int LineTrace (const char * fmt, va_list pArgs)
{
return (vfprintf(stderr, fmt, pArgs));
}
#endif DEBUG_LIBWWW
/*----------------------------------------------------------------------
AHTAcceptTypesInit
This function prepares the Accept header used by Amaya during
the HTTP content negotiation phase
----------------------------------------------------------------------*/
#ifdef __STDC__
static void AHTAcceptTypesInit (HTList *c)
#else /* __STDC__ */
static void AHTAcceptTypesInit (c)
HTList *c;
#endif /* __STDC__ */
{
if (c == (HTList *) NULL)
return;
/* define here all the mime types that Amaya can accept */
HTConversion_add (c, "image/png", "www/present",
HTThroughLine, 1.0, 0.0, 0.0);
HTConversion_add (c, "image/jpeg", "www/present",
HTThroughLine, 1.0, 0.0, 0.0);
HTConversion_add (c, "image/gif", "www/present",
HTThroughLine, 1.0, 0.0, 0.0);
HTConversion_add (c, "image/xbm", "www/present",
HTThroughLine, 1.0, 0.0, 0.0);
HTConversion_add (c, "image/xpm", "www/present",
HTThroughLine, 1.0, 0.0, 0.0);
HTConversion_add (c, "application/postscript",
"www/present", HTThroughLine, 1.0, 0.0, 0.0);
/* Define here the equivalences between MIME types and file extensions for
the types that Amaya can display */
/* Register the default set of file suffix bindings */
HTFileInit ();
/* Don't do any case distinction */
HTBind_caseSensitive (FALSE);
}
/*----------------------------------------------------------------------
AHTConverterInit
Bindings between a source media type and a destination media type
(conversion).
----------------------------------------------------------------------*/
#ifdef __STDC__
static void AHTConverterInit (HTList *c)
#else /* __STDC__ */
static void AHTConverterInit (c)
HTList *c;
#endif /* __STDC__ */
{
/* Handler for custom http error messages */
HTConversion_add (c, "*/*", "www/debug", AHTMemConverter, 1.0, 0.0, 0.0);
/*
** These are converters that converts to something other than www/present,
** that is not directly outputting someting to the user on the screen
*/
HTConversion_add (c, "message/rfc822", "*/*", HTMIMEConvert,
1.0, 0.0, 0.0);
HTConversion_add (c, "message/x-rfc822-foot", "*/*", HTMIMEFooter,
1.0, 0.0, 0.0);
HTConversion_add (c, "message/x-rfc822-head", "*/*", HTMIMEHeader,
1.0, 0.0, 0.0);
HTConversion_add(c,"message/x-rfc822-cont", "*/*", HTMIMEContinue,
1.0, 0.0, 0.0);
HTConversion_add(c,"message/x-rfc822-partial","*/*", HTMIMEPartial,
1.0, 0.0, 0.0);
HTConversion_add (c, "multipart/*", "*/*", HTBoundary,
1.0, 0.0, 0.0);
HTConversion_add (c, "text/plain", "text/html", HTPlainToHTML,
1.0, 0.0, 0.0);
/*
** The following conversions are converting ASCII output from various
** protocols to HTML objects.
*/
HTConversion_add (c, "text/x-http", "*/*", HTTPStatus_new,
1.0, 0.0, 0.0);
/*
** We also register a special content type guess stream that can figure out
** the content type by reading the first bytes of the stream
*/
HTConversion_add (c, "www/unknown", "*/*", HTGuess_new,
1.0, 0.0, 0.0);
#ifdef AMAYA_WWW_CACHE
/*
** Register a persistent cache stream which can save an object to local
** file
*/
HTConversion_add (c, "www/cache", "*/*", HTCacheWriter,
1.0, 0.0, 0.0);
HTConversion_add(c,"www/cache-append", "*/*", HTCacheAppend,
1.0, 0.0, 0.0);
#endif AMAYA_WWW_CACHE
/*
** This dumps all other formats to local disk without any further
** action taken
*/
HTConversion_add (c, "*/*", "www/present", HTSaveLocally,
0.3, 0.0, 0.0);
}
/*----------------------------------------------------------------------
AHTProtocolInit
Registers all amaya supported protocols.
----------------------------------------------------------------------*/
static void AHTProtocolInit (void)
{
char *strptr;
/*
NB. Preemptive == YES means Blocking requests
Non-preemptive == NO means Non-blocking requests
*/
HTTransport_add("tcp", HT_TP_SINGLE, HTReader_new, HTWriter_new);
HTTransport_add("buffered_tcp", HT_TP_SINGLE, HTReader_new,
HTBufferWriter_new);
HTProtocol_add ("http", "buffered_tcp", HTTP_PORT, NO, HTLoadHTTP, NULL);
#ifdef _WINDOWS
HTProtocol_add ("file", "local", 0, YES, HTLoadFile, NULL);
#else
HTProtocol_add ("file", "local", 0, NO, HTLoadFile, NULL);
#endif _WINDOWS
#ifdef AMAYA_WWW_CACHE
HTProtocol_add("cache", "local", 0, YES, HTLoadCache, NULL);
#endif /* AMAYA_WWW_CACHE */
#if 0 /* experimental code */
HTProtocol_add ("ftp", "tcp", FTP_PORT, NO, HTLoadFTP, NULL);
#endif
/* initialize pipelining */
strptr = (char *) TtaGetEnvString ("ENABLE_PIPELINING");
if (strptr && *strptr && strcasecmp (strptr,"yes" ))
HTTP_setConnectionMode (HTTP_11_NO_PIPELINING);
}
/*----------------------------------------------------------------------
AHTNetInit
Reegisters "before" and "after" request filters.
----------------------------------------------------------------------*/
static void AHTNetInit (void)
{
/* Register BEFORE filters
** The BEFORE filters handle proxies, caches, rule files etc.
** The filters are called in the order by which the are registered
** Not done automaticly - may be done by application!
*/
#ifdef AMAYA_WWW_CACHE
HTNet_addBefore (HTCacheFilter, "http://*", NULL, HT_FILTER_MIDDLE);
#endif /* AMAYA_WWW_CACHE */
HTNet_addBefore (HTCredentialsFilter, "http://*", NULL, HT_FILTER_LATE);
HTNet_addBefore (HTProxyFilter, NULL, NULL, HT_FILTER_LATE);
HTHost_setActivateRequestCallback (AHTOpen_file);
/* register AFTER filters
** The AFTER filters handle error messages, logging, redirection,
** authentication etc.
** The filters are called in the order by which the are registered
** Not done automaticly - may be done by application!
*/
HTNet_addAfter (HTAuthFilter, "http://*", NULL, HT_NO_ACCESS,
HT_FILTER_MIDDLE);
HTNet_addAfter(HTAuthFilter, "http://*", NULL, HT_REAUTH,
HT_FILTER_MIDDLE);
HTNet_addAfter (redirection_handler, "http://*", NULL, HT_PERM_REDIRECT,
HT_FILTER_MIDDLE);
HTNet_addAfter (redirection_handler, "http://*", NULL, HT_FOUND,
HT_FILTER_MIDDLE);
HTNet_addAfter (redirection_handler, "http://*", NULL, HT_SEE_OTHER,
HT_FILTER_MIDDLE);
HTNet_addAfter (redirection_handler, "http://*", NULL, HT_TEMP_REDIRECT,
HT_FILTER_MIDDLE);
HTNet_addAfter(HTAuthInfoFilter, "http://*", NULL, HT_ALL,
HT_FILTER_MIDDLE);
HTNet_addAfter (HTUseProxyFilter, "http://*", NULL, HT_USE_PROXY,
HT_FILTER_MIDDLE);
#ifdef AMAYA_WWW_CACHE
HTNet_addAfter (HTCacheUpdateFilter, "http://*", NULL, HT_NOT_MODIFIED,
HT_FILTER_MIDDLE);
#endif /* AMAYA_WWW_CACHE */
#ifdef AMAYA_LOST_UPDATE
HTNet_addAfter (precondition_handler, NULL, NULL, HT_PRECONDITION_FAILED,
HT_FILTER_MIDDLE);
#endif /* AMAYA_LOST_UPDATE */
#ifndef _WINDOWS
HTNet_addAfter (AHTLoadTerminate_handler, NULL, NULL, HT_ALL,
HT_FILTER_LAST);
#endif /* !_WINDOWS */
/**** for later ?? ****/
/* HTNet_addAfter(HTInfoFilter, NULL, NULL, HT_ALL, HT_FILTER_LATE); */
/* handles all errors */
HTNet_addAfter (terminate_handler, NULL, NULL, HT_ALL, HT_FILTER_LAST);
}
/*----------------------------------------------------------------------
AHTAlertInit
Register alert messages and their callbacks.
----------------------------------------------------------------------*/
#ifdef __STDC__
static void AHTAlertInit (void)
#else
static void AHTAlertInit ()
#endif
{
HTAlert_add (AHTProgress, HT_A_PROGRESS);
#ifdef __WINDOWS
HTAlert_add ((HTAlertCallback *) WIN_Activate_Request, HT_PROG_CONNECT);
#endif /* _WINDOWS */
HTAlert_add (AHTError_print, HT_A_MESSAGE);
HTError_setShow (~((unsigned int) 0 ) & ~((unsigned int) HT_ERR_SHOW_DEBUG)); /* process all messages except debug ones*/
HTAlert_add (AHTConfirm, HT_A_CONFIRM);
HTAlert_add (AHTPrompt, HT_A_PROMPT);
HTAlert_add (AHTPromptUsernameAndPassword, HT_A_USER_PW);
}
/*----------------------------------------------------------------------
libwww_CleanCache
frontend to the recursive cache cleaning function
----------------------------------------------------------------------*/
#ifdef __STDC__
void libwww_CleanCache (void)
#else
void libwww_CleanCache ()
Document doc;
View view;
#endif /* __STDC__ */
{
#ifdef AMAYA_WWW_CACHE
char *strptr;
char *cache_dir;
int cache_size;
if (!HTCacheMode_enabled ())
/* don't do anything if we're not using a cache */
return;
/* temporarily close down the cache, purge it, then restart */
cache_dir = TtaStrdup ( (char *) HTCacheMode_getRoot ());
cache_size = HTCacheMode_maxSize ();
/* remove the concurrent cache lock */
#ifdef DEBUG_LIBWWW
fprintf (stderr, "Clearing the cache lock\n");
#endif /* DEBUG_LIBWWW */
clear_cachelock ();
HTCacheTerminate ();
HTCacheMode_setEnabled (NO);
strptr = TtaGetMemory (strlen (cache_dir) + 20);
strcpy (strptr, cache_dir);
strcat (strptr, DIR_STR);
RecCleanCache (strptr);
HTCacheMode_setExpires (HT_EXPIRES_AUTO);
HTCacheInit (cache_dir, cache_size);
/* set a new concurrent cache lock */
strcat (strptr, ".lock");
if (set_cachelock (strptr) == -1)
/* couldn't open the .lock file, so, we close the cache to
be in the safe side */
{
#ifdef DEBUG_LIBWWW
fprintf (stderr, "couldnt set the cache lock\n");
#endif /* DEBUG_LIBWWW */
HTCacheTerminate ();
HTCacheMode_setEnabled (NO);
}
#ifdef DEBUG_LIBWWW
fprintf (stderr, "set a cache lock\n");
#endif /* DEBUG_LIBWWW */
TtaFreeMemory (strptr);
TtaFreeMemory (cache_dir);
#endif /* AMAYA_WWW_CACHE */
}
#ifdef AMAYA_WWW_CACHE
/*----------------------------------------------------------------------
RecCleanCache
Clears an existing cache directory
----------------------------------------------------------------------*/
#ifdef __STDC__
static void RecCleanCache (char *dirname)
#else
static void RecCleanCache (char *dirname)
Document doc;
View view;
#endif /* __STDC__ */
#ifdef _WINDOWS
{
HANDLE hFindFile;
boolean status;
WIN32_FIND_DATA ffd;
char t_dir [MAX_LENGTH];
char *ptr;
/* create a t_dir name to start searching for files */
if ((strlen (dirname) + 10) > MAX_LENGTH)
/* ERROR: directory name is too big */
return;
strcpy (t_dir, dirname);
/* save the end of the dirname. We'll use it to make
a complete pathname when erasing files */
ptr = &t_dir[strlen (t_dir)];
strcat (t_dir, "*");
hFindFile = FindFirstFile (t_dir, &ffd);
if (hFindFile == INVALID_HANDLE_VALUE)
/* nothing to erase? */
return;
status = TRUE;
while (status)
{
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)
{
/* it's a directory, erase it recursively */
if (strcmp (ffd.cFileName, "..") && strcmp (ffd.cFileName, "."))
{
strcpy (ptr, ffd.cFileName);
strcat (ptr, DIR_STR);
RecCleanCache (t_dir);
# ifdef _WINDOWS
_rmdir (t_dir);
# else /* !_WINDOWS */
rmdir (t_dir);
# endif /* _WINDOWS */
}
}
else
{
/* it's a file, erase it */
strcpy (ptr, ffd.cFileName);
TtaFileUnlink (t_dir);
}
status = FindNextFile (hFindFile, &ffd);
}
FindClose (hFindFile);
}
#else /* _WINDOWS */
{
DIR *dp;
struct stat st;
#ifdef HAVE_DIRENT_H
struct dirent *d;
#else
struct direct *d;
#endif /* HAVE_DIRENT_H */
char filename[BUFSIZ+1];
if ((dp = opendir (dirname)) == NULL)
{
/* @@@ we couldn't open the directory ... we need some msg */
perror (dirname);
exit;
}
while ((d = readdir (dp)) != NULL)
{
/* skip the UNIX . and .. links */
if (!strcmp (d->d_name, "..")
|| !strcmp (d->d_name, "."))
continue;
sprintf (filename, "%s"DIR_STR"%s", dirname, d->d_name);
if (lstat (filename, &st) < 0 )
{
/* @@2 need some error message */
perror (filename);
continue;
}
switch (st.st_mode & S_IFMT)
{
case S_IFDIR:
/* if we find a directory, we erase it, recursively */
strcat (filename, DIR_STR);
RecCleanCache (filename);
rmdir (filename);
break;
case S_IFLNK:
/* skip any links we find */
continue;
break;
default:
/* erase the filename */
TtaFileUnlink (filename);
break;
}
}
closedir (dp);
}
#endif /* _WINDOWS */
#endif /* AMAYA_WWW_CACHE */
/*----------------------------------------------------------------------
CacheInit
Reads the cache settings from the thot.ini file.
----------------------------------------------------------------------*/
#ifdef __STDC__
static void CacheInit (void)
#else
static void Cacheinit ()
#endif
{
#ifndef AMAYA_WWW_CACHE
HTCacheMode_setEnabled (NO);
#else /* AMAYA_WWW_CACHE */
char *strptr;
char *cache_dir = NULL;
char *cache_lockfile;
int cache_size;
boolean cache_enabled;
boolean cache_locked;
/* activate cache? */
strptr = (char *) TtaGetEnvString ("ENABLE_CACHE");
if (strptr && *strptr && strcasecmp (strptr, "yes" ))
cache_enabled = NO;
else
cache_enabled = YES;
/* cache protected documents? */
strptr = (char *) TtaGetEnvString ("CACHE_PROTECTED_DOCS");
if (strptr && *strptr && !strcasecmp (strptr, "yes" ))
HTCacheMode_setProtected (YES);
else
HTCacheMode_setProtected (NO);
/* get the cache dir (or use a default one) */
strptr = (char *) TtaGetEnvString ("CACHE_DIR");
if (strptr && *strptr)
{
cache_dir = TtaGetMemory (strlen (strptr) + strlen (CACHE_DIR_NAME) + 1);
strcpy (cache_dir, strptr);
}
else
{
cache_dir = TtaGetMemory (strlen (TempFileDirectory)
+ strlen (CACHE_DIR_NAME) + 1);
strcpy (cache_dir, TempFileDirectory);
}
strcat (cache_dir, CACHE_DIR_NAME);
/* get the cache size (or use a default one) */
strptr = (char *) TtaGetEnvString ("CACHE_SIZE");
if (strptr && *strptr)
cache_size = atoi (strptr);
else
cache_size = DEFAULT_CACHE_SIZE;
if (cache_enabled)
{
/* how to remove the lock? force remove it? */
cache_lockfile = TtaGetMemory (strlen (cache_dir) + 20);
strcpy (cache_lockfile, cache_dir);
strcat (cache_lockfile, DIR_STR".lock");
cache_locked = FALSE;
if (TtaFileExist (cache_lockfile)
&& !(cache_locked = test_cachelock (cache_lockfile)))
{
#ifdef DEBUG_LIBWWW
fprintf (stderr, "found a stale cache, removing it\n");
#endif /* DEBUG_LIBWWW */
/* remove the lock and clean the cache (the clean cache
will remove all, making the following call unnecessary */
/* little trick to win some memory */
strptr = strrchr (cache_lockfile, '.');
*strptr = EOS;
RecCleanCache (cache_lockfile);
*strptr = '.';
}
if (!cache_locked)
{
/* initialize the cache if there's no other amaya
instance running */
HTCacheInit (cache_dir, cache_size);
HTCacheMode_setExpires (HT_EXPIRES_AUTO);
if (set_cachelock (cache_lockfile) == -1)
/* couldn't open the .lock file, so, we close the cache to
be in the safe side */
{
HTCacheTerminate ();
HTCacheMode_setEnabled (NO);
#ifdef DEBUG_LIBWWW
fprintf (stderr, "couldnt set the cache lock\n");
#endif /* DEBUG_LIBWWW */
}
#ifdef DEBUG_LIBWWW
else
fprintf (stderr, "created the cache lock\n");
#endif /* DEBUG_LIBWWW */
}
else
{
HTCacheMode_setEnabled (NO);
#ifdef DEBUG_LIBWWW
fprintf (stderr, "lock detected, starting libwww without a cache/n");
#endif /* DEBUG_LIBWWW */
}
TtaFreeMemory (cache_lockfile);
}
else
HTCacheMode_setEnabled (NO);
if (cache_dir)
TtaFreeMemory (cache_dir);
#endif /* AMAYA_WWW_CACHE */
}
/*----------------------------------------------------------------------
ProxyInit
Reads any proxies settings which may be declared as environmental
variables or in the thot.ini file. The former overrides the latter.
----------------------------------------------------------------------*/
#ifdef __STDC__
static void ProxyInit (void)
#else
static void ProxyInit ()
#endif /* __STDC__ */
{
char *strptr;
char *str = NULL;
char *name;
/* get the proxy settings from the thot.ini file */
strptr = (char *) TtaGetEnvString ("HTTP_PROXY");
if (strptr && *strptr)
HTProxy_add ("http", strptr);
/* get the no_proxy settings from the thot.ini file */
strptr = (char *) TtaGetEnvString ("NO_PROXY");
if (strptr && *strptr)
{
str = TtaStrdup (strptr); /* Get copy we can mutilate */
strptr = str;
while ((name = HTNextField (&strptr)) != NULL) {
char *portstr = strchr (name, ':');
unsigned port=0;
if (portstr) {
*portstr++ = '\0';
if (*portstr) port = (unsigned) atoi (portstr);
}
/* Register it for all access methods */
HTNoProxy_add (name, NULL, port);
}
TtaFreeMemory (str);
}
/* use libw3's routine to get all proxy settings from the environment */
HTProxy_getEnvVar ();
}
/*----------------------------------------------------------------------
AHTProfile_newAmaya
creates the Amaya client profile for libwww.
----------------------------------------------------------------------*/
#ifdef __STDC__
static void AHTProfile_newAmaya (char *AppName, char *AppVersion)
#else /* __STDC__ */
static void AHTProfile_newAmaya (AppName, AppVersion)
char *AppName;
char *AppVersion;
#endif /* __STDC__ */
{
char * strptr;
/* If the Library is not already initialized then do it */
if (!HTLib_isInitialized ())
HTLibInit (AppName, AppVersion);
if (!converters)
converters = HTList_new ();
if (!acceptTypes)
acceptTypes = HTList_new ();
if (!transfer_encodings)
transfer_encodings = HTList_new ();
if (!content_encodings)
content_encodings = HTList_new ();
/* inhibits libwww's automatic file_suffix_binding */
HTFile_doFileSuffixBinding (FALSE);
/* Register the default set of transport protocols */
HTTransportInit ();
/* Register the default set of application protocol modules */
AHTProtocolInit ();
/* Register the default set of messages and dialog functions */
AHTAlertInit ();
HTAlert_setInteractive (YES);
#ifdef AMAYA_WWW_CACHE
/* Enable the persistent cache */
CacheInit ();
#else
HTCacheMode_setEnabled (NO);
#endif /* AMAYA_WWW_CACHE */
/* Register the default set of BEFORE and AFTER filters */
AHTNetInit ();
/* Set up the default set of Authentication schemes */
HTAA_newModule ("basic", HTBasic_generate, HTBasic_parse, NULL,
HTBasic_delete);
/* activate MDA by defaul */
strptr = (char *) TtaGetEnvString ("ENABLE_MDA");
if (!strptr || (strptr && *strptr && strcasecmp (strptr, "no" )))
HTAA_newModule ("digest", HTDigest_generate, HTDigest_parse,
HTDigest_updateInfo, HTDigest_delete);
/* Get any proxy settings */
ProxyInit ();
/* Register the default set of converters */
AHTConverterInit (converters);
HTFormat_setConversion (converters);
AHTAcceptTypesInit (acceptTypes);
/* Register the default set of transfer encoders and decoders */
HTTransferEncoderInit (transfer_encodings);
HTFormat_setTransferCoding(transfer_encodings);
/* Register the default set of content encoders and decoders */
HTContentEncoderInit(content_encodings);
if (HTList_count(content_encodings) > 0)
HTFormat_setContentCoding(content_encodings);
else
{
HTList_delete(content_encodings);
content_encodings = NULL;
}
/* Register the default set of MIME header parsers */
HTMIMEInit (); /* must be called again for language selector */
/* Register the default set of Icons for directory listings */
/*HTIconInit(NULL); *//* experimental */
}
/*----------------------------------------------------------------------
AHTProfile_delete
deletes the Amaya client profile.
----------------------------------------------------------------------*/
#ifdef __STDC__
static void AHTProfile_delete (void)
#else
static void AHTProfile_delete ()
#endif /* __STDC__ */
{
/* free the Amaya global context */
/* Clean up all the registred converters */
HTFormat_deleteAll ();
if (acceptTypes)
HTConversion_deleteAll (acceptTypes);
HTList_delete (Amaya->docid_status);
HTList_delete (Amaya->reqlist);
TtaFreeMemory (Amaya);
if (HTLib_isInitialized ())
#ifdef _WINDOWS
HTEventTerminate ();
#endif _WINDOWS;
/* Clean up the persistent cache (if any) */
#ifdef AMAYA_WWW_CACHE
clear_cachelock ();
HTCacheTerminate ();
#endif /* AMAYA_WWW_CACHE */
/* Terminate libwww */
HTLibTerminate ();
}
/*----------------------------------------------------------------------
AmayacontextInit
initializes an internal Amaya context for our libwww interface
----------------------------------------------------------------------*/
#ifdef __STDC__
static void AmayaContextInit ()
#else
static void AmayaContextInit ()
#endif
{
AmayaAlive_flag = TRUE;
/* Initialization of the global context */
Amaya = (AmayaContext *) TtaGetMemory (sizeof (AmayaContext));
Amaya->reqlist = HTList_new ();
Amaya->docid_status = HTList_new ();
Amaya->open_requests = 0;
}
/*----------------------------------------------------------------------
QueryInit
initializes the libwww interface
----------------------------------------------------------------------*/
#ifdef __STDC__
void QueryInit ()
#else
void QueryInit ()
#endif
{
char *strptr;
int tmp_i;
long tmp_l;
AmayaContextInit ();
AHTProfile_newAmaya (HTAppName, HTAppVersion);
CanDoStop_set (TRUE);
#ifdef _WINDOWS
HTEventInit ();
#endif /* _WINDOWS */
#ifndef _WINDOWS
HTEvent_setRegisterCallback ((void *) AHTEvent_register);
HTEvent_setUnregisterCallback ((void *) AHTEvent_unregister);
HTTimer_registerSetTimerCallback ((void *) AMAYA_SetTimer);
HTTimer_registerDeleteTimerCallback ((void *) AMAYA_DeleteTimer);
#endif /* !_WINDOWS */
WWW_TraceFlag = CACHE_TRACE;
#ifdef DEBUG_LIBWWW
/* forwards error messages to our own function */
WWW_TraceFlag = THD_TRACE;
HTTrace_setCallback(LineTrace);
/* Trace activation (for debugging) */
/***
WWW_TraceFlag = SHOW_CORE_TRACE | SHOW_THREAD_TRACE | SHOW_PROTOCOL_TRACE;
WWW_TraceFlag |= 0xFFFFFFFFl;
***/
#endif
/* Setting up different network parameters */
/* Maximum number of simultaneous open sockets */
strptr = (char *) TtaGetEnvString ("MAX_SOCKET");
if (strptr && *strptr)
tmp_i = atoi (strptr);
else
tmp_i = DEFAULT_MAX_SOCKET;
HTNet_setMaxSocket (tmp_i);
/* different network services timeouts */
/* dns timeout */
strptr = (char *) TtaGetEnvString ("DNS_TIMEOUT");
if (strptr && *strptr)
tmp_i = atoi (strptr);
else
tmp_i = DEFAULT_DNS_TIMEOUT;
HTDNS_setTimeout (tmp_i);
/* persistent connections timeout */
strptr = (char *) TtaGetEnvString ("PERSIST_CX_TIMEOUT");
if (strptr && *strptr)
tmp_l = atol (strptr);
else
tmp_l = DEFAULT_PERSIST_TIMEOUT;
HTHost_setPersistTimeout (tmp_l);
/* default timeout in ms */
strptr = (char *) TtaGetEnvString ("NET_EVENT_TIMEOUT");
if (strptr && *strptr)
tmp_i = atoi (strptr);
else
tmp_i = DEFAULT_NET_EVENT_TIMEOUT;
HTHost_setEventTimeout (tmp_i);
#ifdef CATCH_SIG
signal (SIGPIPE, SIG_IGN);
#endif
}
/*----------------------------------------------------------------------
LoopForStop
a copy of the Thop event loop so we can handle the stop button in Unix
and preemptive requests under Windows
----------------------------------------------------------------------*/
#ifdef __STDC__
static int LoopForStop (AHTReqContext * me)
#else
static int LoopForStop (AHTReqContext * me)
#endif
{
#ifdef _WINDOWS
MSG msg;
unsigned long libwww_msg;
HWND old_active_window, libwww_window;
int status_req = HT_OK;
old_active_window = GetActiveWindow ();
libwww_window = HTEventList_getWinHandle (&libwww_msg);
while (me->reqStatus != HT_END && me->reqStatus != HT_ERR
&& me->reqStatus != HT_ABORT && AmayaIsAlive () &&
GetMessage (&msg, NULL, 0, 0))
{
if (msg.message != WM_QUIT)
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}
else
break;
}
if (!AmayaIsAlive ())
/* Amaya was killed by one of the callback handlers */
exit (0);
#else /* _WINDOWS */
extern ThotAppContext app_cont;
XEvent ev;
XtInputMask status;
int status_req = HT_OK;
/* to test the async calls */
/* Loop while waiting for new events, exists when the request is over */
while (me->reqStatus != HT_ABORT &&
me->reqStatus != HT_END &&
me->reqStatus != HT_ERR) {
if (!AmayaIsAlive ())
/* Amaya was killed by one of the callback handlers */
exit (0);
status = XtAppPending (app_cont);
if (status & XtIMXEvent)
{
XtAppNextEvent (app_cont, &ev);
TtaHandleOneEvent (&ev);
}
else if (status != 0)
XtAppProcessEvent (app_cont, XtIMAll);
}
#endif /* _WINDOWS */
switch (me->reqStatus) {
case HT_ERR:
case HT_ABORT:
status_req = NO;
break;
case HT_END:
status_req = YES;
break;
default:
break;
}
return (status_req);
}
/*----------------------------------------------------------------------
QueryClose
closes all existing threads, frees all non-automatically deallocated
memory and then ends libwww.
----------------------------------------------------------------------*/
void QueryClose ()
{
AmayaAlive_flag = FALSE;
/* remove all the handlers and callbacks that may output a message to
a non-existent Amaya window */
#ifndef _WINDOWS
HTNet_deleteAfter (AHTLoadTerminate_handler);
#endif _WINDOWS
HTNet_deleteAfter (redirection_handler);
HTAlertCall_deleteAll (HTAlert_global () );
HTAlert_setGlobal ((HTList *) NULL);
HTEvent_setRegisterCallback ((HTEvent_registerCallback *) NULL);
HTEvent_setUnregisterCallback ((HTEvent_unregisterCallback *) NULL);
#ifndef _WINDOWS
/** need to erase all existing timers too **/
HTTimer_registerSetTimerCallback (NULL);
HTTimer_registerDeleteTimerCallback (NULL);
#endif /* !_WINDOWS */
HTHost_setActivateRequestCallback (NULL);
Thread_deleteAll ();
HTProxy_deleteAll ();
HTNoProxy_deleteAll ();
HTGateway_deleteAll ();
AHTProfile_delete ();
}
/*----------------------------------------------------------------------
NextNameValue
---------------------------------------------------------------------*/
#ifdef __STDC__
static char * NextNameValue (char ** pstr, char **name, char **value)
#else
static char * NextNameValue (pstr, name, value);
char ** pstr;
char **name;
char **value;
#endif /* __STDC__ */
{
char * p = *pstr;
char * start = NULL;
if (!pstr || !*pstr) return NULL;
if (!*p) {
*pstr = p;
*name = NULL;
*value = NULL;
return NULL; /* No field */
}
/* Now search for the next '&' and ';' delimitators */
start = p;
while (*p && *p != '&' && *p != ';') p++;
if (*p)
*p++ = '\0';
*pstr = p;
/* Search for the name and value */
*name = start;
p = start;
while(*p && *p != '=')
p++;
if (*p)
*p++ = '\0';
*value = p;
return start;
}
/*----------------------------------------------------------------------
PrepareFormdata
---------------------------------------------------------------------*/
#ifdef __STDC__
static HTAssocList * PrepareFormdata (char *string)
#else
static HTAssocList * PrepareFormdata (string)
char *string;
#endif /* __STDC__ */
{
char * tmp_string, * tmp_string_ptr;
char * name;
char * value;
HTAssocList * formdata;
if (!string)
return NULL;
/* store the ptr in another variable, as the original address will
change
*/
tmp_string_ptr = tmp_string = TtaStrdup (string);
formdata = HTAssocList_new();
while (*tmp_string)
{
NextNameValue (&tmp_string, &name, &value);
HTAssocList_addObject(formdata,
name, value);
}
TtaFreeMemory (tmp_string_ptr);
return formdata;
}
/*----------------------------------------------------------------------
InvokeGetObjectWWW_callback
A simple function to invoke a callback function whenever there's an error
in GetObjectWWW
---------------------------------------------------------------------*/
#ifdef __STDC__
void InvokeGetObjectWWW_callback (int docid, char *urlName, char *outputfile, TTcbf *terminate_cbf, void *context_tcbf, int status)
#else
void InvokeGetObjectWWW_callback (docid, urlName, outputfile, terminate_cbf, context_tcbf, status)
int docid;
char *urlName;
char *outputfile;
TTcbf *terminate_cbf;
void *context_tcbf;
#endif /* __STDC__ */
{
if (!terminate_cbf)
return;
(*terminate_cbf) (docid, status, urlName, outputfile,
NULL, context_tcbf);
}
/*----------------------------------------------------------------------
GetObjectWWW
this function requests a resource designated by a URLname into a
temporary filename. The download can come from a simple GET operation,
or can come from POSTING/GETTING a form. In the latter
case, the function receives a query string to send to the server.
4 file retrieval modes are proposed:
AMAYA_SYNC : blocking mode
AMAYA_ISYNC : incremental, blocking mode
AMAYA_ASYNC : non-blocking mode
AMAYA_IASYNC : incremental, non-blocking mode
In the incremental mode, each time a package arrives, it will be
stored in the temporary file. In addition, if an
incremental_callback function is defined, this function will be
called and handled a copy of the newly received data package.
Finally, if a terminate_callback function is defined, it will be
invoked when the request terminates. The caller of this function
can define two different contexts to be passed to the callback
functions.
When the function is called with the SYNC mode, the function will
return only when the requested file has been loaded.
The ASYNC mode will immediately return after setting up the
call.
Notes:
At the end of a succesful request, the urlName string contains the
name of the actually retrieved URL. As a URL can change over the time,
(e.g., be redirected elsewhere), it is advised that the function
caller verify the value of the urlName variable at the end of
a request.
Inputs:
- docid Document identifier for the set of objects being
retrieved.
- urlName The URL to be retrieved (MAX_URL_LENGTH chars length)
- outputfile A pointer to an empty string of MAX_URL_LENGTH.
- mode The retrieval mode.
- incremental_cbf
- context_icbf
Callback and context for the incremental modes
- terminate_cbf
- context_icbf
Callback and context for a terminate handler
-error_html if TRUE, then display any server error message as an
HTML document.
- content_type a string
Outputs:
- urlName The URL that was retrieved
- outputfile The name of the temporary file which holds the
retrieved data. (Only in case of success)
- if content_type wasn't NULL, it will contain a copy of the parameter
sent in the HTTP answer
Returns:
HT_ERROR
HT_OK
----------------------------------------------------------------------*/
#ifdef __STDC__
int GetObjectWWW (int docid, char* urlName, char* formdata,
char* outputfile, int mode, TIcbf* incremental_cbf,
void* context_icbf, TTcbf* terminate_cbf,
void* context_tcbf, boolean error_html, char *content_type)
#else
int GetObjectWWW (docid, urlName, formdata, outputfile, mode,
incremental_cbf, context_icbf,
terminate_cbf, context_tcbf, error_html, content_type)
int docid;
char *urlName;
char *formdata;
char *outputfile;
int mode;
TIcbf *incremental_cbf;
void *context_icbf;
TTcbf *terminate_cbf;
void *context_tcbf;
boolean error_html;
char *content_type;
#endif
{
AHTReqContext *me;
char *ref;
int status, l;
int tempsubdir;
if (urlName == NULL || docid == 0 || outputfile == NULL)
{
/* no file to be loaded */
TtaSetStatus (docid, 1, TtaGetMessage (AMAYA, AM_BAD_URL), urlName);
if (error_html)
/* so we can show the error message */
DocNetworkStatus[docid] |= AMAYA_NET_ERROR;
InvokeGetObjectWWW_callback (docid, urlName, outputfile, terminate_cbf,
context_tcbf, HT_ERROR);
return HT_ERROR;
}
/* if it's a 'docImage', we have already downloaded it */
if (!strncmp ("internal:", urlName, 9))
{
strcpy (outputfile, urlName);
InvokeGetObjectWWW_callback (docid, urlName, outputfile,
terminate_cbf, context_tcbf, HT_OK);
return HT_OK;
}
/* do we support this protocol? */
if (IsValidProtocol (urlName) == NO)
{
/* return error */
outputfile[0] = EOS; /* file could not be opened */
TtaSetStatus (docid, 1,
TtaGetMessage (AMAYA, AM_GET_UNSUPPORTED_PROTOCOL),
urlName);
if (error_html)
/* so we can show the error message */
DocNetworkStatus[docid] |= AMAYA_NET_ERROR;
InvokeGetObjectWWW_callback (docid, urlName, outputfile, terminate_cbf,
context_tcbf, HT_ERROR);
return HT_ERROR;
}
/* we store CSS in subdir named 0; all the other files go to a subidr
named after their own docid */
tempsubdir = (mode & AMAYA_LOAD_CSS) ? 0 : docid;
/* create a tempfilename */
sprintf (outputfile, "%s%c%d%c%04dAM",
TempFileDirectory, DIR_SEP, tempsubdir, DIR_SEP, object_counter);
/* update the object_counter (used for the tempfilename) */
object_counter++;
/* normalize the URL */
ref = AmayaParseUrl (urlName, "", AMAYA_PARSE_ALL);
/* should we abort the request if we could not normalize the url? */
if (ref == (char*) NULL || ref[0] == EOS) {
/*error */
outputfile[0] = EOS;
TtaSetStatus (docid, 1, TtaGetMessage (AMAYA, AM_BAD_URL), urlName);
if (error_html)
/* so we can show the error message */
DocNetworkStatus[docid] |= AMAYA_NET_ERROR;
InvokeGetObjectWWW_callback (docid, urlName, outputfile, terminate_cbf,
context_tcbf, HT_ERROR);
return HT_ERROR;
}
/* verify if that file name existed */
if (TtaFileExist (outputfile))
TtaFileUnlink (outputfile);
/* Initialize the request structure */
me = AHTReqContext_new (docid);
if (me == NULL)
{
outputfile[0] = EOS;
/* need an error message here */
TtaFreeMemory (ref);
InvokeGetObjectWWW_callback (docid, urlName, outputfile, terminate_cbf,
context_tcbf, HT_ERROR);
return HT_ERROR;
}
/* Specific initializations for POST and GET */
if (mode & AMAYA_FORM_POST)
{
me->method = METHOD_POST;
HTRequest_setMethod (me->request, METHOD_POST);
}
else
{
me->method = METHOD_GET;
if (!HasKnownFileSuffix (ref))
HTRequest_setConversion(me->request, acceptTypes, TRUE);
}
/* Common initialization for all HTML methods */
me->mode = mode;
me->error_html = error_html;
me->incremental_cbf = incremental_cbf;
me->context_icbf = context_icbf;
me->terminate_cbf = terminate_cbf;
me->context_tcbf = context_tcbf;
/* for the async. request modes, we need to have our
own copy of outputfile and urlname
*/
if ((mode & AMAYA_ASYNC) || (mode & AMAYA_IASYNC))
{
l = strlen (outputfile);
if (l > MAX_LENGTH)
me->outputfile = TtaGetMemory (l + 2);
else
me->outputfile = TtaGetMemory (MAX_LENGTH + 2);
strcpy (me->outputfile, outputfile);
l = strlen (urlName);
if (l > MAX_LENGTH)
me->urlName = TtaGetMemory (l + 2);
else
me->urlName = TtaGetMemory (MAX_LENGTH + 2);
strcpy (me->urlName, urlName);
#ifdef _WINDOWS
/* force windows ASYNC requests to always be non preemptive */
HTRequest_setPreemptive (me->request, NO);
#endif /*_WINDOWS */
} /* AMAYA_ASYNC mode */
else
#ifdef _WINDOWS
{
me->outputfile = outputfile;
me->urlName = urlName;
/* force windows SYNC requests to always be non preemptive */
HTRequest_setPreemptive (me->request, YES);
}
#else /* !_WINDOWS */
{
me->outputfile = outputfile;
me->urlName = urlName;
}
/***
In order to take into account the stop button,
the requests will be always asynchronous, however, if mode=AMAYA_SYNC,
we will loop until the document has been received or a stop signal
generated
****/
HTRequest_setPreemptive (me->request, NO);
#endif /* _WINDOWS */
/*
** Make sure that the first request is flushed immediately and not
** buffered in the output buffer
*/
if (mode & AMAYA_FLUSH_REQUEST)
HTRequest_setFlush(me->request, YES);
HTRequest_setFlush(me->request, YES);
/* prepare the URLname that will be displayed in teh status bar */
ChopURL (me->status_urlName, me->urlName);
TtaSetStatus (me->docid, 1,
TtaGetMessage (AMAYA, AM_FETCHING),
me->status_urlName);
me->anchor = (HTParentAnchor *) HTAnchor_findAddress (ref);
TtaFreeMemory (ref);
if (mode & AMAYA_NOCACHE)
HTRequest_setReloadMode (me->request, HT_CACHE_FLUSH);
/* prepare the query string and format for POST */
if (mode & AMAYA_FORM_POST)
{
HTAnchor_setFormat ((HTParentAnchor *) me->anchor,
HTAtom_for ("application/x-www-form-urlencoded"));
HTAnchor_setLength ((HTParentAnchor *) me->anchor, me->block_size);
HTRequest_setEntityAnchor (me->request, me->anchor);
}
/* create the formdata element for libwww */
if (formdata)
me->formdata = PrepareFormdata (formdata);
/* do the request */
if (mode & AMAYA_FORM_POST)
{
/* this call doesn't give back a boolean */
HTParentAnchor * posted = NULL;
posted = HTPostFormAnchor (me->formdata, (HTAnchor *) me->anchor,
me->request);
status = posted ? YES : NO;
}
else if (formdata)
status = HTGetFormAnchor(me->formdata, (HTAnchor *) me->anchor,
me->request);
else
status = HTLoadAnchor ((HTAnchor *) me->anchor, me->request);
/* @@@ may need some special windows error msg here */
/* control the errors */
if (status == NO)
/* the request invocation failed */
{
/* show an error message on the status bar */
DocNetworkStatus[docid] |= AMAYA_NET_ERROR;
TtaSetStatus (docid, 1,
TtaGetMessage (AMAYA, AM_CANNOT_LOAD),
urlName);
if (me->reqStatus == HT_NEW)
/* manually invoke the last processing that usually gets done
in a succesful request */
InvokeGetObjectWWW_callback (docid, urlName, outputfile,
terminate_cbf, context_tcbf, HT_ERROR);
/* terminate_handler wasn't called */
AHTReqContext_delete (me);
}
else
/* end treatment for SYNC requests */
if ((mode & AMAYA_SYNC) || (mode & AMAYA_ISYNC))
{
/* wait here untilt the asynchronous request finishes */
status = LoopForStop (me);
/* if status returns HT_ERROR, should we invoke the callback? */
if (!HTRequest_kill (me->request))
AHTReqContext_delete (me);
}
/* an interface problem!!! */
return (status == YES ? 0 : -1);
}
/*----------------------------------------------------------------------
PutObjectWWW
frontend for uploading a resource to a URL. This function downloads
a file to be uploaded into memory, it then calls UploadMemWWW to
finish the job.
2 upload modes are proposed:
AMAYA_SYNC : blocking mode
AMAYA_ASYNC : non-blocking mode
When the function is called with the SYNC mode, the function will
return only when the file has been uploaded.
The ASYNC mode will immediately return after setting up the
call. Furthermore, at the end of an upload, the ASYNC mode will
call back terminate_cbf, handling it the context defined in
context_tcbf.
Notes:
At the end of a succesful request, the urlName string contains the
name of the actually uploaded URL. As a URL can change over the time,
(e.g., be redirected elsewhere), it is advised that the function
caller verifies the value of the urlName variable at the end of
a request.
Inputs:
- docid Document identifier for the set of objects being
retrieved.
- fileName A pointer to the local file to upload
- urlName The URL to be uploaded (MAX_URL_LENGTH chars length)
- mode The retrieval mode.
- terminate_cbf
- context_icbf
Callback and context for a terminate handler
Outputs:
- urlName The URL that was uploaded
Returns:
HT_ERROR
HT_OK
----------------------------------------------------------------------*/
#ifdef __STDC__
int PutObjectWWW (int docid, char *fileName, char *urlName, int mode, PicType contentType,
TTcbf * terminate_cbf, void *context_tcbf)
#else
int PutObjectWWW (docid, urlName, fileName, mode, contentType,
,terminate_cbf, context_tcbf)
int docid;
char *urlName;
char *fileName;
int mode;
PicType contentType;
TTcbf *terminate_cbf;
void *context_tcbf;
#endif /* __STDC__ */
{
AHTReqContext *me;
int status;
int fd;
struct stat file_stat;
char *fileURL;
char *etag = NULL;
HTParentAnchor *dest_anc_parent;
char *tmp;
int UsePreconditions;
UsePreconditions = mode & AMAYA_USE_PRECONDITIONS;
AmayaLastHTTPErrorMsg [0] = EOS;
if (urlName == NULL || docid == 0 || fileName == NULL
|| !TtaFileExist (fileName))
/* no file to be uploaded */
return HT_ERROR;
/* do we support this protocol? */
if (IsValidProtocol (urlName) == NO)
{
/* return error */
TtaSetStatus (docid, 1,
TtaGetMessage (AMAYA, AM_PUT_UNSUPPORTED_PROTOCOL),
urlName);
return HT_ERROR;
}
/* verify the file's size */
#ifndef _WINDOWS
if ((fd = open (fileName, O_RDONLY)) == -1)
#else
if ((fd = _open (fileName, _O_RDONLY | _O_BINARY)) == -1)
#endif /* _WINDOWS */
{
/* if we could not open the file, exit */
/*error msg here */
return (HT_ERROR);
}
fstat (fd, &file_stat);
# ifdef _WINDOWS
_close (fd);
# else /* _WINDOWS */
close (fd);
# endif /* _WINDOWS */
if (file_stat.st_size == 0)
{
/* file was empty */
/*errmsg here */
return (HT_ERROR);
}
/* prepare the request context */
if (THD_TRACE)
fprintf (stderr, "file size == %u\n", (unsigned) file_stat.st_size);
me = AHTReqContext_new (docid);
if (me == NULL)
{
/* @@ need an error message here */
TtaHandlePendingEvents ();
return (HT_ERROR);
}
me->mode = mode;
me->incremental_cbf = (TIcbf *) NULL;
me->context_icbf = (void *) NULL;
me->terminate_cbf = terminate_cbf;
me->context_tcbf = context_tcbf;
me->urlName = urlName;
me->block_size = file_stat.st_size;
/* select the parameters that distinguish a PUT from a GET/POST */
me->method = METHOD_PUT;
HTRequest_setMethod (me->request, METHOD_PUT);
me->output = stdout;
/* we are not expecting to receive any input from the server */
me->outputfile = (char *) NULL;
#ifdef _WINDOWS
/* libwww's HTParse function doesn't take into account the drive name;
so we sidestep it */
fileURL = NULL;
StrAllocCopy (fileURL, "file:");
StrAllocCat (fileURL, fileName);
#else
fileURL = HTParse (fileName, "file:/", PARSE_ALL);
#endif /* _WINDOWS */
me->source = HTAnchor_findAddress (fileURL);
HT_FREE (fileURL);
me->dest = HTAnchor_findAddress (urlName);
/* we memorize the anchor's parent @ as we use it a number of times
in the following lines */
dest_anc_parent = HTAnchor_parent (me->dest);
/*
** Set the Content-Type of the file we are uploading
*/
/* we try to use any content-type previosuly associated
with the parent. If it doesn't exist, we try to guess it
from the URL */
tmp = HTAtom_name (HTAnchor_format (dest_anc_parent));
if (!tmp || !strcmp (tmp, "www/unknown"))
{
HTAnchor_setFormat (dest_anc_parent,
AHTGuessAtom_for (me->urlName, contentType));
tmp = HTAtom_name (HTAnchor_format (dest_anc_parent));
}
/* .. and we give the same type to the source anchor */
/* we go thru setOutputFormat, rather than change the parent's
anchor, as that's the place that libwww expects it to be */
HTAnchor_setFormat (HTAnchor_parent (me->source),
HTAtom_for (tmp));
HTRequest_setOutputFormat (me->request,
HTAtom_for (tmp));
/* associate the anchor to the request */
HTRequest_setEntityAnchor (me->request, HTAnchor_parent (me->source));
/* define other request characteristics */
#ifdef _WINDOWS
HTRequest_setPreemptive (me->request, NO);
#else
HTRequest_setPreemptive (me->request, NO);
#endif /* _WINDOWS */
/*
** Make sure that the first request is flushed immediately and not
** buffered in the output buffer
*/
if (mode & AMAYA_FLUSH_REQUEST)
HTRequest_setFlush(me->request, YES);
/* Should we use preconditions? */
if (UsePreconditions)
etag = HTAnchor_etag (HTAnchor_parent (me->dest));
if (etag)
{
HTRequest_setPreconditions(me->request, HT_MATCH_THIS);
}
else
{
HTRequest_setPreconditions(me->request, HT_NO_MATCH);
HTRequest_addAfter(me->request, check_handler, NULL, NULL, HT_ALL,
HT_FILTER_MIDDLE, YES);
}
/* don't use the cache while saving a document */
HTRequest_setReloadMode (me->request, HT_CACHE_FLUSH);
/* Throw away any reponse body */
/*
HTRequest_setOutputStream (me->request, HTBlackHole());
*/
/* prepare the URLname that will be displayed in the status bar */
ChopURL (me->status_urlName, me->urlName);
TtaSetStatus (me->docid, 1, TtaGetMessage (AMAYA, AM_REMOTE_SAVING),
me->status_urlName);
/* make the request */
if (!UsePreconditions || !etag)
status = HTHeadAnchor (me->dest, me->request);
else
status = HTPutDocumentAnchor (HTAnchor_parent (me->source), me->dest, me->request);
if (status == YES && me->reqStatus != HT_ERR)
{
/* part of the stop button handler */
if ((mode & AMAYA_SYNC) || (mode & AMAYA_ISYNC))
status = LoopForStop (me);
}
if (!HTRequest_kill (me->request))
AHTReqContext_delete (me);
TtaHandlePendingEvents ();
return (status == YES ? 0 : -1);
}
/*----------------------------------------------------------------------
StopRequest
stops (kills) all active requests associated with a docid
----------------------------------------------------------------------*/
#ifdef __STDC__
void StopRequest (int docid)
#else
void StopRequest (docid)
int docid;
#endif
{
if (Amaya && CanDoStop ())
{
#if 0 /* for later */
AHTDocId_Status *docid_status;
/* verify if there are any requests at all associated with docid */
docid_status = (AHTDocId_Status *) GetDocIdStatus (docid,
Amaya->docid_status);
if (docid_status == (AHTDocId_Status *) NULL)
return;
#endif /* 0 */
/* temporary call to stop all requests, as libwww changed its API */
StopAllRequests (docid);
}
}
/*----------------------------------------------------------------------
StopAllRequests
stops (kills) all active requests. We use the docid
----------------------------------------------------------------------*/
#ifdef __STDC__
void StopAllRequests (int docid)
#else
void StopAllRequests (docid)
int docid;
#endif
{
HTList *cur;
AHTReqContext *me;
static boolean lock_stop = 0;
boolean async_flag;
/* only do the stop if we're not being called while processing a
request, and if we're not already dealing with a stop */
if (Amaya && CanDoStop () && !lock_stop)
{
#ifdef DEBUG_LIBWWW
fprintf (stderr, "StopRequest: number of Amaya requests "
"before kill: %d\n", Amaya->open_requests);
#endif /* DEBUG_LIBWWW */
/* enter the critical section */
lock_stop = TRUE;
/* expire all outstanding timers */
HTTimer_expireAll ();
/* HTNet_killAll (); */
cur = Amaya->reqlist;
while ((me = (AHTReqContext *) HTList_nextObject (cur)))
{
if (AmayaIsAlive ())
{
#ifdef DEBUG_LIBWWW
fprintf (stderr,"StopRequest: killing req %p, url %s, status %d\n", me, me->urlName, me->reqStatus);
#endif /* DEBUG_LIBWWW */
if (me->reqStatus != HT_END && me->reqStatus != HT_ABORT)
{
if ((me->mode & AMAYA_ASYNC)
|| (me->mode & AMAYA_IASYNC))
async_flag = TRUE;
else
async_flag = FALSE;
/* change the status to say that the request aborted */
me->reqStatus = HT_ABORT;
/* kill the request, using the appropriate function */
if (me->request->net)
HTNet_killPipe (me->request->net);
else
{
if (me->terminate_cbf)
(*me->terminate_cbf) (me->docid, -1, me->urlName,
me->outputfile,
me->content_type,
me->context_tcbf);
if (async_flag)
/* explicitly free the request context for async
requests. The sync requests context is freed by LoopForStop */
AHTReqContext_delete (me);
}
cur = Amaya->reqlist;
}
#ifndef _WINDOWS
#ifdef WWW_XWINDOWS
/* to be on the safe side, remove all outstanding X events */
else
RequestKillAllXtevents (me);
#endif /* WWW_XWINDOWS */
#endif /* !_WINDOWS */
}
}
/* Delete remaining channels */
HTChannel_safeDeleteAll ();
/* exit the critical section */
lock_stop = FALSE;
#ifdef DEBUG_LIBWWW
fprintf (stderr, "StopRequest: number of Amaya requests "
"after kill: %d\n", Amaya->open_requests);
#endif /* DEBUG_LIBWWW */
}
} /* StopAllRequests */
/*----------------------------------------------------------------------
AmayaIsAlive
returns the value of the AmayaAlive_flag
----------------------------------------------------------------------*/
#ifdef __STDC__
boolean AmayaIsAlive (void)
#else
boolean AmayaIsAlive ()
#endif /* _STDC_ */
{
return AmayaAlive_flag;
}
/*----------------------------------------------------------------------
CanDoStop
returns the value of the CanDoStop flag
----------------------------------------------------------------------*/
#ifdef __STDC__
boolean CanDoStop (void)
#else
boolean CanDoStop ()
#endif /* _STDC_ */
{
return CanDoStop_flag;
}
/*----------------------------------------------------------------------
CanDoStop_set
sets the value of the CanDoStop flag
----------------------------------------------------------------------*/
#ifdef __STDC__
void CanDoStop_set (boolean value)
#else
void CanDoStop (value)
boolean value;
#endif /* _STDC_ */
{
CanDoStop_flag = value;
}
#endif /* AMAYA_JAVA */
/*
end of Module query.c
*/
Webmaster