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