This example can be found in the examples/c/alias
directory.
/* * This example module implements a URL->file aliasing extension to the * AOLserver using the Ns_SetUrlToFileProc function from the C API. Loading * this module enables you to define mappings such as * /oldplace/foo.html=newplace/foo.html. This module maintains compatibility * with the existing (default) AOLserver UrlToFile function by supporting * UserMapDir and PageRoot configuration variables, both of which are * subsumed by the expressive capabilities of this module. * * You can load and configure this module by editing your nsd.ini file as * follows: * 1) In your [ns\server\server-name\modules] section, add the * following: * alias=alias.dll ;or alias.so on Unix platforms * 2) Add a section for the alias module that contains your aliases. E.g.: * [ns\server\server-name\module\alias] * /oldplace/foo.html=newplace/foo.html * ... * * Note that you can accomplish the above with the setup interface by using the * "List AOLserver Configuration Sections" available from the "Setup Home" * page in expert mode, or you can edit the nsd.ini file with your favorite * text editor. * * When defining maps with key/value pairs, keep the following in mind: * 1) The key (`/oldplace/foo.html' above) is in "URI space" and the value * (`newplace/foo.html' above) is in "file space". * 2) By default, the file space value (`newplace/foo.html' above) is assumed * to be relative to the page root defined for the "server-name" * virtual server. In "file space" (right side of =), you can also * specify an absolute pathname such as /home/user/html/index.html * or c:\html\index.html (NT). So, be careful not to use a leading `/' * on the right side of the `=' unless you mean it. * 3) In "file space", `~username' expands to the user's default directory, and * `~' looks for a user name in the second element of the URI, substituting * the user's home directory appropriately. * 4) In "URI space", a leading `/~' mapped to a leading `~' in file space supports * the common ~user usage in URI space. * * Now, let's clear things up with an example. Note that the comments following each * mapping show a sample URI input followed by sample output generated by this module. * [ns\server\server-name\module\alias] * /user1=~godzilla ;/user1 -> /home/godzilla * /user2=~/pages ;/user2/bambi -> /home/bambi/pages * ;OR /user2/bambi/foo -> /home/bambi/pages/foo * /user3=~ ;/user3/bambi -> /home/bambi * ;OR /user3/bambi/html -> /home/bambi/html * /~=~/pages ;/~foot -> /home/foot/pages * ;OR /~foot/section -> /home/foot/pages/section * * Note that the last entry obviates the need for the `UserMapDir' configuration * parameter. To provide compatibility with the default Ns_UrlToFile function, we'll * insert a mapping to reflect the UserMapDir if it is set in the nsd.ini file. * If UserMapDir is defined and there is a mapping of the form: /~=~/otherplace, * the mapping takes precedence. * * Another mapping, more pervasive than the previous examples, is the following: * /=/home/newroot * This overrides the PageRoot parameter defined in the server-specific section * of nsd.ini. As you would expect, it also defines the implicit root for all other * relative "file space" (right side of `=') values that appear in other aliases. * */ #include "ns.h" #include <assert.h> #include <string.h> #define MAPMETHOD "GET" /* placeholder in the general-purpose URL-space routines */ /* The following string is appended to user directories (for compatibility) */ #define CONFIG_USERMAPDIR "UserMapDir" #define MAX_USERNAME 16 /* * This data will be made available to our custom UrlToFile function * (AliasedUrlToFile) */ typedef struct { char *fromUri; char *toUri; } UriMap; static Ns_UrlToFileProc AliasedUrlToFile; static void UriMapFree(UriMap * map); static int TildeReplace(char *hServer, Ns_DString * dsOut, char *in, char *uri); static UriMap *NewMap(char *from, char *to); /* * The Ns_ModuleVersion exported integer is used to verify this module * version when loaded. For AOLserver 2.0, 1 (one) is the only valid value * for this variable. */ DllExport int Ns_ModuleVersion = 1; /* * The following are global IDs used for server-specific storage and * retrieval of data */ static int idAliases = -1; /* * The Ns_ModuleInit function is the function the AOLserver will call each * time the module is loaded into a virtual server. The function is passed * two parameters: * * hServer: The server `handle' as a string. This is the short name given to * the virutal server such as `server1'. * * hModule: The module `handle' as a string. This is the short name given to * the module such as `alias' * * For example, if this module is known as `alias' and loaded into the `server1' * server with entries similar to the following in the nsd.ini file: * * [ns\servers] * server1=My First Server * * [ns\server1\modules] * alias=alias.dll * * This function would be called with "server1" and "alias" as its arguments. * */ DllExport int Ns_ModuleInit(char *hServer, char *hModule) { char *moduleConfigPath; char *serverConfigPath; char *userMapDir; Ns_Set *aliases; int i; if (idAliases < 0) { idAliases = Ns_ServerSpecificAlloc(); } serverConfigPath = Ns_ConfigGetPath(hServer, NULL, NULL); /* * for compatibility with the default UrlToFile, create entry for UserMapDir * if it is defined */ if ((userMapDir = Ns_ConfigGetValue(serverConfigPath, CONFIG_USERMAPDIR)) != NULL) { UriMap *map; Ns_DString dsFilePattern; Ns_DStringInit(&dsFilePattern); Ns_DStringVarAppend(&dsFilePattern, "~/", userMapDir, NULL); map = NewMap("/~", Ns_DStringExport(&dsFilePattern)); Ns_Log(Notice, "Ns_ModuleInit(%s,%s): Mapping %s to %s", hServer, hModule, map->fromUri, map->toUri); Ns_UrlSpecificSet(hServer, MAPMETHOD, map->fromUri, idAliases, map, 0, (void (*) (void *)) UriMapFree); } if ((moduleConfigPath = Ns_ConfigGetPath(hServer, hModule, NULL)) == NULL) { Ns_Log(Warning, "Ns_ModuleInit(%s,%s): No file aliases section found in config file", hServer, hModule); } else { if ((aliases = Ns_ConfigGetSection(moduleConfigPath)) != NULL) { if (Ns_SetSize(aliases) > 0) { Ns_DString dsNormalizedKey; Ns_DStringInit(&dsNormalizedKey); /* * The following function causes the * AOLserver to call our AliasedUrlToFile * function instead of its default when * mapping a URI to a filename. */ Ns_SetUrlToFileProc(hServer, AliasedUrlToFile); for (i = 0; i < Ns_SetSize(aliases); ++i) { UriMap *map; char *value; /* * Normalize the path so that we can * use it for matching in * AliasedUrlToFile */ Ns_NormalizePath(&dsNormalizedKey, Ns_SetKey(aliases, i)); value = Ns_SetValue(aliases, i); if ((strncmp(dsNormalizedKey.string, "/~",2)==0) && (value[0] != `~')) { Ns_Log(Warning, "AliasedUrlToFile(%s): %s->%s ignored, %s must start with `~'", hServer, dsNormalizedKey.string, value, value); } else { map = NewMap(ns_strdup(dsNormalizedKey.string), ns_strdup(value)); Ns_Log(Notice, "Ns_ModuleInit(%s,%s): Mapping %s to %s", hServer, hModule, map->fromUri, map->toUri); Ns_UrlSpecificSet(hServer, MAPMETHOD, map->fromUri, idAliases, map, 0, (void (*) (void *)) UriMapFree); } Ns_DStringTrunc(&dsNormalizedKey, 0); } Ns_DStringFree(&dsNormalizedKey); } } } return NS_OK; } /* * This function, registered above via Ns_SetUrlToFileProc, will be called by * the AOLserver when it maps a URL to a file. */ static int AliasedUrlToFile(Ns_DString * dest, char *hServer, char *relpath) { int retval = NS_OK; Ns_DString dsAliasedPath; UriMap *map; assert(relpath != NULL); assert(dest != NULL); Ns_DStringInit(&dsAliasedPath);
/* special handling of `/~' pattern in URI */ if (relpath[0]=='/' && relpath[1]=='~') { if ((map = Ns_UrlSpecificGet(hServer, MAPMETHOD, "/~", idAliases)) == NULL) { Ns_Log(Error, "AliasedUrlToFile(%s): URI %s has not been aliased", hServer, relpath); retval = NS_ERROR; } else if (map->toUri[0]=='~') { retval = TildeReplace(hServer, &dsAliasedPath, map->toUri, relpath); } else { Ns_Log(Bug, "AliasedUrlToFile(%s): In mapping %s->%s, %s must contain a `~'", hServer, map->fromUri, map->toUri, map->toUri); retval = NS_ERROR; } } else { /* * Check whether the URI (relpath) has been registered for * aliasing... */ if ((map = Ns_UrlSpecificGet(hServer, MAPMETHOD, relpath, idAliases)) == NULL) { /* no match, copy relpath to destination */ Ns_DStringAppend(&dsAliasedPath, relpath); } else { /* matched, check for `~', exact match or initial subpath match of file pattern*/ int relpathLen, fromUriLen; if (map->toUri[0] == `~') { retval = TildeReplace(hServer, &dsAliasedPath, map->toUri, relpath); } else { fromUriLen = strlen(map->fromUri); relpathLen = strlen(relpath); if (fromUriLen == relpathLen) { /* exact match, use simple substitution */ Ns_DStringAppend(&dsAliasedPath, map->toUri); } else if (fromUriLen < relpathLen) { /* * initial subpath match, substitute only for length * of map->fromUri */ Ns_DStringVarAppend(&dsAliasedPath, map->toUri, relpath[fromUriLen]=='/' ? "" : "/", &relpath[fromUriLen], NULL); } else { Ns_Log(Error, "AliasedUrlToFile(%s): %s to %s mapping is not valid", hServer, map->fromUri, relpath); retval = NS_ERROR; } } } } if (retval == NS_OK) { if (Ns_PathIsAbsolute(dsAliasedPath.string) && (map != NULL)) { Ns_MakePath(dest, dsAliasedPath.string, NULL); } else { /* relative path, use pageroot or / from URI space */ UriMap *map; char *root; /* URI `/' takes precedence over pageroot */ if ((map = Ns_UrlSpecificGet(hServer, MAPMETHOD, "/", idAliases)) == NULL) { root = Ns_PageRoot(hServer); } else { root = map->toUri; } Ns_MakePath(dest, root, dsAliasedPath.string, NULL); } } Ns_DStringFree(&dsAliasedPath); return retval; } /* * This function performs the expansion and substitution related to * the `~' character as it appears in the URI and file patterns * defined in the nsd.ini file, and in the URI input. In this function, * remember that `filePattern' is the string that appeared on the right * side of the `=' in the nsd.ini file and `uri' is the URI from our current * request, not to be confused with the URI (left of `=') defined in the * nsd.ini file which is used for pattern matching. * */ static int TildeReplace(char *hServer, Ns_DString * dsOut, char *filePattern, char *uri) { int retval = NS_OK; char *pathRemainder; char *nameEnd; char *uriTail = `\0'; int found; char user[MAX_USERNAME+1]; int len; assert (filePattern[0] == `~'); if (filePattern[1] == `\0' || filePattern[1] == `/') { /*~ or ~/ in filePattern */ char *nameStart; /* parse out user name, second element in uri OR string following /~ */ if (strncmp(uri, "/~", 2) == 0) { /* special case mapping of /~* = ~ */ nameStart = &uri[2]; if (*nameStart == `\0') { nameStart = NULL; } } else { /* skip past first element */ if ((nameStart = strchr(&uri[1], `/')) != NULL) { ++nameStart; } } if (nameStart == NULL) { Ns_Log(Error, "AliasedUrlToFile:TildeReplace(%s): user name not found in URI: %s", hServer, uri); retval = NS_ERROR; } else { uriTail = strchr(nameStart, `/'); len = (uriTail==NULL) ? strlen(nameStart) : uriTail - nameStart; if (len >= sizeof(user)) { Ns_Log(Warning, "AliasedUrlToFile:TildeReplace(%s): URI User name too long", hServer); retval = NS_ERROR; } else { strncpy(user, nameStart, len); user[len] = `\0'; found = Ns_GetUserHome(dsOut, user); pathRemainder = (filePattern[1]=='\0') ? "" : &filePattern[1]; } } } else { uriTail = strchr(&uri[1], `/'); /* skip past substituted field in URI */ if ((nameEnd = strchr(&filePattern[1], `/')) == NULL) { /* ~user */ found = Ns_GetUserHome(dsOut, &filePattern[1]); pathRemainder = ""; } else { /* ~user/more/stuff */ len = nameEnd - filePattern - 1; if (len >= sizeof(user)) { Ns_Log(Warning, "AliasedUrlToFile:TildeReplace(%s): Mapped User name too long", hServer); retval = NS_ERROR; } else { strncpy(user, &filePattern[1], len); user[len] = `\0'; found = Ns_GetUserHome(dsOut, user); pathRemainder = nameEnd; } } } if (retval == NS_OK) { if (!found) { Ns_Log(Warning, "AliasedUrlToFile:TildeReplace(%s): Unknown user", hServer); retval = NS_ERROR; } else { Ns_DStringAppend(dsOut, pathRemainder); if (uriTail != NULL) { Ns_DStringAppend(dsOut, uriTail); } } } return retval; } static UriMap * NewMap(char *from, char *to) { UriMap *map; map = ns_malloc(sizeof(UriMap)); map->fromUri = from; map->toUri = to; return map; } static void UriMapFree(UriMap * map) { ns_free(map->fromUri); ns_free(map->toUri); ns_free(map); }