mod_webkeep.c #2

  • //
  • guest/
  • matt_attaway/
  • webkeeper/
  • mod_webkeep.c
  • View
  • Commits
  • Open Download .zip Download (12 KB)
/*
 * Copyright 1995, 1998 Perforce Software.  
 *
 * This file is part of WebKeeper, a perforce client apache module.
 *
 * License is hereby granted to use this software and distribute it
 * freely, as long as this copyright notice is retained and modifications
 * are clearly marked.
 *
 * ALL WARRANTIES ARE HEREBY DISCLAIMED.
 *
 * $Id: //guest/matt_attaway/webkeeper/mod_webkeep.c#2 $
 */

/*
 * mod_webkeep.c: the apache side glue (in C) to the Perforce client
 *
 * This module by Perforce Software, from a template by the Apache Group.
 */

/*
 * TODO: Is there a good way to support mass virtual hosting?
 */

#include "httpd.h"
#include "http_config.h"
#include "mod_webkeep.h"

int webKeepCallP4( void (*func)(), request_rec *r, WebKeepConnect *p4, const char *arg );

typedef struct {
    char *real;
    char *fake;
} alias_entry;

typedef struct {

    array_header *aliases;
    array_header *index_names;
    array_header *refresh_list;
    WebKeepConnect p4;
} alias_server_conf;


void init_webkeep( server_rec *s, pool *p )
{
    ap_add_version_component( "WebKeeper/0.7" );
}

static
void *create_webkeep_config (pool *p, server_rec *s)
{
    alias_server_conf *a;

    a = (alias_server_conf *)ap_pcalloc (p, sizeof(alias_server_conf));
    a->aliases = ap_make_array (p, 20, sizeof(alias_entry));
    a->index_names = NULL;
    a->refresh_list = NULL;
    a->p4.port = 0;
    a->p4.user = 0;
    a->p4.pass = 0;
    a->p4.client = 0;
    a->p4.sync = 0;

    return a;
}

static
void *merge_webkeep_config (pool *p, void *basev, void *overridesv)
{
    alias_server_conf *a, *b, *o;

    a = (alias_server_conf *)ap_pcalloc (p, sizeof(alias_server_conf));
    b = (alias_server_conf *)basev;
    o = (alias_server_conf *)overridesv;

    a->aliases = ap_append_arrays (p, o->aliases, b->aliases);
    a->index_names = o->index_names ? o->index_names : b->index_names;
    a->refresh_list = o->refresh_list ? o->refresh_list : b->refresh_list;

    a->p4.port = o->p4.port ? o->p4.port : a->p4.port ? a->p4.port : b->p4.port;
    a->p4.user = o->p4.user ? o->p4.user : a->p4.user ? a->p4.user : b->p4.user;
    a->p4.pass = o->p4.pass ? o->p4.pass : a->p4.pass ? a->p4.pass : b->p4.pass;
    a->p4.client = o->p4.client ? o->p4.client : a->p4.client ? a->p4.client : b->p4.client;
    a->p4.sync = o->p4.sync ? o->p4.sync : a->p4.sync ? a->p4.sync : b->p4.sync;

    return a;
}

static
const char *add_webkeep(cmd_parms *cmd, void *dummy, char *f, char *r)
{
    server_rec *s = cmd->server;
    alias_server_conf *conf;
    alias_entry *new;

    conf = (alias_server_conf *)
	ap_get_module_config(s->module_config,&webkeep_module);

    new = ap_push_array (conf->aliases);

    /* XX r can NOT be relative to DocumentRoot here... compat bug. */
    
    new->fake = f;
    new->real = r; 

    return NULL;
}

static
const char *port_webkeep(cmd_parms *cmd, void *d, char *f)
{
    server_rec *s = cmd->server;
    alias_server_conf *conf;

    conf = (alias_server_conf *)
	ap_get_module_config(s->module_config,&webkeep_module);

    conf->p4.port = f;

    return NULL;
}

static
const char *user_webkeep(cmd_parms *cmd, void *d, char *f)
{
    server_rec *s = cmd->server;

    alias_server_conf *conf;

    conf = (alias_server_conf *)
	ap_get_module_config(s->module_config,&webkeep_module);

    conf->p4.user = f;

    return NULL;
}

static
const char *passwd_webkeep(cmd_parms *cmd, void *d, char *f)
{
    server_rec *s = cmd->server;
    alias_server_conf *conf;

    conf = (alias_server_conf *)
	ap_get_module_config(s->module_config,&webkeep_module);

    conf->p4.pass = f;

    return NULL;
}

static
const char *client_webkeep(cmd_parms *cmd, void *d, char *f)
{
    server_rec *s = cmd->server;
    alias_server_conf *conf;

    conf = (alias_server_conf *)
	ap_get_module_config(s->module_config,&webkeep_module);

    conf->p4.client = f;

    return NULL;
}

static
const char *sync_webkeep(cmd_parms *cmd, void *d, int f)
{
    server_rec *s = cmd->server;
    alias_server_conf *conf;

    conf = (alias_server_conf *)
	ap_get_module_config(s->module_config,&webkeep_module);

    conf->p4.sync = f;

    return NULL;
}

static
const char *index_webkeep( cmd_parms *cmd, void *dummy, char *arg )
{
    server_rec *s = cmd->server;
    alias_server_conf *conf;

    conf = (alias_server_conf *)
	ap_get_module_config(s->module_config,&webkeep_module);

    if ( !conf->index_names )
	conf->index_names = ap_make_array( cmd->pool, 2, sizeof( char * ) );

    *(char **)ap_push_array( conf->index_names ) = arg;
    return NULL;
}

static
const char *refresh_webkeep( cmd_parms *cmd, void *dummy, char *arg )
{
    server_rec *s = cmd->server;
    alias_server_conf *conf;

    conf = (alias_server_conf *)
	ap_get_module_config(s->module_config,&webkeep_module);

    if ( !conf->refresh_list )
	conf->refresh_list = ap_make_array( cmd->pool, 2, sizeof( char * ) );

    *(char **)ap_push_array( conf->refresh_list ) = arg;
    return NULL;
}

static
command_rec alias_cmds[] = {
{ "WebKeepAlias", add_webkeep, NULL, RSRC_CONF, TAKE2, 
	"a fakename and a depot name"},
{ "WebKeepPort", port_webkeep, NULL, RSRC_CONF, TAKE1, 
	"a TCP/IP host:port"},
{ "WebKeepUser", user_webkeep, NULL, RSRC_CONF, TAKE1, 
	"a Perforce user name"},
{ "WebKeepPasswd", passwd_webkeep, NULL, RSRC_CONF, TAKE1, 
	"a Perforce user password"},
{ "WebKeepClient", client_webkeep, NULL, RSRC_CONF, TAKE1, 
	"a Perforce client name"},
{ "WebKeepSync", sync_webkeep, NULL, RSRC_CONF, FLAG, 
	"a Perforce client name"},
{ "WebKeepDirectoryIndex", index_webkeep, NULL, OR_INDEXES, ITERATE,
	"a list of file names"},
{ "WebKeepRefresh", refresh_webkeep, NULL, RSRC_CONF, ITERATE,
	"a list of Perforce file specs"},
{ NULL }
};

static
int alias_matches (char *uri, char *alias_fakename)
{
    char *end_fakename = alias_fakename + strlen (alias_fakename);
    char *aliasp = alias_fakename, *urip = uri;

    while (aliasp < end_fakename) {
	if (*aliasp == '/') {
	    /* any number of '/' in the alias matches any number in
	     * the supplied URI, but there must be at least one...
	     */
	    if (*urip != '/') return 0;
	    
	    while (*aliasp == '/') ++ aliasp;
	    while (*urip == '/') ++ urip;
	}
	else if ( aliasp == end_fakename - 1 && *aliasp == '$') {
	    /* a $ at the end of the fakename means it must match
	     * the IRI exactly: i.e. no initial substring match.
	     */
	    if (*urip != 0) return 0;
	    ++ aliasp;
	}
	else {
	    /* Other characters are compared literally */
	    if (*urip++ != *aliasp++) return 0;
	}
    }

    /* Check last alias path component matched all the way */

    if (aliasp[-1] != '/' && *urip != '\0' && *urip != '/')
	return 0;

    /* Return number of characters from URI which matched (may be
     * greater than length of alias, since we may have matched
     * doubled slashes)
     */

    return urip - uri;
}

static
void refresh_sync_webkeep( request_rec *r, alias_server_conf *conf )
{
    if( conf->refresh_list == NULL || conf->p4.client == NULL )
        return;

    char **names_ptr = (char **) conf->refresh_list->elts;
    int num_names    = conf->refresh_list->nelts;

    for( ; num_names; ++names_ptr, --num_names )
    {
        char *name_ptr = *names_ptr;

        webKeepCallP4( webKeepSync, r, &conf->p4, name_ptr );
    }
}

static
int post_read_req_webkeep( request_rec *r )
{
    if ( r->uri[0] != '/' && r->uri[0] != '\0' ) 
        return BAD_REQUEST;

    ap_conf_vector    *sconf    = r->server->module_config;
    alias_server_conf *conf     = ap_get_module_config( sconf, &webkeep_module );
    alias_entry       *entries  = (alias_entry *)conf->aliases->elts;

    refresh_sync_webkeep( r, conf );
    
    int i;
    for (i = 0; i < conf->aliases->nelts; ++i)
    {
        alias_entry *p = &entries[i];
        int l = alias_matches (r->uri, p->fake);

        if (l > 0)
		{
	    char *depotPath;
	    char *dirPath;

	    /*
	     * We stash this under webkeep-path, instead of r->filename,
	     * because (a) we have to communicate to our handle_webkeep
	     * that this is one of ours and (b) because filename gets
	     * mangled by the access checking code.  There must be a
	     * way around this, but it is buried in the minds of the 
	     * apache writers.
	     */

	    depotPath = ap_pstrcat(r->pool, p->real, r->uri + l, NULL);

	    /*
	     * If this is a directory, invoke the DirectoryIndex mechanism.
	     * Although the code is lifted from mod_dir, we don't deal with
	     * this in the content handler, because we can't be guaranteed
	     * that the directory exists in the local file system for the
	     * type checking to have recognized it as a directory.
	     */
	    if( webKeepDirExists( &conf->p4, depotPath ) )
	    {
		char **names_ptr;
		int num_names;
		char *dummy_ptr[1];

		/*
		 * Blatantly stolen from mod_dir.c
		 */
		if( conf->index_names )
		{
		    names_ptr = (char **)conf->index_names->elts;
		    num_names = conf->index_names->nelts;
		}
		else
		{
		    dummy_ptr[0] = DEFAULT_INDEX;
		    names_ptr = dummy_ptr;
		    num_names = 1;
		}

		for( ; num_names; ++names_ptr, --num_names )
		{
		    char *name_ptr = *names_ptr;
		    char *testPath;

		    if( depotPath[strlen( depotPath ) - 1] == '/' )
		    {
		        testPath = ap_pstrcat( r->pool,
			                depotPath, name_ptr, NULL );
		    }
		    else
		    {
		        testPath = ap_pstrcat( r->pool,
			                depotPath, "/", name_ptr, NULL );
		    }

		    if( webKeepFileExists( &conf->p4, testPath ) )
		    {
		        depotPath = testPath;
		        break;
		    }
		}
	    }

	    ap_table_set( r->notes, "webkeep-path", depotPath );

	    if( conf->p4.sync && webKeepFileExists( &conf->p4, depotPath ) )
	    {
	        webKeepCallP4( webKeepSync, r, &conf->p4, depotPath );
	        return DECLINED;
	    }
	    
	    return OK;
	}
    }

    return DECLINED;
}

/* Webkeeper glue */

/*
 * Expected protocol:
 *
 *	Success:
 *		data block with filename/type
 *		one or more text blocks
 *
 *	Error:
 *		error block
 */

void
webKeepData( WebKeepPrinter *printer, int isBinary )
{
	request_rec *r = (request_rec *)printer->closure;

	if( !r->content_type && isBinary )
	    r->content_type = "application/octet-stream";

	/* Now that we've seen the header, we clear the status */

	printer->status = OK;

	ap_send_http_header( r );
}

void
webKeepError( WebKeepPrinter *printer, char *buf )
{
	request_rec *r = (request_rec *)printer->closure;

	ap_log_reason(buf, r->uri, r);

	printer->status = NOT_FOUND;
}


void
webKeepText( WebKeepPrinter *printer, char *buf, int len )
{
	request_rec *r = (request_rec *)printer->closure;

	printer->status = OK;

	while( len-- )
	    ap_rputc( *buf++, r );
}


int
webKeepCallP4( void (*func)(), request_rec *r, WebKeepConnect *p4, const char *arg )
{
    WebKeepPrinter printer;

    printer.status = NOT_FOUND;
    printer.closure = (void *)r;
    printer.data = webKeepData;
    printer.text = webKeepText;
    printer.error = webKeepError;

    (*func)( p4, arg, &printer );

    return printer.status;
}


/* The formal handler... */
static
int webkeep_handler( request_rec *r )
{
    const char *depotPath;

    /* For us? */
    if ( !r->handler || strcmp(r->handler, "helloworld") || r->method_number != M_GET ) 
    {
        return DECLINED;
    }

    depotPath = ap_table_get( r->notes, "webkeep-path" );
    if (! depotPath )
		return DECLINED;

    /* The mulberry bush. */
    ap_conf_vector    *sconf = r->server->module_config;
    alias_server_conf *conf  = ap_get_module_config( sconf, &webkeep_module );

    if( conf->p4.sync ) 
    	return DECLINED;

    return webKeepCallP4( webKeepPrint, r, &conf->p4, depotPath );
}


static void webkeep_hooks(apr_pool_t *pool)
{
    ap_hook_handler(webkeep_handler, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_post_read_request(post_read_req_webkeep, NULL, NULL, APR_HOOK_MIDDLE);
    ap_hook_post_config(init_webkeep, NULL, NULL, APR_HOOK_MIDDLE);
}


module AP_MODULE_DECLARE_DATA webkeep_module = {
    STANDARD20_MODULE_STUFF,
    NULL,
    NULL,
    create_webkeep_config,	
    merge_webkeep_config,
    alias_cmds,
    webkeep_hooks
} ;

   
# Change User Description Committed
#8 6346 Matt Attaway Rollback accidental submit
#7 6345 Matt Attaway Add example client generation trigger
#6 6191 Matt Attaway set the content type as appropriate
#5 6185 Matt Attaway Update makefile to work with modern Unix systems

This change switches to using g+ to link the .so instead
of relying on apxs. It also cleans up some warnings. The
makefile for compiling Webkeeper into Apache probably needs
some work.
#4 5910 Matt Attaway Another step towards conversion.

Everything compiles on Windows now. Incremental checkin.
#3 5909 Matt Attaway More movement towards 2.0.

This is another incremental unbuildable checkin.
#2 5908 Matt Attaway First moves towards modernizing webkeeper.

This is just an intermediate checkin to save my work from home.
It probably doesn't compile and certainly doesn't work.
#1 5894 Matt Attaway Branch Webkeeper code for Apache 2.x compatability project
//guest/perforce_software/webkeeper/mod_webkeep.c
#7 1737 Stephen Vance Add version string to Apache startup string and update index page for changes.
#6 913 Stephen Vance Fix bug in array handling for DirectoryIndex and Refresh directives and change Refresh to only be allowed in httpd.conf.
#5 906 Stephen Vance Added WebKeepRefresh directive and updated index page with that, wordsmithing and another potential enhancement.
#4 826 Stephen Vance Fix error in DirectoryIndex config merging.
#3 806 Stephen Vance Remove TODO for DirectoryIndex.
#2 805 Stephen Vance Integrated changes from guest depot.
 Includes APACI build, static and DSO build, Apache 1.3 API, WebKeepSync, WebKeepDirectoryIndex.  Also updated index page to reflect new functionality.
#1 46 Perforce maintenance Add WebKeeper source.