/*
 * This module provides a simple example of creating a user-defined
 * map rule.  It must be built into a shareable image that exports 2
 * functions: the usermap function (reverse) and the module init
 * function (init_reverse).
 *
 * To install:
 *    1. Compile this file and link with the command:
 *
 *	   $ link/notrace/share=www_system:http_umap_reverse reverse.opt/option
 *
 *    2. Modify the server startup procedures (.i.e HTTP_SYSTARTUP.COM) 
 *	 to install http_umap_reverse.exe, which is required when
 *	 http_server.exe is installed with privileges.
 *
 *    3. Add the following rule to the configuration file:
 *
 *	   usermap /reverse/* / reverse,http_umap_reverse,www_system:
 *
 *    4. Restart the server.
 *
 * Usage:
 *
 *   During server initialization, the usermap rule will cause the designated 
 *   routine name (i.e. reverse) to be loaded from the shareable image and 
 *   it's address saved in the rule list.  If successful, a attempts to
 *   load function INIT_reverse from the same image and call it if found.
 *
 *   When the server then attempts a rule-file translation of a URL
 *   path and gets a successfull match on the usermap's pattern (/reverse/*)
 *   it calls the reverse function.
 *
 * Author:  David Jones
 * Date:    18-JUL-1999
 */
#include <stdio.h>
#include "access.h"
#include "ident_map.h"
#include "tutil.h"

static int (*tlog_putlog) ( int, char *, ... );
static int *log_level_p;
#define http_log_level (*log_level_p)
/**************************************************************************/
/* Define optional initialization routine with the name "INIT_xxxx", where
 * xxxx is the translation routine name will be called once at image
 * load time if defined.  The init routine is called with a callback_vector
 * argument which contains pointers to the logging routine (tlog_putlog)
 * and logging level pointer inside the main image.  See identmap.h for
 * a full list of the callback vector elements.
 */
int INIT_reverse ( struct callback_vector *vec, int vecsize )
{
    /*
     * Save the callback information in static global variables so the
     * reverse() function can use them later.
     */
    log_level_p = vec->trace_level;
    tlog_putlog = vec->putlog;
    /*
     * Report initialization status.
     */
    tlog_putlog ( 0, "User translation module REVERSE initialized!/" );
    return 1;
}
/****************************************************************************/
/* User-defined map rule.  Perform a 'map' operation and then reverse the
 * order of the bytes to the right of the last slash (i.e. the filename).
 *
 * Example:
 *
 *    /reverse/www/lmth.eodnhoj  ==> /wwwjohndoe.html
 *
 * The server calls this function with the following arguments:
 *
 *    info_ptr blk	   Points to the internal rule list entry for this
 *			   usermap rule.  Fields:
 *				code	  Rule type (IDENT_MAP_RULE_USERMAP)
 *				pattern   Matching pattern (e.g. /reverse/*)
 *				value	  Argument 2 from rule in config file.
 *
 *    char *translation	   Buffer that holds the current translation as a
 *			   NUL-terminated string. The map routine is free to
 *			   modify this buffer, which is then used by
 *			   subsequent rules in the rule list.
 *
 *    int maxlen	   Size of translation buffer.
 *
 *    access_info acc	   Indicates if a protect rule has be triggered.
 *
 *    char **compare_buf   Holds a pointer to an up-cased version of the
 *			   translation buffer.  This buffer is what the
 *			   rule file processor actually uses for pattern
 *			   matches (making them case insensitive).  Any edits
 *			   to the translation buffer should be performed on
 *			   the compare_buf as well.  Note however that
 *			   compare_buf may point to the translation buffer,
 *			   in which case you should not edit this buffer.
 *
 *    int cb_len	   Length of compare_buf.  If maxlen is longer than
 *			   cb_len and length of modified translation string
 *			   exceeds cb_len, you must send *compare_buf to
 *			   translation and upcase the translation string.
 *
 * Return values:
 *    IDENT_MAP_TRANSLATED	Indicates to rule processor that it should 
 *				continue with next rule, using translation and 
 *				compare_buf as modified by the map routine.
 *
 *    IDENT_MAP_FAILED		Rule file processing should terminate with a
 *				'failed' status (same as fail rule).
 *
 *    IDENT_MAP_PASS		Rule file processing should terminate with a
 *				'pass' status, the map routine writes the
 *				file specification (unix format) of the file
 *				to transfer into the translation buffer.
 *
 *    IDENT_MAP_REDIRECT	Rule file processing should redirect the
 *				request to the URL  or URL path loaded into
 *				the translation buffer.
 *
 *    IDENT_MAP_EXEC		Rule file processing should terminate with
 *				an 'exec' status, the map routine must write
 *				a munged ident (path*execpath*bindir) into
 *				the translation buffer.
 */
int reverse ( info_ptr blk, char *translation, int maxlen,
	access_info acc, char **compare_buf, int cb_len )
{
    int i, j, slash, length;
    char *cmp_buf;
    /*
     * The compare_buf argument points to an upcased version of the
     * translation buffer.
     */
    cmp_buf = *compare_buf;
    if ( http_log_level > 1 ) tlog_putlog ( 2, 
	"reverse called, tbuf: '!AZ' (!SL, pat=!SL, val='!AZ')!/", 
	    translation, maxlen, blk->pat_len, blk->value );
    /*
     * Replace the pattern portion of the translation buffer with
     * the value defined in the blk argument.
     */
    if ( blk->pat_len > blk->val_len ) {
	/*
	 * Easy case, new string is shorter than pattern.
	 */
	if ( cmp_buf != translation ) {
	    tu_strncpy ( translation, blk->value, blk->val_len );
	    j = blk->val_len;
	    for (i = blk->pat_len; translation[i]; i++) 
		translation[j++] = translation[i];
	}
	tu_strupcase ( cmp_buf, blk->value );
	j = blk->val_len;
	for (i = blk->pat_len; cmp_buf[i]; i++) {
	    cmp_buf[j++] = cmp_buf[i];
	}
	cmp_buf[j] = '\0';
	translation[j] = '\0';
    } else {
	/*
	 * Harder case, new value longer.
	 */
	j = (blk->val_len - blk->pat_len);
	if ( cmp_buf != translation ) {
	    i = tu_strlen ( translation );
	    for ( ; i >= blk->pat_len; --i ) translation[i+j] = translation[i];
	    tu_strncpy ( translation, blk->value, blk->val_len );
	}
	i = tu_strlen ( cmp_buf );
	for ( ; i >= blk->pat_len; --i ) cmp_buf[i+j] = cmp_buf[i];
	tu_strncpy ( cmp_buf, blk->value, blk->val_len );
	tu_strupcase ( cmp_buf, cmp_buf );
    }
    /*
     * Find last slash and reverse the portion after that.
     */
    for ( i = slash = 0; (i < maxlen) && translation[i]; i++ )
	  if ( translation[i] == '/' ) slash = i;

    if ( translation[slash] == '/' ) slash++;
    if ( http_log_level > 1 ) tlog_putlog ( 2, 
	"reversing tbuf[!SL]: '!AZ'!/", slash, &translation[slash] );
    /*
     * reverse the bytes in translation[i..maxlen-1]
     */
    for ( j = tu_strlen(translation) - 1; slash < j; --j ) {
	char tmp, tmp_cp;
	tmp = translation[slash];
	translation[slash] = translation[j];
	translation[j] = tmp;
	if ( cmp_buf != translation ) {
	    /*
	     * Since we are continuing rule processing, swap the bytes
	     * in the case-preserving (CP) buffer as well, but only
	     * if they are different buffers.
	     */
	    tmp = cmp_buf[slash];
	    cmp_buf[slash] = cmp_buf[j];
	    cmp_buf[j] = tmp;
	}
	slash++;
    }
    return IDENT_MAP_LOOP;	/* continue with next rule */
}
