libwww_request.c

00001 /* File:      libwww_request.c
00002 ** Author(s): kifer
00003 ** Contact:   xsb-contact@cs.sunysb.edu
00004 ** 
00005 ** Copyright (C) The Research Foundation of SUNY, 2000
00006 ** 
00007 ** XSB is free software; you can redistribute it and/or modify it under the
00008 ** terms of the GNU Library General Public License as published by the Free
00009 ** Software Foundation; either version 2 of the License, or (at your option)
00010 ** any later version.
00011 ** 
00012 ** XSB is distributed in the hope that it will be useful, but WITHOUT ANY
00013 ** WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
00014 ** FOR A PARTICULAR PURPOSE.  See the GNU Library General Public License for
00015 ** more details.
00016 ** 
00017 ** You should have received a copy of the GNU Library General Public License
00018 ** along with XSB; if not, write to the Free Software Foundation,
00019 ** Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00020 **
00021 ** $Id: libwww_request.c,v 1.17 2005/02/26 19:19:51 kifer Exp $
00022 ** 
00023 */
00024 
00025 
00026 #include "libwww_util.h"
00027 #include "libwww_req.h"
00028 #include "deref.h"
00029 #include "cinterf.h"
00030 
00031 int total_number_of_requests = 0;
00032 int event_loop_runnung = FALSE;
00033 
00034 
00035 /* Calling sequence:
00036        libwww_request([req1,req2,...])
00037 
00038    Each req: functor(URL, REQUEST_Params, PARSED-Result, ERROR-Code)
00039    functor: htmlparse, xmlparse, rdfparse, fetch, header.
00040             The first two are requests to parse HTML/XML. Fetch means retrieve
00041             a page without parsing; header means retrieve header only.
00042             All except "header" could be form fillouts, which return a page or
00043             a parsed page.
00044    REQUEST_Params: [param, param, ...]
00045            Param: timeout(secs), if_modified_since(date-in-GMT-format),
00046                   authentication(realm,username,passwd),
00047                   formdata('attr-val-pair-list'),
00048                   selection(chosen-taglist,suppressed-taglist,stripped-taglist)
00049                   means:
00050                     parse only inside the tags on the chosen tag list. Stop
00051                     parsing if a suppressed tag is found. Resume if a chosen
00052                     tag is found, etc. 
00053                     Stripped tags are those that just get discarded.
00054                     selection(_,suppressed-tag-list,...) means: parse all tags
00055                     except those in the suppressed tags list.
00056                     f(chosen-tag-list,_,...) means: parse only inside the
00057                     chosen tags. 
00058 */
00059 DllExport int call_conv do_libwww_request___(void)
00060 {
00061   prolog_term request_term_list = reg_term(1), request_list_tail;
00062   int request_id=0;
00063 
00064   /* Create a new premptive client */
00065   /* note that some sites block user agents that aren't Netscape or IE.
00066      So we fool them!!! */
00067   HTProfile_newHTMLNoCacheClient("Mozilla", "6.0");
00068 
00069   /* We must enable alerts in order for authentication modules to call our own
00070      callback defined by HTAlert_add below. However, we delete all alerts other
00071      than those needed for authentication */ 
00072   HTAlert_setInteractive(YES);
00073   /* Note: we just register a function to send the credentials.
00074      We don't need to register authentication filters, because they are already
00075      registered by profile initialization */
00076   HTAlert_deleteOpcode(HT_A_PROGRESS); /* harmless, but useless */
00077   HTAlert_deleteOpcode(HT_A_MESSAGE);
00078   HTAlert_deleteOpcode(HT_A_CONFIRM);  /* the next 3 coredump, if allowed */
00079   HTAlert_deleteOpcode(HT_A_PROMPT);
00080   HTAlert_deleteOpcode(HT_A_USER_PW);
00081   /* register alert callbacks that supplies credentials */
00082   HTAlert_add(libwww_send_credentials,HT_A_USER_PW); /* usrname and password */
00083   HTAlert_add(libwww_send_credentials,HT_A_SECRET);  /* just the password    */
00084 
00085   HTPrint_setCallback(printer);
00086   HTTrace_setCallback(tracer);
00087 #if 0
00088   HTSetTraceMessageMask("sob");
00089 #endif
00090 
00091   /* use abort here, because this is a programmatic mistake */
00092   if (!is_list(request_term_list))
00093     libwww_abort_all("[LIBWWW_REQUEST] Argument must be a list of requests");
00094 
00095   request_list_tail = request_term_list;
00096   total_number_of_requests=0;
00097   event_loop_runnung = FALSE;
00098   timeout_value = -1;
00099   while (is_list(request_list_tail) && !is_nil(request_list_tail)) {
00100     request_id++;
00101     total_number_of_requests++;
00102     setup_request_structure(p2p_car(request_list_tail), request_id);
00103     request_list_tail = p2p_cdr(request_list_tail);
00104   }
00105 
00106   if (timeout_value <= 0)
00107     timeout_value = DEFAULT_TIMEOUT;
00108 
00109   /* start the event loop and begin to parse all requests in parallel */
00110   if (total_number_of_requests > 0) {
00111     /* periodic timer that kills the event loop, if it stays on due to a bug */
00112     HTTimer* timer = HTTimer_new(NULL, timer_cbf, NULL, 2*timeout_value, 1, 1);
00113 
00114 #ifdef LIBWWW_DEBUG
00115     xsb_dbgmsg((LOG_DEBUG,"***In libwww_request: Starting event loop. Total requests=%d, timeout=%d",
00116                 total_number_of_requests, timeout_value));
00117 #endif
00118 
00119     HTTimer_dispatch(timer);
00120 
00121     event_loop_runnung = TRUE;
00122     HTEventList_newLoop();
00123     /* it is important to set this to false, because otherwise,
00124        HTEventList_stopLoop might set HTEndLoop and then HTEventList_newLoop()
00125        will always exit immediately. This is a libwww bug, it seems. */
00126     event_loop_runnung = FALSE;
00127 
00128     /* expiring remaining timers is VERY important in order to avoid them
00129        kicking in at the wrong moment and killing subsequent requests */
00130 #ifdef LIBWWW_DEBUG
00131     xsb_dbgmsg((LOG_DEBUG,"***Expiring timers"));
00132 #endif
00133     HTTimer_expireAll();
00134     HTTimer_delete(timer);
00135 
00136 #ifdef LIBWWW_DEBUG
00137     xsb_dbgmsg((LOG_DEBUG,"***In libwww_request: event loop ended: total outstanding requests=%d", total_number_of_requests));
00138 #endif
00139   }
00140   
00141   /* free all registered callbacks and global preferences, so that this won't
00142      interfere with other applications */
00143   HTProfile_delete();
00144   return TRUE;
00145 }
00146 
00147 
00148 /* Sets up the libwww request structure for the request specified in
00149    PROLOG_REQ, including the request context, which contains the info about the
00150    return parameters. */
00151 PRIVATE void setup_request_structure(prolog_term req_term, int request_id)
00152 {
00153   int         status;
00154   HTAnchor    *anchor = NULL;
00155   HTRequest   *request=NULL;
00156   HTAssocList *formdata=NULL;
00157   char        *uri = NULL;
00158   char        *cwd = HTGetCurrentDirectoryURL();
00159   REQUEST_CONTEXT *context;
00160 
00161   /* Create a new request and attach the context structure to it */
00162   request=HTRequest_new();
00163   context=set_request_context(request,req_term,request_id);
00164   setup_termination_filter(request, request_termination_handler);
00165   setup_callbacks(context->type);
00166   /* get URL */
00167   uri = extract_uri(req_term,request, request_id);
00168   /* get other params */
00169   get_request_params(req_term, request);
00170 
00171   /* we set the timer only once (libwww trouble otherwise);
00172      timer must be set before achor is loaded into request */
00173   if (timeout_value <= 0 && context->timeout > 0) {
00174     timeout_value = context->timeout;
00175     HTHost_setEventTimeout(timeout_value);
00176   }
00177 
00178   formdata = (context->formdata ?
00179               get_form_params(context->formdata,request_id) : NULL);
00180 
00181   uri = HTParse(uri, cwd, PARSE_ALL);
00182   /* Create a new Anchor */
00183   anchor = HTAnchor_findAddress(uri);
00184   /* make requests to local files preemptive (synchronous)---a workaround 
00185      for a bug in Libwww */
00186   if (strncmp(uri,"file:/",6) == 0)
00187     HTRequest_setPreemptive(request,YES);
00188 
00189   /* check if the page has expired by first bringing the header */
00190   if ((context->type != HEADER) && (context->user_modtime > 0)) {
00191     HTRequest *header_req = HTRequest_new();
00192     context->is_subrequest = TRUE; /* should change to something better */
00193     context->subrequest_id++;
00194     setup_termination_filter(header_req,handle_dependent_termination);
00195     HTRequest_setPreemptive(header_req, YES);
00196     /* closing connection hangs libwww on concurrent requests
00197        HTRequest_addConnection(header_req, "close", "");
00198     */
00199     /* attach parent's context to this request */
00200     HTRequest_setContext(header_req, (void *)context);
00201     HTHeadAnchor(anchor,header_req);
00202     context->last_modtime = HTAnchor_lastModified((HTParentAnchor *)anchor);
00203 
00204     if (context->user_modtime > context->last_modtime) {
00205       /* cleanup the request and don't start it */
00206 #ifdef LIBWWW_DEBUG
00207       xsb_dbgmsg((LOG_DEBUG,"***Request %s: Page older(%d) than if-modified-since time(%d)",
00208                  RequestID(request),
00209                   context->last_modtime, context->user_modtime));
00210 #endif
00211 
00212       total_number_of_requests--;
00213       /* set result status */
00214       if (is_var(context->status_term)) {
00215         if (context->last_modtime <= 0)
00216           c2p_int(HT_ERROR, context->status_term);
00217         else
00218           c2p_int(WWW_EXPIRED_DOC, context->status_term);
00219       } else
00220         libwww_abort_all("[LIBWWW_REQUEST] Request %s: Arg 5 (Status) must be unbound variable",
00221                          RequestID(request));
00222       /* set the result params (header info); */
00223       extract_request_headers(header_req);
00224       /* terminate the result parameters list */
00225       c2p_nil(context->result_params);
00226 
00227       release_libwww_request(request);
00228       HT_FREE(uri);
00229       return;
00230     }
00231   }
00232 
00233   /* Hook up anchor to our request */
00234   switch (context->type) {
00235   case HEADER:
00236     /* header-only requests seem to require preemptive mode (otherwise timer
00237        interrupts and crashes them */
00238     HTRequest_setPreemptive(request, YES);
00239     status = (YES == HTHeadAnchor(anchor,request));
00240     break;
00241   case FETCH:
00242     {
00243       HTStream *target;
00244       HTRequest_setOutputFormat(request, WWW_SOURCE);
00245       /* redirect stream to chunk */
00246       target = HTStreamToChunk(request, &(context->result_chunk), 0);
00247       HTRequest_setOutputStream(request, target);
00248       /* then do the same as in the case of parsing */
00249       goto LBLREQUEST;
00250     }
00251   case XMLPARSE:
00252     /* This sets stream conversion for the XML request.
00253        Needed to make sure libwww doesn't treat it as an HTML stream.
00254        If libwww incorrectly recognizes an XML request, it will start calling
00255        HTML parser's callbacks instead of XML callbacks. */
00256     set_xml_conversions();
00257     HTRequest_setConversion(request, XML_converter, YES);
00258     /* www/xml forces libwww to recognize this as an XML request even if the
00259        document was received from a server that dosn't recognize XML.
00260        We can't use text/xml here, because then libwww gives error when a
00261        document with text/xml is received. Dunno why */
00262     HTRequest_setOutputFormat(request, HTAtom_for("www/xml"));
00263     goto LBLREQUEST;
00264   case RDFPARSE:
00265     /* The conversion type www/rdf is invented in order to force RDF parsing on
00266        streams that have other MIME types. */
00267     set_rdf_conversions();
00268     HTRequest_setConversion(request, RDF_converter, YES);
00269     HTRequest_setOutputFormat(request, HTAtom_for("www/rdf"));
00270     goto LBLREQUEST;
00271   case HTMLPARSE:
00272     /* We invent conversion type www/html, which forces HTML parsing even if
00273        MIME type is different, say, text/xml. */
00274     set_html_conversions();
00275     HTRequest_setConversion(request, HTML_converter, YES);
00276     HTRequest_setOutputFormat(request, HTAtom_for("www/html"));
00277     goto LBLREQUEST;
00278   LBLREQUEST:
00279     if (formdata) {
00280       if (context->method == METHOD_GET)
00281         status = (YES == HTGetFormAnchor(formdata,anchor,request));
00282       else if (context->method == METHOD_POST)
00283         status = (NULL != HTPostFormAnchor(formdata,anchor,request));
00284     } else {
00285       /* not a form request */
00286       status = (YES==HTLoadAnchor(anchor, request));
00287     }
00288     break;
00289   default:
00290     libwww_abort_all("[LIBWWW_REQUEST] Request %d: Invalid request type",
00291                      request_id);
00292   }
00293 
00294 #ifdef LIBWWW_DEBUG_TERSE
00295   switch (context->type) {
00296   case HTMLPARSE:
00297     xsb_dbgmsg((LOG_DEBUG,"***Request %d: request type: htmlparse", request_id));
00298     break;
00299   case XMLPARSE:
00300     xsb_dbgmsg((LOG_DEBUG,"***Request %d: request type: xmlparse", request_id));
00301     break;
00302   case RDFPARSE:
00303     xsb_dbgmsg((LOG_DEBUG,"***Request %d: request type: rdfparse", request_id));
00304     break;
00305   case HEADER:
00306     xsb_dbgmsg((LOG_DEBUG,"***Request %d: request type: header", request_id));
00307     break;
00308   case FETCH:
00309     xsb_dbgmsg((LOG_DEBUG,"***Request %d: request type: fetch", request_id));
00310     break;
00311   default:
00312     xsb_dbgmsg((LOG_DEBUG,"***Request %d: request type: invalid", request_id));
00313   }
00314   if (formdata)
00315     xsb_dbgmsg((LOG_DEBUG,"***Request %d: HTTP Method: %s, preemptive: %d",
00316                request_id,
00317                (context->method==METHOD_GET ? "FORM,GET" : "FORM,POST"),
00318                 HTRequest_preemptive(request)));
00319   else
00320     xsb_dbgmsg((LOG_DEBUG,"***Request %d: HTTP Method: NON-FORM REQ, preemptive: %d",
00321                 request_id, HTRequest_preemptive(request)));
00322 #endif
00323 
00324   if (formdata) HTAssocList_delete(formdata);
00325 
00326   /* bad uri syntax */
00327   if (!status) {
00328 #ifdef LIBWWW_DEBUG
00329     xsb_dbgmsg((LOG_DEBUG,"***In setup_request_structure: Request %d failed: bad uri",
00330                 request_id));
00331 #endif
00332     total_number_of_requests--;
00333     if (is_var(context->status_term))
00334       c2p_int(WWW_URI_SYNTAX, context->status_term);
00335     else
00336       libwww_abort_all("[LIBWWW_REQUEST] Request %s: Arg 5 (Status) must be unbound variable",
00337                        RequestID(request));
00338 
00339     c2p_nil(context->result_params);
00340     release_libwww_request(request);
00341   }
00342   HT_FREE(uri);
00343 }
00344 
00345 
00346 /* In XML parsing, we sometimes have to issue additional requests to go and
00347    fetch external entities. Here we handle termination of such subrequests.
00348    A subrequest is independent of its parent request, except that it inherits
00349    the parent's id and context. When a subrequest returns, it should NOT
00350    release the context. */
00351 PRIVATE int handle_dependent_termination(HTRequest   *request,
00352                                           HTResponse  *response,
00353                                           void        *param,
00354                                           int         status)
00355 {
00356   REQUEST_CONTEXT *context = (REQUEST_CONTEXT *)HTRequest_context(request);
00357 #ifdef LIBWWW_DEBUG
00358   xsb_dbgmsg((LOG_DEBUG,"***In handle_dependent_termination(%s): user_modtime=%d status=%d",
00359               RequestID(request), context->user_modtime, status));
00360 #endif
00361 
00362   /* the following conditions are handled by standard libwww filters */
00363   if (context->retry && AUTH_OR_REDIRECTION(status))
00364     return HT_OK; /* this causes other filters to be used */
00365 
00366   if (status != HT_LOADED)
00367     report_synch_subrequest_status(request, status);
00368 
00369   /* Note: this still preserves the anchor and the context; we use them after
00370      this call to extract last modified time from the header */
00371   HTRequest_clear(request);
00372   /* restore parent process' context */
00373   context->is_subrequest = FALSE;
00374   return !HT_OK;
00375 }
00376 
00377 
00378 
00379 PRIVATE void libwww_abort_all(char *msg, ...)
00380 {
00381   va_list args;
00382   char buf[MAXBUFSIZE];
00383 
00384   HTNet_killAll();
00385   va_start(args, msg);
00386   vsprintf(buf, msg, args);
00387   xsb_abort(buf);
00388   va_end(args);
00389 }
00390 
00391 
00392 void report_synch_subrequest_status(HTRequest *request, int status)
00393 {
00394   prolog_term uri_term=p2p_new(), error_term=p2p_new();
00395   REQUEST_CONTEXT *context = (REQUEST_CONTEXT *)HTRequest_context(request);
00396   char *uri = HTAnchor_physical(HTRequest_anchor(request));
00397   c2p_string(uri,uri_term);
00398   c2p_int(status, error_term);
00399   add_result_param(&(context->result_params),
00400                    "subrequest",2,uri_term,error_term);
00401 }
00402 
00403 
00404 void report_asynch_subrequest_status(HTRequest *request, int status)
00405 {
00406   REQUEST_CONTEXT *context = (REQUEST_CONTEXT *)HTRequest_context(request);
00407   char *uri = HTAnchor_physical(HTRequest_anchor(request));
00408 
00409   c2p_functor("subrequest",2,context->result_params);
00410   c2p_string(uri,p2p_arg(context->result_params,1));
00411   c2p_int(status, p2p_arg(context->result_params,2));
00412 }
00413 
00414 
00415 PRIVATE REQUEST_CONTEXT *set_request_context(HTRequest *request,
00416                                              prolog_term req_term,
00417                                              int request_id)
00418 {
00419   REQUEST_CONTEXT *context;
00420 
00421   if ((context=(REQUEST_CONTEXT *)calloc(1,sizeof(REQUEST_CONTEXT))) == NULL)
00422     libwww_abort_all("[LIBWWW_REQUEST] Not enough memory");
00423 
00424   context->request_id = request_id;
00425   context->subrequest_id = 0;
00426   context->suppress_is_default = FALSE;
00427   context->convert2list = FALSE;
00428   context->statusOverride = 0;
00429   context->is_subrequest = FALSE;
00430   context->userdata = NULL;
00431   context->last_modtime = 0;
00432   context->timeout = DEFAULT_TIMEOUT;
00433   context->user_modtime = 0;
00434   context->formdata=0;
00435   context->auth_info.realm = "";
00436   context->auth_info.uid = "foo";
00437   context->auth_info.pw = "foo";
00438   context->retry = TRUE;
00439   context->method = METHOD_GET;
00440   context->selected_tags_tbl.table = NULL;
00441   context->suppressed_tags_tbl.table = NULL;
00442   context->stripped_tags_tbl.table = NULL;
00443 
00444   context->type = get_request_type(req_term, request_id);
00445   context->result_chunk = NULL;
00446 
00447   init_htable(&(context->selected_tags_tbl),
00448               SELECTED_TAGS_TBL_SIZE,context->type);
00449   init_htable(&(context->suppressed_tags_tbl),
00450               SUPPRESSED_TAGS_TBL_SIZE,context->type);
00451   init_htable(&(context->stripped_tags_tbl),
00452               STRIPPED_TAGS_TBL_SIZE,context->type);
00453   /* output */
00454   context->result_params = p2p_arg(req_term,3);
00455   if(!is_var(context->result_params))
00456     libwww_abort_all("[LIBWWW_REQUEST] Request %d: Arg 3 (Result parameters) must be unbound variable", request_id);
00457   c2p_list(context->result_params);
00458 
00459   context->request_result = p2p_arg(req_term,4);
00460   if(!is_var(context->request_result))
00461     libwww_abort_all("[LIBWWW_REQUEST] Request %d: Arg 4 (Result) must be unbound variable", request_id);
00462 
00463   context->status_term = p2p_arg(req_term,5);
00464   if(!is_var(context->status_term))
00465     libwww_abort_all("[LIBWWW_REQUEST] Request %d: Arg 5 (Status) must be unbound variable", request_id);
00466 
00467   /* attach context to the request */
00468   HTRequest_setContext(request, (void *) context);
00469 
00470 #ifdef LIBWWW_DEBUG
00471   xsb_dbgmsg((LOG_DEBUG,"***Request %d: context set", request_id));
00472 #endif
00473 
00474   return context;
00475 }
00476 
00477 
00478 PRIVATE void free_request_context (REQUEST_CONTEXT *context)
00479 {
00480   AUTHENTICATION *next_auth, *curr_auth;
00481   if (!context) return;
00482   free_htable(&(context->selected_tags_tbl));
00483   free_htable(&(context->suppressed_tags_tbl));
00484   free_htable(&(context->stripped_tags_tbl));
00485   /* Note: we don't need to free context->result_chunk, since HTChunk_toCString
00486      deleted the chunk object, and we freed the chunk data earlier. */
00487   /* release authentication info, unless it is a subrequest (because request
00488      and subrequest share authinfo) */
00489   if (!(context->is_subrequest)) {
00490     next_auth = context->auth_info.next;
00491     while (next_auth) {
00492       curr_auth = next_auth;
00493       next_auth=next_auth->next;
00494       free(curr_auth);
00495     }
00496   }
00497   free(context);
00498 }
00499 
00500 
00501 /* Copy FROM to TO and lowercase on the way; assume TO is large enough */
00502 void strcpy_lower(char *to, const char *from)
00503 {
00504   int i=0;
00505   if (from)
00506     while (from[i]) {
00507       to[i] = tolower(from[i]);
00508       i++;
00509     }
00510   to[i] = '\0';
00511 }
00512 
00513 
00514 void print_prolog_term(prolog_term term, char *message)
00515 { 
00516   static XSB_StrDefine(StrArgBuf);
00517   prolog_term term2 = p2p_deref(term);
00518   XSB_StrSet(&StrArgBuf,"");
00519   print_pterm(term2, 1, &StrArgBuf); 
00520 #ifdef LIBWWW_DEBUG
00521   xsb_dbgmsg((LOG_DEBUG,"***%s = %s", message, StrArgBuf.string));
00522 #endif
00523 } 
00524 
00525 
00526 /* these are for tracing */
00527 PRIVATE int printer (const char * fmt, va_list pArgs)
00528 {
00529     return (vfprintf(stdout, fmt, pArgs));
00530 }
00531 
00532 PRIVATE int tracer (const char * fmt, va_list pArgs)
00533 {
00534     return (vfprintf(stderr, fmt, pArgs));
00535 }
00536 
00537 
00538 /* This procedure gets the credentials from the input parameters
00539    and passes them back through the REPLY argument.
00540    For this to happen, alerts must be enabled. */
00541 BOOL libwww_send_credentials(HTRequest * request, HTAlertOpcode op,
00542                              int msgnum, const char * dfault, void * realm,
00543                              HTAlertPar * reply)
00544 {
00545   REQUEST_CONTEXT *context = (REQUEST_CONTEXT *)HTRequest_context(request);
00546   AUTHENTICATION *authinfo = &(context->auth_info);
00547   AUTHENTICATION *credentials;
00548 
00549 #ifdef LIBWWW_DEBUG
00550   xsb_dbgmsg((LOG_DEBUG,"***In libwww_send_credentials: Request=%s, realm: '%s' msgnum=%d",
00551               RequestID(request), realm, msgnum));
00552 #endif
00553 
00554   /* don't authenticate on retry */
00555   context->retry = FALSE;
00556 
00557   credentials = find_credentials(authinfo,realm);
00558   if (credentials) {
00559     /* have authentication info */
00560     HTAlert_setReplyMessage(reply, credentials->uid);
00561     HTAlert_setReplySecret(reply, credentials->pw);
00562     return TRUE;
00563   }
00564   /* if no credentials supplied, send some phony stuff */
00565   HTAlert_setReplyMessage(reply, "foo");
00566   HTAlert_setReplySecret(reply, "foo");
00567   return TRUE;
00568 }
00569 
00570 
00571 PRIVATE AUTHENTICATION *find_credentials(AUTHENTICATION *auth_info,char *realm)
00572 {
00573   AUTHENTICATION *credentials = auth_info;
00574 
00575   while (credentials) {
00576     if ((credentials->realm == NULL)
00577         || (strcmp(credentials->realm, realm) == 0))
00578       return credentials;
00579     credentials = credentials->next;
00580   }
00581   return NULL;
00582 }
00583 
00584 
00585 PRIVATE char *extract_uri(prolog_term req_term, HTRequest *request,
00586                           int request_id)
00587 {
00588   /*
00589   static  XSB_StrDefine(uristr);
00590   */
00591   int     urilen;
00592   char    *uri;
00593   prolog_term uri_term;
00594 
00595   uri_term=p2p_arg(req_term,1);
00596   if (is_charlist(uri_term, &urilen)) {
00597     ((REQUEST_CONTEXT *)HTRequest_context(request))->convert2list=TRUE;
00598     /*
00599     p2c_chars(uri_term, &uristr);
00600     uri = uristr.string;
00601     */
00602     p2c_chars(uri_term, uri, urilen);
00603   } else if (is_string(uri_term))
00604     uri=string_val(uri_term);
00605   else {
00606     release_libwww_request(request);
00607     /* use abort here, because it is a programmatic mistake */
00608     libwww_abort_all("[LIBWWW_REQUEST] Request %d: Arg 1 (URI) must be an atom or a string", request_id);
00609   }
00610   return uri;
00611 }
00612 
00613 
00614 PRIVATE void release_libwww_request(HTRequest *request)
00615 {
00616   free_request_context((REQUEST_CONTEXT *) HTRequest_context(request));
00617   HTRequest_kill(request);
00618 }
00619 
00620 
00621 /* Extraction of individual parameters from the request_params argument */
00622 PRIVATE void get_request_params(prolog_term req_term, HTRequest *request)
00623 {
00624   prolog_term param, req_params=p2p_arg(req_term,2);
00625   char *paramfunctor;
00626   REQUEST_CONTEXT *context = (REQUEST_CONTEXT *)HTRequest_context(request);
00627 
00628   if (!is_list(req_params) && !is_var(req_params) && !is_nil(req_params))
00629     libwww_abort_all("[LIBWWW_REQUEST] Request %s: Arg 2 (Request params) must be a list or a variable",
00630                      RequestID(request));
00631   while(is_list(req_params) && !is_nil(req_params)) {
00632     param = p2p_car(req_params);
00633     paramfunctor = p2c_functor(param);
00634 
00635     switch (paramfunctor[0]) {
00636     case 't': case 'T': /* user-specified timeout */ 
00637       if (!is_int(p2p_arg(param, 1)))
00638         libwww_abort_all("[LIBWWW_REQUEST] Request %s: Timeout parameter must be an integer",
00639                          RequestID(request));
00640       context->timeout = p2c_int(p2p_arg(param, 1)) * 1000;
00641       if (context->timeout <= 0)
00642         context->timeout = DEFAULT_TIMEOUT;
00643       break;
00644     case 'i': case 'I': /* if-modified-since */
00645       if (!is_string(p2p_arg(param, 1)))
00646         libwww_abort_all("[LIBWWW_REQUEST] Request %s: If_modified_since parameter must be a string",
00647                   RequestID(request));
00648       context->user_modtime =
00649         (long)HTParseTime(string_val(p2p_arg(param,1)), NULL, YES);
00650       break;
00651     case 'a': case 'A': {  /* authorization */
00652       prolog_term auth_head, auth_tail=p2p_arg(param,1);
00653       AUTHENTICATION *auth_info = &(context->auth_info);
00654       
00655       do {
00656         if (is_list(auth_tail)) {
00657           auth_head=p2p_car(auth_tail);
00658           auth_tail=p2p_cdr(auth_tail);
00659         } else auth_head = auth_tail;
00660 
00661         if (is_string(p2p_arg(auth_head, 1)))
00662           auth_info->realm = p2c_string(p2p_arg(auth_head, 1));
00663         else auth_info->realm = NULL;
00664         if (is_string(p2p_arg(auth_head, 2)))
00665           auth_info->uid = p2c_string(p2p_arg(auth_head, 2));
00666         else auth_info->uid = NULL;
00667         /* passwd is always required in auth info */
00668         if (is_string(p2p_arg(auth_head, 3)))
00669           auth_info->pw = p2c_string(p2p_arg(auth_head, 3));
00670         else auth_info->pw = NULL;
00671 
00672         if (is_list(auth_tail) && !is_nil(auth_tail)) {
00673           auth_info->next = (AUTHENTICATION *)calloc(1,sizeof(AUTHENTICATION));
00674           auth_info = auth_info->next;
00675         } else break;
00676 
00677       } while (TRUE);
00678       auth_info->next=NULL;
00679       break;
00680     }
00681     case 'f': case 'F':  /* formdata: name/value pair list to fill out forms */
00682       context->formdata = p2p_arg(param, 1);
00683       break;
00684     case 'm': case 'M': {  /* HTTP method: GET/POST/PUT */
00685       char *method = p2c_string(p2p_arg(param, 1));
00686       switch (method[1]) {
00687       case 'O': case 'o':
00688         context->method = METHOD_POST;
00689         break;
00690       case 'E': case 'e':
00691         context->method = METHOD_GET;
00692         break;
00693       case 'P': case 'p':
00694         context->method = METHOD_PUT;
00695         break;
00696       }
00697       break;
00698     }
00699     case 's': case 'S':  /* selection of tags to parse */
00700       /* tag selection: selection(chosen-list,suppressed-list,strip-list) */
00701       if (p2c_arity(param)==3) {
00702         prolog_term
00703           select_term=p2p_arg(param,1),
00704           suppressed_term=p2p_arg(param,2),
00705           strip_term=p2p_arg(param,3);
00706         
00707         if (is_var(select_term))
00708           context->suppress_is_default=FALSE;
00709         else if (is_list(select_term)) {
00710           context->suppress_is_default=TRUE;
00711           init_tag_table(select_term, &(context->selected_tags_tbl));
00712         } else
00713           libwww_abort_all("[LIBWWW_REQUEST] Request %d: In Arg 2, selection(CHOOSE,_,_): CHOOSE must be a var or a list");
00714 
00715         if (is_list(suppressed_term)) {
00716           init_tag_table(suppressed_term, &(context->suppressed_tags_tbl));
00717         } else if (!is_var(suppressed_term))
00718           libwww_abort_all("[LIBWWW_REQUEST] Request %s: In Arg 2, selection(_,SUPPRESS,_): SUPPRESS must be a var or a list",
00719                            RequestID(request));
00720         
00721         if (is_list(strip_term)) {
00722           init_tag_table(strip_term, &(context->stripped_tags_tbl));
00723         } else if (!is_var(strip_term))
00724           libwww_abort_all("[LIBWWW_REQUEST] Request %s: In Arg 2, selection(_,_,STRIP): STRIP must be a var or a list",
00725                            RequestID(request));
00726       } else {
00727         libwww_abort_all("[LIBWWW_REQUEST] Request %s: In Arg 2, wrong number of arguments in selection parameter",
00728                          RequestID(request));
00729       }
00730       break;
00731     default:  /* ignore unknown params */
00732       break;
00733     }
00734     req_params = p2p_cdr(req_params);
00735   } 
00736   return;
00737 }
00738 
00739 
00740 
00741 PRIVATE HTAssocList *get_form_params(prolog_term form_params, int request_id)
00742 {
00743   HTAssocList *formfields=NULL;
00744 
00745   if (!is_list(form_params))
00746     libwww_abort_all("[LIBWWW_REQUEST] Request %d: List of form parameters must not be empty",
00747                      request_id);
00748   
00749   while (!is_nil(form_params)) {
00750     prolog_term head;
00751     char *string;
00752 
00753     head = p2p_car(form_params);
00754     if (is_string(head))
00755       string = p2c_string(head);
00756     else
00757       libwww_abort_all("[LIBWWW_REQUEST] Request %d: Non-string in form parameter list",
00758                        request_id);
00759 
00760     form_params = p2p_cdr(form_params);
00761                 
00762     /* create a list to hold the form arguments */
00763     if (!formfields) formfields = HTAssocList_new();
00764 
00765     /* parse the content and add it to the association list */
00766     HTParseFormInput(formfields, string);
00767   }
00768   return formfields;
00769 }
00770 
00771 
00772 PRIVATE REQUEST_TYPE get_request_type(prolog_term req_term, int request_id)
00773 {
00774   char *functor;
00775   if (!is_functor(req_term)) {
00776     libwww_abort_all("[LIBWWW_REQUEST] Request %d: Bad request syntax",
00777                      request_id);
00778   }
00779   functor = p2c_functor(req_term);
00780 
00781   if (strncmp("fetch",functor,3)==0) return FETCH;
00782   if (strncmp("xmlparse",functor,3)==0) return XMLPARSE;
00783   if (strncmp("rdfparse",functor,3)==0) return RDFPARSE;
00784   if (strncmp("htmlparse",functor,3)==0) return HTMLPARSE;
00785   if (strncmp("header",functor,3)==0) return HEADER;
00786   libwww_abort_all("[LIBWWW_REQUEST] Request %d: Invalid request type: %s",
00787                    request_id, functor);
00788   return TRUE; /* just to pacify the compiler */
00789 }
00790 
00791 
00792 PRIVATE void init_htable(HASH_TABLE *htable, int size, REQUEST_TYPE type)
00793 {
00794   int i;
00795   /* RDFPARSE, FETCH, and HEADER requests don't use the hash table */
00796   if ((type != XMLPARSE) && (type != HTMLPARSE)) {
00797     htable->table = NULL;
00798     return;
00799   }
00800 
00801   htable->type = type;
00802   htable->size = size;
00803   if ((htable->table=(HKEY *)calloc(size, sizeof(HKEY))) == NULL )
00804     libwww_abort_all("[LIBWWW_REQUEST] Not enough memory");
00805   for (i=0; i<size; i++)
00806     if (type == HTMLPARSE)
00807       htable->table[i].intkey = -1;
00808     else /* XML */
00809       htable->table[i].strkey = NULL;
00810 }
00811 
00812 
00813 PRIVATE void free_htable(HASH_TABLE *htable)
00814 {
00815   if (!htable || !htable->table) return;
00816   if (htable->type == HTMLPARSE) {
00817     free(htable->table);
00818   } else if (htable->type == XMLPARSE) {
00819     int i;
00820     if (!htable) return;
00821     for (i=0; i < htable->size; i++)
00822       if (htable->table[i].strkey != NULL)
00823         free(htable->table[i].strkey);
00824     free(htable->table);
00825   } else return;
00826 }
00827 
00828 
00829 PRIVATE unsigned long key2int(HKEY s, REQUEST_TYPE type)
00830 {
00831   if (type == HTMLPARSE)
00832     return s.intkey;
00833   else if (type == XMLPARSE) {
00834     unsigned long h = 0;
00835     while (*(s.strkey))
00836       h = (h << 5) + h + (unsigned char)*(s.strkey)++;
00837     return h;
00838   }
00839   return 0; /* this is just to make the compiler happy */
00840 }
00841 
00842 
00843 #define FREE_CELL(hkey,type) \
00844      (type==HTMLPARSE ? (hkey.intkey==-1) : (hkey.strkey==NULL))
00845 
00846 int add_to_htable(HKEY item, HASH_TABLE *htable)
00847 {
00848   int idx, i;
00849   if (!htable || !htable->table) return FALSE;
00850 
00851   idx = (int) (key2int(item, htable->type) % htable->size);
00852   i = idx;
00853   while (!FREE_CELL(htable->table[i], htable->type)) {
00854     i++;
00855     i = i % htable->size;
00856     if (i == idx) /* reached full circle */
00857       return FALSE;
00858   }
00859   /* found spot */
00860   if (htable->type==HTMLPARSE)
00861     htable->table[i].intkey = item.intkey;
00862   else {
00863     htable->table[i].strkey = (char *)malloc(strlen(item.strkey)+1);
00864     strcpy_lower(htable->table[i].strkey, item.strkey);
00865   }
00866   return TRUE;
00867 }
00868 
00869 
00870 #define HASH_CELL_EQUAL(cell,item,type) \
00871     (type==HTMLPARSE ? (cell.intkey==item.intkey) \
00872                      : (cell.strkey && strcasecmp(cell.strkey,item.strkey)==0))
00873 
00874 /* hash table stuff; deals with HKEY's stored in a table */
00875 int is_in_htable(const HKEY item, HASH_TABLE *htable)
00876 {
00877   int idx, i;
00878   if (!htable || !htable->table) return FALSE;
00879 
00880   idx = (int) (key2int(item,htable->type) % htable->size);
00881   i = idx;
00882   while (!FREE_CELL(htable->table[i], htable->type)) {
00883     if (HASH_CELL_EQUAL(htable->table[i], item, htable->type)) {
00884       return TRUE;
00885     }
00886     i++;
00887     i = i % htable->size;
00888     if (i == idx) /* reached full circle */
00889       return FALSE;
00890   }
00891   return FALSE;
00892 }
00893 
00894 
00895 /* This is a per-request termination handler */
00896 PRIVATE int request_termination_handler (HTRequest   *request,
00897                                          HTResponse  *response,
00898                                          void        *param,
00899                                          int         status)
00900 {
00901   REQUEST_CONTEXT *context = ((REQUEST_CONTEXT *)HTRequest_context(request));
00902   USERDATA *userdata = (USERDATA *)(context->userdata);
00903 
00904 #ifdef LIBWWW_DEBUG
00905   xsb_dbgmsg((LOG_DEBUG,"***Request %s: In request_termination_handler, status %d",
00906               RequestID(request), status));
00907 #endif
00908 
00909   /* the following conditions are handled by standard libwww filters */
00910   if (context->retry && AUTH_OR_REDIRECTION(status))
00911     return HT_OK; /* this causes other filters to be used */
00912 
00913   /* Redirection code is commented out. It is better handled by the standard
00914      Libwww redirection/proxy handling filters */
00915 #if 0
00916   if (status == HT_TEMP_REDIRECT || status == HT_PERM_REDIRECT ||
00917         status == HT_FOUND || status == HT_SEE_OTHER) {
00918     HTAnchor *redirection = HTResponse_redirection(response);
00919     /* if loaded redirection successfully, then return: the request will be
00920        processed by the existing event loop; otherwise, drop down and terminate
00921        the request */ 
00922     if (YES==HTLoadAnchor(redirection,request))
00923       return !HT_OK;
00924   }
00925 #endif
00926 
00927   if (total_number_of_requests > 0)
00928     total_number_of_requests--;
00929   /* when the last request is done, stop the event loop */
00930   if ((total_number_of_requests == 0) && event_loop_runnung) {
00931     HTEventList_stopLoop();
00932     event_loop_runnung = FALSE;
00933 #ifdef LIBWWW_DEBUG
00934     xsb_dbgmsg((LOG_DEBUG,"***In request_termination_handler: event loop halted, status=%d, HTNetCount=%d",
00935                 status, HTNet_count()));
00936 #endif
00937   }
00938 
00939   status = (context->statusOverride ? context->statusOverride : status);
00940   if (userdata)
00941     userdata->status = status;
00942   /* we should have checked already that status is a var */
00943   if (is_var(context->status_term))
00944     c2p_int(status, context->status_term);
00945 
00946   extract_request_headers(request);
00947   /* terminate the result parameters list */
00948   c2p_nil(context->result_params);
00949 
00950   /* Clean Up */
00951   if (userdata) {
00952     (userdata->delete_method)(userdata);
00953   } else if (context->type == FETCH) {
00954     char *result_as_string = HTChunk_toCString(context->result_chunk);
00955 
00956     if (!is_var(context->request_result))
00957       libwww_abort_all("[LIBWWW_REQUEST] Request %s: Arg 4 (Result) must be unbound variable",
00958                        RequestID(request));
00959 
00960     if (result_as_string) {
00961       if (context->convert2list)
00962         c2p_chars(result_as_string, context->request_result);
00963       else c2p_string(result_as_string, context->request_result);
00964     }
00965     /* Note: HTChunk_toCString frees the chunk, and here we free the chank
00966        data. Thus, the chunk is completely cleared out. */
00967     HT_FREE(result_as_string);
00968   }
00969 
00970 #ifdef LIBWWW_DEBUG
00971   xsb_dbgmsg((LOG_DEBUG,"***In request_termination_handler: Cleanup: request %s, status=%d remaining requests: %d",
00972               RequestID(request), status, total_number_of_requests));
00973 #endif
00974 
00975   release_libwww_request(request);
00976   /* when a filter returns something other than HT_OK, no other after filters
00977      will be called */
00978   return !HT_OK;
00979 }
00980 
00981 
00982 PRIVATE void setup_callbacks(REQUEST_TYPE type)
00983 {
00984   switch (type) {
00985   case HTMLPARSE:
00986     html_register_callbacks();
00987     break;
00988   case XMLPARSE:
00989     /* Register our new XML Instance handler */
00990     HTXMLCallback_registerNew(HTXML_newInstance, NULL);
00991     break;
00992   case RDFPARSE:
00993     HTRDF_registerNewParserCallback(libwww_newRDF_parserHandler, NULL);
00994     break;
00995   case FETCH:
00996     break;
00997   case HEADER:
00998     break;
00999   }
01000 }
01001 
01002 
01003 PRIVATE void init_tag_table(prolog_term tag_list, HASH_TABLE *tag_tbl)
01004 {
01005   prolog_term tail, head;
01006   int i=0;
01007   char *tagname;
01008   HKEY taghandle;
01009   if ((tag_tbl->type != XMLPARSE) && (tag_tbl->type != HTMLPARSE))
01010     return;
01011   /* Save tag numbers in the table */
01012   tail=tag_list;
01013   while (is_list(tail) && !is_nil(tail) && i < tag_tbl->size) {
01014     head= p2p_car(tail);
01015     tail=p2p_cdr(tail);
01016     tagname = string_val(head);
01017     if (tag_tbl->type == XMLPARSE)
01018       taghandle = (HKEY)tagname;
01019     else 
01020       taghandle = (HKEY)(strcasecmp(tagname,"pcdata")==0?
01021                          PCDATA_SPECIAL
01022                          : SGML_findElementNumber(HTML_dtd(), tagname));
01023     add_to_htable(taghandle, tag_tbl);
01024     i++;
01025   }
01026 }
01027 
01028 
01029 /* Add term to the result parameter list. FUNCTOR is the name of the functor to
01030    use for this parameter; CNT is how many args to pass. The rest must be
01031    prolog terms that represent what is to appear inside */
01032 void add_result_param(prolog_term *result_param, 
01033                       char *functor, int cnt, ...)
01034 {
01035   prolog_term listHead;
01036   int i;
01037   va_list ap;
01038 
01039 #ifdef LIBWWW_DEBUG_VERBOSE
01040   xsb_dbgmsg((LOG_DEBUG,"***Starting add_result_param"));
01041 #endif
01042 
01043   XSB_Deref(*result_param);
01044   if (is_list(*result_param))
01045     listHead = p2p_car(*result_param);
01046   else {
01047     print_prolog_term(*result_param, "In add_result_param: result_param");
01048     libwww_abort_all("[LIBWWW_REQUEST] Bug: result_param is not a list");
01049   }
01050   c2p_functor(functor, cnt, listHead);
01051   va_start(ap,cnt);
01052   for (i=0; i<cnt; i++)
01053     p2p_unify(va_arg(ap, prolog_term), p2p_arg(listHead, i+1));
01054   va_end(ap);
01055 
01056 #ifdef LIBWWW_DEBUG_VERBOSE
01057   print_prolog_term(listHead, "In add_result_param: listHead");
01058 #endif
01059   *result_param = p2p_cdr(*result_param);
01060   c2p_list(*result_param);
01061 }
01062 
01063 
01064 /* returns an uninstantiated var, which is a placeholder in the list of result
01065    parameters. The list of params is moved one elt down */
01066 PRIVATE prolog_term get_result_param_stub(prolog_term *result_param)
01067 {
01068   prolog_term listHead;
01069 
01070   XSB_Deref(*result_param);
01071   if (is_list(*result_param))
01072     listHead = p2p_car(*result_param);
01073   else {
01074     print_prolog_term(*result_param, "In get_result_param_stub: result_param");
01075     libwww_abort_all("[LIBWWW_REQUEST] Bug: result_param is not a list");
01076   }
01077   *result_param = p2p_cdr(*result_param);
01078   c2p_list(*result_param);
01079   return listHead;
01080 }
01081 
01082 
01083 /* extract request headers and add then to the result parameters kept in the
01084    request context */
01085 PRIVATE void extract_request_headers(HTRequest *request)
01086 {
01087   HTParentAnchor * anchor;
01088   HTAssocList * headers;
01089   prolog_term paramvalue_term=p2p_new(), paramname_term=p2p_new();
01090   REQUEST_CONTEXT *context = (REQUEST_CONTEXT *)HTRequest_context(request);
01091 
01092   anchor = HTRequest_anchor(request);
01093   headers = HTAnchor_header(anchor);
01094   if (headers) {
01095     HTAssocList *cur = headers;
01096     HTAssoc *pres;
01097     char *paramname, *paramvalue;
01098 
01099     while ((pres = (HTAssoc *) HTAssocList_nextObject(cur))) {
01100       paramname = HTAssoc_name(pres);
01101       paramvalue = HTAssoc_value(pres);
01102       c2p_string(paramvalue, paramvalue_term);
01103       c2p_string(paramname, paramname_term);
01104 
01105       add_result_param(&(context->result_params),
01106                        "header",2,paramname_term,paramvalue_term);
01107 
01108       /* save the page last_modified time */
01109       if (HTStrCaseMatch("Last-Modified", paramname))
01110         context->last_modtime = (long)HTParseTime(paramvalue,NULL,YES);
01111     }
01112   }
01113 }
01114 
01115 
01116 PRIVATE int timer_cbf(HTTimer *timer, void *param, HTEventType type)
01117 {
01118   return !HT_OK;
01119 }
01120 
01121 
01122 int verifyMIMEformat(HTRequest *request, REQUEST_TYPE type)
01123 {
01124   if (((REQUEST_CONTEXT *)HTRequest_context(request))->type == type)
01125     return TRUE;
01126   /*
01127     xsb_warn("LIBWWW_REQUEST Bug: Request %s Request type/MIME type mismatch",
01128              RequestID(request));
01129   */
01130   return FALSE;
01131 }
01132 
01133 
01134 char *RequestID(HTRequest *request)
01135 {
01136   REQUEST_CONTEXT *context = (REQUEST_CONTEXT *)HTRequest_context(request);
01137   static char idstr[200];
01138 
01139   if (!context) return "null";
01140 
01141   if (context->is_subrequest)
01142     sprintf(idstr, "%d.%d", context->request_id, context->subrequest_id);
01143   else
01144     sprintf(idstr, "%d", context->request_id);
01145 
01146   return idstr;
01147 }
01148 
01149 
01150 REQUEST_CONTEXT *set_subrequest_context(HTRequest *request,
01151                                         HTRequest *subrequest,
01152                                         prolog_term result_term)
01153 {
01154   REQUEST_CONTEXT *parent_context =
01155     (REQUEST_CONTEXT *)HTRequest_context(request);
01156   REQUEST_CONTEXT *context;
01157   HTStream *target;
01158 
01159   if ((context=(REQUEST_CONTEXT *)calloc(1,sizeof(REQUEST_CONTEXT))) == NULL)
01160     libwww_abort_all("[LIBWWW_REQUEST] Not enough memory");
01161 
01162   context->request_id = parent_context->request_id;
01163   context->subrequest_id = ++(parent_context->subrequest_id);
01164   context->suppress_is_default = FALSE;
01165   context->convert2list = parent_context->convert2list;
01166   context->statusOverride = 0;
01167   context->is_subrequest = TRUE;
01168   context->userdata = NULL;
01169   context->last_modtime = 0;
01170   context->timeout = DEFAULT_TIMEOUT;
01171   context->user_modtime = 0;
01172   context->formdata=0;
01173   context->auth_info = parent_context->auth_info;
01174   context->retry = TRUE;
01175   context->method = METHOD_GET;
01176   /* for subrequests, hash tables remain uninitialized: we don't do tag
01177      exclusion for external entities and such */
01178   context->selected_tags_tbl.table = NULL;
01179   context->suppressed_tags_tbl.table = NULL;
01180   context->stripped_tags_tbl.table = NULL;
01181 
01182   context->type = parent_context->type;
01183   context->result_chunk = NULL;
01184 
01185   /* redirect stream to chunk */
01186   target = HTStreamToChunk(subrequest, &(context->result_chunk), 0);
01187   HTRequest_setOutputStream(subrequest, target);
01188 
01189   /* output */
01190   context->result_params =
01191     get_result_param_stub(&parent_context->result_params);
01192   //c2p_list(context->result_params);
01193 
01194   context->request_result = result_term;
01195 
01196   context->status_term = p2p_new();
01197 
01198   /* attach context to the request */
01199   HTRequest_setContext(subrequest, (void *) context);
01200   /* we handle only HT_LOADED, HT_ERROR, and HT_NO_DATA conditions.
01201      We let the others (redirection, authentication, proxy) to be handled by
01202      the standard Libwww filters. */
01203 
01204 #ifdef LIBWWW_DEBUG
01205   xsb_dbgmsg((LOG_DEBUG,"***Subrequest %s: context set", RequestID(subrequest)));
01206 #endif
01207 
01208   return context;
01209 
01210 }
01211 
01212 
01213 /* This sets termination filter for a request.
01214    The idea is that the filter catches all except some standard conditions,
01215    like authentication or redirection responses. */
01216 void setup_termination_filter(HTRequest *request, HTNetAfter *filter)
01217 {
01218   HTRequest_addAfter(request,
01219                      filter,
01220                      NULL,
01221                      NULL,
01222                      HT_ALL,
01223                      HT_FILTER_LAST,
01224                      NO); /* don't override global filters! */
01225 }

Generated on Wed Jul 26 13:30:45 2006 for XSB by  doxygen 1.4.5