/*
* 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#8 $
*/
/*
* 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 <http_protocol.h>
#include <http_log.h>
#include <http_core.h>
#include <ap_compat.h>
#include <apr_strings.h>
#include "mod_webkeep.h"
int webKeepCallP4( void (*func)(), request_rec *r, WebKeepConnect *p4, const char *arg );
module AP_MODULE_DECLARE_DATA webkeep_module;
typedef struct
{
char *real;
char *fake;
} alias_entry;
typedef struct
{
apr_array_header_t *aliases;
apr_array_header_t *index_names;
apr_array_header_t *refresh_list;
WebKeepConnect p4;
} alias_server_conf;
/*
* functions to create and merge the webkeeper configuration object
*/
static
void *create_webkeep_config( apr_pool_t *p, server_rec *s )
{
alias_server_conf *a;
a = (alias_server_conf *)apr_palloc( p, sizeof(alias_server_conf) );
a->aliases = apr_array_make( 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( apr_pool_t *p, void *basev, void *overridesv )
{
alias_server_conf *a, *b, *o;
a = (alias_server_conf *)apr_palloc (p, sizeof(alias_server_conf));
b = (alias_server_conf *)basev;
o = (alias_server_conf *)overridesv;
a->aliases = apr_array_append (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;
}
/*
* functions to implement webkeeper commands
*/
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 = apr_array_push( 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 = ap_get_module_config( s->module_config, &webkeep_module );
if ( !conf->index_names )
conf->index_names = apr_array_make( cmd->pool, 2, sizeof( char * ) );
*(char **)apr_array_push( 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 = apr_array_make( cmd->pool, 2, sizeof(char *) );
*(char **)apr_array_push( conf->refresh_list ) = arg;
return 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 )
{
char **names_ptr;
int num_names;
if( conf->refresh_list == NULL || conf->p4.client == NULL )
return;
names_ptr = (char **) conf->refresh_list->elts;
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 );
}
}
/*
* the hook functions
*/
int init_webkeep( apr_pool_t *p, apr_pool_t *l, apr_pool_t *t, server_rec *s )
{
ap_add_version_component( p, "WebKeeper/0.8" );
return OK;
}
static
int post_read_req_webkeep( request_rec *r )
{
void *sconf = r->server->module_config;
alias_server_conf *conf = ap_get_module_config( sconf, &webkeep_module );
alias_entry *entries = (alias_entry *)conf->aliases->elts;
int i;
if ( r->uri[0] != '/' && r->uri[0] != '\0' )
return HTTP_BAD_REQUEST;
refresh_sync_webkeep( r, conf );
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;
/*
* 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 = apr_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] = AP_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 = apr_pstrcat( r->pool,
depotPath, name_ptr, NULL );
}
else
{
testPath = apr_pstrcat( r->pool,
depotPath, "/", name_ptr, NULL );
}
if( webKeepFileExists( &conf->p4, testPath ) )
{
depotPath = testPath;
break;
}
}
}
apr_table_set( r->notes, "webkeep-path", depotPath );
if( conf->p4.sync && webKeepFileExists( &conf->p4, depotPath ) )
{
webKeepCallP4( webKeepSync, r, &conf->p4, depotPath );
return DECLINED;
}
char* ext = strrchr( depotPath, '.') ;
if ( !ext )
ap_set_content_type( r, "text/plain");
else
{
*ext++;
if( strcmp( ext, "html" ) == 0 )
ap_set_content_type( r, "text/html");
else if( strcmp( ext, "css" ) == 0 )
ap_set_content_type( r, "text/css");
else
ap_set_content_type( r, "text/plain");
}
return OK;
}
}
return DECLINED;
}
static
int webkeep_handler( request_rec *r )
{
const char *depotPath;
void *sconf;
alias_server_conf *conf;
/* For us? */
if ( !r->handler || strcmp(r->handler, "webkeep") || r->method_number != M_GET )
return DECLINED;
depotPath = apr_table_get( r->notes, "webkeep-path" );
if ( !depotPath )
return DECLINED;
/* The mulberry bush. */
sconf = r->server->module_config;
conf = ap_get_module_config( sconf, &webkeep_module );
if( conf->p4.sync )
return DECLINED;
return webKeepCallP4( webKeepPrint, r, &conf->p4, depotPath );
}
/* 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, const char *buf )
{
request_rec *r = (request_rec *)printer->closure;
ap_log_rerror( APLOG_MARK,APLOG_ERR, 0, r,
"access to %s failed for %s, reason: %s",
r->uri,
r->hostname,
buf );
printer->status = HTTP_NOT_FOUND;
}
void
webKeepText( WebKeepPrinter *printer, const 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 = HTTP_NOT_FOUND;
printer.closure = (void *)r;
printer.data = webKeepData;
printer.text = webKeepText;
printer.error = webKeepError;
(*func)( p4, arg, &printer );
return printer.status;
}
/*
* Apache module basics: the list of commands we are adding, the list of hooks,
* and the module data structure that tells Appache about the aforementioned things
*/
static const
command_rec webkeep_cmds[] = {
AP_INIT_TAKE2( "WebKeepAlias", (void*)add_webkeep, NULL, RSRC_CONF,
"a fakename and a depot name" ),
AP_INIT_TAKE1( "WebKeepPort", (void*)port_webkeep, NULL, RSRC_CONF,
"a TCP/IP host:port" ),
AP_INIT_TAKE1( "WebKeepUser", (void*)user_webkeep, NULL, RSRC_CONF,
"a Perforce user name" ),
AP_INIT_TAKE1( "WebKeepPasswd", (void*)passwd_webkeep, NULL, RSRC_CONF,
"a Perforce user password" ),
AP_INIT_TAKE1( "WebKeepClient", (void*)client_webkeep, NULL, RSRC_CONF,
"a Perforce client name" ),
AP_INIT_FLAG( "WebKeepSync", (void*)sync_webkeep, NULL, RSRC_CONF,
"a Perforce client name" ),
AP_INIT_ITERATE( "WebKeepDirectoryIndex", (void*)index_webkeep, NULL, OR_INDEXES,
"a list of file names" ),
AP_INIT_ITERATE( "WebKeepRefresh", (void*)refresh_webkeep, NULL, RSRC_CONF,
"a list of Perforce file specs" ),
{ NULL }
};
static void webkeep_hooks( apr_pool_t *pool )
{
ap_hook_post_config( init_webkeep, NULL, NULL, APR_HOOK_MIDDLE );
ap_hook_post_read_request( post_read_req_webkeep, NULL, NULL, APR_HOOK_MIDDLE );
ap_hook_handler( webkeep_handler, NULL, NULL, APR_HOOK_MIDDLE );
}
module AP_MODULE_DECLARE_DATA webkeep_module = {
STANDARD20_MODULE_STUFF,
NULL,
NULL,
create_webkeep_config,
merge_webkeep_config,
webkeep_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. | ||