/* * 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$ */ /* * 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; WebKeepConnect p4; } alias_server_conf; module webkeep_module; 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->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->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 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"}, { 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 int post_read_req_webkeep(request_rec *r) { int i; void *sconf; alias_server_conf *conf; alias_entry *entries; if (r->uri[0] != '/' && r->uri[0] != '\0') return BAD_REQUEST; sconf = r->server->module_config; conf = (alias_server_conf *)ap_get_module_config(sconf, &webkeep_module); entries = (alias_entry *)conf->aliases->elts; 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 handle_webkeep (request_rec *r) { void *sconf; alias_server_conf *conf; const char *depotPath; /* For us? */ if (r->method_number != M_GET) return DECLINED; depotPath = ap_table_get(r->notes, "webkeep-path"); if (! depotPath ) return DECLINED; /* The mulberry bush. */ sconf = r->server->module_config; conf = (alias_server_conf *)ap_get_module_config(sconf, &webkeep_module); if( conf->p4.sync ) return DECLINED; return webKeepCallP4( webKeepPrint, r, &conf->p4, depotPath ); } handler_rec webkeep_handlers[] = { { "*/*", handle_webkeep }, { NULL } }; module webkeep_module = { STANDARD_MODULE_STUFF, NULL, /* initializer */ NULL, /* dir config creater */ NULL, /* dir merger --- default is to override */ create_webkeep_config, /* server config */ merge_webkeep_config, /* merge server configs */ alias_cmds, /* command table */ webkeep_handlers, /* handlers */ NULL, /* filename translation */ NULL, /* check_user_id */ NULL, /* check auth */ NULL, /* check access */ NULL, /* type_checker */ NULL, /* fixups */ NULL, /* logger */ NULL, /* header parser */ NULL, /* process initialization */ NULL, /* process exit/cleanup */ post_read_req_webkeep /* post read_request handling */ };
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#7 | 809 | Stephen Vance | Take mainline changes back to my guest branch. | ||
#6 | 803 | Stephen Vance |
Update to Apache 1.3 API. Eliminate compilation warning. Add WebKeepDirectoryIndex. |
||
#5 | 766 | Stephen Vance | Add WebKeepSync to configuration directives, allowing a local workspace to be sync'ed based on the web request and letting the normal Apache mechanisms do the rest. | ||
#4 | 765 | Stephen Vance | One set of source updates. | ||
#3 | 730 | Stephen Vance |
Update so that everything builds smoothly in both loadable and static configs while still following the Apache conventions exactly. Update documentation to show how to load the module in the httpd.conf. |
||
#2 | 634 | Stephen Vance | Add tested platforms and update for DSO builds. | ||
#1 | 603 | Stephen Vance |
First public checkin of Webkeeper updated for Apache 1.3.X. Tested on FreeBSD 3.5 and Red Hat Linux 6.2 and 7.0. |