[0001]
[0002]
[0003]
[0004]
[0005]
[0006]
[0007]
[0008]
[0009]
[0010]
[0011]
[0012]
[0013]
[0014]
[0015]
[0016]
[0017]
[0018]
[0019]
[0020]
[0021]
[0022]
[0023]
[0024]
[0025]
[0026]
[0027]
[0028]
[0029]
[0030]
[0031]
[0032]
[0033]
[0034]
[0035]
[0036]
[0037]
[0038]
[0039]
[0040]
[0041]
[0042]
[0043]
[0044]
[0045]
[0046]
[0047]
[0048]
[0049]
[0050]
[0051]
[0052]
[0053]
[0054]
[0055]
[0056]
[0057]
[0058]
[0059]
[0060]
[0061]
[0062]
[0063]
[0064]
[0065]
[0066]
[0067]
[0068]
[0069]
[0070]
[0071]
[0072]
[0073]
[0074]
[0075]
[0076]
[0077]
[0078]
[0079]
[0080]
[0081]
[0082]
[0083]
[0084]
[0085]
[0086]
[0087]
[0088]
[0089]
[0090]
[0091]
[0092]
[0093]
[0094]
[0095]
[0096]
[0097]
[0098]
[0099]
[0100]
[0101]
[0102]
[0103]
[0104]
[0105]
[0106]
[0107]
[0108]
[0109]
[0110]
[0111]
[0112]
[0113]
[0114]
[0115]
[0116]
[0117]
[0118]
[0119]
[0120]
[0121]
[0122]
[0123]
[0124]
[0125]
[0126]
[0127]
[0128]
[0129]
[0130]
[0131]
[0132]
[0133]
[0134]
[0135]
[0136]
[0137]
[0138]
[0139]
[0140]
[0141]
[0142]
[0143]
[0144]
[0145]
[0146]
[0147]
[0148]
[0149]
[0150]
[0151]
[0152]
[0153]
[0154]
[0155]
[0156]
[0157]
[0158]
[0159]
[0160]
[0161]
[0162]
[0163]
[0164]
[0165]
[0166]
[0167]
[0168]
[0169]
[0170]
[0171]
[0172]
[0173]
[0174]
[0175]
[0176]
[0177]
[0178]
[0179]
[0180]
[0181]
[0182]
[0183]
[0184]
[0185]
[0186]
[0187]
[0188]
[0189]
[0190]
[0191]
[0192]
[0193]
[0194]
[0195]
[0196]
[0197]
[0198]
[0199]
[0200]
[0201]
[0202]
[0203]
[0204]
[0205]
[0206]
[0207]
[0208]
[0209]
[0210]
[0211]
[0212]
[0213]
[0214]
[0215]
[0216]
[0217]
[0218]
[0219]
[0220]
[0221]
[0222]
[0223]
[0224]
[0225]
[0226]
[0227]
[0228]
[0229]
[0230]
[0231]
[0232]
[0233]
[0234]
[0235]
[0236]
[0237]
[0238]
[0239]
[0240]
[0241]
[0242]
[0243]
[0244]
[0245]
[0246]
[0247]
[0248]
[0249]
[0250]
[0251]
[0252]
[0253]
[0254]
[0255]
[0256]
[0257]
[0258]
[0259]
[0260]
[0261]
[0262]
[0263]
[0264]
[0265]
[0266]
[0267]
[0268]
[0269]
[0270]
[0271]
[0272]
[0273]
[0274]
[0275]
[0276]
[0277]
[0278]
[0279]
[0280]
[0281]
[0282]
[0283]
[0284]
[0285]
[0286]
[0287]
[0288]
[0289]
[0290]
[0291]
[0292]
[0293]
[0294]
[0295]
[0296]
[0297]
[0298]
[0299]
[0300]
[0301]
[0302]
[0303]
[0304]
[0305]
[0306]
[0307]
[0308]
[0309]
[0310]
[0311]
[0312]
[0313]
[0314]
[0315]
[0316]
[0317]
[0318]
[0319]
[0320]
[0321]
[0322]
[0323]
[0324]
[0325]
[0326]
[0327]
[0328]
[0329]
[0330]
[0331]
[0332]
[0333]
[0334]
[0335]
[0336]
[0337]
[0338]
[0339]
[0340]
[0341]
[0342]
[0343]
[0344]
[0345]
[0346]
[0347]
[0348]
[0349]
[0350]
[0351]
[0352]
[0353]
[0354]
[0355]
[0356]
[0357]
[0358]
[0359]
[0360]
[0361]
[0362]
[0363]
[0364]
[0365]
[0366]
[0367]
[0368]
[0369]
[0370]
[0371]
[0372]
[0373]
[0374]
[0375]
[0376]
[0377]
[0378]
[0379]
[0380]
[0381]
[0382]
[0383]
[0384]
[0385]
[0386]
[0387]
[0388]
[0389]
[0390]
[0391]
[0392]
[0393]
[0394]
[0395]
[0396]
[0397]
[0398]
[0399]
[0400]
[0401]
[0402]
[0403]
[0404]
[0405]
[0406]
[0407]
[0408]
[0409]
[0410]
[0411]
[0412]
[0413]
[0414]
[0415]
[0416]
[0417]
[0418]
[0419]
[0420]
[0421]
[0422]
[0423]
[0424]
[0425]
[0426]
[0427]
[0428]
[0429]
[0430]
[0431]
[0432]
[0433]
[0434]
[0435]
[0436]
[0437]
[0438]
[0439]
[0440]
[0441]
[0442]
[0443]
[0444]
[0445]
[0446]
[0447]
[0448]
[0449]
[0450]
[0451]
[0452]
[0453]
[0454]
[0455]
[0456]
[0457]
[0458]
[0459]
[0460]
[0461]
[0462]
[0463]
[0464]
[0465]
[0466]
[0467]
[0468]
[0469]
[0470]
[0471]
[0472]
[0473]
[0474]
[0475]
[0476]
[0477]
[0478]
[0479]
[0480]
[0481]
[0482]
[0483]
[0484]
[0485]
[0486]
[0487]
[0488]
[0489]
[0490]
[0491]
[0492]
[0493]
[0494]
[0495]
[0496]
[0497]
[0498]
[0499]
[0500]
[0501]
[0502]
[0503]
[0504]
[0505]
[0506]
[0507]
[0508]
[0509]
[0510]
[0511]
[0512]
[0513]
[0514]
[0515]
[0516]
[0517]
[0518]
[0519]
[0520]
[0521]
[0522]
[0523]
[0524]
[0525]
[0526]
[0527]
[0528]
[0529]
[0530]
[0531]
[0532]
[0533]
[0534]
[0535]
[0536]
[0537]
[0538]
[0539]
[0540]
[0541]
[0542]
[0543]
[0544]
[0545]
[0546]
[0547]
[0548]
[0549]
[0550]
[0551]
[0552]
[0553]
[0554]
[0555]
[0556]
[0557]
[0558]
[0559]
[0560]
[0561]
[0562]
[0563]
[0564]
[0565]
[0566]
[0567]
[0568]
[0569]
[0570]
[0571]
[0572]
[0573]
[0574]
[0575]
[0576]
[0577]
[0578]
[0579]
[0580]
[0581]
[0582]
[0583]
[0584]
[0585]
[0586]
[0587]
[0588]
[0589]
[0590]
[0591]
[0592]
[0593]
[0594]
[0595]
[0596]
[0597]
[0598]
[0599]
[0600]
[0601]
[0602]
[0603]
[0604]
[0605]
[0606]
[0607]
[0608]
[0609]
[0610]
[0611]
[0612]
[0613]
[0614]
[0615]
[0616]
[0617]
[0618]
[0619]
[0620]
[0621]
[0622]
[0623]
[0624]
[0625]
[0626]
[0627]
[0628]
[0629]
[0630]
[0631]
[0632]
[0633]
[0634]
[0635]
[0636]
[0637]
[0638]
[0639]
[0640]
[0641]
[0642]
[0643]
[0644]
[0645]
[0646]
[0647]
[0648]
[0649]
[0650]
[0651]
[0652]
[0653]
[0654]
[0655]
[0656]
[0657]
[0658]
[0659]
[0660]
[0661]
[0662]
[0663]
[0664]
[0665]
[0666]
[0667]
[0668]
[0669]
[0670]
[0671]
[0672]
[0673]
[0674]
[0675]
[0676]
[0677]
[0678]
[0679]
[0680]
[0681]
[0682]
[0683]
[0684]
[0685]
[0686]
[0687]
[0688]
[0689]
[0690]
[0691]
[0692]
[0693]
[0694]
[0695]
[0696]
[0697]
[0698]
[0699]
[0700]
[0701]
[0702]
[0703]
[0704]
[0705]
[0706]
[0707]
[0708]
[0709]
[0710]
[0711]
[0712]
[0713]
[0714]
[0715]
[0716]
[0717]
[0718]
[0719]
[0720]
[0721]
[0722]
[0723]
[0724]
[0725]
[0726]
[0727]
[0728]
[0729]
[0730]
[0731]
[0732]
[0733]
[0734]
[0735]
[0736]
[0737]
[0738]
[0739]
[0740]
[0741]
[0742]
[0743]
[0744]
[0745]
[0746]
[0747]
[0748]
[0749]
[0750]
[0751]
[0752]
[0753]
[0754]
[0755]
[0756]
[0757]
[0758]
[0759]
[0760]
[0761]
[0762]
[0763]
[0764]
[0765]
[0766]
[0767]
[0768]
[0769]
[0770]
[0771]
[0772]
[0773]
[0774]
[0775]
[0776]
[0777]
[0778]
[0779]
[0780]
[0781]
[0782]
[0783]
[0784]
[0785]
[0786]
[0787]
[0788]
[0789]
[0790]
[0791]
[0792]
[0793]
[0794]
[0795]
[0796]
[0797]
[0798]
[0799]
[0800]
[0801]
[0802]
[0803]
[0804]
[0805]
[0806]
[0807]
[0808]
/*****************************************************************************/
/*
                                 DAVdelete.c

Also see comments on DELETE in DAVWEB.C.

Implement the DELETE method individual resources (files) and collections
(directory trees).

This module is also used by a MOVE of resources between different volumes. 
After a successful MOVE the source is then DELETEd.

It does not meet the RFC 2518 8.6.2 requirement that if any child resource
deletion would fail then the deletion of the collection not proceed at all
(returning a 424).  So, in the case of WASD WebDAV it's just best-effort and if
something down the tree won't disappear, it just reports the failure in the 207
response and carries merrily on through the tree regardless.  This IS
acceptable WebDAV server behaviour!

Tree deletion occurs in three phases:

1) Files (those files not ending in .DIR).  A single pass.  Deletion errors
are reported in a 207 multistatus response.

2) Directories (those files ending in .DIR).  One of more passes, until all
directory that can be deleted are deleted.  Deletion errors other than
SS$_DIRNOTEMPTY are reported in a 207 multistatus response.

3) Root (parent) directory.


VERSION HISTORY
---------------
19-OCT-2018  MGD  bugfix; suppress DIRNOTEMPTY as note above requires
13-APR-2018  MGD  SS$_ABORT on ->RequestRundown
29-APR-2015  MGD  bugfix; DavDeleteParse() enable access around OdsParse()
29-AUG-2009  MGD  DavDeleteErase() VMS status processing
                  DavDeleteBegin() concession to Microsoft collection depth
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 <stdio.h>
#include <ctype.h>

#include "wasd.h"
#include "davweb.h"

#define WASD_MODULE "DAVDELETE"

#ifndef WASD_WEBDAV
#define WASD_WEBDAV 1
#endif

/******************/
/* global storage */
/******************/

/********************/
/* external storage */
/********************/

extern int  HttpdTickSecond;

extern int  ToLowerCase[];

extern unsigned long  SysPrvMask[];

extern char  ErrorSanityCheck[];

extern ACCOUNTING_STRUCT  *AccountingPtr;
extern CONFIG_STRUCT  Config;
extern WATCH_STRUCT  Watch;

/*****************************************************************************/
/*
Deletes a collection (directory) or single resource (file).
*/ 
 
DavDeleteBegin (REQUEST_STRUCT *rqptr)

{
   int  status;
   WEBDAV_TASK  *tkptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavDeleteBegin()");

   if (!rqptr->RemoteUser[0])
   {
      DavWebResponse (rqptr, 403, SS$_BUGCHECK, "RemoteUser?", FI_LI);
      DavWebEnd (rqptr);
      return;
   }

   tkptr = rqptr->WebDavTaskPtr;

   tkptr->TestLockState = 0;

   OdsStructInit (&tkptr->SearchOds, false);

   if (!(rqptr->ParseOds.NamNameLength > 0 ||
         rqptr->ParseOds.NamTypeLength > 1))
   {
      /* delete MUST act as if infinity depth */
      if (tkptr->ToDepth != WEBDAV_DEPTH_INFINITY)
      {
         if (!tkptr->MicrosoftAgent)
         {
            DavWebResponse (rqptr, 400, 0, "inappropriate depth", FI_LI);
            DavWebEnd (rqptr);
            return;
         }
      }
   }

   if (WATCHING (rqptr, WATCH_RESPONSE))
      WatchThis (WATCHITM(rqptr), WATCH_RESPONSE,
                 "DELETE !AZ depth:!UL path:!AZ",
                 rqptr->ParseOds.ExpFileName, tkptr->ToDepth,
                 DavWebPathAccess(rqptr));

   DavDeleteBegin2 (rqptr);
}

/*****************************************************************************/
/*
Entry point for MOVE using rename that needs to delete residual directory
files.  This function will be called multiple times as asynchronous testing of
the DLM and meta locking occurs.
*/ 
 
DavDeleteBegin2 (REQUEST_STRUCT *rqptr)

{
   BOOL  IsDir;
   int  status;
   WEBDAV_TASK  *tkptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavDeleteBegin2() !UL",
                 rqptr->WebDavTaskPtr->TestLockState);

   tkptr = rqptr->WebDavTaskPtr;

   /***********/
   /* 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,
                           DavDeleteBegin2, 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);
            DavDeleteEnd (rqptr);
            return;
         }

         /* check for meta-lock on existing source */
         tkptr->TestLockState++;
         DavLockTest (rqptr, rqptr->ParseOds.ExpFileName, false,
                      DavDeleteBegin2, rqptr);
         return;

      case 2 :

         if (VMSnok (tkptr->TestLockStatus))
         {
            DavWebResponse (rqptr, 423, 0, "source locked", FI_LI);
            DavDeleteEnd (rqptr);
            return;
         }
   }

   /**************/
   /* not locked */
   /**************/

   if (rqptr->ParseOds.NamNameLength > 0 ||
       rqptr->ParseOds.NamTypeLength > 1)
   {
      /* delete single file */
      status = DavDeleteParse (rqptr, false, ";*");
      tkptr->DeleteData.DelPhase = WEBDAV_DELETE_SINGLE_FILE;
   }
   else
   {
      /* delete collection */
      status = DavDeleteParse (rqptr, true, "...]*.*;*");
      tkptr->DeleteData.DelPhase = WEBDAV_DELETE_TREE_FILES;
      /* set counter that will be used when time to delete directory tree */
      tkptr->DeleteData.TreeDirCount = -1;
   }

   if (VMSnok (status))
   {
      DavWebResponse (rqptr, 0, status, NULL, FI_LI);
      DavWebEnd (rqptr);
      return;
   }

   DavDeleteSearch (rqptr);
}

/*****************************************************************************/
/*
As necessary, close the multistatus XML.
*/ 

DavDeleteEnd (REQUEST_STRUCT *rqptr)

{
   int  status;
   WEBDAV_TASK  *tkptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV,
                 "DavDeleteEnd() !&F", &DavDeleteEnd);

   if (rqptr->rqHeader.Method == HTTP_METHOD_WEBDAV_MOVE)
   {
      /* have been cleaning up residual directories from move using copy */
      DavMoveEnd (rqptr);
      return;
   }

   tkptr = rqptr->WebDavTaskPtr;

   if (WATCHING (rqptr, WATCH_WEBDAV))
      if (tkptr->DeleteData.DirCount ||
          tkptr->DeleteData.DirFailCount ||
          tkptr->DeleteData.FileCount ||
          tkptr->DeleteData.FileFailCount ||
          tkptr->DeleteData.MetaCount ||
          tkptr->DeleteData.MetaFailCount)
         WatchThis (WATCHITM(rqptr), WATCH_WEBDAV,
"DELETED dir:!UL fail:!UL file:!UL fail:!UL meta:!UL fail:!UL",
                    tkptr->DeleteData.DirCount,
                    tkptr->DeleteData.DirFailCount +
                       tkptr->DeleteData.TreeDirNotEmptyCount,
                    tkptr->DeleteData.FileCount,
                    tkptr->DeleteData.FileFailCount,
                    tkptr->DeleteData.MetaCount,
                    tkptr->DeleteData.MetaFailCount);

   if (rqptr->rqHeader.Method == HTTP_METHOD_WEBDAV_MOVE)
   {
      /* move to different device via copy and delete */
      DavDeleteEnd (rqptr);
      return;
   }

   /* if an 'empty' multistatus response was generated */
   if (!rqptr->rqResponse.HttpStatus)
      DavWebResponse (rqptr, 204, 0, "successful delete", FI_LI);
   else
   if (rqptr->rqResponse.HttpStatus == 207)
      FaoToNet (rqptr, "</D:multistatus>\n");

   DavWebEnd (rqptr);
}

/*****************************************************************************/
/*
AST function to invoke another $SEARCH call.
*/ 

DavDeleteSearch (REQUEST_STRUCT *rqptr)

{
   WEBDAV_TASK  *tkptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV, "DavDeleteSearch() !&F !&S",
                 &DavDeleteSearch, rqptr->NetIoPtr->WriteStatus);

   tkptr = rqptr->WebDavTaskPtr;

   sys$setprv (1, &SysPrvMask, 0, 0);
   OdsSearch (&tkptr->SearchOds, &DavDeleteSearchAst, rqptr);
   sys$setprv (0, &SysPrvMask, 0, 0);
}

/*****************************************************************************/
/*
AST from completed $SEARCH.
*/ 
 
DavDeleteSearchAst (REQUEST_STRUCT *rqptr)

{
   int  status,
        DirectoryFileLength;
   char  DirectoryFile [ODS_MAX_FILE_NAME_LENGTH+1];
   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,
                 "DavDeleteSearchAst() !&F sts:!&S stv:!&S",
                 &DavDeleteSearchAst,
                 rqptr->WebDavTaskPtr->SearchOds.Fab.fab$l_sts,
                 rqptr->WebDavTaskPtr->SearchOds.Fab.fab$l_stv);

   if (rqptr->RequestState >= REQUEST_STATE_ABORT)
   {
      DavDeleteEnd (rqptr);
      return;
   }

   tkptr = rqptr->WebDavTaskPtr;

   if (VMSnok (status = tkptr->SearchOds.Fab.fab$l_sts))
   {
      if (status == RMS$_NMF || status == RMS$_FNF)
      {
         /*****************/
         /* no more files */
         /*****************/

         if (tkptr->DeleteData.DelPhase == WEBDAV_DELETE_SINGLE_FILE)
         {
            /***************/
            /* single file */
            /***************/

            DavWebResponse (rqptr, 0, status, NULL, FI_LI);
            DavDeleteEnd (rqptr);
            return;
         }

         if (tkptr->DeleteData.DelPhase == WEBDAV_DELETE_TREE_FILES)
            tkptr->DeleteData.DelPhase = WEBDAV_DELETE_TREE_DIRS;

         if (tkptr->DeleteData.DelPhase == WEBDAV_DELETE_TREE_DIRS)
         {
            /**************************/
            /* now delete directories */
            /**************************/

            /* may require multiple passes */
            if (tkptr->DeleteData.TreeDirCount)
            {
               /* reset counter which if non-zero results in another pass */
               tkptr->DeleteData.TreeDirCount =
                   tkptr->DeleteData.TreeDirNotEmptyCount = 0;

               status = DavDeleteParse (rqptr, true, "...]*.DIR;*");
               if (VMSnok (status))
               {
                  ErrorNoticed (rqptr, status, NULL, FI_LI);
                  DavDeleteEnd (rqptr);
                  return;
               }
      
               DavDeleteSearch (rqptr);
               return;
            }
            /* otherwise fall through to delete parent */
         }

         if (tkptr->DeleteData.DelPhase == WEBDAV_DELETE_TREE_DIRS)
         {
            /*********************/
            /* now delete parent */
            /*********************/

            tkptr->DeleteData.DelPhase = WEBDAV_DELETE_TREE_PARENT;

            /* get name of directory in parent */
            OdsNameOfDirectoryFile (rqptr->ParseOds.NamDevicePtr,
                                    rqptr->ParseOds.NamNamePtr -
                                    rqptr->ParseOds.NamDevicePtr,
                                    DirectoryFile, &DirectoryFileLength);

            OdsParse (&rqptr->ParseOds, DirectoryFile, DirectoryFileLength,
                      NULL, 0, NULL, NULL, rqptr);

            if (VMSok (status = rqptr->ParseOds.Fab.fab$l_sts))
               if (VMSok (status = OdsParseTerminate (&rqptr->ParseOds)))
                  status = DavDeleteParse (rqptr, false, ";*");
            if (VMSnok(status))
            {
               ErrorNoticed (rqptr, status, NULL, FI_LI);
               DavDeleteEnd (rqptr);
               return;
            }

            DavDeleteSearch (rqptr);
            return;
         }

         if (tkptr->DeleteData.DelPhase == WEBDAV_DELETE_TREE_PARENT)
         {
            /***************/
            /* end of task */
            /***************/

            DavDeleteEnd (rqptr);
            return;
         }

         ErrorNoticed (rqptr, SS$_BUGCHECK, NULL, FI_LI);
         return;
      }

      DavWebMultiStatus (rqptr, status, tkptr->SearchOds.ExpFileName);
   }

   if (tkptr->DeleteData.DelPhase == WEBDAV_DELETE_TREE_FILES)
   {
      if (tkptr->SearchOds.NamTypeLength == 4 &&
          (MATCH5 (tkptr->SearchOds.NamTypePtr, ".DIR;") ||
           MATCH5 (tkptr->SearchOds.NamTypePtr, ".dir;")))
      {
         status = OdsReallyADir (rqptr, &tkptr->SearchOds);
         if (VMSok (status))
         {
            /* this is a directory file, search for next */
            DavDeleteSearch (rqptr);
            return;
         }
      }
   }
   else
   if (tkptr->DeleteData.DelPhase == WEBDAV_DELETE_TREE_DIRS ||
       tkptr->DeleteData.DelPhase == WEBDAV_DELETE_TREE_PARENT)
   {
      status = OdsReallyADir (rqptr, &tkptr->SearchOds);
      if (VMSnok (status))
      {
         /* NOT a directory file (bit strange, should be deleted above) */
         tkptr->DeleteData.FileFailCount++;
         ErrorNoticed (rqptr, SS$_BUGCHECK, NULL, FI_LI);
         DavDeleteSearch (rqptr);
         return;
      }
   }

   if (DavMetaFile (&tkptr->SearchOds) ||
       DavMetaDir (rqptr, &tkptr->SearchOds))
   {
      /* this is a metadata file or contains a metadata (sub)directory */
      DavDeleteSearch (rqptr);
      return;
   }

   tkptr->TestLockState = 0;

   DavDeleteErase (rqptr);
}

/*****************************************************************************/
/*
Remove the file and any associated metadata.  Remove metadata contingent on the
file successfully erased, unless it's an actual directory file in which case
the metadata and any container directory need to be removed prior.
*/ 
 
void DavDeleteErase (REQUEST_STRUCT *rqptr)

{
   BOOL  IsDir;
   int  status,
        DeleteStatus,
        EraseCount;
   char  *cptr, *sptr, *zptr;
   ODS_STRUCT  DeleteOds;
   WEBDAV_META  *mtaptr;
   WEBDAV_TASK  *tkptr;

   /*********/
   /* begin */
   /*********/

#if WATCH_MOD
   HttpdCheckPriv (FI_LI);
#endif /* WATCH_MOD */

   tkptr = rqptr->WebDavTaskPtr;

   if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV,
                 "DavDeleteErase() sts:!&S stv:!&S !&Z",
                 tkptr->SearchOds.Fab.fab$l_sts,
                 tkptr->SearchOds.Fab.fab$l_stv,
                 tkptr->SearchOds.ResFileName);

   /* when move using copy residual directory deletion do not check locks */
   if (rqptr->rqHeader.Method != HTTP_METHOD_WEBDAV_MOVE)
   {
      if (tkptr->TestLockState++ == 0)
      {
         /* asynchronous test for lock then AST straight back to check */
         if (tkptr->DeleteData.DelPhase == WEBDAV_DELETE_TREE_DIRS)
            DavLockTest (rqptr, tkptr->SearchOds.ResFileName, true,
                         DavDeleteErase, rqptr);
         else
            DavLockTest (rqptr, tkptr->SearchOds.ResFileName, false,
                         DavDeleteErase, rqptr);
         return;
      }
      else
      {
         if (VMSnok (tkptr->TestLockStatus))
         {
            tkptr->DeleteData.FileFailCount++;

            /* just report the lock in the 207 and continue */
            if (tkptr->ToDepth == WEBDAV_DEPTH_INFINITY)
               DavWebMultiStatus (rqptr, RMS$_FLK, rqptr->ParseOds.ResFileName);
            else
               DavWebResponse (rqptr, 0, RMS$_FLK, NULL, FI_LI);

            DavDeleteSearch (rqptr);
            return;
         }
      }
   }

   mtaptr = &rqptr->WebDavTaskPtr->MetaData;
   mtaptr->TaskPtr = rqptr->WebDavTaskPtr;

   /***************/
   /* delete file */
   /***************/

   OdsStructInit (&DeleteOds, true);
   OdsParse (&DeleteOds,
             tkptr->SearchOds.ResFileName,
             tkptr->SearchOds.ResFileNameLength,
             NULL, 0, NAM$M_SYNCHK, NULL, rqptr);

   if (VMSnok (status = DeleteOds.Fab.fab$l_sts))
   {
      if (tkptr->ToDepth == WEBDAV_DEPTH_INFINITY)
         DavWebMultiStatus (rqptr, status, rqptr->ParseOds.ResFileName);
      else
         DavWebResponse (rqptr, 0, status, NULL, FI_LI);
      DavDeleteSearch (rqptr);
      return;
   }

   DeleteOds.NamVersionPtr[0] = '\0'; 
   if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV,
                 "!&Z", DeleteOds.ExpFileName);

   DavMetaName (mtaptr, DeleteOds.ExpFileName, 'r'); 

   /* if it's a directory file then it likely has a metadata directory */
   if (IsDir = (MATCH5 (DeleteOds.NamTypePtr, ".DIR") ||
                MATCH5 (DeleteOds.NamTypePtr, ".dir")))
   {
      /* need to delete metadata prior to deleting the directory file */
      DavDeleteMetaErase (mtaptr);
      DavMetaDeleteDir (mtaptr);
   }

   AuthAccessEnable (rqptr, DeleteOds.ExpFileName, AUTH_ACCESS_WRITE);

   EraseCount = 0;
   while (VMSok (status = sys$erase (&DeleteOds.Fab, 0, 0)))
   {
      EraseCount++;
      if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
         WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV,
                    "!UL sys$erase() !&S", EraseCount, status);
   }
   if (status == RMS$_FNF && EraseCount) status = SS$_NORMAL;

   AuthAccessEnable (rqptr, 0, 0);

   if (VMSnok (status) && DeleteOds.Fab.fab$l_stv)
      status = DeleteOds.Fab.fab$l_stv;

   DeleteStatus = status;

   if (WATCHING (rqptr, WATCH_WEBDAV))
      if (VMSok (status))
         WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "DELETED !AZ",
                    DeleteOds.ExpFileName);
      else
         WatchThis (WATCHITM(rqptr), WATCH_WEBDAV, "DELETE !AZ !&S",
                    DeleteOds.ExpFileName, status);

   if (tkptr->DeleteData.DelPhase == WEBDAV_DELETE_SINGLE_FILE ||
       tkptr->DeleteData.DelPhase == WEBDAV_DELETE_TREE_FILES)
   {
      /* only need this expense when WATCHing */
      if (WATCHING (rqptr, WATCH_WEBDAV))
      {
         if (VMSok (status))
            tkptr->DeleteData.FileCount++;
         else
            tkptr->DeleteData.FileFailCount++;
      }
   }
   else
   if (tkptr->DeleteData.DelPhase == WEBDAV_DELETE_TREE_DIRS ||
       tkptr->DeleteData.DelPhase == WEBDAV_DELETE_TREE_PARENT)
   {
      if (VMSok (status))
      {
         tkptr->DeleteData.DirCount++;
         tkptr->DeleteData.TreeDirCount++;
      }
      else
      if (status == SS$_DIRNOTEMPTY)
         tkptr->DeleteData.TreeDirNotEmptyCount++;
      else
         tkptr->DeleteData.DirFailCount++;
   }

   /* if not the directory file then delete metadata now */
   if (!IsDir && VMSok (DeleteStatus)) DavDeleteMetaErase (mtaptr);

   /*************/
   /* finish up */
   /*************/

   if (VMSnok (DeleteStatus) && DeleteStatus != SS$_DIRNOTEMPTY)
   {
      if (tkptr->ToDepth == WEBDAV_DEPTH_INFINITY)
         DavWebMultiStatus (rqptr, DeleteStatus, DeleteOds.ExpFileName);
      else
         DavWebResponse (rqptr, 0, DeleteStatus, NULL, FI_LI);
   }

   OdsParseRelease (&DeleteOds);

   DavWebDequeue (&tkptr->MetaData.DlmData);

   if (tkptr->DeleteData.DelPhase == WEBDAV_DELETE_SINGLE_FILE)
   {
      DavDeleteEnd (rqptr);
      return;
   }

   SysDclAst (DavDeleteSearch, rqptr);
}

/*****************************************************************************/
/*
Remove the metadata file.  The metadata read file name must already have been
initialised using DavMetaName() before calling.
*/ 
 
int DavDeleteMetaErase (WEBDAV_META *mtaptr)

{
   int  status,
        EraseCount;
   ODS_STRUCT  DeleteOds;
   REQUEST_STRUCT  *rqptr;
   WEBDAV_TASK  *tkptr;

   /*********/
   /* begin */
   /*********/

#if WATCH_MOD
   HttpdCheckPriv (FI_LI);
#endif /* WATCH_MOD */

   tkptr = mtaptr->TaskPtr;
   rqptr = tkptr->RequestPtr;

   if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV,
                 "DavDeleteMetaErase() !&Z", mtaptr->ReadMetaName);

   OdsStructInit (&DeleteOds, true);
   OdsParse (&DeleteOds, mtaptr->ReadMetaName, mtaptr->ReadMetaNameLength,
             NULL, 0, NAM$M_SYNCHK, NULL, rqptr);
 
   /* delete any multiples */
   if (VMSok (status = DeleteOds.Fab.fab$l_sts))
   {
      DeleteOds.NamVersionPtr[0] = '\0'; 

      sys$setprv (1, &SysPrvMask, 0, 0);

      EraseCount = 0;
      while (VMSok (status = sys$erase (&DeleteOds.Fab, 0, 0)))
         EraseCount++;
      if (status == RMS$_FNF && EraseCount) status = SS$_NORMAL;

      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 (VMSok(status))
         tkptr->DeleteData.MetaCount++;
      else
      if (status != RMS$_FNF && status != RMS$_DNF)
      {
         tkptr->DeleteData.MetaFailCount++;
         ErrorNoticed (rqptr, status, NULL, FI_LI);
      }
   }

   return (status);
}

/*****************************************************************************/
/*
Copies the request file specification into the WebDAV task search
specification, substituting the supplied wildcard specification according to
whether it is a directory or individual file, and then parses the resulting
search specification.  Returns a VMS status value.
*/ 

int DavDeleteParse
(
REQUEST_STRUCT *rqptr,
BOOL IsDirectory,
char *WildSpec
)
{
   int  status;
   char  *cptr, *czptr, *sptr, *zptr;
   WEBDAV_TASK  *tkptr;

   /*********/
   /* begin */
   /*********/

   if (WATCHMOD (rqptr, WATCH_MOD_WEBDAV))
      WatchThis (WATCHITM(rqptr), WATCH_MOD_WEBDAV,
                 "DavDeleteParse() !&Z !&B !&Z",
                 rqptr->ParseOds.NamDevicePtr, IsDirectory, WildSpec);

   tkptr = rqptr->WebDavTaskPtr;

   zptr = (sptr = tkptr->SearchSpec) + sizeof(tkptr->SearchSpec)-1;

   if (IsDirectory)
      czptr = rqptr->ParseOds.NamNamePtr - 1;
   else
      czptr = rqptr->ParseOds.NamVersionPtr;
   for (cptr = rqptr->ParseOds.NamDevicePtr;
        cptr < czptr && sptr < zptr;
        *sptr++ = *cptr++);
   for (cptr = WildSpec; *cptr && sptr < zptr; *sptr++ = *cptr++);
   if (sptr >= zptr) return (SS$_RESULTOVF);
   tkptr->SearchSpecLength = sptr - tkptr->SearchSpec;
   *sptr = '\0';

   sys$setprv (1, &SysPrvMask, 0, 0);
   OdsParse (&tkptr->SearchOds, tkptr->SearchSpec, tkptr->SearchSpecLength,
             NULL, 0, 0, NULL, rqptr);
   sys$setprv (0, &SysPrvMask, 0, 0);

   if (VMSnok (status = tkptr->SearchOds.Fab.fab$l_sts)) return (status);

   return (SS$_NORMAL);
}

/*****************************************************************************/