/*
* 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. | ||