Beispiel #1
0
/*	Link me Anchor to another given one
**	-------------------------------------
*/
PUBLIC BOOL HTLink_add (HTAnchor *	source,
                        HTAnchor * 	destination,
                        HTLinkType	type,
                        HTMethod	method)
{
    if (source && destination) {
        HTTRACE(ANCH_TRACE, "Link create. from anchor %p to %p with type %s, method %s\n" _
                (void *) source _ (void *) destination _
                type ? HTAtom_name(type) : "NONE" _
                method != METHOD_INVALID ? HTMethod_name(method) : "NONE");
        if (!source->mainLink.dest) {
            source->mainLink.dest = destination;
            source->mainLink.type = type;
            source->mainLink.method = method;
        } else {
            HTLink * newLink = HTLink_new();
            newLink->dest = destination;
            newLink->type = type;
            newLink->method = method;
            if (!source->links) source->links = HTList_new();
            HTList_addObject (source->links, newLink);
        }
        if (!destination->parent->sources)
            destination->parent->sources = HTList_new();
        HTList_addObject (destination->parent->sources, source);
        return YES;
    } else
        HTTRACE(ANCH_TRACE, "Link........ Bad argument\n");
    return NO;
}
Beispiel #2
0
/*	Is method in a list of method names?
**	-----------------------------------
*/
PUBLIC BOOL HTMethod_inList ARGS2(HTMethod,	method,
				  HTList *,	list)
{
    char * method_name = HTMethod_name(method);
    HTList *cur = list;
    char *item;

    while (NULL != (item = (char*)HTList_nextObject(cur))) {
	CTRACE(stderr, " %s", item);
	if (0==strcasecomp(item, method_name))
	    return YES;
    }
    return NO;	/* Not found */
}
Beispiel #3
0
/*	Add entry to the log file
**	-------------------------
**	Format: <HOST> - - <DATE> <METHOD> <URI> <RESULT> <CONTENT_LENTGH>
**	which is almost equivalent to Common Logformat. Permissions on UNIX
**	are modified by umask.
**
**	Returns YES if OK, NO on error
**
**	BUG: No result code is produced :-( Should be taken from HTError.c
*/
PUBLIC BOOL HTLog_addCLF (HTLog * log, HTRequest * request, int status)
{
    if (log && log->fp) {
	time_t now = time(NULL);	
	HTParentAnchor * anchor = HTRequest_anchor(request);
	char * uri = HTAnchor_address((HTAnchor *) anchor);
	HTTRACE(APP_TRACE, "Log......... Writing CLF log\n");
	fprintf(log->fp, "localhost - - [%s] %s %s %d %ld\n",
		HTDateTimeStr(&now, log->localtime),
		HTMethod_name(HTRequest_method(request)),
		uri ? uri : "<null>",			/* Bill Rizzi */
		abs(status),
		HTAnchor_length(anchor));
	HT_FREE(uri);
	log->accesses++;
	return (fflush(log->fp) != EOF); /* Actually update it on disk */
    }
    return NO;
}
Beispiel #4
0
/*----------------------------------------------------------------------
  DAVShowInfo: shows the request's results for the user.
  ---------------------------------------------------------------------- */
void DAVShowInfo (AHTReqContext *context) 
{
  AHTDAVContext *davctx = NULL;
  char *status_msg = NULL;
    
#ifdef DEBUG_DAV        
  printf ("****** DAVShowInfo ****** \n");
#endif
  if (context) 
    { 
      davctx = (AHTDAVContext*) context->dav_context;
      if (davctx) 
        {
          /* it's a WebDAV request */
          switch (context->method)
            {
              /* **** LOCK requests **** */
            case METHOD_LOCK:      
              /* Normal results */
              if (davctx->status > 0 && davctx->status != HT_MULTI_STATUS) 
                {
                  /* lock succeed */                         
                  status_msg = TtaGetMessage (AMAYA, AM_LOCK_SUCCEED);
                }
              /* 207 Multi-Status - Error! */
              else if (davctx->status == HT_MULTI_STATUS) 
                {
                  DAVShowMultiStatusInfo (context);
                  /*set the status line */
                  status_msg = TtaGetMessage (AMAYA, AM_LOCK_FAILED);
		  
                }
              /*405 Method not allowed*/
              else if (davctx->status == DAV_METHOD_NOT_ALLOWED) 
                {
                  DAVDisplayMessage (TtaGetMessage (AMAYA, AM_DAV_NOT_ALLOWED),
                                     context->urlName);
                }
              /* -400 Bad Request */
              else if (davctx->status == DAV_BAD_REQUEST) 
                {
                  /* It may happens when user interrupts the request,
                   * and the XML body is not sent to the server */
#ifdef DEBUG_DAV                                
                  fprintf (stderr,"BAD REQUEST - XML body not sent\n");
#endif 
                  DAVDisplayMessage (TtaGetMessage (AMAYA, AM_LOCK_FAILED),NULL);
                  status_msg = TtaGetMessage (AMAYA, AM_LOCK_FAILED);
                }                                        
              else 
                {  /* other error codes */    
                  DAVDisplayMessage (TtaGetMessage (AMAYA, AM_LOCK_FAILED),NULL);
		  
                  /* set the status line */
                  status_msg = TtaGetMessage (AMAYA, AM_LOCK_FAILED);
                }
              break;
	      
              /* **** UNLOCK requests **** */
            case METHOD_UNLOCK:
              /* Normal results */
              if (davctx->status > 0 && davctx->status != HT_MULTI_STATUS) 
                {
                   /*set the status line */
                  status_msg = TtaGetMessage (AMAYA, AM_UNLOCK_SUCCEED);
                }
              /*405 Method not allowed*/
              else if (davctx->status == DAV_METHOD_NOT_ALLOWED) 
                {
                  DAVDisplayMessage (TtaGetMessage (AMAYA, AM_DAV_NOT_ALLOWED),
                                     context->urlName);
                }
              /*-400 Bad Request */
              else if (davctx->status == DAV_BAD_REQUEST) 
                {
                  DAVDisplayMessage (TtaGetMessage (AMAYA, AM_UNLOCK_FAILED),NULL); 
		  
                  /*set the status line */
                  status_msg = TtaGetMessage (AMAYA, AM_UNLOCK_FAILED);
                }
              else 
                { /* other error codes */    
                  DAVDisplayMessage (TtaGetMessage (AMAYA, AM_UNLOCK_FAILED), NULL);
		  
                  /*set the status line */
                  status_msg = TtaGetMessage (AMAYA, AM_UNLOCK_FAILED);
                }
              break;
	      
              /* **** PROPFIND requests **** */
            case METHOD_PROPFIND:
              /* 207 Multi-Status - normal case */
              if (davctx->status == HT_MULTI_STATUS) 
                {
                  DAVShowPropfindInfo(context);    
                }
              /*405 Method not allowed*/
              else if (davctx->status == DAV_METHOD_NOT_ALLOWED) 
                {
                  DAVDisplayMessage (TtaGetMessage (AMAYA, AM_DAV_NOT_ALLOWED),
                                     context->urlName);
                }
              /* -400 Bad Request */
              else if (davctx->status == DAV_BAD_REQUEST) 
                {
                  /* It may happens when user interrupts the request,
                   * and the XML body is not sent to the server */
#ifdef DEBUG_DAV                                
                  fprintf (stderr,"BAD REQUEST - XML body not sent\n");
#endif 
                  DAVDisplayMessage (TtaGetMessage (AMAYA,
                                                    AM_PROPFIND_FAILED_TEMP), NULL);
                  StopAllRequests(context->docid);
                }
              else 
                /* other error codes */
                DAVDisplayMessage (TtaGetMessage (AMAYA, AM_PROPFIND_FAILED), NULL);
              break;
	      
            default:
              if (davctx->status != HT_MULTI_STATUS) 
                printf ("Request %s Completed. Status %d\n", 
                        HTMethod_name (context->method), davctx->status);
              else 
                printf ("Request %s - Multi-Status\n", HTMethod_name (context->method));
            } /* switch (context->method) */
	  
          if (status_msg) 
            TtaSetStatus (context->docid, DAV_VIEW, status_msg, NULL);
        } /* if (davctx) */
    } /* if (context) */
}
Beispiel #5
0
/* PUBLIC							HTVMS_checkAccess()
**		CHECKS ACCESS TO FILE FOR CERTAIN USER
** ON ENTRY:
**	FileName	The file to be accessed
**	UserName	Name of the user to check access for.
**			User nobody, represented by "" is given NO for an answer
**	Method		Method to be checked for
**			'method'	'access reuired'
**			METHOD_GET	read
**			METHOD_HEAD	read
**
** ON EXIT:
**	returns YES if access is allowed
**
** Not only filename is checked but also filename.dir...
**	
*/
PUBLIC BOOL HTVMS_checkAccess ARGS3(
	CONST char *, FileName,
	CONST char *, UserName,
	HTMethod, Method)
{
unsigned long Result;
ItemStruct ItemList[2];
unsigned long Flags;
unsigned long FlagsLength;
unsigned long Access;
unsigned long AccessLength;
unsigned long ObjType;

char *VmsName;
char Fname[256];

struct dsc$descriptor_s FileNameDesc;
struct dsc$descriptor_s UserNameDesc;

char *colon;

   /* user nobody should access as from account under which server is running */
   if (0 == strcmp(UserName,""))
   {
      CTRACE(stderr, "VMSAccess... No access allowed user nobody. Error in rulefile specifying 'nobody' as uid for protect rule\n");
      return(NO);
   }

   /* make local copy */
   strcpy(Fname,FileName);

   /* strip off last slash anyway */
   if (Fname[strlen(Fname)-1] == '/')
      Fname[strlen(Fname)-1] = '\0';

   /* check Filename and convert */
   colon = strchr(Fname,':');
   if (colon)
      VmsName = HTVMS_name("",colon+1);
   else
      VmsName = HTVMS_name("",Fname);

   /* check for GET */
   if ((Method == METHOD_GET) ||
       (Method == METHOD_HEAD)) 
   {
     Access = ARM$M_READ;
     Flags = CHP$M_READ;

     /* fill Access */
     ItemList[0].BufferLength = sizeof(Access);
     ItemList[0].BufferAddress = &Access;
     ItemList[0].ReturnLengthAddress = &AccessLength;
     ItemList[0].ItemCode = CHP$_ACCESS;

     /* fill Flags */
     ItemList[1].BufferLength = sizeof(Flags);
     ItemList[1].BufferAddress = &Flags;
     ItemList[1].ReturnLengthAddress = &FlagsLength;
     ItemList[1].ItemCode = CHP$_FLAGS;

     /* terminate list */
     ItemList[2].ItemCode = 0;
     ItemList[2].BufferLength = 0;

     /* fill input */
     ObjType = ACL$C_FILE;
     UserNameDesc.dsc$w_length = strlen(UserName);
     UserNameDesc.dsc$b_dtype = DSC$K_DTYPE_T;
     UserNameDesc.dsc$b_class = DSC$K_CLASS_S;
     UserNameDesc.dsc$a_pointer = UserName;
     FileNameDesc.dsc$w_length = strlen(VmsName);
     FileNameDesc.dsc$b_dtype = DSC$K_DTYPE_T;
     FileNameDesc.dsc$b_class = DSC$K_CLASS_S;
     FileNameDesc.dsc$a_pointer = VmsName;

     /* call system for filename */
     Result = SYS$CHECK_ACCESS(&ObjType,&FileNameDesc,&UserNameDesc,ItemList);

     if (Result == SS$_NORMAL)
        return(YES);

     /* try with extension .dir... */
     strcat(VmsName,".dir");
     FileNameDesc.dsc$w_length = strlen(VmsName);
     Result = SYS$CHECK_ACCESS(&ObjType,&FileNameDesc,&UserNameDesc,ItemList);

     if (Result == SS$_NORMAL)
        return(YES);

     /* failed for filename and .dir */
     CTRACE(stderr, "VMSAccess... No access allowed for user '%s', file '%s' under method '%s'\n",UserName,Fname,HTMethod_name(Method));
     return(NO);
   }

   CTRACE(stderr, "VMSAccess... No access allowed for method '%s'\n",HTMethod_name(Method));

   return(NO);
}
Beispiel #6
0
/*
**	Redirection AFTER filter
**	------------------------
**	The redirection handler only handles redirections
**	on the GET or HEAD method (or any other safe method)
*/
PUBLIC int HTRedirectFilter (HTRequest * request, HTResponse * response,
			     void * param, int status)
{
    HTMethod method = HTRequest_method(request); 
    HTAnchor * new_anchor = HTResponse_redirection(response); 

    /* Check for destination */
    if (!new_anchor) {
	HTTRACE(PROT_TRACE, "Redirection. No destination\n");
	return HT_OK;
    }

    /*
    ** Only do automatic redirect on GET and HEAD. Ask for all
    ** other methods.
    */
    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) {
	    HTTRACE(PROT_TRACE, "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;
	    }
	}
    } 

    /* Register the redirection as a link relationship */
    {
	HTLinkType ltype = status==HT_PERM_REDIRECT ? HT_LR_PERM_REDIRECT :
	    (status==HT_TEMP_REDIRECT || status==HT_FOUND) ? HT_LR_TEMP_REDIRECT :
	    status==HT_SEE_OTHER ? HT_LR_SEE_OTHER : NULL;
	if (ltype) {
	    HTLink_add((HTAnchor *) HTRequest_anchor(request), new_anchor, 
		       ltype, method);
	}
    }

    /* Delete any auth credendials as they get regenerated */
    HTRequest_deleteCredentialsAll(request);

    /*
    **  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)) { 
	HTLoadAnchor(new_anchor, request);
    } else {
	HTRequest_addError(request, ERR_FATAL, NO, HTERR_MAX_REDIRECT,
			   NULL, 0, "HTRedirectFilter");
	return HT_OK;		/* Wanna fall through */
    }

    /*
    **  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;
} 
Beispiel #7
0
/*	HTTPMakeRequest
**	---------------
**	Makes a HTTP/1.0-1.1 request header.
*/
PRIVATE int HTTPMakeRequest (HTStream * me, HTRequest * request)
{
    HTMethod method = HTRequest_method(request);
    HTRqHd request_mask = HTRequest_rqHd(request);
    HTParentAnchor * anchor = HTRequest_anchor(request);
    char * etag = HTAnchor_etag(anchor);
    char crlf[3];
    char qstr[10];
    *crlf = CR; *(crlf+1) = LF; *(crlf+2) = '\0';

    /* Generate the HTTP/1.x RequestLine */
    if (me->state == 0) {
	if (method != METHOD_INVALID) {
	    PUTS(HTMethod_name(method));
	    PUTC(' ');
	} else
	    PUTS("GET ");
	me->state++;
    }

    /*
    **  Generate the Request URI. If we are using full request URI then it's
    **  easy. Otherwise we must filter out the path part of the URI.
    **  In case it's a OPTIONS request then if there is no pathinfo then use
    **  a * instead. If we use a method different from GET or HEAD then use
    **  the content-location if available.
    */
    if (me->state == 1) {
	char * abs_location = NULL;
	char * addr = HTAnchor_physical(anchor);
	char * location;

        /* JK: If the application specified a content-location (which is
           stored in the request in default put-name!), we use it instead
           of the URL that's being saved to. This is like having a user
           defined Content-Location */
        location = HTRequest_defaultPutName (request);
        if (location)
	  {
	    if (HTURL_isAbsolute (location))
	      {
		char * relative;
		relative = HTRelative (location, location);
		abs_location = HTParse (relative + 2, addr, PARSE_ALL);
		HT_FREE (relative);
	      }
	    else
	      abs_location = HTParse (location, addr, PARSE_ALL);
	    addr = abs_location;
	  }

#if 0
	/*
	**  We don't use the content-location any more as it is superseeded
	**  by etags and the combination of the two might do more harm than
	**  good (The etag is not guaranteed to be unique over multiple URIs)
	*/

	/*
	**  If we are using a method different from HEAD and GET then use
	**  the Content-Location if available, else the Request-URI.
	*/
	if (!HTMethod_isSafe(method)) {
	    char * location = HTAnchor_location(anchor);
	    if (location) {
		if (HTURL_isAbsolute(location))
		    addr = location;
		else {
		    /*
		    **  We have a content-location but it is relative and
		    **  must expand it either to the content-base or to
		    **  the Request-URI itself.
		    */
		    char * base = HTAnchor_base(anchor);
		    abs_location = HTParse(location, base, PARSE_ALL);
		    addr = abs_location;
		}
	    }
	}
#endif

	/*
	**  If we are using a proxy or newer versions of HTTP then we can
	**  send the full URL. Otherwise we only send the path.
	*/
	if (HTRequest_fullURI(request))
	    StrAllocCopy(me->url, addr);
	else {
	    me->url = HTParse(addr, "", PARSE_PATH | PARSE_PUNCTUATION);
	    if (method == METHOD_OPTIONS) {
		/*
		** We don't preserve the final slash or lack of same through
		** out the code. This is mainly for optimization reasons
		** but it gives a problem OPTIONS. We can either send a "*"
		** or a "/" but not both. For now we send a "*".
		*/
		if (!strcmp(me->url, "/")) *me->url = '*';
	    }
	}
	HT_FREE(abs_location);
	me->state++;
    }

    /*
    **  Now send the URL that we have put together
    */
    if (me->state == 2) {
	int status = HT_OK;
	if ((status = PUTS(me->url)) != HT_OK) return status;
	me->state++;

#if 0
	fprintf(stderr, "Requesting '%s'\n", me->url);
#endif

    }
    PUTC(' ');

    /*
    **  Send out the version number. If we know it is a HTTP/1.0 server we
    **  are talking to then use HTTP/1.0, else use HTTP/1.1 as default version
    **  number
    */
    if (me->version == HTTP_10)
	PUTS(HTTP_VERSION_10);
    else
	PUTS(HTTP_VERSION);
    PUTBLOCK(crlf, 2);

    /* Request Headers */
    if (request_mask & HT_C_ACCEPT_TYPE) {
	HTFormat format = HTRequest_outputFormat(request);
	
	/*
	** If caller has specified a specific output format then use this.
	** Otherwise use all the registered converters to generate the 
	** accept header
	*/
	if (format == WWW_PRESENT) {
	    int list;
	    HTList *cur;
	    BOOL first=YES;
	    for (list=0; list<2; list++) {
		if ((!list && ((cur = HTFormat_conversion()) != NULL)) ||
		    (list && ((cur = HTRequest_conversion(request))!=NULL))) {
		    HTPresentation * pres;
		    while ((pres=(HTPresentation *) HTList_nextObject(cur))) {
			if (pres->rep_out==WWW_PRESENT && pres->quality<=1.0) {
			    if (first) {
				PUTS("Accept: ");
				first=NO;
			    } else
				PUTC(',');
			    PUTS(HTAtom_name(pres->rep));
			    if (pres->quality < 1.0 && pres->quality >= 0.0) {
				sprintf(qstr, ";q=%1.1f", pres->quality);
				PUTS(qstr);
			    }
			}
		    }
		}
	    }
	    if (!first) PUTBLOCK(crlf, 2);
	} else {

	    /*
	    **  If we have an explicit output format then only send
	    **  this one if not this is an internal libwww format
	    **	of type www/<star>
	    */
	    if (!HTMIMEMatch(WWW_INTERNAL, format)) {
		PUTS("Accept: ");
		PUTS(HTAtom_name(format));
		PUTBLOCK(crlf, 2);
	    }
	}	
    }
    if (request_mask & HT_C_ACCEPT_CHAR) {
	int list;
	HTList *cur;
	BOOL first=YES;
	for (list=0; list<2; list++) {
	    if ((!list && ((cur = HTFormat_charset()) != NULL)) ||
		(list && ((cur = HTRequest_charset(request)) != NULL))) {
		HTAcceptNode *pres;
		while ((pres = (HTAcceptNode *) HTList_nextObject(cur))) {
		    if (first) {
			PUTS("Accept-Charset: ");
			first=NO;
		    } else
			PUTC(',');
		    PUTS(HTAtom_name(pres->atom));
		    if (pres->quality < 1.0 && pres->quality >= 0.0) {
			sprintf(qstr, ";q=%1.1f", pres->quality);
			PUTS(qstr);
		    }
		}
	    }
	}
	if (!first) PUTBLOCK(crlf, 2);
    }
    if (request_mask & HT_C_ACCEPT_ENC) {
	int list;
	HTList *cur;
	BOOL first=YES;
	for (list=0; list<2; list++) {
	    if ((!list && ((cur = HTFormat_contentCoding()) != NULL)) ||
		(list && ((cur = HTRequest_encoding(request)) != NULL))) {
		HTCoding * pres;
		while ((pres = (HTCoding *) HTList_nextObject(cur))) {
		    double quality = HTCoding_quality(pres);
		    if (first) {
			PUTS("Accept-Encoding: ");
			first = NO;
		    } else
			PUTC(',');
		    PUTS(HTCoding_name(pres));
		    if (quality < 1.0 && quality >= 0.0) {
			sprintf(qstr, ";q=%1.1f", quality);
			PUTS(qstr);
		    }
		}
	    }
	}
	if (!first) PUTBLOCK(crlf, 2);
    }
    if (request_mask & HT_C_ACCEPT_TE) {
	int list;
	HTList *cur;
	BOOL first=YES;
	for (list=0; list<2; list++) {
	    if ((!list && ((cur = HTFormat_transferCoding()) != NULL)) ||
		(list && ((cur = HTRequest_transfer(request)) != NULL))) {
		HTCoding * pres;
		while ((pres = (HTCoding *) HTList_nextObject(cur))) {
		    double quality = HTCoding_quality(pres);
		    const char * coding = HTCoding_name(pres);
		    if (first) {
			PUTS("TE: ");
			first = NO;
		    } else
			PUTC(',');

		    /* Special check for "chunked" which is translated to "trailers" */
		    if (!strcasecomp(coding, "chunked"))
			PUTS("trailers");
		    else
			PUTS(coding);
		    if (quality < 1.0 && quality >= 0.0) {
			sprintf(qstr, ";q=%1.1f", quality);
			PUTS(qstr);
		    }
		}
	    }
	}
	if (!first) PUTBLOCK(crlf, 2);
    }
    if (request_mask & HT_C_ACCEPT_LAN) {
	int list;
	HTList *cur;
	BOOL first=YES;
	for (list=0; list<2; list++) {
	    if ((!list && ((cur = HTFormat_language()) != NULL)) ||
		(list && ((cur = HTRequest_language(request)) != NULL))) {
		HTAcceptNode *pres;
		while ((pres = (HTAcceptNode *) HTList_nextObject(cur))) {
		    if (first) {
			PUTS("Accept-Language: ");
			first=NO;
		    } else
			PUTC(',');
		    PUTS(HTAtom_name(pres->atom));
		    if (pres->quality < 1.0 && pres->quality >= 0.0) {
			sprintf(qstr, ";q=%1.1f", pres->quality);
			PUTS(qstr);
		    }
		}
	    }
	}
	if (!first) PUTBLOCK(crlf, 2);
    }
    if (request_mask & HT_C_AUTH) {
	HTAssocList * cur = HTRequest_credentials(request);
	if (cur) {				    /* Access authentication */
	    HTAssoc * pres;
	    while ((pres = (HTAssoc *) HTAssocList_nextObject(cur))) {
		PUTS(HTAssoc_name(pres));
		PUTS(": ");
		PUTS(HTAssoc_value(pres));
		PUTBLOCK(crlf, 2);
	    }
	}
    }
    if (request_mask & HT_C_EXPECT) {
	HTAssocList * cur = HTRequest_expect(request);
	if (cur) {
	    BOOL first=YES;
	    HTAssoc * pres;
	    while ((pres = (HTAssoc *) HTAssocList_nextObject(cur))) {
		char * value = HTAssoc_value(pres);
		if (first) {
		    PUTS("Expect: ");
		    first = NO;
		} else
		    PUTC(',');

		/* Output the name */
		PUTS(HTAssoc_name(pres));

		/* Only output the value if not empty string */
		if (*value) {
		    PUTS("=");
		    PUTS(value);
		}
	    }
	    PUTBLOCK(crlf, 2);
	}
    }
    if (request_mask & HT_C_FROM) {
	HTUserProfile * up = HTRequest_userProfile(request);
	const char * mailaddress = HTUserProfile_email(up);
	if (mailaddress) {
	    PUTS("From: ");
	    PUTS(mailaddress);
	    PUTBLOCK(crlf, 2);
	}
    }
    if (request_mask & HT_C_HOST) {
	char *orig = HTAnchor_address((HTAnchor *) anchor);
	char *host = HTParse(orig, "", PARSE_HOST);
	char hostace[256];
#if 0
	/* Keep the port number for HTTP/1.1 compliance */
	char *ptr = strchr(host, ':');		     /* Chop off port number */
	if (ptr) *ptr = '\0';
#endif
        PUTS("Host: ");
    /****** still have to check UTF8toACE with port number */
    if (!HTACEfromUTF8 (host, hostace, 255)) {
	    PUTS(hostace);
	}
	else {
	    PUTS(host); /* this may be dangerous, but helps server side debugging */
        HTTRACE(PROT_TRACE, "HTTP........ Error: Cannot convert to ACE: `%s\'\n" _ host);
	}
	PUTBLOCK(crlf, 2);
	HT_FREE(orig);
	HT_FREE(host);
    }

    /*
    **  In the "If-*" series of headers, the ones related to etags have higher
    **  priority than the date relates ones. That is, if we have a etag then
    **  use that, otherwise use the date. First we check for range, match, and
    **  unmodified-since.
    */
    if (request_mask & HT_C_IF_RANGE && etag) {
	PUTS("If-Range: \"");
	PUTS(etag);
	PUTC('"');
	PUTBLOCK(crlf, 2);
	HTTRACE(PROT_TRACE, "HTTP........ If-Range using etag `%s\'\n" _ etag);
    } else if (request_mask & HT_C_IF_MATCH_ANY) {
	PUTS("If-Match: *");
	PUTBLOCK(crlf, 2);
	HTTRACE(PROT_TRACE, "HTTP........ If-Match using `*\'\n");
    } else if (request_mask & HT_C_IF_MATCH && etag) {
	PUTS("If-Match: \"");
	PUTS(etag);
	PUTC('"');
	PUTBLOCK(crlf, 2);
	HTTRACE(PROT_TRACE, "HTTP........ If-Match using etag `%s\'\n" _ etag);
    } else if (request_mask & HT_C_IF_UNMOD_SINCE) {
	time_t lm = HTAnchor_lastModified(anchor);
	if (lm > 0) {
	    PUTS("If-Unmodified-Since: ");
	    PUTS(HTDateTimeStr(&lm, NO));
	    PUTBLOCK(crlf, 2);
	    HTTRACE(PROT_TRACE, "HTTP........ If-Unmodified-Since `%s\'\n" _ HTDateTimeStr(&lm, NO));
	}
    }

    /*
    **  If-None-Match and If-Modified-Since are equivalent except that the
    **  first uses etags and the second uses dates. Etags have precedence over
    **  dates.
    */
    if (request_mask & HT_C_IF_NONE_MATCH_ANY) {
	PUTS("If-None-Match: *");
	PUTBLOCK(crlf, 2);
	HTTRACE(PROT_TRACE, "HTTP........ If-None-Match using `*\'\n");
    } else if (request_mask & HT_C_IF_NONE_MATCH && etag) {
	PUTS("If-None-Match: \"");
	PUTS(etag);
	PUTC('"');
	PUTBLOCK(crlf, 2);
	HTTRACE(PROT_TRACE, "HTTP........ If-None-Match `%s\'\n" _ etag);
    }
    if (request_mask & HT_C_IMS) {
	time_t lm = HTAnchor_lastModified(anchor);
	if (lm > 0) {
	    PUTS("If-Modified-Since: ");
	    PUTS(HTDateTimeStr(&lm, NO));
	    PUTBLOCK(crlf, 2);
	    HTTRACE(PROT_TRACE, "HTTP........ If-Modified-Since `%s\'\n" _ HTDateTimeStr(&lm, NO));
	}
    }

    /*
    **  Max forwards is mainly for TRACE where we want to be able to stop the
    **  TRACE at a specific location un the message path.
    */
    if (request_mask & HT_C_MAX_FORWARDS) {
	int hops = HTRequest_maxForwards(request);
	if (hops >= 0) {
	    sprintf(qstr, "%d", hops);
	    PUTS("Max-Forwards: ");
	    PUTS(qstr);
	    PUTBLOCK(crlf, 2);
	}
    }

    /*
    **  Range requests. For now, we only take the first entry registered for
    **  this request. This means that you can only send a single "unit" and
    **  then a set of range within this unit. This is in accordance with 
    **  HTTP/1.1. Multiple units will go on multiple lines.
    */
    if (request_mask & HT_C_RANGE) {
	HTAssocList * cur = HTRequest_range(request);
	if (cur) {				    	   /* Range requests */
	    HTAssoc * pres;
	    while ((pres = (HTAssoc *) HTAssocList_nextObject(cur))) {
		PUTS("Range: ");
		PUTS(HTAssoc_name(pres));			     /* Unit */
		PUTS("=");
		PUTS(HTAssoc_value(pres));	  /* Ranges within this unit */
		PUTBLOCK(crlf, 2);
	    }
	}
    }
    if (request_mask & HT_C_REFERER) {
	HTParentAnchor * parent_anchor = HTRequest_parent(request);
	if (parent_anchor) {
	    char * act = HTAnchor_address((HTAnchor *) anchor);
	    char * parent = HTAnchor_address((HTAnchor *) parent_anchor);
#if 1
	    char * relative = HTRelative(parent, act);
#else
	    char * relative = HTParse(parent, act,
				      PARSE_ACCESS|PARSE_HOST|PARSE_PATH|PARSE_PUNCTUATION);
#endif
	    if (relative && *relative) {
		PUTS("Referer: ");
		PUTS(relative);
		PUTBLOCK(crlf, 2);
	    }
	    HT_FREE(act);
	    HT_FREE(parent);
	    HT_FREE(relative);
	}
    }
    if (request_mask & HT_C_USER_AGENT) {
	PUTS("User-Agent: ");
	PUTS(HTLib_appName());
	PUTC('/');
	PUTS(HTLib_appVersion());
	PUTC(' ');
	PUTS(HTLib_name());
	PUTC('/');
	PUTS(HTLib_version());
	PUTBLOCK(crlf, 2);
    }
    HTTRACE(PROT_TRACE, "HTTP........ Generating HTTP/1.x Request Headers\n");
    return HT_OK;
}