/*****************************************************************************/ /* MapCon.c Mapping conditional processing. See MAPURL.C for description of "Mapping Conditionals". VERSION HISTORY --------------- 07-OCT-2004 MGD unbundled from MAPURL.C */ /*****************************************************************************/ #ifdef WASD_VMS_V7 #undef _VMS__V6__SOURCE #define _VMS__V6__SOURCE #undef __VMS_VER #undef __VMS_VER #undef __CRTL_VER #define __CRTL_VER 70000000 #endif /* application header */ #include "wasd.h" #define WASD_MODULE "MAPCON" /********************/ /* external storage */ /********************/ extern WATCH_STRUCT Watch; /*****************************************************************************/ /* Scan the conditional string evaluating the conditions! Return true or false. Anything it cannot understand it ignores! */ BOOL MapConString ( REQUEST_STRUCT *rqptr, MAP_RULE_META *mrptr, REQUEST_PATHSET *rqpsptr ) { BOOL NegateThisCondition, NegateEntireConditional, Result, SoFarSoGood; int idx, status, ConditionalCount, WatchThisOne; char *cptr, *sptr, *tptr, *zptr, *CurrentPtr; char Scratch [2048]; DICT_ENTRY_STRUCT *denptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_MAPURL)) WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "MapConString() !&Z", mrptr->ConditionalPtr); if (WATCHING (rqptr, WATCH_MAPPING)) WatchThisOne = WATCH_MAPPING; else WatchThisOne = 0; CurrentPtr = NULL; ConditionalCount = 0; NegateEntireConditional = NegateThisCondition = SoFarSoGood = false; cptr = mrptr->ConditionalPtr; if (WATCH_CAT && WatchThisOne) WatchDataFormatted ("conditional !AZ\n", cptr); while (*cptr) { while (ISLWS(*cptr)) cptr++; if (!*cptr) break; if (*cptr == '[' || SAME2(cptr,'![')) { if (*cptr == '!') { NegateEntireConditional = true; cptr++; } else NegateEntireConditional = false; cptr++; ConditionalCount = 0; SoFarSoGood = false; continue; } if (*cptr == ']') { cptr++; if (NegateEntireConditional) { SoFarSoGood = !SoFarSoGood; NegateEntireConditional = false; } if (ConditionalCount && !SoFarSoGood) { cptr = ""; break; } continue; } if (SoFarSoGood) { if (NegateEntireConditional) { SoFarSoGood = !SoFarSoGood; NegateEntireConditional = false; } /* at least one OK, skip to the end of the conditional */ while (*cptr && *cptr != ']') cptr++; /* remember, there may be more than one per line */ if (*cptr) continue; break; } CurrentPtr = cptr; NegateThisCondition = Result = false; zptr = (sptr = Scratch) + sizeof(Scratch)-1; if (*cptr == '!') { cptr++; NegateThisCondition = true; } switch (*(USHORTPTR)cptr) { case 'AC' : /*************/ /* "Accept:" */ /*************/ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; if (rqptr->rqHeader.AcceptPtr) Result = MapConList (rqptr, Scratch, rqptr->rqHeader.AcceptPtr); break; case 'AL' : /**********************/ /* "Accept-Language:" */ /**********************/ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; Result = MapConList (rqptr, Scratch, rqptr->rqHeader.AcceptLangPtr); break; case 'AS' : /*********************/ /* "Accept-Charset:" */ /*********************/ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; Result = MapConList (rqptr, Scratch, rqptr->rqHeader.AcceptCharsetPtr); break; case 'CA' : /************/ /* callout? */ /************/ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && *cptr != ']') cptr++; Result = rqptr->rqCgi.CalloutInProgress; break; case 'CK' : /**********/ /* cookie */ /**********/ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; Result = StringMatchGreedyRegex (rqptr, rqptr->rqHeader.CookiePtr, Scratch); break; case 'DR' : /*****************/ /* document root */ /*****************/ /* map=root= */ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; if (rqpsptr->MapRootPtr) Result = StringMatchGreedyRegex (rqptr, rqpsptr->MapRootPtr, Scratch); else Result = false; break; case 'EX' : /**********************/ /* extended file path */ /**********************/ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && *cptr != ']') cptr++; Result = rqptr->PathOdsExtended; break; case 'HM' : /****************************/ /* client host network mask */ /****************************/ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; tptr = Scratch; /* preserve string address using ptr */ status = TcpIpNetMask (rqptr, WATCH_MAPPING, &tptr, &rqptr->ClientPtr->IpAddress); /* if there's a problem with the mask then just fail it */ if (VMSnok(status) && status != SS$_UNREACHABLE) return (false); Result = (status == SS$_NORMAL); break; case 'HO' : /****************************/ /* client host name/address */ /****************************/ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; for (sptr = Scratch; *sptr && !isalnum(*sptr); sptr++); if (isdigit(*sptr)) sptr = &rqptr->ClientPtr->IpAddressString; else sptr = rqptr->ClientPtr->Lookup.HostName; Result = StringMatchGreedyRegex (rqptr, sptr, Scratch); break; case 'FO' : /*******************/ /* proxy forwarded */ /*******************/ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; /* it's unlikely we'll have any of this header field */ if (rqptr->rqHeader.ForwardedPtr) Result = MapConList (rqptr, Scratch, rqptr->rqHeader.ForwardedPtr); break; case 'ME' : /***************/ /* HTTP method */ /***************/ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; if (Scratch[0] == '?') Result = (rqptr->rqHeader.Method == HTTP_METHOD_EXTENSION); else Result = StringMatchGreedyRegex (rqptr, rqptr->rqHeader.MethodName, Scratch); break; case 'MP' : /*********************/ /* being mapped path */ /*********************/ /* path remaining after script/exec rule, or after a 'map' rule */ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; if (rqptr->MetaConMappedPtr) Result = StringMatchGreedyRegex (rqptr, rqptr->MetaConMappedPtr, Scratch); else Result = false; break; case 'NO' : /***********/ /* notepad */ /***********/ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; if (rqptr->NotePadPtr) Result = StringMatchGreedyRegex (rqptr, rqptr->NotePadPtr, Scratch); else Result = false; break; case 'PA' : /********/ /* pass */ /********/ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; Result = (Scratch[0] - '0' == rqptr->MetaConPass); break; case 'PI' : /*************/ /* path-info */ /*************/ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; Result = StringMatchGreedyRegex (rqptr, rqptr->rqHeader.PathInfoPtr, Scratch); break; case 'QS' : /****************/ /* query string */ /****************/ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; Result = StringMatchGreedyRegex (rqptr, rqptr->rqHeader.QueryStringPtr, Scratch); break; case 'RC' : /********************/ /* redirected count */ /********************/ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; if (Scratch[0]) Result = (Scratch[0] - '0' == rqptr->RedirectCount); else Result = rqptr->RedirectCount; break; case 'RF' : /****************/ /* refering URL */ /****************/ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; Result = StringMatchGreedyRegex (rqptr, rqptr->rqHeader.RefererPtr, Scratch); break; case 'RQ' : /****************4*/ /* request field */ /*****************/ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && \ *cptr != ':' && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; if (denptr = DictLookup (rqptr->rqDictPtr, DICT_TYPE_REQUEST, Scratch, sptr - Scratch)) { if (*sptr == ':') { sptr++; while (*cptr && !ISLWS(*cptr) && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; Result = StringMatchGreedyRegex (rqptr, sptr, Scratch); } else Result = true; if (Result) break; } break; case 'RU' : /***************/ /* request URI */ /***************/ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; Result = StringMatchGreedyRegex (rqptr, rqptr->rqHeader.RequestUriPtr, Scratch); break; case 'SC' : /****************************************/ /* request scheme ("http:" or "https:") */ /****************************************/ ConditionalCount++; cptr += 3; /* also ignore any trailing colon on the scheme string */ while (*cptr && !ISLWS(*cptr) && *cptr != ':' && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; if (*cptr == ':') cptr++; /* any trailing colon has been stripped */ if (strsame (Scratch, "https", -1) && rqptr->ServicePtr->RequestScheme == SCHEME_HTTPS) Result = true; else if (strsame (Scratch, "http", -1) && rqptr->ServicePtr->RequestScheme == SCHEME_HTTP) Result = true; else Result = false; break; case 'SN' : /***************/ /* server name */ /***************/ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; Result = StringMatchGreedyRegex (rqptr, rqptr->ServicePtr->ServerHostName, Scratch); break; case 'SP' : /***************/ /* server port */ /***************/ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; Result = StringMatchGreedyRegex (rqptr, rqptr->ServicePtr->ServerPortString, Scratch); break; case 'ST' : /***************/ /* script name */ /***************/ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; if (sptr = rqptr->MetaConScriptPtr) { if (*sptr == '+') { /* CGIplus indicator */ *sptr = '/'; Result = StringMatchGreedyRegex (rqptr, sptr, Scratch); *sptr = '+'; } else Result = StringMatchGreedyRegex (rqptr, sptr, Scratch); } else Result = false; break; case 'UA' : /*****************/ /* "User-Agent:" */ /*****************/ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; Result = StringMatchGreedyRegex (rqptr, rqptr->rqHeader.UserAgentPtr, Scratch); break; case 'VS' : case 'HH' : /* backward compatibility */ /******************************************/ /* virtual service ("Host:") name/address */ /******************************************/ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; Result = StringMatchGreedyRegex (rqptr, rqptr->rqHeader.HostPtr, Scratch); break; case 'XF' : /*******************/ /* x-forwarded-for */ /*******************/ ConditionalCount++; cptr += 3; while (*cptr && !ISLWS(*cptr) && *cptr != ']' && sptr < zptr) { if (*cptr == '\\') cptr++; if (*cptr) *sptr++ = *cptr++; } *sptr = '\0'; Result = StringMatchGreedyRegex (rqptr, rqptr->rqHeader.XForwardedForPtr, Scratch); break; default : /*******************/ /* unknown, ignore */ /*******************/ /* should never occur due to basic check in MapUrl_ConfigLoad()! */ if (WATCH_CAT && WatchThisOne) WatchDataFormatted ("IGNORE !AZ\n", CurrentPtr); while (*cptr && !ISLWS(*cptr) && *cptr != ']') cptr++; continue; } if (NegateThisCondition) SoFarSoGood = SoFarSoGood || !Result; else SoFarSoGood = SoFarSoGood || Result; if (WATCH_CAT && WatchThisOne) WatchDataFormatted ("!AZ !AZ\n", SoFarSoGood ? "MATCH" : "NOMATCH", CurrentPtr); } if (!ConditionalCount) SoFarSoGood = true; else if (NegateEntireConditional) SoFarSoGood = !SoFarSoGood; if (WATCH_CAT && WatchThisOne) WatchDataFormatted ("!AZ conditional\n", SoFarSoGood ? "PASSED" : "FAILED"); return (SoFarSoGood); } /*****************************************************************************/ /* Compare single "??:" conditional string to each of a comma-separated list in the form "item" or "item1, item2, item3", etc. String may contain '*' and '%' wildcards. We can munge the list string like this because we're operating at AST-delivery level and cannot be preempted! */ BOOL MapConList ( REQUEST_STRUCT *rqptr, char *String, char *List ) { char ch; char *cptr, *lptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_MAPURL)) WatchThis (WATCHITM(rqptr), WATCH_MOD_MAPURL, "MapConList() !&Z !&Z", String, List); if (!(lptr = List)) return (false); if (!String) return (false); while (*lptr) { /* spaces and commas are element separators, step over */ while (ISLWS(*lptr) || *lptr == ',') lptr++; /* if end of list (or empty) then finished */ if (!*lptr) return (false); /* find end of this element, save next character, terminate element */ for (cptr = lptr; *lptr && !ISLWS(*lptr) && *lptr != ','; lptr++); ch = *lptr; *lptr = '\0'; /* look for what we want */ if (StringMatchGreedyRegex (rqptr, cptr, String)) { /* restore list */ *lptr = ch; return (true); } /* restore list */ *lptr = ch; } /* ran out of list elements, so it can't be a match! */ return (false); } /*****************************************************************************/