/* * 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/paul_goffin/apache1.3.6/src/modules/perforce/mod_webkeep.c#3 $ */ /* * 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. */ #include "httpd.h" #include "http_config.h" #include "mod_webkeep.h" #define MAX_SERVERS 3 typedef struct { char *real; char *fake; } alias_entry; typedef struct { array_header *aliases; WebKeepConnect p4[MAX_SERVERS]; } alias_server_conf; typedef struct { char *filename; int revision; char *filetype; } file_dir_info; module webkeep_module; static void *create_webkeep_config (pool *p, server_rec *s) { alias_server_conf *a; int i; a = (alias_server_conf *)ap_pcalloc (p, sizeof(alias_server_conf)); a->aliases = ap_make_array (p, 20, sizeof(alias_entry)); for (i = 0; i < MAX_SERVERS; i++) { a->p4[i].port = 0; a->p4[i].user = 0; a->p4[i].pass = 0; a->p4[i].client = 0; a->p4[i].indexname = 0; } return a; } static void *merge_webkeep_config (pool *p, void *basev, void *overridesv) { alias_server_conf *a, *b, *o; int i; 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); for (i = 0; i <MAX_SERVERS; i++) { a->p4[i].port = o->p4[i].port ? o->p4[i].port : b->p4[i].port; a->p4[i].user = o->p4[i].user ? o->p4[i].user : b->p4[i].user; a->p4[i].pass = o->p4[i].pass ? o->p4[i].pass : b->p4[i].pass; a->p4[i].client = o->p4[i].client ? o->p4[i].client : b->p4[i].client; a->p4[i].indexname = o->p4[i].indexname ? o->p4[i].indexname : b->p4[i].indexname; } 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 *arg, char *f) { server_rec *s = cmd->server; alias_server_conf *conf; int server; if (sscanf(arg, "%d", &server) != 1) { return "Must Specify an index number for the server (0, 1, ...)"; } if ((server < 0) || (server >= MAX_SERVERS)) { return "Server index number out of range"; } conf = (alias_server_conf *) ap_get_module_config(s->module_config,&webkeep_module); conf->p4[server].port = f; return NULL; } static const char *user_webkeep(cmd_parms *cmd, void *d, char *f) { server_rec *s = cmd->server; int i; alias_server_conf *conf; conf = (alias_server_conf *) ap_get_module_config(s->module_config,&webkeep_module); for (i=0; i < MAX_SERVERS; i++) { conf->p4[i].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; int i; conf = (alias_server_conf *) ap_get_module_config(s->module_config,&webkeep_module); for (i=0; i < MAX_SERVERS; i++) { conf->p4[i].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; int i; conf = (alias_server_conf *) ap_get_module_config(s->module_config,&webkeep_module); for (i=0; i < MAX_SERVERS; i++) { conf->p4[i].client = f; } return NULL; } static const char *index_webkeep(cmd_parms *cmd, void *d, char *f) { server_rec *s = cmd->server; alias_server_conf *conf; int i; conf = (alias_server_conf *) ap_get_module_config(s->module_config,&webkeep_module); for (i=0; i < MAX_SERVERS; i++) { conf->p4[i].indexname = f; } 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, TAKE2, "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"}, { "WebKeepIndex", index_webkeep, NULL, RSRC_CONF, TAKE1, "File name to use for a default index"}, { 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 translate_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) { /* * 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. */ ap_table_set( r->notes, "webkeep-path", ap_pstrcat(r->pool, p->real, r->uri + l, NULL) ); /* And we store which Perforce server to use */ ap_table_set( r->notes, "webkeep-port", conf->p4[i].port ); return OK; } } return DECLINED; } void parsefileinfo(request_rec *r, const char *info, char **fname, char **rev, char **action, char **chgnum, char **filetype) { const char *q; /* sigh... */ char *p; char *buf; /* filename follows last '/'. Allow for the (possible?) case that * there is no path */ q = strrchr(info, '/'); q = q ? q + 1 : info; /* allocate buffer to hold a copy of the entire info string, from * the beginning of the filename and onwards. the pointer-pointers * passed as parameters are set to point into the copy of the * string, with ascii-nulls inserted to delimit parameters in the * string. */ buf = ap_pstrcat(r->pool, q, 0); ap_assert(buf != 0); /* Ok - filename up until '#' ... */ *fname = buf; p = strchr(*fname, '#'); ap_assert(p != 0); *p = 0; /* Then file revision up until the first blank (space) */ *rev = p + 1; p = strchr(*rev, ' '); ap_assert(p != 0); *p = 0; /* skip following "- "... */ ap_assert(*++p == '-'); ap_assert(*++p == ' '); /* action up until next blank */ *action = p + 1; p = strchr(*action, ' '); ap_assert(p != 0); *p = 0; /* filetype in brackets - find opening bracket */ p = strchr(p + 1, '('); ap_assert(p != 0); *filetype = p + 1; p = strchr(*filetype, ')'); ap_assert(p != 0); *p = 0; } static void appendfile(WebKeepPrinter *printer, const char *info) { file_dir_info *new; char *fname, *rev, *action, *chgnum, *ftype; request_rec *r = (request_rec *)printer->closure; parsefileinfo(r, info, &fname, &rev, &action, &chgnum, &ftype); if (strcmp(action, "delete") == 0) { return; } /* else */ if (printer->files == 0) { printer->files = ap_make_array(r->pool, 10, sizeof(file_dir_info)); ap_assert(printer->files != 0); } new = (file_dir_info *) ap_push_array((array_header *) printer->files); ap_assert(new != 0); new->filename = fname; new->revision = atoi(rev); new->filetype = ftype; } static void appenddir(WebKeepPrinter *printer, const char *info) { char **new; char *p; const char *q; request_rec *r = (request_rec *)printer->closure; if (printer->dirs == 0) { printer->dirs = ap_make_array(r->pool, 10, sizeof(char *)); ap_assert(printer->dirs != 0); } new = (char **) ap_push_array(printer->dirs); ap_assert(new != 0); /* find last path separator, if any */ /* assume that there is no trailing path separator */ q = strrchr(info, '/'); q = q ? q + 1 : info; *new = ap_pstrcat(r->pool, q, 0); ap_assert(*new != 0); } /* Webkeeper glue */ /* Protocol: * * webKeepData() is called every time something is sent through the * OutputInfo() method. webKeepData() examines the WebKeepPrinter * structure, and does one of the following: * 1) If we're currently creating a "directory listing": analyse the * information we were passed, and append file or directory data to * the WebKeepPrinter structure. * 2) If we're printing a file: analyse the information passed, and * set the content-type element in the WebKeepPrinter structure * accordingly. * * webKeepError() is called whenever something is sent through the * OutputError() method. webKeepError() examines the WebKeepPrinter * structure, increments the error count in the WebKeepPrinter structure * and logs an error (unless a flag in the WebKeepPrinter structure * tells it to be quiet). * * webKeepText() is called to output data from the Perforce * API. * * webKeepDir() is called to dump out a directory structure. * */ void webKeepData( WebKeepPrinter *printer, const char *info ) { request_rec *r = (request_rec *)printer->closure; if (printer->collectinfo) { /* directory building */ if (strchr(info, '#') != 0) { /* file element */ appendfile(printer, info); } else { appenddir(printer, info); } } else if (printer->print) { /* printing file */ if (strlen(info) > 5 && strcmp(info + strlen(info) - 5, "text)") != 0) { r->content_type = "application/octet-stream"; } ap_send_http_header( r ); } } void webKeepError( WebKeepPrinter *printer, char *buf ) { printer->errorcount++; if (! printer->ignoreerrors) { request_rec *r = (request_rec *)printer->closure; ap_log_reason(buf, r->uri, r); } } void webKeepText( WebKeepPrinter *printer, char *buf, int len ) { request_rec *r = (request_rec *)printer->closure; while( len-- ) ap_rputc( *buf++, r ); } const char *iconbytype(const char *type) { static const char* lookup[] = { "dir", "folder.gif", /* non-perforce file type */ "text", "text.gif", "ktext", "text.gif", "binary", "binary.gif", 0, 0 }; const char **p; for (p = lookup; *p; p += 2) { if (strcmp(*p, type) == 0) { return *(p + 1); } } /* else */ return "unknown.gif"; } void embedimagebytype(request_rec *r, const char *type) { ap_rprintf(r, "%s", "<img src=\"/icons/"); ap_rprintf(r, "%s", iconbytype(type)); ap_rprintf(r, "%s", "\">"); } void webKeepDir(WebKeepPrinter *printer) { request_rec *r = printer->closure; int i; char **cpp; file_dir_info *fdip; r->content_type = "text/html"; ap_send_http_header(r); ap_rprintf(r, "<html><head>\n"); ap_rprintf(r, "<title>Directory Output</title>\n"); ap_rprintf(r, "</head>\n"); ap_rprintf(r, "<body>\n"); ap_rprintf(r, "<h2>Contents of %s:</h2><br>\n", r->uri); embedimagebytype(r, "dir"); ap_rprintf(r, "<a href=\"../\">Parent directory</a><br>\n"); if (printer->dirs != 0) { for (i = 0, cpp = (char **) ((array_header *) printer->dirs)->elts; i < ((array_header *) printer->dirs)->nelts; cpp++, i++) { embedimagebytype(r, "dir"); ap_rprintf(r, "<a href=\"%s/\">%s</a><br>\n", *cpp, *cpp); } } if (printer->files != 0) { for (i = 0, fdip = (file_dir_info *) ((array_header *) printer->files)->elts; i < ((array_header *) printer->files)->nelts; i++, fdip++) { embedimagebytype(r, fdip->filetype); ap_rprintf(r, "<a href=\"%s\">%s#%d</a>(%s)<br>\n", fdip->filename, fdip->filename, fdip->revision, fdip->filetype); } } ap_rprintf(r, "</body></html>\n"); } /* The formal handler... */ static int handle_webkeep (request_rec *r) { void *sconf; alias_server_conf *conf; WebKeepPrinter printer; const char *depotPath; WebKeepConnect p4; /* For us? */ if (r->method_number != M_GET) return DECLINED; if (!(depotPath = ap_table_get(r->notes, "webkeep-path"))) { return DECLINED; } if (!(p4.port = (char *) ap_table_get(r->notes, "webkeep-port"))) { return DECLINED; } /* The mulberry bush. */ sconf = r->server->module_config; conf = (alias_server_conf *)ap_get_module_config(sconf, &webkeep_module); printer.closure = (void *)r; printer.data = webKeepData; printer.text = webKeepText; printer.error = webKeepError; printer.dir = webKeepDir; printer.dirs = printer.files = 0; p4.indexname = conf->p4[0].indexname; p4.user = conf->p4[0].user; p4.pass = conf->p4[0].pass; p4.client = conf->p4[0].client; return webKeepPrint( &p4, depotPath, &printer ) != 0 ? OK : NOT_FOUND; } 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 */ translate_webkeep, /* filename translation */ NULL, /* check_user_id */ NULL, /* check auth */ NULL, /* check access */ NULL, /* type_checker */ NULL, /* fixups */ NULL /* logger */ };
# | Change | User | Description | Committed | |
---|---|---|---|---|---|
#3 | 234 | paul_goffin |
Extended mod_webkeep.c to support multiple perforce servers via introducing an index mechanism for the WebKeepAlias line. This works as follows: suppose there are two perforce servers: server1:1666 (serving depot-1) and server2:1666 (serving depot-2), Then set MAX_SERVERS to 2 (in mod_webkeep.c) and set up the httpd.conf file as follows: WebKeepPort 0 server1:1666 WebKeepPort 1 server2:1666 WebKeepAlias /depot-1 //depot-1 WebKeepAlias /depot-2 //depot-2 WebKeepUser <whatever> WebKeepClient <whatever> WebKeepIndex index.html The order of the "WebKeepAlias" lines is used to associate depot-1 with server 0 - i.e. server1:1666 and depot-2 with server 1 - i.e. server2:1666 This has been tested with Apache 1.3.6 and 1.3.9 on Redhat Linux 5.2 and 6.0 (I admit the implementation could be a lot more elegant, but it works...) |
||
#2 | 174 | paul_goffin |
Adopting Raymonds latest versions. Fixes directory listing problem. |
||
#1 | 169 | paul_goffin |
Added the Apache directory structure to raymond wiker's webkeeper update. (So novices like me can find where to put the stuff!) Inlcuded the mods to the Apache Configuration.tmpl and main Makefile to actually make it build. (The Makefile needs to use "CPP" instead of "CC" to do the final link with the g++ libraries.) This isn't put in by "configure", so you need to edit Makefile (or replace it with the one in this submission if you're building just a standard apache with webkeeper) after the configure process. (This Makefile includes the change, so you can just "diff" it.) Also had to change the include in mod_webkeeper2.cc to "" style from <> style. This version built (and running) on Redhat Linux 5.2 (x86). |
||
//guest/raymond_wiker/webkeeper/mod_webkeep.c | |||||
#5 | 127 | raymond_wiker |
Removed "<base ...>" tag from generated html code. Removed unused variable uripath from webKeepDir(). |
||
#4 | 125 | raymond_wiker |
Debugged & improved somewhat... Now includes icons for directory listing. |
||
#3 | 124 | raymond_wiker |
Added directory indexing. Next: debugging. |
||
#2 | 123 | raymond_wiker | Added "ap_" prefix to all calls to the Apache API. | ||
#1 | 121 | raymond_wiker | Branched webkeeper files to own view. | ||
//guest/perforce_software/webkeeper/mod_webkeep.c | |||||
#1 | 46 | Perforce maintenance | Add WebKeeper source. |