/*****************************************************************************/ /* DAVmeta.c The WASD WebDAV meta-data is used to store lock and 'dead' property data potentially for each file-system object (file and directory) accessable from the server using WebDAV. There are three locations used to store meta-data files, the default and two by global or per-path configuration. 1. the same directory as the resource (default) 2. a specified subdirectory of the resource (e.g. [.^.dav] 3. a completely independent directory (e.g. DKA0:[WASDAV]) Meta-data files are stored using the same name as the resource, with "__wasdav" concatenated to the file type (note the two underscores). Collection (directory) meta-data is associated with the directory file (e.g. A_Directory.DIR__wasdav). This was done for reasons of programmatic simplicity, file-system efficiency, reducing WebDAV latency and for the convenience of command-line management of the meta-data files. No specialised tools are required. All non-WebDAV WASD functionality ignores *.*__wasdav; files (e.g. directory listing, file GET). 1. Same Directory A directory listing using the default metadata location (with the resource) containing two resources with associated meta-data looks something like: admin.c;1 125KB 3-JAN-2009 06:06:55.46 admin.c__wasdav;1 0.50KB 11-JUN-2009 18:26:27.82 admin.h;1 8KB 17-FEB-2008 15:02:53.32 auth.c;1 142KB 27-APR-2008 08:28:59.21 auth.c__wasdav;1 0.50KB 11-JUN-2009 18:27:17.02 auth.h;1 18KB 29-APR-2009 21:16:31.33 authaccess.c;1 16KB 6-AUG-2007 17:43:51.68 authacme.c;1 24KB 30-APR-2009 06:23:51.94 2. Specified Subdirectory When meta-data files are stored in a subdirectory (automatically created) of the resource location the associated files will look like: admin.c;1 125KB 3-JAN-2009 06:06:55.46 admin.h;1 8KB 17-FEB-2008 15:02:53.32 auth.c;1 142KB 27-APR-2008 08:28:59.21 auth.h;1 18KB 29-APR-2009 21:16:31.33 authaccess.c;1 16KB 6-AUG-2007 17:43:51.68 authacme.c;1 24KB 30-APR-2009 06:23:51.94 [.^.dav]admin.c__wasdav;1 0.50KB 11-JUN-2009 18:26:27.82 [.^.dav]auth.c__wasdav;1 0.50KB 11-JUN-2009 18:27:17.02 Note: The meta-data for a directory is contained in the meta-data subdirectory contained within that same directory, not in the parent where the directory file ("whatever.DIR") actually resides, in a nameless file with the type (extension) ".DIR__wasdav". For example, meta-data for the above directory would be contained in "[.WHATEVER.^.DAV].DIR__wasdav". 3. Independent Directory When meta-data files are stored in an independent directory they are distributed into one of 16 (hexadecimally named, automatically created) subdirectories. The full file path of the resource is ODS-5 escaped and used to uniquely identify the meta-data. The above example translated into this will look something like: DKA0:[WASDAV.00]wasd_root^:^[src^.httpd^]admin.c__wasdav;1 DKA0:[WASDAV.05]wasd_root^:^[src^.httpd^]auth.c__wasdav;1 Of course this cannot be used within an ODS-2 environment (VAX) or in an extended-capable environment on an ODS-2 volume. META-DATA XML ------------- Meta-data files contain XML. WASD name-space contains zero or more WASD lock elements and/or zero or more property elements. If there is zero of both (as might occur after an UNLOCK or PROPPATCH remove, the meta-data file is deleted. The WASD element also contains the (file-system) resource the meta-data represents (file or directory specification) and the date/time of the last update to that meta-data. The following is an example of this XML structure: ... ... LOCK META-DATA -------------- All lock characteristics except the potentially XML-containing owner element are contained in attributes to the WASD lock element. One or more lock elements may be present representing compatible locks against the resource. Example lock meta-data: blah blah PROPERTY META-DATA ------------------ The property elements are stored as-supplied by client. It is presumed that their XML well-formedness is guaranteed by the original request XML parsing. For ease of processing (by WASD DAV) each element in a property contains an 'xmlns=""' attribute. An example of such meta-data generated by a Microsoft Windows (not Internet) Explorer client (example is intentionally not wrapped): Tue, 26 Jun 2007 02:00:48 GMT Mon, 23 Jul 2007 01:52:32 GMT Mon, 23 Jul 2007 01:52:32 GMT 00000020 VERSION HISTORY --------------- 03-DEC-2018 MGD bugfix; DavMetaDir() ACCVIO from !SAME2(mfdptr,'[.') 12-SEP-2018 MGD bugfix; significant refactor of locking 06-SEP-2018 MGD bugfix; DavMeta..() explicitly ->ReadOds.DeleteOnClose 17-MAY-2018 MGD DavXmlParserCreate() instead of XML_ParserCreateNS() 09-MAY-2018 MGD bugfix; DavMetaParseXml() detect out-of-memory 01-OCT-2014 MGD directory meta-data goes into its own metadata (sub)directory 27-SEP-2014 MGD bugfix; DavMetaOpenAst() retry after meta directory creation 05-JUL-2014 MGD configurable metadata (sub)directory 07-SEP-2013 MGD bugfix; DavMetaUpdate() abort if SetTimeout() fails 08-JUN-2010 MGD bugfix; DavMetaReadName() and DavMetaWriteName() allow for typeless file names (e.g. ]AFILE.;) 10-MAY-2010 JPP bugfix; do not set new token when refreshing 02-SEP-2009 MGD bugfix; scanning backwards through extended file spec 31-DEC-2006 MGD initial */ /*****************************************************************************/ #ifdef WASD_VMS_V7 #undef _VMS__V6__SOURCE #define _VMS__V6__SOURCE #undef __VMS_VER #define __VMS_VER 70000000 #undef __CRTL_VER #define __CRTL_VER 70000000 #endif #include #include #include #include #include #include "wasd.h" #include "davweb.h" #define WASD_MODULE "DAVMETA" #ifndef WASD_WEBDAV #define WASD_WEBDAV 1 #endif #define DAVMETA_READ_CHUNK (512 * 32) /* just reduce the amount of XML parse noise when WATCHing */ #define WATCH_XML 1 /******************/ /* global storage */ /******************/ /********************/ /* external storage */ /********************/ extern BOOL OdsExtended, WebDavEnabled, WebDavLockingEnabled; extern int EfnWait, EfnNoWait; extern int ToLowerCase[], ToUpperCase[]; extern unsigned long HttpdTime64[], SysPrvMask[]; extern char ErrorSanityCheck[], HttpdSoftwareIdName[], HttpdVersion[]; extern ACCOUNTING_STRUCT *AccountingPtr; extern CONFIG_STRUCT Config; extern LIST_HEAD RequestList; extern WATCH_STRUCT Watch; /*****************************************************************************/ /* Build the data buffer containing the XML formated WASD lock and properties associated with the resource (file). This can then be written to disk. The two descriptor collections, 'MetaLockDsc' containing any existing lock elements, and 'MetaPropDsc', containing any existing property elements are merged with the lock elements from the request contained in 'XmlLockSetDsc', and the property elements in 'XmlPropSetDsc'. The merging is performed by emptying any element of 'MetaLockDsc' or 'MetaPropDsc' that is to be removed, by overwriting any existing element that is to be set, or appended to the collection any new element, both to 'MetaLockDsc' or 'MetaPropDsc' as required. */ int DavMetaBuild (WEBDAV_META *mtaptr) { int status, ItemCount; char *cptr, *czptr; char DateTime [32]; STR_DSC *sdptr, *wdptr; WEBDAV_LOCK *lckptr; REQUEST_STRUCT *rqptr; WEBDAV_TASK *tkptr; /*********/ /* begin */ /*********/ rqptr = mtaptr->RequestPtr; if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavMetaBuild()"); tkptr = rqptr->WebDavTaskPtr; /*************************/ /* remove/set properties */ /*************************/ StrDscColIfNotBegin (rqptr, &mtaptr->PropDsc, 256); if (STR_DSC_LEN(&mtaptr->PropDsc)) { /* remove properties specified in the request XML */ STR_DSC_ITERATE (sdptr, &tkptr->XmlData.PropRemoveDsc) { if (!STR_DSC_LEN(sdptr)) continue; /* advise that DAV properties cannot be removed by clients */ for (czptr = (cptr = STR_DSC_PTR(sdptr)) + STR_DSC_LEN(sdptr); cptr < czptr && (*cptr != 'x' || !MATCH6(cptr,"xmlns:")); cptr++); if (cptr < czptr && MATCH9(cptr+6,"NS=\"DAV:\"")) return (SS$_INCOMPAT); DavMetaRemove (rqptr, &mtaptr->PropDsc, sdptr); } } if (STR_DSC_LEN(&tkptr->XmlData.PropSetDsc)) { /* set properties specified in the request XML */ STR_DSC_ITERATE (sdptr, &tkptr->XmlData.PropSetDsc) { if (!STR_DSC_LEN(sdptr)) continue; /* ensure DAV properties are not overridden by (nefarious?) clients */ for (czptr = (cptr = STR_DSC_PTR(sdptr)) + STR_DSC_LEN(sdptr); cptr < czptr && (*cptr != 'x' || !MATCH6(cptr,"xmlns:")); cptr++); if (cptr < czptr && MATCH9(cptr+6,"NS=\"DAV:\"")) return (SS$_INCOMPAT); DavMetaSet (rqptr, &mtaptr->PropDsc, sdptr); } } if (WATCHING (rqptr, WATCH_WEBDAV)) { if (STR_DSC_LEN(&mtaptr->LockDsc)) { WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "LOCK built"); STR_DSC_ITERATE (sdptr, &mtaptr->LockDsc) if (STR_DSC_LEN(sdptr)) WatchDataFormatted ("!AZ\n", STR_DSC_PTR(sdptr)); } if (STR_DSC_LEN(&mtaptr->PropDsc)) { WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "PROP built"); STR_DSC_ITERATE (sdptr, &mtaptr->PropDsc) if (STR_DSC_LEN(sdptr)) WatchDataFormatted ("!AZ\n", STR_DSC_PTR(sdptr)); } } /******************/ /* build meta XML */ /******************/ /* write will be by block and so must be evenly divisible by 512! */ wdptr = StrDscBegin (rqptr, &mtaptr->WriteDsc, 4096); ItemCount = 0; DavWebDateTimeTo3339 (DateTime, &HttpdTime64); FaoToBuffer (wdptr, -1, NULL, "\n\ \n", DateTime, &HttpdTime64); /* step through each lock in the list generating an XML text equivalent */ LIST_ITERATE (lckptr, &mtaptr->LockList) { DavLockXml (rqptr, lckptr, wdptr); ItemCount++; } /* step through each property in the list adding the XML */ STR_DSC_ITERATE (sdptr, &mtaptr->PropDsc) { if (!STR_DSC_LEN(sdptr)) continue; StrDscBuild (wdptr, NULL, "\n"); StrDscBuild (wdptr, sdptr, NULL); StrDscNewLine (wdptr); StrDscBuild (wdptr, NULL, "\n"); ItemCount++; } StrDscBuild (wdptr, NULL, "\n"); if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "!&Z", STR_DSC_PTR(wdptr)); if (ItemCount) return (SS$_NORMAL); /* return this code to indicate the file is empty and should be deleted */ return (SS$_FORGET); } /*****************************************************************************/ /* Add the XML element specified in 'adptr' to the descriptor collection specified by 'cdptr'. Over-write an existing identical element in the collection. Returns a pointer to the new (or over-written) element. */ STR_DSC* DavMetaSet ( REQUEST_STRUCT *rqptr, STR_DSC *cdptr, STR_DSC *adptr ) { STR_DSC *sdptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavMetaSet()"); if (sdptr = DavMetaSearch (rqptr, cdptr, adptr)) { /* overwite existing element */ STR_DSC_LEN(sdptr) = 0; sdptr = StrDscBuild (sdptr, adptr, NULL); } else sdptr = StrDscBuild (cdptr, adptr, NULL); STR_DSC_SET_FROZEN(sdptr) return (sdptr); } /*****************************************************************************/ /* Remove the XML element specified in 'rdptr' from the descriptor collection specified by 'cdptr'. Returns true if removed, false if not found. */ BOOL DavMetaRemove ( REQUEST_STRUCT *rqptr, STR_DSC *cdptr, STR_DSC *rdptr ) { STR_DSC *sdptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavMetaRemove()"); if (sdptr = DavMetaSearch (rqptr, cdptr, rdptr)) { STR_DSC_LEN(sdptr) = 0; return (true); } return (false); } /*****************************************************************************/ /* Search the collection specified by 'cdptr' for the element specified by 'edptr'. Return a pointer to the descriptor if found, otherwise return NULL. */ STR_DSC* DavMetaSearch ( REQUEST_STRUCT *rqptr, STR_DSC *cdptr, STR_DSC *edptr ) { char *cptr, *sptr; STR_DSC *sdptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavMetaSearch()"); if (!cdptr || !edptr) return (NULL); STR_DSC_ITERATE (sdptr, cdptr) { if (!(cptr = STR_DSC_PTR(edptr))) continue; if (!(sptr = STR_DSC_PTR(sdptr))) continue; while (*cptr && *sptr && *cptr != '>' && *sptr != '>' && *cptr == *sptr) { cptr++; sptr++; } /* if the two elements matched then it's a hit */ if (*cptr == '>' && *sptr == '>') return (sdptr); } return (NULL); } /*****************************************************************************/ /* (EXPAT) XML parse the text read from the database file. This will parse the lock elements into 'MetaLockDsc' and the property elements into 'MetaPropDsc'. */ int DavMetaParseXml (WEBDAV_META *mtaptr) { int status, xmlsts; STR_DSC *sdptr; REQUEST_STRUCT *rqptr; WEBDAV_LOCK *lckptr; WEBDAV_TASK *tkptr; /*********/ /* begin */ /*********/ rqptr = mtaptr->RequestPtr; if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavMetaParseXml()"); sdptr = &mtaptr->ReadDsc; /* shouldn't be calling this without content to parse! */ if (!(STR_DSC_SANITY(sdptr) && STR_DSC_LEN(sdptr))) return (SS$_BUGCHECK); tkptr = rqptr->WebDavTaskPtr; /* successful unless an error is encountered :-) */ status = SS$_NORMAL; StrDscColIfNotBegin (rqptr, &mtaptr->PropDsc, 256); mtaptr->ParseData = mtaptr->ParseLock = mtaptr->ParseLockExpires = mtaptr->ParseLockOwner = mtaptr->ParseProp = WEBDAV_STATE_NOT_PARSING; tkptr->XmlData.ParserPtr = DavXmlParserCreate (rqptr); if (!tkptr->XmlData.ParserPtr) return (vaxc$errno); mtaptr->ElementDepth = 0; XML_SetUserData (tkptr->XmlData.ParserPtr, mtaptr); XML_SetElementHandler (tkptr->XmlData.ParserPtr, DavMetaStartElement, DavMetaEndElement); XML_SetCharacterDataHandler (tkptr->XmlData.ParserPtr, DavMetaCharData); xmlsts = XML_Parse (tkptr->XmlData.ParserPtr, STR_DSC_PTR(sdptr), STR_DSC_LEN(sdptr), 1); if (xmlsts == XML_STATUS_ERROR) { DavXmlParseError (rqptr, tkptr->XmlData.ParserPtr, sdptr, &mtaptr->ParseErrorDsc); if (DavXmlMemoryError (rqptr, tkptr->XmlData.ParserPtr)) status = LIB$_INSVIRMEM; } if (tkptr->XmlData.ParserPtr) XML_ParserFree (tkptr->XmlData.ParserPtr); if (xmlsts == XML_STATUS_ERROR) { /* shouldn't be getting parse errors from our own data */ ErrorNoticed (rqptr, status = SS$_BUGCHECK, STR_DSC_PTR(&mtaptr->ParseErrorDsc), FI_LI); } else { if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) { if (STR_DSC_LEN(&mtaptr->LockDsc)) { WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "LOCKS current"); STR_DSC_ITERATE (sdptr, &mtaptr->LockDsc) if (STR_DSC_LEN(sdptr)) WatchDataFormatted ("!AZ\n", STR_DSC_PTR(sdptr)); } if (STR_DSC_LEN(&mtaptr->PropDsc)) { WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "META current"); STR_DSC_ITERATE (sdptr, &mtaptr->PropDsc) if (STR_DSC_LEN(sdptr)) WatchDataFormatted ("!AZ\n", STR_DSC_PTR(sdptr)); } } } return (status); } /*****************************************************************************/ /* (EXPAT) This function and DavMetaEndElement() set various flags to indicate the state of the current XML element. From those various flags indicating properties to be manipulated are set. This pretty-much assumes that the parsing library is ensuring document well-formedness, so the checks on element nesting are very elementary. */ void XMLCALL DavMetaStartElement ( WEBDAV_META *mtaptr, char *ElementPtr, char **AttrPtr ) { int idx, len, status; char *cptr, *sptr, *zptr; char ResourceBuffer [ODS_MAX_FILE_NAME_LENGTH+1]; STR_DSC *sdptr; STR_DSC_AUTO (ScratchDsc); REQUEST_STRUCT *rqptr; WEBDAV_LOCK *lckptr; /*********/ /* begin */ /*********/ rqptr = mtaptr->RequestPtr; if (WATCH_XML && WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavMetaStartElement() !UL !&Z", mtaptr->ElementDepth+1, ElementPtr); mtaptr->ElementDepth++; if (WATCH_XML && WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) { WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "ELEMENT !AZ", ElementPtr); for (idx = 0; AttrPtr[idx]; idx += 2) WatchDataFormatted ("!AZ=\"!AZ\"\n", AttrPtr[idx], AttrPtr[idx+1]); } sdptr = NULL; if (WEBDAV_IS_PARSING(mtaptr->ParseLock)) { /* the most recently added lock will be at the head of the list */ lckptr = LIST_GET_HEAD(&mtaptr->LockList); if (WEBDAV_IS_PARSING(mtaptr->ParseLockOwner)) sdptr = &lckptr->OwnerDsc; else sdptr = &lckptr->LockDsc; } else if (WEBDAV_IS_PARSING(mtaptr->ParseProp)) sdptr = &mtaptr->PropDsc; if (WEBDAV_IS_WASDAVEL (ElementPtr, "data")) { /* should only be the one data element per meta-data file! */ if (WEBDAV_IS_PARSED(mtaptr->ParseData)) { if (WATCHING (rqptr, WATCH_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "META multiple (ignoring subsequent)"); return; } WEBDAV_PARSING(mtaptr->ParseData); } else if (WEBDAV_IS_WASDAVEL (ElementPtr, "lock")) { lckptr = (WEBDAV_LOCK*)VmGetHeap (rqptr, sizeof(WEBDAV_LOCK)); ListAddHead (&mtaptr->LockList, lckptr, LIST_ENTRY_TYPE_DAVLOCK); StrDscColIfNotBegin (rqptr, &lckptr->LockDsc, 256); StrDscBegin (rqptr, &lckptr->OwnerDsc, 256); WEBDAV_PARSING(mtaptr->ParseLock); sdptr = &lckptr->LockDsc; sdptr = StrDscBuild (sdptr, NULL, "ParseLock) && !WEBDAV_IS_PARSING(mtaptr->ParseLockOwner)) { if (!strcmp (AttrPtr[idx], "depth")) { if (!strcmp (AttrPtr[idx+1], "0")) lckptr->Depth = WEBDAV_DEPTH_ZERO; else if (!strcmp (AttrPtr[idx+1], "1")) lckptr->Depth = WEBDAV_DEPTH_ONE; else if (!strcmp (AttrPtr[idx+1], "infinity")) lckptr->Depth = WEBDAV_DEPTH_INFINITY; else ErrorNoticed (rqptr, SS$_BUGCHECK, AttrPtr[idx+1], FI_LI); } else if (!strcmp (AttrPtr[idx], "expires")) { zptr = (sptr = lckptr->ExpiresString) + sizeof(lckptr->ExpiresString)-1; for (cptr = AttrPtr[idx+1]; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr < zptr) { *sptr = '\0'; status = DavWebDateTimeFrom3339 (lckptr->ExpiresString, &lckptr->ExpiresTime64); if (VMSnok (status)) ErrorNoticed (rqptr, status, AttrPtr[idx+1], FI_LI); } else { PUT_ZERO_QUAD(&lckptr->ExpiresTime64); lckptr->ExpiresString[0] = '\0'; ErrorNoticed (rqptr, SS$_BUGCHECK, AttrPtr[idx+1], FI_LI); } } else if (!strcmp (AttrPtr[idx], "token")) { zptr = (sptr = lckptr->TokenString) + sizeof(lckptr->TokenString); for (cptr = AttrPtr[idx+1]; *cptr && sptr < zptr; *sptr++ = *cptr++); if (sptr < zptr) *sptr = '\0'; else { lckptr->TokenString[0] = '\0'; ErrorNoticed (rqptr, SS$_BUGCHECK, AttrPtr[idx+1], FI_LI); } } else if (!strcmp (AttrPtr[idx], "type")) { if (!strcmp (AttrPtr[idx+1], "write")) lckptr->Type = WEBDAV_LOCK_TYPE_WRITE; else ErrorNoticed (rqptr, SS$_BUGCHECK, AttrPtr[idx+1], FI_LI); } else if (!strcmp (AttrPtr[idx], "scope")) { if (!strcmp (AttrPtr[idx+1], "exclusive")) lckptr->Scope = WEBDAV_LOCK_SCOPE_EXCLUSIVE; else if (!strcmp (AttrPtr[idx+1], "shared")) lckptr->Scope = WEBDAV_LOCK_SCOPE_SHARED; else ErrorNoticed (rqptr, SS$_BUGCHECK, AttrPtr[idx+1], FI_LI); } else if (!strcmp (AttrPtr[idx], "timeout")) { if (!strcmp (AttrPtr[idx+1], "infinite")) { lckptr->Timeout = WEBDAV_TIMEOUT_INFINITE; strcpy (lckptr->TimeoutString, "infinite"); } else if (!strncmp (AttrPtr[idx+1], "Second-", 7) && isdigit(AttrPtr[idx+1][7])) { lckptr->Timeout = atoi(AttrPtr[idx+1]+7); sprintf (lckptr->TimeoutString, "Second-%d", lckptr->Timeout); } else ErrorNoticed (rqptr, SS$_BUGCHECK, AttrPtr[idx+1], FI_LI); } } } StrDscBuild (sdptr, NULL, ">\n"); sdptr = NULL; } else if (WEBDAV_IS_WASDAVEL (ElementPtr, "owner")) { if (WEBDAV_IS_PARSING(mtaptr->ParseLock)) WEBDAV_PARSING(mtaptr->ParseLockOwner); else ErrorNoticed (rqptr, SS$_BUGCHECK, ErrorSanityCheck, FI_LI); } else if (WEBDAV_IS_WASDAVEL (ElementPtr, "prop")) WEBDAV_PARSING(mtaptr->ParseProp); if (sdptr) { for (cptr = ElementPtr; *cptr && *cptr != ' '; cptr++); sdptr = StrDscBuild (sdptr, NULL, ""); WEBDAV_PARSING(mtaptr->ParseCharData); } else WEBDAV_NOT_PARSING(mtaptr->ParseCharData); } /*****************************************************************************/ /* (EXPAT) This function and DavMetaStartElement() set various flags to indicate the state of the current XML element. */ void XMLCALL DavMetaEndElement ( WEBDAV_META *mtaptr, char *ElementPtr ) { char *cptr, *sptr; STR_DSC *sdptr; STR_DSC_AUTO (ScratchDsc); REQUEST_STRUCT *rqptr; WEBDAV_LOCK *lckptr; /*********/ /* begin */ /*********/ rqptr = mtaptr->RequestPtr; if (WATCH_XML && WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavMetaEndElement() !UL !&Z", mtaptr->ElementDepth, ElementPtr); if (!WEBDAV_IS_PARSING(mtaptr->ParseData)) return; if (WEBDAV_IS_PARSING(mtaptr->ParseLock)) { if (WEBDAV_IS_WASDAVEL (ElementPtr, "lock")) { /* no longer parsing the lock, freeze current content */ WEBDAV_PARSED(mtaptr->ParseLock); /* the most recently added lock will be at the head of the list */ lckptr = LIST_GET_HEAD(&mtaptr->LockList); sdptr = &lckptr->LockDsc; sdptr = StrDscBuild (sdptr, NULL, ""); StrDscTailFrozen (&lckptr->LockDsc); } else if (WEBDAV_IS_WASDAVEL (ElementPtr, "owner")) WEBDAV_PARSED(mtaptr->ParseLockOwner); } else if (WEBDAV_IS_PARSING(mtaptr->ParseProp)) { if (WEBDAV_IS_WASDAVEL (ElementPtr, "prop")) { /* no longer parsing this property, freeze current content */ WEBDAV_PARSED(mtaptr->ParseProp); StrDscTailFrozen (&mtaptr->PropDsc); } } else /* as this is the level 0 element it MUST be the last checked! */ if (WEBDAV_IS_PARSING(mtaptr->ParseData)) { if (WEBDAV_IS_WASDAVEL (ElementPtr, "data")) WEBDAV_PARSED(mtaptr->ParseData); } sdptr = NULL; if (WEBDAV_IS_PARSING(mtaptr->ParseLock)) { /* the most recently added lock will be at the head of the list */ lckptr = LIST_GET_HEAD(&mtaptr->LockList); if (WEBDAV_IS_PARSING(mtaptr->ParseLockOwner)) sdptr = &lckptr->OwnerDsc; else sdptr = &lckptr->LockDsc; } else if (WEBDAV_IS_PARSING(mtaptr->ParseProp)) sdptr = &mtaptr->PropDsc; if (sdptr) { for (cptr = ElementPtr; *cptr && *cptr != ' '; cptr++); sdptr = StrDscBuild (sdptr, NULL, ""); if (mtaptr->ElementDepth == 3) { if (WEBDAV_IS_PARSING(mtaptr->ParseProp)) { /* end of this property, freeze current content */ STR_DSC_SET_FROZEN (sdptr) } WEBDAV_NOT_PARSING(mtaptr->ParseCharData); } else WEBDAV_PARSING(mtaptr->ParseCharData); } else WEBDAV_NOT_PARSING(mtaptr->ParseCharData); if (mtaptr->ElementDepth) mtaptr->ElementDepth--; } /*****************************************************************************/ /* (EXPAT) XML character data. */ void XMLCALL DavMetaCharData ( WEBDAV_META *mtaptr, XML_Char *DataPtr, int DataLength ) { STR_DSC *sdptr; STR_DSC_AUTO (CharDataDsc); REQUEST_STRUCT *rqptr; WEBDAV_LOCK *lckptr; /*********/ /* begin */ /*********/ rqptr = mtaptr->RequestPtr; if (WATCH_XML && WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavMetaCharData() !&B !UL |!#AZ", mtaptr->ParseCharData, DataLength, DataLength, DataPtr); if (!WEBDAV_IS_PARSING(mtaptr->ParseCharData)) return; sdptr = NULL; if (WEBDAV_IS_PARSING(mtaptr->ParseLock)) { /* the most recently added lock will be at the head of the list */ lckptr = LIST_GET_HEAD(&mtaptr->LockList); if (WEBDAV_IS_PARSING(mtaptr->ParseLockOwner)) sdptr = &lckptr->OwnerDsc; else sdptr = &lckptr->LockDsc; } else if (WEBDAV_IS_PARSING(mtaptr->ParseProp)) sdptr = &mtaptr->PropDsc; if (!sdptr) return; StrDscThis (NULL, &CharDataDsc, DataPtr, DataLength); StrDscBuild (sdptr, &CharDataDsc, NULL); } /*****************************************************************************/ /* Generate the name of the meta-data file. If ReadWrite is 'r' then it generates the read metadata filename. If 'w' then the write name. */ DavMetaName ( WEBDAV_META *mtaptr, char *FileSpec, int ReadWrite ) { static char HexDigits [] = "0123456789ABCDEF"; int status, BufferLength; unsigned long BufferHash [4]; char *aptr, *cptr, *sptr, *zptr, *BufferPtr, *FilePtr, *HexPtr; REQUEST_STRUCT *rqptr; /*********/ /* begin */ /*********/ rqptr = mtaptr->RequestPtr; if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavMetaName() !&Z !&C", FileSpec, ReadWrite); if (ReadWrite == 'r') zptr = (sptr = mtaptr->ReadMetaName) + sizeof(mtaptr->ReadMetaName); else if (ReadWrite == 'w') zptr = (sptr = mtaptr->WriteMetaName) + sizeof(mtaptr->WriteMetaName); else ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); zptr -= 3; BufferPtr = FilePtr = sptr; mtaptr->MetaDirNamePtr = NULL; mtaptr->MetaDirNameLength = 0; HexPtr = NULL; if (mtaptr->TaskPtr->MetaFileDirPtr && SAME2(mtaptr->TaskPtr->MetaFileDirPtr,'[.')) { /****************/ /* subdirectory */ /****************/ /* subdirectory of file directory (e.g. "[.^.DAV]", "[.WHATEVER]") */ for (cptr = FileSpec; *cptr && (*cptr != ']' || SAME2(cptr-1,'^]') || SAME2(cptr,'][')) && sptr < zptr; cptr++) { if (SAME2(cptr,'][')) cptr++; else *sptr++ = TOLO(*cptr); } if (*cptr == ']') { /* if the file type is ".DIR" then turn it into an effective ']' */ for (aptr = cptr; *aptr; aptr++); while (aptr > cptr && (*aptr != '.' || SAME2(aptr-1,'^.'))) aptr--; if (*aptr == '.' && (MATCH5(aptr,".DIR;") || MATCH5(aptr,".DIR") || MATCH5(aptr,".dir;") || MATCH5(aptr,".dir"))) { /* insert the file name as part of the directory structure */ if (sptr < zptr) *sptr++ = '.'; for (cptr++; cptr < aptr && sptr < zptr; *sptr++ = *cptr++); /* no (file) name anymore */ cptr = ""; } /* insert the metadata subdirectory */ for (aptr = mtaptr->TaskPtr->MetaFileDirPtr+1; *aptr && sptr < zptr; *sptr++ = TOLO(*aptr++)); mtaptr->MetaDirNamePtr = BufferPtr; mtaptr->MetaDirNameLength = sptr - BufferPtr; /* append the file name and type */ for (cptr++; *cptr; *sptr++ = TOLO(*cptr++)); } } else if (mtaptr->TaskPtr->MetaFileDirPtr) { /******************/ /* meta directory */ /******************/ /* independent metadata directory (e.g. "DKA0:[WASDAV_META]" */ if (!OdsExtended) ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI); #ifndef __VAX for (cptr = mtaptr->TaskPtr->MetaFileDirPtr; *cptr && sptr < zptr; *sptr++ = TOLO(*cptr++)); *(sptr-1) = '.'; HexPtr = sptr; *sptr++ = '0'; *sptr++ = '0'; *sptr++ = ']'; mtaptr->MetaDirNamePtr = BufferPtr; mtaptr->MetaDirNameLength = sptr - BufferPtr; /* hash is generated excluding the metadata directory */ FilePtr = sptr; for (cptr = FileSpec; *cptr && sptr < zptr; *sptr++ = TOLO(*cptr++)) { if ((*cptr == ':' && !SAME2(cptr-1,'^:')) || (*cptr == '[' && !SAME2(cptr-1,'^[')) || (*cptr == ']' && !SAME2(cptr-1,'^]')) || (*cptr == '.' && !SAME2(cptr-1,'^.'))) { *sptr++ = '^'; if (*cptr == ']' && *(cptr+1) != '[') break; } } while (*cptr && sptr < zptr) *sptr++ = TOLO(*cptr++); #endif } else { /******************/ /* same directory */ /******************/ /* same directory as resource */ for (cptr = FileSpec; *cptr && sptr < zptr; *sptr++ = TOLO(*cptr++)); } *sptr = '\0'; /************/ /* sentinal */ /************/ while (sptr > BufferPtr && (*sptr != ';' || SAME2(sptr-1,'^;')) && (*sptr != '.' || SAME2(sptr-1,'^.')) && (*sptr != ']' || SAME2(sptr-1,'^]'))) sptr--; if (*sptr == ']') { while (*sptr) sptr++; if (*(sptr-1) != ']' && !SAME2(sptr-2,'^]') && sptr < zptr) *sptr++ = '.'; } else if (*sptr != ';') while (*sptr) sptr++; *sptr = '\0'; if (*(sptr-1) == ']') { /* special case - meta-data for a directory - no file name plus this */ for (cptr = ".DIR__wasdav"; *cptr && sptr < zptr; *sptr++ = *cptr++); mtaptr->MetaForDir = true; } else { /* append the WASD WebDAV sentinal to the current file type */ for (cptr = "__wasdav"; *cptr && sptr < zptr; *sptr++ = *cptr++); } *sptr = '\0'; BufferLength = sptr - BufferPtr; if (sptr >= zptr) { /* disable the use of this meta-data file name */ SET4(BufferPtr,'****'); ErrorNoticed (rqptr, SS$_BUFFEROVF, FileSpec, FI_LI); } /******************/ /* digest for DLM */ /******************/ Md5Digest (FilePtr, BufferLength-(FilePtr-BufferPtr), BufferHash); if (HexPtr) { /* seamless expansion from 16 to 256 subdirectories (!) if necessary */ /** HexPtr[0] = HexDigits[BufferHash[0] & 0xf0]; **/ HexPtr[1] = HexDigits[BufferHash[0] & 0x0f]; } if (ReadWrite == 'r') { mtaptr->ReadMetaNameLength = BufferLength; mtaptr->ReadHash[0] = BufferHash[0]; mtaptr->ReadHash[1] = BufferHash[1]; mtaptr->ReadHash[2] = BufferHash[2]; mtaptr->ReadHash[3] = BufferHash[3]; } else { mtaptr->WriteMetaNameLength = BufferLength; mtaptr->WriteHash[0] = BufferHash[0]; mtaptr->WriteHash[1] = BufferHash[1]; mtaptr->WriteHash[2] = BufferHash[2]; mtaptr->WriteHash[3] = BufferHash[3]; } if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "!&Z !8XL!8XL!8XL!8XL", BufferPtr, BufferHash[0], BufferHash[1], BufferHash[2], BufferHash[3]); } /*****************************************************************************/ /* Attempt to read a meta-data file corresponding to the file name provided in the parameter. We're not sure whether such a meta-data file exists at this stage so it might just end up a file-not-found at the AST. */ DavMetaRead ( REQUEST_STRUCT *rqptr, WEBDAV_META *mtaptr, char *FileSpec, REQUEST_AST AstFunction, unsigned long AstParam ) { /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavMetaRead() !&Z", FileSpec); InstanceGblSecIncrLong (&AccountingPtr->WebDavMetaReadAttemptCount); /* dispose of dynamic buffers from any previous call */ StrDscEnd (&mtaptr->ReadDsc, FI_LI); memset (mtaptr, 0, sizeof(WEBDAV_META)); mtaptr->RequestPtr = rqptr; mtaptr->TaskPtr = rqptr->WebDavTaskPtr; DavMetaName (mtaptr, FileSpec, 'r'); mtaptr->UpdateLocked = false; mtaptr->ReadAstFunction = AstFunction; mtaptr->ReadAstParam = AstParam; DavWebDlmEnqueue (rqptr, &mtaptr->DlmData, mtaptr->ReadMetaName, &mtaptr->ReadHash, false, false, DavMetaReadOpen, mtaptr); } /*****************************************************************************/ /* AST from VMS lock enqueue attempt. */ DavMetaReadOpen (WEBDAV_META *mtaptr) { REQUEST_STRUCT *rqptr; /*********/ /* begin */ /*********/ rqptr = mtaptr->RequestPtr; if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavMetaReadOpen() !UL !&Z", mtaptr->ReadMetaNameLength, mtaptr->ReadMetaName); mtaptr->VmsStatus = mtaptr->DlmData.LockSb.lksb$w_status; if (VMSnok (mtaptr->VmsStatus)) { /* VMS DLM enqueue failed, explicitly call any AST */ if (WATCHING (rqptr, WATCH_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "META read !AZ !&S", mtaptr->ReadMetaName, mtaptr->VmsStatus); SysDclAst (mtaptr->ReadAstFunction, mtaptr->ReadAstParam); return; } /* turn on SYSPRV to allow access to the file */ sys$setprv (1, &SysPrvMask, 0, 0); OdsOpen (&mtaptr->ReadOds, mtaptr->ReadMetaName, mtaptr->ReadMetaNameLength, NULL, 0, FAB$M_GET + FAB$M_BIO, 0, FAB$M_SHRGET, &DavMetaOpenAst, mtaptr); sys$setprv (0, &SysPrvMask, 0, 0); } /*****************************************************************************/ /* Set up the meta-data creation attributes then queue an exclusive VMS LDM lock on the meta-data hash. When granted this VMS DLM lock *queues* and blocks all subsequent requests to modify the meta-data (and hence represented resource) for the duration of the VMS DLM lock. */ DavMetaLock ( REQUEST_STRUCT *rqptr, WEBDAV_META *mtaptr, char *FileSpec, REQUEST_AST AstFunction, unsigned long AstParam ) { /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavMetaLock() !&Z", FileSpec); InstanceGblSecIncrLong (&AccountingPtr->WebDavMetaReadAttemptCount); /* dispose of dynamic buffers from any previous call */ StrDscEnd (&mtaptr->ReadDsc, FI_LI); memset (mtaptr, 0, sizeof(WEBDAV_META)); mtaptr->RequestPtr = rqptr; mtaptr->TaskPtr = rqptr->WebDavTaskPtr; DavMetaName (mtaptr, FileSpec, 'r'); mtaptr->UpdateLocked = true; mtaptr->ReadAstFunction = AstFunction; mtaptr->ReadAstParam = AstParam; DavWebDlmEnqueue (rqptr, &mtaptr->DlmData, mtaptr->ReadMetaName, &mtaptr->ReadHash, true, false, DavMetaLockAst, mtaptr); } /*****************************************************************************/ /* Check the VMS DLM status value for success or failure. If failed then just queue the AST with the status code set. For success, open a file for exclusive access. Create it if it does not already exist. We're not sure whether such a meta-data file exists at this stage so it might just end up a file-not-found at the AST. */ DavMetaLockAst (WEBDAV_META *mtaptr) { REQUEST_STRUCT *rqptr; WEBDAV_DLM *dlmptr; /*********/ /* begin */ /*********/ rqptr = mtaptr->RequestPtr; if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavMetaLockAst()"); dlmptr = &mtaptr->DlmData; mtaptr->VmsStatus = dlmptr->LockSb.lksb$w_status; if (VMSnok (mtaptr->VmsStatus)) { /* VMS DLM enqueue failed, explicitly call any AST */ if (WATCHING (rqptr, WATCH_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "META read !AZ !&S", mtaptr->ReadMetaName, mtaptr->VmsStatus); SysDclAst (mtaptr->ReadAstFunction, mtaptr->ReadAstParam); return; } /* turn on SYSPRV to allow access to the file */ sys$setprv (1, &SysPrvMask, 0, 0); OdsCreate (&mtaptr->ReadOds, mtaptr->ReadMetaName, mtaptr->ReadMetaNameLength, NULL, 0, FAB$M_PUT + FAB$M_GET + FAB$M_BIO + FAB$M_TRN, FAB$M_CIF + FAB$M_TEF, FAB$M_NIL, FAB$C_STMLF, FAB$M_CR, NULL, &DavMetaOpenAst, mtaptr); sys$setprv (0, &SysPrvMask, 0, 0); } /*****************************************************************************/ /* If the open fails (for any reason, including file-not-found) explicitly call the AST function. If the open succeeds allocate a dynamic buffer the same size of the file and $READ that as blocks (if larger than a possible single $READ) from the file call DavMetaReadAst() when complete. */ DavMetaOpenAst (WEBDAV_META *mtaptr) { int status; REQUEST_STRUCT *rqptr; WEBDAV_DLM *dlmptr; WEBDAV_TASK *tkptr; /*********/ /* begin */ /*********/ rqptr = mtaptr->RequestPtr; if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavMetaOpenAst() !&S", mtaptr->ReadOds.Fab.fab$l_sts); tkptr = rqptr->WebDavTaskPtr; dlmptr = &mtaptr->DlmData; if (VMSok (mtaptr->VmsStatus = mtaptr->ReadOds.Fab.fab$l_sts) && (mtaptr->ReadOds.Fab.fab$b_rfm == FAB$C_VAR || mtaptr->ReadOds.Fab.fab$b_rfm == FAB$C_VFC)) { if (WATCHING (rqptr, WATCH_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "META ***** MUST BE STREAM-LF FORMAT!! *****"); mtaptr->VmsStatus = SS$_ABORT; } if (VMSnok(mtaptr->VmsStatus)) { if (mtaptr->MetaDirNameLength && mtaptr->VmsStatus == RMS$_DNF && /* meta-file directory created only when opened for write */ (mtaptr->ReadOds.Fab.fab$b_fac & FAB$M_PUT) && !mtaptr->MetaDirCreate) { DavMetaCreateDir (mtaptr); /* retry the access (just the once) */ mtaptr->MetaDirCreate = true; DavMetaLockAst (mtaptr); return; } /* open failed, explicitly call any AST */ if (mtaptr->VmsStatus != RMS$_FNF || mtaptr->VmsStatus != RMS$_DNF) if (WATCHING (rqptr, WATCH_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "META read open !AZ !&S", mtaptr->ReadMetaName, mtaptr->VmsStatus); mtaptr->UpdateLocked = false; DavWebDequeue (&mtaptr->DlmData); SysDclAst (mtaptr->ReadAstFunction, mtaptr->ReadAstParam); return; } mtaptr->ReadOds.Rab = cc$rms_rab; mtaptr->ReadOds.Rab.rab$l_fab = &mtaptr->ReadOds.Fab; status = sys$connect (&mtaptr->ReadOds.Rab, 0, 0); if (VMSnok (status)) ErrorNoticed (rqptr, status, mtaptr->ReadOds.ExpFileName, FI_LI); mtaptr->ReadOds.Rab.rab$l_ctx = mtaptr; mtaptr->ReadOds.Rab.rab$l_sts = mtaptr->ReadOds.Rab.rab$l_stv = 0; if (mtaptr->VmsStatus == RMS$_CREATED) { /* if newly created, obviously nothing to read - fake the status */ mtaptr->MetaCreated = true; mtaptr->ReadOds.Rab.rab$l_sts = RMS$_EOF; mtaptr->ReadOds.DeleteOnClose = true; SysDclAst (mtaptr->ReadAstFunction, mtaptr->ReadAstParam); return; } /* access to the meta data was successful */ InstanceGblSecIncrLong (&AccountingPtr->WebDavMetaReadCount); DavMetaReadAst (&mtaptr->ReadOds.Rab); } /*****************************************************************************/ /* Create the metadata directory using lib$create_dir()! */ int DavMetaCreateDir (WEBDAV_META *mtaptr) { /* ensure SYSPRV can control the directory */ static unsigned short ProtEnable = 0x000f, ProtValue = 0x0000; /* S:RWED */ static $DESCRIPTOR (DirNameDsc, ""); int status; char ch; REQUEST_STRUCT *rqptr; /*********/ /* begin */ /*********/ rqptr = mtaptr->RequestPtr; ch = mtaptr->MetaDirNamePtr[mtaptr->MetaDirNameLength]; mtaptr->MetaDirNamePtr[mtaptr->MetaDirNameLength] = '\0'; if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavMetaCreateDir() !AZ", mtaptr->MetaDirNamePtr); DirNameDsc.dsc$a_pointer = mtaptr->MetaDirNamePtr; DirNameDsc.dsc$w_length = mtaptr->MetaDirNameLength; AuthAccessEnable (rqptr, DirNameDsc.dsc$a_pointer, AUTH_ACCESS_WRITE); status = lib$create_dir (&DirNameDsc, 0, &ProtEnable, &ProtValue, 0, 0); AuthAccessEnable (rqptr, 0, 0); if (WATCHING (rqptr, WATCH_WEBDAV)) if (status == SS$_NORMAL) WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "META EXISTS !AZ", mtaptr->MetaDirNamePtr); else if (status == SS$_CREATED) WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "META CREATED !AZ", mtaptr->MetaDirNamePtr); else WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "META FAILED !AZ !&S", mtaptr->MetaDirNamePtr, status); mtaptr->MetaDirNamePtr[mtaptr->MetaDirNameLength] = ch; return (status); } /*****************************************************************************/ /* Delete a metadata directory (allowed only if it is empty of course!) It is intended to be used during WebDAV DELETE operations. When deleting a directory file this function should be called to proactively delete any [WebDavMetaDir] specified metadata directory file prior to trying to delete the directory file itself. */ int DavMetaDeleteDir (WEBDAV_META *mtaptr) { int status, DirFileNameLength; char ch; char DirFileName [ODS_MAX_FILE_NAME_LENGTH+1]; REQUEST_STRUCT *rqptr; ODS_STRUCT DeleteOds; /*********/ /* begin */ /*********/ rqptr = mtaptr->RequestPtr; if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavMetaDeleteDir() !AZ", mtaptr->MetaDirNamePtr); ch = mtaptr->MetaDirNamePtr[mtaptr->MetaDirNameLength]; mtaptr->MetaDirNamePtr[mtaptr->MetaDirNameLength] = '\0'; OdsNameOfDirectoryFile (mtaptr->MetaDirNamePtr, mtaptr->MetaDirNameLength, DirFileName, &DirFileNameLength); mtaptr->MetaDirNamePtr[mtaptr->MetaDirNameLength] = ch; OdsStructInit (&DeleteOds, true); OdsParse (&DeleteOds, DirFileName, DirFileNameLength, NULL, 0, NAM$M_SYNCHK, NULL, rqptr); /* only if it is a metadata subdirectory */ if (DavMetaDir (rqptr, &DeleteOds)) { if (VMSok (status = DeleteOds.Fab.fab$l_sts)) { DeleteOds.NamVersionPtr[0] = '\0'; sys$setprv (1, &SysPrvMask, 0, 0); status = sys$erase (&DeleteOds.Fab, 0, 0); sys$setprv (0, &SysPrvMask, 0, 0); if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "sys$erase() !&Z !&S", DeleteOds.ExpFileName, status); if (VMSnok (status) && status != RMS$_MKD && status != SS$_DIRNOTEMPTY) ErrorNoticed (rqptr, status, DeleteOds.ExpFileName, FI_LI); } } OdsParseRelease (&DeleteOds); return (status); } /*****************************************************************************/ /* The asynchronous $READ has completed. Check the status and the proportion of the file read. If another $READ is required then queue that. Once the file has been completely read, or at an error status, call the AST function. */ DavMetaReadAst (struct RAB *RabPtr) { int status, BucketNumber, SizeInBytes; REQUEST_STRUCT *rqptr; WEBDAV_LOCK *lckptr; WEBDAV_META *mtaptr; WEBDAV_TASK *tkptr; /*********/ /* begin */ /*********/ mtaptr = RabPtr->rab$l_ctx; rqptr = mtaptr->RequestPtr; if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavMetaReadAst() sts:!&X stv:!&X rsz:!UL", RabPtr->rab$l_sts, RabPtr->rab$l_stv, RabPtr->rab$w_rsz); tkptr = rqptr->WebDavTaskPtr; if (mtaptr->ReadOds.Rab.rab$l_sts) { /*******************/ /* subsequent read */ /*******************/ mtaptr->VmsStatus = mtaptr->ReadOds.Rab.rab$l_sts; if (VMSok (mtaptr->VmsStatus)) { STR_DSC_LEN(&mtaptr->ReadDsc) += RabPtr->rab$w_rsz; /* artifically generate an EOF as we've got all there is to get */ if (STR_DSC_LEN(&mtaptr->ReadDsc) >= STR_DSC_SIZE(&mtaptr->ReadDsc)) mtaptr->VmsStatus = RMS$_EOF; } } else { /****************/ /* initial read */ /****************/ if (mtaptr->ReadOds.XabFhc.xab$l_ebk <= 1) SizeInBytes = mtaptr->ReadOds.XabFhc.xab$w_ffb; else SizeInBytes = ((mtaptr->ReadOds.XabFhc.xab$l_ebk-1) << 9) + mtaptr->ReadOds.XabFhc.xab$w_ffb; /* unlikely to be legitimate meta-data if less than this */ if (SizeInBytes < 128) { /* can happen if the server exits without closing RMS$M_DLT files */ mtaptr->VmsStatus = SS$_ENDOFFILE; } else { StrDscBegin (rqptr, &mtaptr->ReadDsc, SizeInBytes); /* asynchronous block $READs */ mtaptr->ReadOds.Rab.rab$l_rop = RAB$M_BIO | RAB$M_ASY; mtaptr->VmsStatus = SS$_NORMAL; } } if (VMSok (mtaptr->VmsStatus)) { /*************/ /* read more */ /*************/ SizeInBytes = STR_DSC_SIZE(&mtaptr->ReadDsc) - STR_DSC_LEN(&mtaptr->ReadDsc); if (SizeInBytes > DAVMETA_READ_CHUNK) SizeInBytes = DAVMETA_READ_CHUNK; BucketNumber = (STR_DSC_LEN(&mtaptr->ReadDsc) / 512) + 1; if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "!UL-!UL=!UL !UL !UL", STR_DSC_SIZE(&mtaptr->ReadDsc), STR_DSC_LEN(&mtaptr->ReadDsc), STR_DSC_SIZE(&mtaptr->ReadDsc) - STR_DSC_LEN(&mtaptr->ReadDsc), BucketNumber, SizeInBytes); mtaptr->ReadOds.Rab.rab$l_ubf = STR_DSC_PTR(&mtaptr->ReadDsc) + STR_DSC_LEN(&mtaptr->ReadDsc); mtaptr->ReadOds.Rab.rab$w_usz = SizeInBytes; mtaptr->ReadOds.Rab.rab$l_bkt = BucketNumber; sys$read (&mtaptr->ReadOds.Rab, &DavMetaReadAst, &DavMetaReadAst); return; } /*****************/ /* read finished */ /*****************/ /* if we retrieved the entire file then that's a good thing! */ if (mtaptr->VmsStatus == RMS$_EOF) mtaptr->VmsStatus = SS$_NORMAL; if (WATCHING (rqptr, WATCH_WEBDAV)) { WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "META read !AZ !&S", mtaptr->ReadOds.ExpFileName, mtaptr->VmsStatus); if (VMSok(mtaptr->VmsStatus)) if (STR_DSC_LEN(&mtaptr->ReadDsc)) WatchDataFormatted ("!#AZ", STR_DSC_LEN(&mtaptr->ReadDsc), STR_DSC_PTR(&mtaptr->ReadDsc)); } if (VMSnok (mtaptr->VmsStatus)) { /*******/ /* nbg */ /*******/ /* a fatal error so ensure the metadata file is deleted */ mtaptr->ReadOds.DeleteOnClose = true; status = OdsClose (&mtaptr->ReadOds, NULL, NULL); } else { /*********/ /* parse */ /*********/ if (!mtaptr->UpdateLocked) { /* do NOT modify any meta newly SS$_CREATED setting */ status = OdsClose (&mtaptr->ReadOds, NULL, NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, mtaptr->ReadMetaName, FI_LI); } mtaptr->VmsStatus = DavMetaParseXml (mtaptr); if (VMSok (mtaptr->VmsStatus)) DavLockPreProcess (rqptr); } if (!mtaptr->UpdateLocked && !mtaptr->CopyLocked) DavWebDequeue (&mtaptr->DlmData); SysDclAst (mtaptr->ReadAstFunction, mtaptr->ReadAstParam); } /*****************************************************************************/ /* The file has been opened for write exclusively locked by DavMetaRead(). If it was an existing meta-data file the content can be updated or if it was created the content can be 'updated' for the first time. If there are no properties data to be written to the file then flag it for delete-on-close and close the file, otherwise build a buffer of the properties data and queue an asynchronous $WRITE. */ DavMetaUpdate ( WEBDAV_META *mtaptr, REQUEST_AST AstFunction, unsigned long AstParam ) { int status, MaxKbytes, PropCount, SizeInBytes; STR_DSC *sdptr; REQUEST_STRUCT *rqptr; WEBDAV_LOCK *lckptr; WEBDAV_TASK *tkptr; /*********/ /* begin */ /*********/ rqptr = mtaptr->RequestPtr; if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavMetaUpdate() !&S", mtaptr->VmsStatus); tkptr = rqptr->WebDavTaskPtr; mtaptr->UpdateAstFunction = AstFunction; mtaptr->UpdateAstParam = AstParam; if (VMSnok (mtaptr->VmsStatus)) goto DavMetaUpdateAbort; lckptr = NULL; if (rqptr->rqHeader.Method == HTTP_METHOD_WEBDAV_LOCK) { /********/ /* lock */ /********/ /* if the request didn't supply complete lock data */ if (!(tkptr->LockData.Type && tkptr->LockData.Scope)) { DavWebResponse (rqptr, 412, 0, "LOCK type/scope undefined", FI_LI); goto DavMetaUpdateAbort; } /* search for an existing exclusive lock that matches an If: token */ if (lckptr = DavLockFindIf (rqptr, WEBDAV_LOCK_SCOPE_EXCLUSIVE)) if (lckptr == NOTPTR) goto DavMetaUpdateAbort; /* if DavLockFindIf() returned lock then it's an update */ if (lckptr) { /* check existing lock compatibility */ if (!(lckptr->Type == WEBDAV_LOCK_SCOPE_SHARED && tkptr->LockData.Type == WEBDAV_LOCK_SCOPE_SHARED)) { DavWebResponse (rqptr, 412, 0, "LOCKS incomapatible", FI_LI); goto DavMetaUpdateAbort; } } else { /* new lock - add it to the list and generate a token */ ListAddHead (&mtaptr->LockList, lckptr = &tkptr->LockData, LIST_ENTRY_TYPE_DAVLOCK); DavLockSetUrnUuidToken (rqptr, lckptr); mtaptr->LockCreated = true; } /* set or update (existing lock) the timeout value */ if (DavLockSetTimeout (rqptr, lckptr)) goto DavMetaUpdateAbort; } else if (rqptr->rqHeader.Method == HTTP_METHOD_WEBDAV_UNLOCK) { /**********/ /* unlock */ /**********/ /* must be an existing lock */ lckptr = DavLockFindToken (rqptr); if (!lckptr || lckptr == NOTPTR) goto DavMetaUpdateAbort; /* simply remove the lock from the list */ ListRemove (&mtaptr->LockList, lckptr); } else { /******************/ /* something else */ /******************/ if (!mtaptr->MetaCreated) { /* if existing metadata */ if (lckptr = DavLockFindIf (rqptr, WEBDAV_LOCK_SCOPE_EXCLUSIVE)) if (lckptr == NOTPTR) { mtaptr->VmsStatus = SS$_ACCONFLICT; goto DavMetaUpdateAbort; } /* if didn't return an exclusive lock look for a shared one */ if (!lckptr) if (lckptr = DavLockFindIf (rqptr, WEBDAV_LOCK_SCOPE_SHARED)) if (lckptr == NOTPTR) { mtaptr->VmsStatus = SS$_ACCONFLICT; goto DavMetaUpdateAbort; } } } /*******************/ /* build meta-data */ /*******************/ status = DavMetaBuild (mtaptr); if (status == SS$_INCOMPAT) { /**********************/ /* forbidden metadata */ /**********************/ DavWebResponse (rqptr, 403, 0, "cannot patch property", FI_LI); tkptr->MetaData.VmsStatus = status; goto DavMetaUpdateAbort; } if (VMSnok(status) || status == SS$_FORGET) { /********************/ /* delete meta-data */ /********************/ tkptr->MetaData.VmsStatus = status; mtaptr->ReadOds.DeleteOnClose = true; goto DavMetaUpdateAbort; } /*******************/ /* limit meta size */ /*******************/ /* to something reasonable (say 1/32 of the maximum PUTable) */ SizeInBytes = 0; for (sdptr = &mtaptr->WriteDsc; sdptr; sdptr = STR_DSC_NEXT(sdptr)) SizeInBytes += STR_DSC_LEN(sdptr); if (!(MaxKbytes = rqptr->rqPathSet.PutMaxKbytes)) MaxKbytes = Config.cfMisc.PutMaxKbytes; if ((SizeInBytes >> 10) > (MaxKbytes >> 5)) { DavWebResponse (rqptr, 412, 0, "META too large", FI_LI); mtaptr->ReadOds.DeleteOnClose = true; goto DavMetaUpdateAbort; } /********************/ /* set up the write */ /********************/ mtaptr->ReadOds.Rab.rab$l_ctx = mtaptr; mtaptr->ReadOds.Rab.rab$l_sts = mtaptr->ReadOds.Rab.rab$l_stv = 0; DavMetaUpdateWrite (&mtaptr->ReadOds.Rab); return; DavMetaUpdateAbort: { /********************/ /* abort the update */ /********************/ if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavMetaUpdateAbort() !&B !&S", mtaptr->ReadOds.DeleteOnClose, mtaptr->VmsStatus); if (mtaptr->VmsStatus != SS$_FORGET && mtaptr->VmsStatus != SS$_INCOMPAT && mtaptr->VmsStatus != SS$_ACCONFLICT) mtaptr->VmsStatus = SS$_ABORT; status = OdsClose (&mtaptr->ReadOds, NULL, NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, mtaptr->ReadMetaName, FI_LI); DavWebDequeue (&mtaptr->DlmData); SysDclAst (mtaptr->UpdateAstFunction, mtaptr->UpdateAstParam); } } /*****************************************************************************/ /* This function queues $WRITEs of the meta-data and acts as it's own AST, assessing the status of the previous write and queuing then next, if required. When the write has completed (successfully or unsuccessfully) the 'MetaUpdateAstFunction' is called which should (as always) check 'MetaStatus'. */ DavMetaUpdateWrite (struct RAB *RabPtr) { int status; REQUEST_STRUCT *rqptr; STR_DSC *sdptr; WEBDAV_META *mtaptr; WEBDAV_TASK *tkptr; /*********/ /* begin */ /*********/ mtaptr = RabPtr->rab$l_ctx; rqptr = mtaptr->RequestPtr; if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavMetaUpdateWrite() sts:!&S stv:!&S", RabPtr->rab$l_sts, RabPtr->rab$l_stv); tkptr = rqptr->WebDavTaskPtr; if (mtaptr->ReadOds.Rab.rab$l_sts) { /********************/ /* subsequent write */ /********************/ status = mtaptr->VmsStatus = mtaptr->ReadOds.Rab.rab$l_sts; if (VMSnok (status)) { if (WATCHING (rqptr, WATCH_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "META update !AZ !&S", mtaptr->ReadMetaName, status); ErrorNoticed (rqptr, status, mtaptr->ReadMetaName, FI_LI); /* a fatal error so ensure the metadata file is deleted */ mtaptr->ReadOds.DeleteOnClose = true; status = OdsClose (&mtaptr->ReadOds, NULL, NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, mtaptr->ReadMetaName, FI_LI); DavWebDequeue (&mtaptr->DlmData); SysDclAst (mtaptr->UpdateAstFunction, mtaptr->UpdateAstParam); return; } } else { /*****************/ /* initial write */ /*****************/ InstanceGblSecIncrLong (&AccountingPtr->WebDavMetaWriteAttemptCount); /* check the data is acceptable for $WRITE */ for (sdptr = &mtaptr->WriteDsc; sdptr; sdptr = STR_DSC_NEXT(sdptr)) { /* all but the last descriptor must be a full block */ if (STR_DSC_LEN(sdptr) > 0xffff || (STR_DSC_NEXT(sdptr) && STR_DSC_LEN(sdptr) % 512)) { mtaptr->VmsStatus = SS$_BUGCHECK; ErrorNoticed (rqptr, SS$_BUGCHECK, ErrorSanityCheck, FI_LI); /* a fatal error so ensure the metadata file is deleted */ mtaptr->ReadOds.DeleteOnClose = true; status = OdsClose (&mtaptr->ReadOds, NULL, NULL); if (VMSnok (status)) ErrorNoticed (rqptr, status, mtaptr->ReadMetaName, FI_LI); DavWebDequeue (&mtaptr->DlmData); SysDclAst (mtaptr->UpdateAstFunction, mtaptr->UpdateAstParam); return; } } mtaptr->RabBucket = 1; mtaptr->ReadOds.Rab.rab$l_rop = RAB$M_BIO | RAB$M_ASY | RAB$M_TPT; } /* find the next descriptor to be written */ for (sdptr = &mtaptr->WriteDsc; sdptr; sdptr = STR_DSC_NEXT(sdptr)) if (!STR_DSC_IS_NOTED(sdptr)) break; if (!sdptr) { /***************/ /* end of data */ /***************/ if (WATCHING (rqptr, WATCH_WEBDAV)) { WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "META update !AZ !&S", mtaptr->ReadOds.ExpFileName, SS$_NORMAL); if (STR_DSC_LEN(&mtaptr->WriteDsc)) WatchDataFormatted ("!#AZ", STR_DSC_LEN(&mtaptr->WriteDsc), STR_DSC_PTR(&mtaptr->WriteDsc)); } mtaptr->VmsStatus = SS$_NORMAL; /* success so need to undo any meta newly SS$_CREATED */ mtaptr->ReadOds.DeleteOnClose = false; status = OdsClose (&mtaptr->ReadOds, NULL, NULL); if (VMSok (status)) InstanceGblSecIncrLong (&AccountingPtr->WebDavMetaWriteCount); else ErrorNoticed (rqptr, status, mtaptr->ReadMetaName, FI_LI); DavWebDequeue (&mtaptr->DlmData); SysDclAst (mtaptr->UpdateAstFunction, mtaptr->UpdateAstParam); return; } /**************/ /* write data */ /**************/ if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "rsz: !UL", STR_DSC_LEN(sdptr)); mtaptr->ReadOds.Rab.rab$l_rbf = STR_DSC_PTR(sdptr); mtaptr->ReadOds.Rab.rab$w_rsz = STR_DSC_LEN(sdptr); mtaptr->ReadOds.Rab.rab$l_bkt = mtaptr->RabBucket; mtaptr->RabBucket += STR_DSC_LEN(sdptr) / 512; STR_DSC_SET_NOTED(sdptr) sys$write (&mtaptr->ReadOds.Rab, &DavMetaUpdateWrite, &DavMetaUpdateWrite); } /*****************************************************************************/ /* WASD files containing WebDAV metadata have the same name with "__wasdav" appended to the file type. Return true if the supplied file complies. */ BOOL DavMetaFile (ODS_STRUCT *odsptr) { /*********/ /* begin */ /*********/ if (WATCH_MODULE (WATCH_MOD_WEBDAV)) WatchThis (WATCHALL, WATCH_MOD_WEBDAV, "DavMetaFile() !&Z", odsptr->NamDevicePtr); if (odsptr->NamTypeLength < 8) return (false); if (MATCH8 (odsptr->NamVersionPtr-8, "__wasdav")) return (true); if (MATCH8 (odsptr->NamVersionPtr-8, "__WASDAV")) return (true); return (false); } /*****************************************************************************/ /* Return true if this specification is the meta-subdirectory or contains a metadata subdirectory anywhere in the directory structure, and false if it doesn't. If the metadata subdirectory is for example "[.meta]" then match "[meta]", "[meta.", ".meta]" and "[.meta.", or if "[.^.dav]" then this function must match file specifications containing "[^.dav]", "[^.dav.", ".^.dav." and ".^.dav]". */ BOOL DavMetaDir ( REQUEST_STRUCT *rqptr, ODS_STRUCT *odsptr ) { char *cptr, *mfdptr, *sptr, *zptr; WEBDAV_TASK *tkptr; /*********/ /* begin */ /*********/ if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV)) WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavMetaDir() !&Z", odsptr->NamDevicePtr); if (tkptr = rqptr->WebDavTaskPtr) mfdptr = tkptr->MetaFileDirPtr; else if (rqptr->rqPathSet.WebDavMetaDirPtr) mfdptr = rqptr->rqPathSet.WebDavMetaDirPtr; else if (Config.cfWebDav.MetaFileDirectoryLength) mfdptr = Config.cfWebDav.MetaFileDirectory; else return (false); if (!mfdptr) return (false); if (!SAME2(mfdptr,'[.')) return (false); /* first check if it is the directory file itself */ cptr = odsptr->NamNamePtr; sptr = mfdptr + 2; if (MATCH2(cptr,sptr)) { while (*cptr && *cptr != '.' && (TOLO(*cptr) == TOLO(*sptr))) { if (*cptr == '^' && *(cptr+1)) cptr++; if (*sptr == '^' && *(sptr+1)) sptr++; cptr++; sptr++; } if (*cptr == '.' && *sptr == ']' && (MATCH5(cptr,".DIR;") || MATCH5(cptr,".dir;") || MATCH5(cptr,".DIR") || MATCH5(cptr,".dir"))) { if (WATCHING (rqptr, WATCH_THIS)) WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "METADATA !AZ", odsptr->NamNamePtr); return (true); } } /* then check if directory name occurs anywhere in the specification */ for (cptr = odsptr->NamDirectoryPtr; *cptr; cptr++) { if (*cptr == '^') { /* absorb escaped characters */ if (*++cptr) cptr++; continue; } if (*cptr == ']') { /* if concealed continue otherwise end of directory */ if (SAME3(cptr,'][.')) cptr += 2; else break; } else if (!(*cptr == '[' || *cptr == '.')) /* just another non-significant character */ continue; /* beginning of a directory */ cptr++; sptr = mfdptr + 2; while (*cptr && *cptr != '.' && *cptr != ']' && (TOLO(*cptr) == TOLO(*sptr))) { if (*cptr == '^' && *(cptr+1)) cptr++; if (*sptr == '^' && *(sptr+1)) sptr++; cptr++; sptr++; } /* if it matched the meta directory */ if ((*cptr == ']' || *cptr == '.') && *sptr == ']') { if (WATCHING (rqptr, WATCH_THIS)) WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "METADATA !AZ CONTAINS !AZ", odsptr->NamDirectoryPtr, mfdptr); return (true); } /* span to the end of the directory */ while (*cptr) { if (*cptr == '^') { if (*++cptr) cptr++; continue; } if (*(cptr+1) == '.' || *(cptr+1) == ']') break; cptr++; } } return (false); } /*****************************************************************************/