/*****************************************************************************/
/*
DAVprop.c
Provide DAV properties of collections (directories) and resources (files).
These include 'live' properties (those stored as part of the file-system, e.g.
CDT, RDT) and 'dead' properties (those provided by clients and stored as XML in
the meta-data).
Apparently is deprecated by RFC 4981.
VERSION HISTORY
---------------
12-SEP-2018 MGD bugfix; significant refactor of locking
06-SEP-2018 MGD bugfix; DavPropLive() metadata file also needs to be read
for ->LockDiscovery as well as ->AllProp and ->FindProp
15-APR-2018 MGD SS$_ABORT on ->RequestRundown
bugfix; DavPropDead() flush the network buffer periodically
(for rediculously but legally sized file it might
not quickly enough to prevent no-progress timeout)
bugfix; DavPropSearchAst() resultant name already terminated
22-JUN-2016 MGD DavPropSearchAst() ignore ambiguous file names
containing an escaped ("^.") period but no type
27-SEP-2014 MGD improve "no reverse mapping" detection and reporting
bugfix; DavPropEnd() ensure unused meta-data file deleted
16-JUN-2014 MGD DavPropSearchAst() meta file subdirectory detect and suppress
06-JUN-2013 MGD bugfix; DavPropSearchAst() on non-ODS_EXTENDED platforms
(i.e. VAX) reset .nam$b_rsl to changed resultant length
or it can generate RMS$_RSL errors - check it out!
05-SEP-2009 MGD refinements
24-JUN-2009 MGD DavPropQuota() RFC 4331 quota properties
bugfix; DavPropName() XML
19-APR-2007 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
/* standard C header files */
#include
#include
#include
#include
/* VMS related header files */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
/* application related header file */
#include "wasd.h"
#include "davweb.h"
#define WASD_MODULE "DAVPROP"
#define FCH$M_DIRECTORY 0x2000
#define TEST_PROP_QUOTA 0
#define _TEST_PROP_QUOTA_USERNAME "DANIEL"
/********************/
/* external storage */
/********************/
extern BOOL WebDavQuotaEnabled;
extern char ErrorSanityCheck[];
extern int SysPrvMask[],
ToLowerCase[],
ToUpperCase[];
extern int EfnWait,
EfnNoWait,
HttpdTickSecond;
extern ACCOUNTING_STRUCT *AccountingPtr;
extern CONFIG_STRUCT Config;
extern HTTPD_PROCESS HttpdProcess;
extern WATCH_STRUCT Watch;
/*****************************************************************************/
/*
This function should check for a mapping-generated error message to determine
is there were any problems.
*/
DavPropBegin (REQUEST_STRUCT *rqptr)
{
int status,
Length;
char *sptr;
char Scratch [ODS_MAX_FILE_NAME_LENGTH+1];
WEBDAV_TASK *tkptr;
/*********/
/* begin */
/*********/
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV,
"DavPropBegin() depth:!UL !&Z",
rqptr->WebDavTaskPtr->ToDepth,
rqptr->ParseOds.ExpFileName);
tkptr = rqptr->WebDavTaskPtr;
if (WATCHING (rqptr, WATCH_RESPONSE))
WatchThis (WATCHITM(rqptr), WATCH_RESPONSE,
"PROPFIND!AZ !AZ depth:!UL path:!AZ",
rqptr->rqPathSet.WebDavNoProp ?
" (path set NOprop)" : "",
rqptr->ParseOds.ExpFileName, tkptr->ToDepth,
DavWebPathAccess(rqptr));
/* always flush before no-progress timeout (at 75%) */
tkptr->PropFindFlushAfter = Config.cfTimeout.NoProgress * 75 / 100;
tkptr->PropFindFlushSecond = HttpdTickSecond + tkptr->PropFindFlushAfter;
/* ensure a reverse mapping exists for the file specification */
Scratch[0] = '\0';
sptr = MapUrl_Map (Scratch, sizeof(Scratch),
rqptr->ParseOds.NamDevicePtr, 0,
NULL, 0, NULL, 0, NULL, 0, NULL, rqptr, NULL);
if (!sptr[0] && sptr[1] ||
(MATCH8(sptr,MAPURL_NO_REVERSE_PATH) &&
strsame(sptr,MAPURL_NO_REVERSE_PATH,-1)))
{
if (!sptr[0] && sptr[1])
if (WATCHING (rqptr, WATCH_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_WEBDAV,
"REVERSE MAPPING !AZ error !AZ",
rqptr->ParseOds.NamDevicePtr, sptr+1);
else
if (WATCHING (rqptr, WATCH_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_WEBDAV,
"NO REVERSE MAPPING from !AZ to URI",
rqptr->ParseOds.NamDevicePtr);
rqptr->rqResponse.HttpStatus = 500;
ErrorVmsStatus (rqptr, SS$_ABORT, FI_LI);
DavPropEnd (rqptr);
return;
}
/* default to reporting all properties */
if (!rqptr->rqHeader.ContentLength) tkptr->PropData.AllProp = true;
/* take out a concurrent read lock on the resource */
DavWebDlmEnqueue (rqptr, &tkptr->DlmSource, rqptr->ParseOds.ExpFileName,
NULL, false, false, DavPropBegin2, rqptr);
}
/*****************************************************************************/
/*
Entry point for AST from VMS DLM lock.
*/
DavPropBegin2 (REQUEST_STRUCT *rqptr)
{
int status;
WEBDAV_DLM *dlmptr;
WEBDAV_TASK *tkptr;
/*********/
/* begin */
/*********/
tkptr = rqptr->WebDavTaskPtr;
dlmptr = &tkptr->DlmSource;
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV,
"DavPropBegin2() !&S", dlmptr->LockSb.lksb$w_status);
if (VMSnok (dlmptr->LockSb.lksb$w_status))
{
DavPropEnd (rqptr);
return;
}
/* network writes are checked for success, fudge the first one! */
rqptr->NetIoPtr->WriteStatus = SS$_NORMAL;
if (rqptr->ParseOds.NamNameLength > 0 ||
rqptr->ParseOds.NamTypeLength > 1)
{
/*******************/
/* single resource */
/*******************/
/* any other depth is meaningless */
tkptr->ToDepth = WEBDAV_DEPTH_ZERO;
status = DavPropParse (rqptr);
if (VMSnok(status))
{
/* HTTP status will be set from VMS status */
ErrorVmsStatus (rqptr, status, FI_LI);
DavPropEnd (rqptr);
return;
}
if (tkptr->PropData.PropName)
DavPropName (rqptr);
else
DavPropLiveAcp (rqptr);
return;
}
/**************/
/* collection */
/**************/
if (tkptr->ToDepth == WEBDAV_DEPTH_ZERO)
{
/* collection itself */
DavPropParent (rqptr);
return;
}
if (tkptr->ToDepth == WEBDAV_DEPTH_ONE)
{
/* collection and children, provide the parent */
DavPropParent (rqptr);
return;
}
if (tkptr->ToDepth == WEBDAV_DEPTH_INFINITY)
{
/* potentially too expensive! */
rqptr->rqResponse.HttpStatus = 501;
DavPropEnd (rqptr);
return;
}
ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
}
/*****************************************************************************/
/*
As necessary, close the multistatus XML.
*/
DavPropEnd (REQUEST_STRUCT *rqptr)
{
int status;
WEBDAV_META *mtaptr;
WEBDAV_TASK *tkptr;
/*********/
/* begin */
/*********/
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV,
"DavPropEnd() !&F", &DavPropEnd);
tkptr = rqptr->WebDavTaskPtr;
mtaptr = &tkptr->MetaData;
if (mtaptr->ReadOds.Fab.fab$l_sts == RMS$_CREATED &&
mtaptr->ReadOds.DeleteOnClose)
{
/* meta-data file created and never updated */
status = OdsClose (&mtaptr->ReadOds, NULL, NULL);
if (VMSnok (status))
ErrorNoticed (rqptr, status, mtaptr->ReadMetaName, FI_LI);
}
/* if an 'empty' multistatus response was generated */
if (!rqptr->rqResponse.HttpStatus)
{
rqptr->rqResponse.NoGzip = true;
DavWebResponse207 (rqptr);
}
if (rqptr->rqResponse.HttpStatus == 207)
FaoToNet (rqptr, "\n");
DavWebEnd (rqptr);
}
/*****************************************************************************/
/*
Get the file name of the directory containing this collection and begin to
process to get it's WebDAV properties.
*/
DavPropParent (REQUEST_STRUCT *rqptr)
{
int status,
DirectoryFileLength;
char DirectoryFile [ODS_MAX_FILE_NAME_LENGTH+1];
WEBDAV_TASK *tkptr;
/*********/
/* begin */
/*********/
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV,
"DavPropParent() !AZ", rqptr->ParseOds.NamDevicePtr);
tkptr = rqptr->WebDavTaskPtr;
/* get name of directory in parent */
OdsNameOfDirectoryFile (rqptr->ParseOds.NamDevicePtr,
rqptr->ParseOds.NamNamePtr -
rqptr->ParseOds.NamDevicePtr,
DirectoryFile, &DirectoryFileLength);
AuthAccessEnable (rqptr, rqptr->ParseOds.ExpFileName, AUTH_ACCESS_READ);
if (VMSok (status = OdsFileExists (NULL, DirectoryFile)))
OdsParse (&tkptr->SearchOds, DirectoryFile, DirectoryFileLength,
NULL, 0, NULL, NULL, rqptr);
AuthAccessEnable (rqptr, 0, 0);
if (VMSnok (status))
{
DavWebResponse (rqptr, 404, status, "collection does not exist", FI_LI);
DavPropEnd (rqptr);
return;
}
if (VMSok (status = tkptr->SearchOds.Fab.fab$l_sts))
status = OdsParseTerminate (&tkptr->SearchOds);
if (VMSnok(status))
{
/* HTTP status will be set from VMS status */
ErrorNoticed (rqptr, status, NULL, FI_LI);
ErrorVmsStatus (rqptr, status, FI_LI);
DavPropEnd (rqptr);
return;
}
tkptr->PropParent = true;
AuthAccessEnable (rqptr, 0, AUTH_ACCESS_SYSPRV);
OdsSearch (&tkptr->SearchOds, &DavPropSearchAst, rqptr);
AuthAccessEnable (rqptr, 0, 0);
}
/*****************************************************************************/
/*
Parse the search structure.
*/
int DavPropParse (REQUEST_STRUCT *rqptr)
{
int status;
char *cptr, *sptr, *zptr;
WEBDAV_TASK *tkptr;
/*********/
/* begin */
/*********/
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavPropParse()");
tkptr = rqptr->WebDavTaskPtr;
cptr = rqptr->ParseOds.ExpFileName;
zptr = (sptr = tkptr->SearchSpec) + sizeof(tkptr->SearchSpec);
while (*cptr && sptr < zptr) *sptr++ = *cptr++;
if (!(rqptr->ParseOds.NamNameLength > 0 ||
rqptr->ParseOds.NamTypeLength > 1))
{
/* no name or type was supplied with the request, wildcard it */
for (cptr = "*.*;0"; *cptr && sptr < zptr; *sptr++ = *cptr++);
}
if (sptr >= zptr)
{
ErrorNoticed (rqptr, SS$_RESULTOVF, NULL, FI_LI);
return (SS$_RESULTOVF);
}
*sptr = '\0';
tkptr->SearchSpecLength = sptr - tkptr->SearchSpec;
AuthAccessEnable (rqptr, rqptr->ParseOds.ExpFileName, AUTH_ACCESS_READ);
OdsParse (&tkptr->SearchOds, tkptr->SearchSpec, tkptr->SearchSpecLength,
NULL, 0, NULL, NULL, rqptr);
AuthAccessEnable (rqptr, 0, 0);
if (VMSok (status = tkptr->SearchOds.Fab.fab$l_sts))
status = OdsParseTerminate (&rqptr->ParseOds);
return (status);
}
/*****************************************************************************/
/*
AST function to invoke another $SEARCH call.
*/
DavPropSearch (REQUEST_STRUCT *rqptr)
{
int status;
WEBDAV_TASK *tkptr;
/*********/
/* begin */
/*********/
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavPropSearch() !&F !&S",
&DavPropSearch, rqptr->NetIoPtr->WriteStatus);
if (VMSnok (rqptr->NetIoPtr->WriteStatus))
{
/* network write has failed (as AST), bail out now */
DavPropEnd (rqptr);
return;
}
tkptr = rqptr->WebDavTaskPtr;
if (tkptr->PropParent)
{
/*******************/
/* finished parent */
/*******************/
tkptr->PropParent = false;
OdsParseRelease (&tkptr->SearchOds);
if (tkptr->ToDepth == WEBDAV_DEPTH_ZERO)
{
DavPropEnd (rqptr);
return;
}
/******************/
/* begin children */
/******************/
status = DavPropParse (rqptr);
if (VMSnok(status))
{
/* HTTP status will be set from VMS status */
ErrorVmsStatus (rqptr, status, FI_LI);
DavPropEnd (rqptr);
return;
}
}
AuthAccessEnable (rqptr, 0, AUTH_ACCESS_SYSPRV);
OdsSearch (&tkptr->SearchOds, &DavPropSearchAst, rqptr);
AuthAccessEnable (rqptr, 0, 0);
}
/*****************************************************************************/
/*
AST completion routine called each time sys$search() completes. It will
either point to another file name found or have "no more files found" status
(or an error!).
*/
DavPropSearchAst (REQUEST_STRUCT *rqptr)
{
int status;
char *cptr, *sptr;
WEBDAV_TASK *tkptr;
/*********/
/* begin */
/*********/
#if WATCH_MOD
HttpdCheckPriv (FI_LI);
#endif /* WATCH_MOD */
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV,
"DavPropSearchAst() !&F sts:!&S stv:!&S",
&DavPropSearchAst,
rqptr->WebDavTaskPtr->SearchOds.Fab.fab$l_sts,
rqptr->WebDavTaskPtr->SearchOds.Fab.fab$l_stv);
if (rqptr->RequestState >= REQUEST_STATE_ABORT)
{
DavPropEnd (rqptr);
return;
}
tkptr = rqptr->WebDavTaskPtr;
if (VMSnok (status = tkptr->SearchOds.Fab.fab$l_sts))
{
/* if its a search list treat directory not found as if file not found */
if ((tkptr->SearchOds.Nam_fnb & NAM$M_SEARCH_LIST) && status == RMS$_DNF)
status = RMS$_FNF;
if (status == RMS$_FNF || status == RMS$_NMF)
{
/***************************/
/* end of directory search */
/***************************/
tkptr->SearchOds.ParseInUse = false;
DavPropEnd (rqptr);
return;
}
/*************************/
/* protection violation? */
/*************************/
if (status == SS$_NOPRIV || status == RMS$_PRV)
{
/* continue the search */
DavPropSearch (rqptr);
return;
}
/**********************/
/* sys$search() error */
/**********************/
/* assume this can only happen on the first call */
ErrorVmsStatus (rqptr, status, FI_LI);
DavPropEnd (rqptr);
return;
}
/*****************/
/* search result */
/*****************/
#ifndef ODS_EXTENDED
/*
06-JUN-2013 MGD VAX VMS V7.3 seems to require this adjusted
or it generates RMS$_RSL errors (!?)
*/
if (tkptr->SearchOds.NamTypeLength > 1)
tkptr->SearchOds.Nam.nam$b_rsl = tkptr->SearchOds.NamVersionPtr -
tkptr->SearchOds.NamDevicePtr;
else
tkptr->SearchOds.Nam.nam$b_rsl = tkptr->SearchOds.NamTypePtr -
tkptr->SearchOds.NamDevicePtr;
#endif
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV,
"!&Z", tkptr->SearchOds.NamDevicePtr);
if (MATCH7(tkptr->SearchOds.NamNamePtr,"000000.") &&
strsame (tkptr->SearchOds.NamNamePtr, "000000.DIR;", 11))
{
status = OdsReallyADir (rqptr, &tkptr->SearchOds);
if (VMSok (status))
{
/* no need to report the MFD */
DavPropSearch (rqptr);
return;
}
}
/* if a meta file directory it is not accessible */
if (DavMetaDir (rqptr, &tkptr->SearchOds))
{
DavPropSearch (rqptr);
return;
}
if (DavMetaFile (&tkptr->SearchOds))
{
DavPropSearch (rqptr);
return;
}
/* in true Unix-style "hidden" files do not appear (those beginning ".") */
if (SAME1(tkptr->SearchOds.NamNamePtr,'.') ||
SAME2(tkptr->SearchOds.NamNamePtr,'^.'))
{
/* except in demo mode or to VMS authenticated and profiled requests */
if (rqptr->rqPathSet.WebDavNoHidden)
{
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, ".FILE (hidden)");
DavPropSearch (rqptr);
return;
}
}
if (tkptr->SearchOds.NamTypeLength <= 1)
{
/* no explicit type - look to see if name contains escaped period */
for (cptr = tkptr->SearchOds.NamNamePtr;
cptr < tkptr->SearchOds.NamTypePtr;
cptr++)
if (*(USHORTPTR)cptr == '^.') break;
if (cptr < tkptr->SearchOds.NamTypePtr)
{
/* escaped period - do not list these ambiguous file names */
if (WATCHING (rqptr, WATCH_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_WEBDAV,
"^.NAME.; !AZ", tkptr->SearchOds.NamNamePtr);
DavPropSearch (rqptr);
return;
}
}
/**************/
/* properties */
/**************/
if (tkptr->PropData.PropName)
DavPropName (rqptr);
else
DavPropLiveAcp (rqptr);
}
/*****************************************************************************/
/*
Provide the property (all property names without values).
*/
DavPropName (REQUEST_STRUCT *rqptr)
{
WEBDAV_TASK *tkptr;
/*********/
/* begin */
/*********/
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavPropName()");
tkptr = rqptr->WebDavTaskPtr;
DavWebHref (rqptr, tkptr->SearchOds.NamDevicePtr, 0);
if (!rqptr->rqResponse.HttpStatus) DavWebResponse207 (rqptr);
FaoToNet (rqptr,
" \n\
!AZ!&%AZ\n\
\n\
HTTP/1.1 200 OK\n\
\n\
\n\
\n\
\n\
\n\
\n\
\n\
\n\
\n\
\n",
tkptr->HrefHost,
tkptr->PropParent ?
rqptr->rqHeader.PathInfoPtr : tkptr->HrefPath);
if (tkptr->LockingEnabled)
FaoToNet (rqptr,
" \n\
\n");
if (tkptr->QuotaEnabled)
FaoToNet (rqptr,
" \n\
\n");
FaoToNet (rqptr,
" \n\
\n\
\n");
DavPropEnd (rqptr);
}
/*****************************************************************************/
/*
Using the ACP-QIO interface obtain the live file properties (size, created
date/time, last modified, etc.)
*/
DavPropLiveAcp (REQUEST_STRUCT *rqptr)
{
int status;
char *cptr;
WEBDAV_TASK *tkptr;
/*********/
/* begin */
/*********/
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavPropLiveAcp() !AZ",
rqptr->WebDavTaskPtr->SearchOds.NamDevicePtr);
tkptr = rqptr->WebDavTaskPtr;
AuthAccessEnable (rqptr, tkptr->SearchOds.NamDevicePtr, AUTH_ACCESS_READ);
OdsFileAcpInfo (&tkptr->SearchOds, &DavPropLive, rqptr);
AuthAccessEnable (rqptr, 0, 0);
}
/****************************************************************************/
/*
AST called from OdsFileAcpInfo() (invoked by DavPropLiveAcp()) when ACP QIO
completes. This function supplies the 'live' properties associated with a file
(or directory file).
*/
DavPropLive (REQUEST_STRUCT *rqptr)
{
int status,
Bytes,
NameLength;
unsigned long AllocatedVbn,
EndOfFileVbn;
char *cptr, *sptr, *zptr;
char DateTime [32],
EntityTag [32],
FileSpec [ODS_MAX_FILE_NAME_LENGTH+1];
WEBDAV_PROP *proptr;
WEBDAV_TASK *tkptr;
/*********/
/* begin */
/*********/
#if WATCH_MOD
HttpdCheckPriv (FI_LI);
#endif /* WATCH_MOD */
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV,
"DavPropLive() !&F !&S", &DavPropLive,
rqptr->WebDavTaskPtr->SearchOds.FileQio.IOsb.Status);
tkptr = rqptr->WebDavTaskPtr;
proptr = &tkptr->PropData;
/* first deassign the channel allocated by OdsFileAcpInfo() */
sys$dassgn (tkptr->SearchOds.FileQio.AcpChannel);
if ((status = tkptr->SearchOds.FileQio.IOsb.Status) == SS$_NOSUCHFILE)
status = RMS$_FNF;
if (VMSnok (status))
{
/****************/
/* error status */
/****************/
if (tkptr->ToDepth == WEBDAV_DEPTH_ZERO)
{
/* just the single resource */
ErrorVmsStatus (rqptr, status, FI_LI);
DavPropEnd (rqptr);
return;
}
/*************************/
/* protection violation? */
/*************************/
if (status == SS$_NOPRIV || status == RMS$_PRV)
{
DavPropSearch (rqptr);
return;
}
/*******************/
/* something else! */
/*******************/
DavWebMultiStatus (rqptr, status, tkptr->SearchOds.ExpFileName);
DavPropEnd (rqptr);
return;
}
if (!rqptr->rqResponse.HttpStatus) DavWebResponse207 (rqptr);
/**********/
/* report */
/**********/
/* same status as OdsReallyADir() */
if (tkptr->SearchOds.FileQio.AtrUchar & FCH$M_DIRECTORY)
{
tkptr->SearchIsDirectory = true;
status = SS$_NORMAL;
}
else
{
tkptr->SearchIsDirectory = false;
status = SS$_ABORT;
}
DavWebHref (rqptr, tkptr->SearchOds.NamDevicePtr, status);
FaoToNet (rqptr,
" \n\
!AZ!&%AZ\n\
\n\
HTTP/1.1 200 OK\n\
\n",
tkptr->HrefHost,
tkptr->PropParent ?
rqptr->rqHeader.PathInfoPtr : tkptr->HrefPath);
if (proptr->AllProp || proptr->DisplayName)
{
/********/
/* name */
/********/
if (tkptr->PropParent)
zptr = cptr = "";
else
{
/* resolve the name from the href path */
for (cptr = sptr = tkptr->HrefPath; *cptr; cptr++);
zptr = cptr;
while (cptr > sptr && *cptr != '/') cptr--;
if (tkptr->SearchIsDirectory)
{
if (cptr > sptr && *cptr == '/')
{
cptr--;
zptr--;
}
while (cptr > sptr && *cptr != '/') cptr--;
}
if (*cptr == '/') cptr++;
}
FaoToNet (rqptr,
" !#&;AZ\n",
zptr-cptr, cptr);
}
if (proptr->AllProp || proptr->ResourceType)
{
/******************/
/* file/directory */
/******************/
if (tkptr->SearchIsDirectory)
FaoToNet (rqptr,
" \n");
else
FaoToNet (rqptr, " \n");
}
if (proptr->AllProp || proptr->CreationDate)
{
/***********/
/* created */
/***********/
/* this property format is "1995-0825T17:32:40Z" */
DavWebDateTimeTo3339 (DateTime, &tkptr->SearchOds.FileQio.CdtTime64);
FaoToNet (rqptr,
" !AZ\n",
DateTime);
}
if (proptr->AllProp || proptr->GetContentLanguage)
/* WASD dosn't handle this datum */
FaoToNet (rqptr,
" \n");
if (proptr->AllProp || proptr->GetContentLength)
{
/******************/
/* content length */
/******************/
if (!tkptr->SearchIsDirectory)
{
AllocatedVbn =
((tkptr->SearchOds.FileQio.RecAttr.fat$l_hiblk & 0xffff) << 16) |
((tkptr->SearchOds.FileQio.RecAttr.fat$l_hiblk & 0xffff0000) >> 16);
EndOfFileVbn =
((tkptr->SearchOds.FileQio.RecAttr.fat$l_efblk & 0xffff) << 16) |
((tkptr->SearchOds.FileQio.RecAttr.fat$l_efblk & 0xffff0000) >> 16);
if (EndOfFileVbn <= 1)
Bytes = tkptr->SearchOds.FileQio.RecAttr.fat$w_ffbyte;
else
Bytes = ((EndOfFileVbn-1) << 9) +
tkptr->SearchOds.FileQio.RecAttr.fat$w_ffbyte;
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV,
"AllocatedVbn:!UL EndOfFileVbn:!UL FirstFreeByte:!UL Bytes;!UL",
AllocatedVbn, EndOfFileVbn,
tkptr->SearchOds.FileQio.RecAttr.fat$w_ffbyte,
Bytes);
FaoToNet (rqptr,
" !UL\n",
Bytes);
}
}
if (proptr->AllProp || proptr->GetContentType)
{
/****************/
/* content type */
/****************/
if (tkptr->SearchIsDirectory)
cptr = "httpd/unix-directory";
else
{
if (rqptr->PathOds == MAPURL_PATH_ODS_ADS ||
rqptr->PathOds == MAPURL_PATH_ODS_SMB)
cptr = MapOdsAdsFileType (tkptr->SearchOds.NamTypePtr);
else
if (rqptr->PathOds == MAPURL_PATH_ODS_SRI)
cptr = MapOdsSriFileType (tkptr->SearchOds.NamTypePtr);
else
cptr = tkptr->SearchOds.NamTypePtr;
ConfigContentType (&tkptr->ContentInfo, cptr);
cptr = tkptr->ContentInfo.ContentTypePtr;
/* transmogrify WASD internal types */
if (SAME2(cptr,'x-') || SAME2(cptr,'X-'))
cptr = "application/octet-stream";
}
FaoToNet (rqptr,
" !AZ\n",
cptr);
}
if (proptr->AllProp || proptr->GetEtag)
{
/********/
/* etag */
/********/
FileGenerateEntityTag (EntityTag, &tkptr->SearchOds.FileQio);
FaoToNet (rqptr,
" \"!AZ\"\n",
EntityTag);
}
if (proptr->AllProp || proptr->GetLastModified)
{
/*****************/
/* last modified */
/*****************/
/* this property format is "Fri, 25 Aug 1995 17:32:40 GMT" */
HttpGmTimeString (DateTime, &tkptr->SearchOds.FileQio.RdtTime64);
FaoToNet (rqptr,
" !AZ\n",
DateTime);
}
#if TEST_PROP_QUOTA
WebDavQuotaEnabled = proptr->QuotaUsedBytes =
proptr->QuotaAvailableBytes = true;
#endif
if (WebDavQuotaEnabled)
if (proptr->QuotaUsedBytes || proptr->QuotaAvailableBytes)
DavPropQuota (rqptr);
if (tkptr->LockingEnabled &&
(proptr->AllProp || proptr->LockDiscovery))
{
/*********************/
/* locking available */
/*********************/
FaoToNet (rqptr,
" \n\
\n\
\n\
\n\
\n\
\n\
\n\
\n\
\n\
\n");
}
if (tkptr->MicrosoftAgent &&
rqptr->rqPathSet.WebDavNoWinProp &&
(proptr->AllProp || proptr->FindProp))
{
/******************************/
/* fudge Microsoft properties */
/******************************/
/* these property formats are "Fri, 25 Aug 1995 17:32:40 GMT" */
HttpGmTimeString (DateTime, &tkptr->SearchOds.FileQio.CdtTime64);
FaoToNet (rqptr,
" \
!AZ\n",
DateTime);
HttpGmTimeString (DateTime, &tkptr->SearchOds.FileQio.RdtTime64);
FaoToNet (rqptr,
" \
!AZ\n",
DateTime);
/* NOT the last access time but what the hey it's a fudge! */
FaoToNet (rqptr,
" \
!AZ\n",
DateTime);
/* apparently it is good practice to always set the archive bit */
FaoToNet (rqptr,
" \
00000020\n");
}
tkptr->MetaData.VmsStatus = 0;
if (rqptr->rqPathSet.WebDavNoProp)
DavPropDead (rqptr);
else
if (proptr->AllProp || proptr->FindProp || proptr->LockDiscovery)
{
/*************/
/* meta-data */
/*************/
if (tkptr->ToDepth == WEBDAV_DEPTH_ZERO)
{
/*
The collection itself.
Do NOT use DavPropParent() non-concealed specification to
access the meta-data. Use the original request specification.
*/
DavMetaRead (rqptr, &tkptr->MetaData, rqptr->ParseOds.NamDevicePtr,
&DavPropDead, rqptr);
return;
}
zptr = (sptr = FileSpec) + sizeof(FileSpec)-1;
cptr = tkptr->SearchOds.NamDevicePtr;
if (tkptr->SearchOds.FileQio.AtrUchar & FCH$M_DIRECTORY)
{
/* munge the ]NAME.DIR into a directory specification .NAME] */
while (*cptr && cptr < tkptr->SearchOds.NamNamePtr-1 && sptr < zptr)
*sptr++ = *cptr++;
if (sptr < zptr) *sptr++ = '.';
if (*cptr) cptr++;
while (*cptr && cptr < tkptr->SearchOds.NamTypePtr && sptr < zptr)
*sptr++ = *cptr++;
if (sptr < zptr) *sptr++ = ']';
}
else
{
/* just a POF */
while (*cptr && cptr < tkptr->SearchOds.NamVersionPtr && sptr < zptr)
*sptr++ = *cptr++;
}
*sptr = '\0';
DavMetaRead (rqptr, &tkptr->MetaData, FileSpec, &DavPropDead, rqptr);
}
else
DavPropDead (rqptr);
}
/*****************************************************************************/
/*
AST called from DavMetaRead() (invoked by DavPropLive()), or called directly
from DavPropLive(). This function supplies the 'dead' properties assocated
with a file (or directory). These properties are stored in the meta-data and
accessed as required.
*/
DavPropDead (REQUEST_STRUCT *rqptr)
{
int status;
STR_DSC *fdptr, *sdptr;
WEBDAV_META *mtaptr;
WEBDAV_PROP *proptr;
WEBDAV_TASK *tkptr;
/*********/
/* begin */
/*********/
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV,
"DavPropDead() !&S",
rqptr->WebDavTaskPtr->MetaData.VmsStatus);
tkptr = rqptr->WebDavTaskPtr;
mtaptr = &tkptr->MetaData;
proptr = &tkptr->PropData;
if (VMSok (tkptr->MetaData.VmsStatus))
{
if (tkptr->LockingEnabled &&
(proptr->AllProp || proptr->LockDiscovery))
{
/* what locking is available */
DavLockDiscovery (rqptr);
}
if (proptr->AllProp)
{
/* list all of the 'dead' properties */
STR_DSC_ITERATE (sdptr, &tkptr->MetaData.PropDsc)
if (STR_DSC_LEN(sdptr))
FaoToNet (rqptr, " !AZ\n", STR_DSC_PTR(sdptr));
}
else
if (proptr->FindProp)
{
/* search for requested properties and report any found */
STR_DSC_ITERATE (sdptr, &tkptr->XmlData.PropFindDsc)
if (fdptr = DavMetaSearch (rqptr, &tkptr->MetaData.PropDsc, sdptr))
if (STR_DSC_LEN(fdptr))
FaoToNet (rqptr, " !AZ\n", STR_DSC_PTR(fdptr));
}
}
FaoToNet (rqptr,
" \n\
\n\
\n");
if (tkptr->ToDepth == WEBDAV_DEPTH_ZERO)
DavPropEnd (rqptr);
else
if (tkptr->PropFindFlushSecond <= HttpdTickSecond)
{
/* no output for this period so let's force a flush */
tkptr->PropFindFlushSecond = HttpdTickSecond +
tkptr->PropFindFlushAfter;
NetWriteBuffered (rqptr, DavPropSearch, NULL, 0);
}
else
{
/* reset flush timer if the buffer was at the last attempt */
if (STR_DSC_LEN(&rqptr->NetWriteBufferDsc) < tkptr->PropFindFlushLength)
tkptr->PropFindFlushSecond = HttpdTickSecond +
tkptr->PropFindFlushAfter;
/* set the current length of the buffer */
tkptr->PropFindFlushLength = STR_DSC_LEN(&rqptr->NetWriteBufferDsc);
/* flush if buffer full(ish) */
NetWriteBuffered (rqptr, DavPropSearch, NULL, -1);
}
}
/*****************************************************************************/
/*
Implement the PROPPATCH method. Modfiy the meta-data properties.
*/
DavPropPatchBegin (REQUEST_STRUCT *rqptr)
{
STR_DSC *sdptr;
WEBDAV_TASK *tkptr;
/*********/
/* begin */
/*********/
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV,
"DavPropPatchBegin() !&Z", rqptr->ParseOds.ExpFileName);
if (!rqptr->RemoteUser[0])
ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
tkptr = rqptr->WebDavTaskPtr;
if (rqptr->rqPathSet.WebDavNoProp)
{
if (WATCHING (rqptr, WATCH_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_WEBDAV,
"PROPPATCH (path set NOprop) !AZ",
rqptr->ParseOds.ExpFileName);
DavPropPatchEnd (rqptr);
return;
}
if (tkptr->MicrosoftAgent &&
rqptr->rqPathSet.WebDavNoWinProp)
{
/* step through each property in the request body */
STR_DSC_ITERATE (sdptr, &tkptr->XmlData.PropSetDsc)
{
if (!STR_DSC_LEN(sdptr)) continue;
if (strstr(STR_DSC_PTR(sdptr),"urn:schemas-microsoft-com:")) continue;
/* break on the first non-MS property */
break;
}
if (!sdptr)
{
/* no properties (unlikely) or all MS properties (almost certainly) */
if (WATCHING (rqptr, WATCH_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_WEBDAV,
"PROPPATCH (path set NOWINPROP) !AZ",
rqptr->ParseOds.ExpFileName);
tkptr->MetaData.VmsStatus = SS$_NORMAL;
DavPropPatchEnd (rqptr);
return;
}
/* otherwise drop thru to continue */
}
tkptr->TestLockState = 0;
DavPropPatchBegin2 (rqptr);
}
/*****************************************************************************/
/*
Asynchronously test for locking then if not begin the patching.
*/
DavPropPatchBegin2 (REQUEST_STRUCT *rqptr)
{
int status;
char *cptr;
WEBDAV_META *mtaptr;
WEBDAV_TASK *tkptr;
/*********/
/* begin */
/*********/
tkptr = rqptr->WebDavTaskPtr;
mtaptr = &tkptr->MetaData;
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV,
"DavPropPatchBegin2() !&F !UL",
DavPropPatchBegin2, tkptr->TestLockState);
if (tkptr->SearchOds.NamDeviceLength)
cptr = tkptr->SearchOds.ExpFileName;
else
if (rqptr->ParseOds.NamDeviceLength)
cptr = rqptr->ParseOds.ExpFileName;
else
ErrorExitVmsStatus (SS$_BUGCHECK, ErrorSanityCheck, FI_LI);
/***********/
/* locking */
/***********/
switch (tkptr->TestLockState)
{
case 0 :
/* establish a request (overall) VMS DLM lock on source */
tkptr->TestLockState++;
DavWebDlmEnqueue (rqptr, &tkptr->DlmSource,
rqptr->ParseOds.ExpFileName,
NULL, true, false,
DavPropPatchBegin2, rqptr);
return;
case 1 :
if (VMSnok (status = tkptr->DlmSource.LockSb.lksb$w_status))
{
/* ordinarily shouldn't return any errors */
DavWebResponse (rqptr, 500, status, NULL, FI_LI);
DavPropPatchEnd (rqptr);
return;
}
/* check for meta-lock on existing source */
tkptr->TestLockState++;
DavLockTest (rqptr, rqptr->ParseOds.ExpFileName, false,
DavPropPatchBegin2, rqptr);
return;
case 2 :
if (VMSnok (tkptr->TestLockStatus))
{
mtaptr->VmsStatus = tkptr->TestLockStatus;
DavWebResponse (rqptr, 423, 0, "source locked", FI_LI);
DavPropPatchEnd (rqptr);
return;
}
}
/**************/
/* not locked */
/**************/
if (WATCHING (rqptr, WATCH_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "PROPPATCH !AZ", cptr);
DavMetaLock (rqptr, &tkptr->MetaData, rqptr->ParseOds.ExpFileName,
&DavPropPatchUpdate, &tkptr->MetaData);
}
/*****************************************************************************/
/*
*/
DavPropPatchUpdate (WEBDAV_META *mtaptr)
{
int status;
REQUEST_STRUCT *rqptr;
/*********/
/* begin */
/*********/
rqptr = mtaptr->RequestPtr;
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV,
"DavPropPatchUpdate() !&F !&S",
DavPropPatchUpdate, mtaptr->VmsStatus);
if (VMSok (mtaptr->VmsStatus))
DavMetaUpdate (mtaptr, DavPropPatchEnd, rqptr);
else
DavPropPatchEnd (rqptr);
}
/*****************************************************************************/
/*
Complete a PROPPATCH request.
*/
DavPropPatchEnd (REQUEST_STRUCT *rqptr)
{
int status, xmlsts;
char *cptr;
WEBDAV_META *mtaptr;
WEBDAV_TASK *tkptr;
/*********/
/* begin */
/*********/
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavPropPatchEnd()");
tkptr = rqptr->WebDavTaskPtr;
mtaptr = &tkptr->MetaData;
if (VMSok (mtaptr->VmsStatus))
{
rqptr->rqResponse.NoGzip = true;
DavWebResponse207 (rqptr);
DavWebHref (rqptr, rqptr->ParseOds.ExpFileName, 0);
FaoToNet (rqptr,
" \n\
!AZ!&%AZ\n\
\n\
\n",
tkptr->HrefHost,
tkptr->PropParent ?
rqptr->rqHeader.PathInfoPtr : tkptr->HrefPath);
}
else
{
if (mtaptr->VmsStatus == SS$_ACCONFLICT)
{
/* resource is locked */
DavWebResponse (rqptr, 423, 0, NULL, FI_LI);
}
else
{
/* other reason for failure */
DavWebResponse (rqptr, 500, mtaptr->VmsStatus, NULL, FI_LI);
}
}
DavWebEnd (rqptr);
}
/*****************************************************************************/
/*
Provide RFC 4331 quota properties (all synchronous).
If the path access is SET webdav=server attempt to return any disk quota for
the HTTPd server account or if SET webdav=profile the SYSUAF authenticated
request's VMS username. Both of these require a $GETUAI to obtain the UIC. If
neither of these (or if the $GETUAI fails for some reason) then return the
request path's device used and available space.
*/
DavPropQuota (REQUEST_STRUCT *rqptr)
{
static unsigned long AddendZero = 0,
FiveTwelve = 512,
UaiContext = -1;
static unsigned long DviFreeBlocks,
DviMaxBlock,
UaiUic;
static char UserName [12+1];
static int UserNameLength;
static $DESCRIPTOR (UserNameDsc, UserName);
static VMS_ITEM_LIST2 DeviceFibDsc,
InQuotaDsc,
OutQuotaDsc;
static VMS_ITEM_LIST3
UaiItems [] =
{
{ sizeof(UaiUic), UAI$_UIC, &UaiUic, 0 },
{ 0,0,0,0 }
},
BlocksItemList [] =
{
{ sizeof(DviMaxBlock), DVI$_MAXBLOCK, &DviMaxBlock, 0 },
{ sizeof(DviFreeBlocks), DVI$_FREEBLOCKS, &DviFreeBlocks, 0 },
{ 0, 0, 0, 0 }
};
int status;
unsigned short DeviceChannel,
OutLength;
unsigned long BytesAvailable,
BytesUsed;
unsigned long QuadBytes [2];
$DESCRIPTOR (DeviceDsc, "");
struct fibdef DeviceFib;
WEBDAV_PROP *proptr;
WEBDAV_TASK *tkptr;
struct {
unsigned long flags;
unsigned long uic;
unsigned long used;
unsigned long perm;
unsigned long over;
unsigned long unused[3];
} InQuota, OutQuota;
struct {
unsigned short iosb$w_status;
unsigned short iosb$w_bcnt;
unsigned long iosb$l_reserved;
} IOsb;
/*********/
/* begin */
/*********/
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavPropQuota()");
tkptr = rqptr->WebDavTaskPtr;
proptr = &tkptr->PropData;
DeviceDsc.dsc$a_pointer = rqptr->ParseOds.NamDevicePtr;
DeviceDsc.dsc$w_length = rqptr->ParseOds.NamDeviceLength;
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "!#AZ",
DeviceDsc.dsc$w_length, DeviceDsc.dsc$a_pointer);
status = sys$assign (&DeviceDsc, &DeviceChannel, 0, 0);
if (VMSnok (status))
{
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV,
"sys$assign() !&S", status);
return (status);
}
UserNameDsc.dsc$a_pointer = NULL;
if (rqptr->rqPathSet.WebDavServer)
{
/* try to check HTTPd server account quota */
UserNameDsc.dsc$a_pointer = HttpdProcess.UserName;
UserNameDsc.dsc$w_length = HttpdProcess.UserNameLength;
}
else
if (rqptr->rqPathSet.WebDavProfile)
{
/* try to check SYSUAF authenticated user account quota */
if (rqptr->rqAuth.SysUafAuthenticated &&
rqptr->rqAuth.VmsUserProfileLength)
{
UserNameDsc.dsc$a_pointer = rqptr->RemoteUser;
UserNameDsc.dsc$w_length = rqptr-> RemoteUserLength;
}
}
#if TEST_PROP_QUOTA
#ifdef TEST_PROP_QUOTA_USERNAME
UserNameDsc.dsc$a_pointer = TEST_PROP_QUOTA_USERNAME;
UserNameDsc.dsc$w_length = strlen(TEST_PROP_QUOTA_USERNAME);
#endif
#endif
status = 0;
if (UserNameDsc.dsc$a_pointer)
{
/****************/
/* username UIC */
/****************/
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "{!UL}!#AZ !&Z",
UserNameDsc.dsc$w_length, UserNameDsc.dsc$w_length,
UserNameDsc.dsc$a_pointer, UserName);
if (UserNameLength != UserNameDsc.dsc$w_length ||
strncmp (UserName, UserNameDsc.dsc$a_pointer, UserNameLength))
{
/* buffer the username (and consequently the UIC) */
char *cptr, *sptr, *zptr;
zptr = (sptr = UserName) + sizeof(UserName)-1;
for (cptr = UserNameDsc.dsc$a_pointer;
cptr < UserNameDsc.dsc$a_pointer + UserNameDsc.dsc$w_length;
*sptr++ = *cptr++);
*sptr = '\0';
UserNameLength = sptr - UserName;
UserNameDsc.dsc$a_pointer = UserName;
UserNameDsc.dsc$w_length = UserNameLength;
sys$setprv (1, &SysPrvMask, 0, 0);
status = sys$getuai (0, &UaiContext, &UserNameDsc, &UaiItems,
0, 0, 0);
sys$setprv (0, &SysPrvMask, 0, 0);
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV,
"sys$getuai() !&S", status);
}
else
status = SS$_NORMAL;
}
/* if UIC was successfully retrieved */
if (VMSok (status))
{
/********************/
/* check disk quota */
/********************/
memset (&DeviceFib, 0, sizeof(DeviceFib));
DeviceFib.fib$w_exctl = FIB$C_EXA_QUOTA;
DeviceFibDsc.buf_len = sizeof(DeviceFib);
DeviceFibDsc.buf_addr = &DeviceFib;
memset (&InQuota, 0, sizeof(InQuota));
InQuotaDsc.buf_len = sizeof(InQuota);
InQuotaDsc.buf_addr = &InQuota;
InQuota.uic = UaiUic;
memset (&OutQuota, 0, sizeof(OutQuota));
OutQuotaDsc.buf_len = sizeof(OutQuota);
OutQuotaDsc.buf_addr = &OutQuota;
status = sys$qiow (0, DeviceChannel, IO$_ACPCONTROL, &IOsb, 0, 0,
&DeviceFibDsc, &InQuotaDsc, &OutLength, &OutQuotaDsc,
0, 0);
if (VMSok (status)) status = IOsb.iosb$w_status;
if (VMSok (status))
{
BytesAvailable = OutQuota.perm - OutQuota.used;
BytesUsed = OutQuota.used;
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV,
"QUOTA perm:!UL used:!UL",
OutQuota.perm, OutQuota.used);
}
else
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV,
"sys$qiow() !&S", status);
}
/* if the quota ACP failed or if quota not checked */
if (VMSnok (status))
{
/********************/
/* get device usage */
/********************/
status = sys$getdviw (EfnWait, DeviceChannel, 0,
&BlocksItemList, &IOsb, 0, 0, 0);
if (VMSok (status)) status = IOsb.iosb$w_status;
if (VMSok (status))
{
BytesAvailable = DviFreeBlocks;
BytesUsed = DviMaxBlock - DviFreeBlocks;
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV,
"BLOCKS max:!UL free:!UL", DviMaxBlock, DviFreeBlocks);
}
else
if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV,
"sys$qiow() !&S", status);
}
if (VMSok (status))
{
/***************/
/* list quotas */
/***************/
if (proptr->QuotaUsedBytes)
{
status = lib$emul (&FiveTwelve, &BytesUsed,
&AddendZero, &QuadBytes);
if (VMSok (status))
FaoToNet (rqptr,
" !@SQ\n",
&QuadBytes);
else
ErrorNoticed (rqptr, status, NULL, FI_LI);
}
if (proptr->QuotaAvailableBytes)
{
status = lib$emul (&FiveTwelve, &BytesAvailable,
&AddendZero, &QuadBytes);
if (VMSok (status))
FaoToNet (rqptr,
" !@SQ\n",
&QuadBytes);
else
ErrorNoticed (rqptr, status, NULL, FI_LI);
}
}
sys$dassgn (DeviceChannel);
return (status);
}
/*****************************************************************************/