NSAPI CENTRAL

Coding Lab

Back to NSAPI Central home page

This page is a "pot-pourri" of useful stuff posted to Netscape's newsgroup on NSAPI (I used to be the DevEdge NSAPI Champions).

Best thanks to Rob McCool, Steve Buffum, Mike Shaver, Scott Leerssen, Richard Careaga, Hitesh A. Bosamiya and the others ...

Cookies

param_free( pblock_remove( "set-cookie", rq->srvhdrs ) );
pblock_nvinsert( "set-cookie", "Cook1=ROAD_RUNNER; Expires=; 
			path=/; domain=.hp.com", rq->srvhdrs );
char *pszCook;
request_header( "cookie", &pszCook, sn, rq );
if( pszCook == NULL)
	printf( "No cookie\n" );
int i;
char szCookie_str[81];
for (i = 1; i <= 3; i++) {
	util_sprintf(szCookie_str, "MyCookie%d=Value%d; path=/", i, i );
	pblock_nvinsert( "set-cookie", szCookie_str, rq->srvhdrs );
}
/*This 1st method doesn't use strtok which could be troublesome if not thread safe*/
char *pszPtr, *pszName, *pszValue, *pszCookieName[ 20 ], *pszCookieValue[ 20 ];
int iCount = 0, iClen;

request_header( "cookie", &pszPtr, sn, rq );
if( pszPtr == NULL) {
	printf( "No cookie\n" ); }
else {
	//removes trailing spaces
 while(*pszPtr && isspace(*pszPtr))
		pszPtr++;
	pszCookieName[ iCount ] = pszPtr;
	while(!uidone) {
		switch(*pszPtr) {
			case '=':
				if (!uifInValue) {
					*pszPtr = '\0';
					pszPtr++;
					while(*pszPtr && isspace(*pszPtr)) 
						pszPtr++;
					pszCookieValue[ iCount++ ] = pszPtr;
					uifInValue = 1;
				}
				break;
			case '\0':
			case ';':
				if (uifInValue) {
					if (*pszPtr) {
						*pszPtr = '\0';
						pszPtr++;
						while(*pszPtr && isspace(*pszPtr)) 
							pszPtr++;
 					if (!pszPtr)    /* don't go past edge */
						 pszPtr--;
					} else  
						uidone = 1;	
					pszCookieName[ iCount ] = pszPtr;
					uifInValue = 0;
 				}
 				break;
		}
	pszPtr++;
}
/*This 2nd method use util_strtok which is a (NSAPI) thread safe version of strtok*/
char *pszPtr, *pszName, *pszValue, *pszCookieName[ 20 ], *pszCookieValue[ 20 ];
int iCount = 0, iClen;

request_header( "cookie", &pszPtr, sn, rq );
if( pszPtr == NULL) {
	printf( "No cookie\n" ); }
else {
	pszName = util_strtok( pszPtr, ";" );
	while( pszName != NULL ) {
		if( (pszValue = strchr( pszName, '=' )) != NULL ) {
			*pszValue = '\0';
			pszCookieName[iCount] = pszName;
			pszCookieValue[iCount] = pszValue + 1;
			iCount ++;
		}
	pszName = strtok( NULL, ";" );
	}
}
  • How to set a cookie without returning a page (reload or HEAD) ?
    • Set a cookie in a PathCheck directive.
  • Is Communicator compliant with RFC 2109 ?
    • Response from Lou Montulli (Netscape co-writer of the RFC) as of Mon, 18 May 1998 : <<No, it is not.>>
  • Why my cookie is not set by the browser ?
    • Your site is on the server hello.nsapi.com. If your computer is in the domain nsapi.com and in the browser location you type "http://hello", the browser will find the server but it may not accept the cookie. Why ? Because if you have set the cookie's domain field to ".nsapi.com", the host name typed in the location doesn't match the domain !
    • Same trouble has above if you type the IP address.


HTTP Redirection

/*IE 4.0 could have problem with HTTP 302 answer which contains a body. Corrected in NES 3.6 */
param_free( pblock_remove( "Location", rq->srvhdrs ) );
pblock_nvinsert( "Location", pszTo, rq->srvhdrs );
protocol_status( sn, rq, PROTOCOL_REDIRECT, NULL );
protocol_start_response( sn, rq ); return REQ_ABORTED; 
pblock_nvinsert( "Set-cookie", "Cook1=ROAD_RUNNER; path=/; domain=.hp.com", rq->srvhdrs );
param_free( pblock_remove( "Location", rq->srvhdrs ) );
pblock_nvinsert( "Location", pszTo, rq->srvhdrs );
protocol_status( sn, rq, PROTOCOL_REDIRECT, NULL );
protocol_start_response( sn, rq );
return REQ_ABORTED; 
/* This tells the browser (well, NS Navigator at least) to tell the user where
 he's been redirected to, but without him having to click a link */ 
protocol_status(sn,rq,PROTOCOL_REDIRECT,NULL);
/* We know where we're going, so we can force the type, etc. */ 
param_free(pblock_remove("content-type", rq->srvhdrs)); 
pblock_nvinsert("content-type", "text/html", rq->srvhdrs);
param_free(pblock_remove("path", rq->vars));
pblock_nvinsert("path", "/path/to/redirect.html", rq->vars);
/* This allows the user to know where he went instead of what he wanted */ 
param_free(pblock_remove("Location", rq->srvhdrs));
pblock_nvinsert("Location", "http://myserver/redirect.html", rq->srvhdrs); 
/* This function redirects POSTs to the script "spam.cgi" to the
 other function "echo.cgi". It doesn't tell the client it's doing so, though. */
NSAPI_PUBLIC int cgi_redirect(pblock *pb,Session *sn,Request *rq) {
	FuncPtr F;
	char *old = pblock_findval("uri",rq->reqpb);
	char *find;

	find = strstr(old,"spam.cgi");
	if (find != NULL) {
		param_free(pblock_remove("path",rq->vars));
		pblock_nvinsert("path","/serverpath/cgi-bin/echo.cgi",rq->vars);
		F = func_find("send-cgi");
		return (F) (pb,sn,rq); 
	} else { 
		return REQ_NOACTION;
	}
}
/*In some case (IE) redirection with SSL on could failed,
to avoid this Set the content-length header*/
NSAPI_PUBLIC int cgi_redirect(pblock *pb,Session *sn,Request *rq) {
	pblock_nvinsert ("set-cookie", auth_cookie, rq->srvhdrs);
    pblock_nvinsert ("Location", redirect_url, rq->srvhdrs);
    pblock_nvinsert ("Content-Length", "221", rq->srvhdrs);
    protocol_status (sn, rq, PROTOCOL_REDIRECT, "Moved Temporarily");
    protocol_start_response (sn, rq);
    return REQ_ABORTED;}


URL

char *url = http_uri2url(pblock_findval("uri", rq->reqpb), ""); 
for ( char* ptr = query;  *ptr != '\0'; ptr++ )
{
    if ( *ptr == '+' )
        *ptr = ' ';
}
util_uri_unescape( query );


CGI

void SetAsCgiVariable( char *vars_name, char *headers_name, char *value ) {
	param_free( pblock_remove( vars_name, rq->vars ) ); //Removes any previous variable
	if( value != NULL ) //if value is NULL set it to "" (empty)
	{
		pblock_nvinsert( vars_name, value , rq->vars );
		pblock_nvinsert( headers_name, value, rq->headers );
	} else {
		pblock_nvinsert( vars_name, "" , rq->vars );
		pblock_nvinsert( headers_name, "", rq->headers );
	} 
}
pb_param *pp = pblock_find("path", rq->vars);
FREE(pp->value);
pp->value = STRDUP("/path/to/cgi-bin/doit.cgi");
param_free(pblock_remove("content-type",rq->srvhdrs));
pblock_nvinsert("content-type","magnus-internal/cgi",rq->srvhdrs);
FuncPtr myptr;
myptr = func_find("send-cgi");
rval=(myptr)(pb,sn,rq);
NSAPI_PUBLIC intpost2qstr(netbuf *buf, char *qstr, int clen) {
	int i=0; /* index into qstr */
	int ichar=1;  /* char read in from netbuf */
	int pos = sn->inbuf->pos;

	while (clen && ichar != IO_EOF) {
		ichar = netbuf_getc(buf); /* check for error in reading */
 		if(ichar == IO_ERROR) 
			break;
		qstr[i++] = ichar;
		clen--;
	}
	sn->inbuf->pos = pos;
	qstr[i] = '\0';
	return(i);
}


Certificates Processing

int auth_cert(pblock *pb, Session *sn, Request *rq) {
	int status = REQ_PROCEED;
	FuncPtr get_client_cert = func_find("get-client-cert");
	 /* are we even doing certificate authentication? */
	if (!secure_auth) 
		{ return(REQ_NOACTION); } 
	/*use the built-in "get-client-cert" to go get the certificate.
	this is a little nicer called in our own function so we don't 
	have to muck with obj.conf when SSLClientAuth is turned on or off */
	(*get_client_cert) (pb, sn, rq);
	/*we need a critical section here since NDBM is not re-entrant */
	crit_enter(cert_crit);
	status = check_cert(pb, sn, rq);
	crit_exit(cert_crit);
	return(status);
}
/*first adding "AuthTrans fn=get-sslid"*/
sslid = pblock_findval("ssl-id", sn->client);
user_dn = pblock_findval("user_dn", sn->client);
issuer_dn = pblock_findval("issuer_dn", sn->client);
  • How to force server to re-ask client for certificate?
    • Under UNIX, you can use the call SSL_InvalidateSession(sn->csd) in a PathCheck function which executes before get-client-cert. This should cause the server to ask for another certificate.


Authentication

temp = (char *) MALLOC( sizeof( char ) * (strlen(realm) + 15));
strcpy(temp, "basic realm=\"");
strcat(temp, realm);
strcat(temp, "\"");
pblock_nvinsert("WWW-authenticate", temp, rq->srvhdrs);
protocol_status(sn, rq, PROTOCOL_UNAUTHORIZED, NULL);
protocol_start_response(sn, rq);
FREE(temp);
return REQ_ABORTED;


Configuration

/* This example demonstrates how to get server's IP address
 * Hitesh A. Bosamiya*/
struct sockaddr_in serv_addr;
int serv_addr_len = sizeof(serv_addr);
char *temp;

#ifdef XP_WIN32
 #ifdef net_native_handle /* Available with 3.5.1 */
    SOCKET native_sock = (SOCKET)net_native_handle(sn->csd);
 #elif PROTOCOL_CONTINUE 
	/* This is specific to HTTP 1.1, means could be 3.x server */
    #pragma message( "You need to switch to 3.5.1 server, 
		no external API to convert the SYS_NETFD to SOCKET" )
 #else /* HTTP 1.0, means 2.x server */
    SOCKET native_sock = (SOCKET)sn->csd;
 #endif
#else
    int native_sock;
 #ifdef net_native_handle /* Available with 3.5.1 */
    native_sock = net_native_handle(sn->csd);
 #elif PROTOCOL_CONTINUE /* This is specific to HTTP 1.1, means could be 3.x server */
  #ifdef  __cplusplus
    extern "C" int PR_FileDesc2NativeHandle(SYS_NETFD sfd);
  #else
    int PR_FileDesc2NativeHandle(SYS_NETFD sfd);
  #endif
    native_sock = PR_FileDesc2NativeHandle(sn->csd);
 #else /* HTTP 1.0, means 2.x server */
    native_sock = sn->csd;
 #endif
#endif
 getsockname(native_sock, (struct sockaddr *)&serv_addr, &serv_addr_len);
 temp = inet_ntoa(serv_addr.sin_addr);
int my_initfn(pblock *args, Session *unused, Request *unused) {
	/* Make sure we're running in an Enterprise server, if we're not, do something */
	char *version = pblock_findval("server-version", args);
	if((!version) || strncmp(version, "Netscape-Enterprise", 19))
 	{  /* do something */ }
	return REQ_PROCEED;
}
conf_getglobals()->Vport
conf_getglobals()->Vserver_hostname
conf_getglobals()->Vsecurity_active //(or Vssl2_active, or Vssl3_active) 
//Replace the default busy function for the entire server
extern "C" NSAPI_PUBLIC int my_custom_busy_function(pblock *pb, Session *sn, Request *rq);

my_init(pblock *pb, Session *, Request *) 
     { 
         func_replace("service-toobusy", my_custom_busy_function); 
     }


Misc

char *host; 
    char *just_host; 
    char *port_num; 
  
    /* Get "host" header to determine the virtual server name */ 
    if (request_header ("host", &host, sn, rq) == REQ_ABORTED) 
        return REQ_ABORTED; 
    /* check for host name from client. If not given, is old client, punt */ 
    if (! host) return REQ_NOACTION; 

    /* Copy the host name and make sure it is JUST the host name (ie: trim the port number) */ 
    just_host = STRDUP (host); 
    port_num = strchr (just_host, ':'); 
    if (port_num) 
         *port_num = '\0'; 
 
void SetExpires( int iTime ){
	char expdate[50];
	struct tm *resp
	time_t tp;

	tp = time(NULL);
	resp=gmttime(&tp);
	util_strftime(expdate,HTTP_DATE_FMT, resp)
	param_free( pblock_remove( "expires", rq->srvhdrs ) );
	pblock_nvinsert( "expires", expdate, rq->srvhdrs );
	param_free( pblock_remove( "date", rq->srvhdrs ) );
	pblock_nvinsert( "date", expdate, rq->srvhdrs );
 }
NSAPI_PUBLIC intpost2qstr(netbuf *buf, char *qstr, int clen) {
	int i=0; /* index into qstr */
	int ichar=1;  /* char read in from netbuf */
	/* Loop through reading a character and writing it to qstr, 
	until either len characters have been read, there's no more input,
	or there's an IO error. */
	while (clen && ichar != IO_EOF) {
		ichar = netbuf_getc(buf); /* check for error in reading */
 		if(ichar == IO_ERROR) 
			break;
		qstr[i++] = ichar;
		clen--;
	}
	qstr[i] = '\0';
	return(i);
}
int my_upload_file(pblock *pb, Session *sn, Request *rq) {
	char *path;
	int rv;

	pblock *args = pblock_create(2);
	pblock_copy(pb, args);
	param_free(pblock_remove("fn", args));
	pblock_nvinsert("fn", "upload-file", args);
	rv = func_exec(args, sn, rq);
	if(rv != REQ_PROCEED)
		return rv;
	path = pblock_findval("path", rq->vars); 
	/* do specific stuff */
	return REQ_PROCEED;
} 
p = (char *) request_translate_uri(pblock_findval("path-info", rq->vars), sn); 
char *url = http_uri2url(pblock_findval("uri", rq->reqpb), "");
list_t *lp;
char list_pointers[9];

/* save pointer in rq->vars */
sprintf(list_pointers, "%8.8x", lp);
pblock_nvinsert("list_pointers", list_pointers, rq->vars);

/* retrieve saved pointer */
lp = strtoul(pblock_findval("list_pointers", rq->vars));


Sockets

param_free(pblock_remove("content-type", rq->srvhdrs));
    pblock_nvinsert("content-type", "text/html", rq->srvhdrs);
    protocol_status(sn, rq, PROTOCOL_OK, "DYNAMIC_PAGE"); //to prevent error "Can not close socket descriptor"
    protocol_start_response(sn, rq);
    net_write (sn->csd, pErrorPage,nPageLen);
    return REQ_ABORTED;

/*And in Obj.conf define the function in error directive
 as Error fn="My_ErrorHandler" reason="DYNAMIC_PAGE"*/
NSAPI_PUBLIC int  My_ErrorHandler(pblock *pb, Session *sn, Request *rq)
    {
     return REQ_PROCEED ;
    }


Proxy

NSAPI routeauthprox(pblock *pb, Session *sn, Request *rq)
{
        NSAPI rval;
        FuncPtr myptr;
        int rv;
        pblock *args = pblock_create(2);
        caddr_t prox_auth;

        if (route_auth) {       /* so we can switch it off */
                prox_auth = pblock_findval("proxy-authorization", rq->headers);
                pblock_copy(pb, args);
                param_free(pblock_remove("fn", args));
                pblock_nvinsert("fn", "set-or-replace-header", args);
                param_free(pblock_remove("name", args));
                pblock_nvinsert("name", "Proxy-Authorization", args);
                param_free(pblock_remove("value", args));
                pblock_nvinsert("value", prox_auth, args);
                rv = func_exec(args, sn, rq);
                if(rv != REQ_PROCEED)
                        return rv;
        }
        /* Call the normal Netscape proxy retrieve with our 
                editted request */
        myptr = func_find("proxy-retrieve");
        rval=(myptr)(pb,sn,rq);
}


Debugging


Back to Top of the Page
Copyright (c) 1997-2001, Philippe Le Berre,
All Rights Reserved.
07/01