KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > caucho > servlets > webdav > WebDavServlet


1 /*
2  * Copyright (c) 1998-2006 Caucho Technology -- all rights reserved
3  *
4  * This file is part of Resin(R) Open Source
5  *
6  * Each copy or derived work must preserve the copyright notice and this
7  * notice unmodified.
8  *
9  * Resin Open Source is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * Resin Open Source is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE, or any warranty
17  * of NON-INFRINGEMENT. See the GNU General Public License for more
18  * details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with Resin Open Source; if not, write to the
22  * Free SoftwareFoundation, Inc.
23  * 59 Temple Place, Suite 330
24  * Boston, MA 02111-1307 USA
25  *
26  * @author Scott Ferguson
27  */

28
29 package com.caucho.servlets.webdav;
30
31 import com.caucho.log.Log;
32 import com.caucho.server.webapp.Application;
33 import com.caucho.util.CharBuffer;
34 import com.caucho.util.HTTPUtil;
35 import com.caucho.util.QDate;
36 import com.caucho.vfs.Path;
37 import com.caucho.vfs.ReadStream;
38 import com.caucho.vfs.Vfs;
39 import com.caucho.vfs.WriteStream;
40 import com.caucho.xml.XmlParser;
41
42 import org.xml.sax.Attributes JavaDoc;
43 import org.xml.sax.SAXException JavaDoc;
44
45 import javax.naming.Context JavaDoc;
46 import javax.naming.InitialContext JavaDoc;
47 import javax.servlet.GenericServlet JavaDoc;
48 import javax.servlet.ServletContext JavaDoc;
49 import javax.servlet.ServletException JavaDoc;
50 import javax.servlet.ServletRequest JavaDoc;
51 import javax.servlet.ServletResponse JavaDoc;
52 import javax.servlet.http.HttpServletRequest JavaDoc;
53 import javax.servlet.http.HttpServletResponse JavaDoc;
54 import java.io.IOException JavaDoc;
55 import java.io.InputStream JavaDoc;
56 import java.io.OutputStream JavaDoc;
57 import java.util.ArrayList JavaDoc;
58 import java.util.Collections JavaDoc;
59 import java.util.HashMap JavaDoc;
60 import java.util.Iterator JavaDoc;
61 import java.util.logging.Level JavaDoc;
62 import java.util.logging.Logger JavaDoc;
63
64 /**
65  * Serves the WebDAV protocol. The underlying AbstractPath controls
66  * the actual files served and modified. The default AbstractPath
67  * just uses getRealPath from the current ServletContext.
68  *
69  * <p>More sophisticated users can customize AbstractPath to provide their
70  * own WebDAV view for their objects, much like the Linux /proc
71  * filesystem provides a view to Linux kernel modules.
72  *
73  * <pre>
74  * &lt;resource-ref res-ref-name='resin/webdav'>
75  * &lt;class-name>test.foo.MyDataSource&lt;/class-name>
76  * &lt;init-param my-foo='bar'/>
77  * &lt;/resource-ref>
78  *
79  * &lt;servlet-mapping url-pattern='/webdav/*'
80  * servlet-name='com.caucho.http.webdav.WebDavServlet'>
81  * &lt;init-param enable='write'/>
82  * &lt;init-param path-source='resin/webdav'/>
83  * &lt;/servlet-mapping>
84  * </pre>
85  */

86 public class WebDavServlet extends GenericServlet JavaDoc {
87   private static final Logger JavaDoc log = Log.open(WebDavServlet.class);
88   
89   private QDate _calendar = new QDate();
90
91   private boolean _enable = false;
92   private boolean _enableWrite = false;
93
94   private boolean _addCrLf = false;
95
96   private String JavaDoc _user;
97   private String JavaDoc _role;
98   private boolean _needsSecure;
99   private AbstractPath _path;
100   private String JavaDoc _root;
101
102   /**
103    * Sets the enable value.
104    */

105   public void setEnable(String JavaDoc enable)
106   {
107
108     if (enable == null || enable.equals(""))
109       return;
110     else if (enable.equals("read"))
111       _enable = true;
112     else if (enable.equals("write") ||
113              enable.equals("all") ||
114              enable.equals("yes") ||
115              enable.equals("true")) {
116       _enable = true;
117       _enableWrite = true;
118     }
119   }
120
121   /**
122    * Sets the allowed role.
123    */

124   public void setRole(String JavaDoc role)
125   {
126     _role = role;
127   }
128
129   /**
130    * Sets the allowed user.
131    */

132   public void setUser(String JavaDoc user)
133   {
134     _user = user;
135   }
136
137   /**
138    * Set true for securted.
139    */

140   public void setSecure(boolean needsSecure)
141   {
142     _needsSecure = needsSecure;
143   }
144
145   /**
146    * Sets the path.
147    */

148   public void setPathSource(AbstractPath path)
149   {
150     _path = path;
151   }
152
153   /**
154    * Sets the root.
155    */

156   public void setRoot(String JavaDoc root)
157   {
158     _root = root;
159   }
160
161   /**
162    * Sets true if should add cr/lf
163    */

164   public void setCrLf(boolean addCrLf)
165   {
166     _addCrLf = addCrLf;
167   }
168
169   public void init()
170     throws ServletException JavaDoc
171   {
172     String JavaDoc enable = getInitParameter("enable");
173
174     if (enable != null)
175       setEnable(enable);
176     
177     String JavaDoc role = getInitParameter("role");
178     if (role != null)
179       setRole(role);
180     
181     if (_role == null)
182       _role = "webdav";
183     else if (_role.equals("*"))
184       _role = null;
185     
186     String JavaDoc user = getInitParameter("user");
187     if (user != null)
188       setUser(user);
189
190     String JavaDoc secure = getInitParameter("secure");
191     if (secure == null) {
192     }
193     else if ("false".equalsIgnoreCase(secure) ||
194          "no".equalsIgnoreCase(secure))
195       _needsSecure = false;
196     else
197       _needsSecure = true;
198
199     String JavaDoc pathSource = getInitParameter("path-source");
200     try {
201       if (pathSource != null) {
202         Context JavaDoc env = (Context JavaDoc) new InitialContext JavaDoc().lookup("java:comp/env");
203         _path = (AbstractPath) env.lookup(pathSource);
204       }
205     } catch (Exception JavaDoc e) {
206       log.log(Level.FINE, e.toString(), e);
207     }
208
209     try {
210       if (pathSource != null && _path == null) {
211         _path = (AbstractPath) new InitialContext JavaDoc().lookup(pathSource);
212       }
213     } catch (Exception JavaDoc e) {
214       throw new ServletException JavaDoc(e);
215     }
216
217     String JavaDoc root = getInitParameter("root");
218
219     if (_path != null) {
220     }
221     else if (_root != null) {
222       Path pwd = ((Application) getServletContext()).getAppDir();
223
224       _path = new FilePath(pwd.lookup(_root));
225     }
226     else if (root != null) {
227       Path pwd = ((Application) getServletContext()).getAppDir();
228
229       _path = new FilePath(pwd.lookup(root));
230     }
231     else
232       _path = new ApplicationPath();
233   }
234
235   /**
236    * Service the webdav request.
237    */

238   public void service(ServletRequest JavaDoc request, ServletResponse JavaDoc response)
239     throws ServletException JavaDoc, IOException JavaDoc
240   {
241     HttpServletRequest JavaDoc req = (HttpServletRequest JavaDoc) request;
242     HttpServletResponse JavaDoc res = (HttpServletResponse JavaDoc) response;
243
244     if (! _enable) {
245       res.sendError(res.SC_FORBIDDEN);
246       return;
247     }
248
249     if (_needsSecure && ! req.isSecure()) {
250       res.sendError(res.SC_FORBIDDEN);
251       return;
252     }
253
254     if (_role != null && ! req.isUserInRole(_role)) {
255       res.sendError(res.SC_FORBIDDEN);
256       return;
257     }
258
259     if (_user != null) {
260       java.security.Principal JavaDoc principal = req.getUserPrincipal();
261       if (principal == null) {
262         res.sendError(res.SC_FORBIDDEN);
263         return;
264       }
265       if (! principal.getName().equals(_user)) {
266         res.sendError(res.SC_FORBIDDEN);
267         return;
268       }
269     }
270     
271     ServletContext JavaDoc app = getServletContext();
272     String JavaDoc requestURI = req.getRequestURI();
273     String JavaDoc pathInfo = req.getPathInfo();
274     
275     String JavaDoc depthString = req.getHeader("Depth");
276     int depth = Integer.MAX_VALUE;
277
278     OutputStream JavaDoc os = res.getOutputStream();
279     WriteStream out = Vfs.openWrite(os);
280     out.setEncoding("UTF-8");
281
282     if (_addCrLf)
283       out.setNewlineString("\r\n");
284
285     try {
286       if ("0".equals(depthString))
287     depth = 0;
288       else if ("1".equals(depthString))
289     depth = 1;
290
291       if (req.getMethod().equals("OPTIONS")) {
292     res.setHeader("DAV", "1");
293
294     res.setHeader("MS-Author-Via", "DAV");
295     if (_enableWrite)
296       res.setHeader("Allow", "OPTIONS, PROPFIND, GET, HEAD, PUT, MKCOL, DELETE, COPY, MOVE, PROPPATCH");
297     else if (_enable)
298       res.setHeader("Allow", "OPTIONS, PROPFIND, GET, HEAD");
299       }
300       else if (req.getMethod().equals("PROPFIND")) {
301     handlePropfind(req, res, out, depth);
302       }
303       else if (req.getMethod().equals("GET") ||
304            req.getMethod().equals("HEAD")) {
305     handleGet(req, res, out);
306       }
307       else if (req.getMethod().equals("PUT") && _enableWrite) {
308     handlePut(req, res, out);
309       }
310       else if (req.getMethod().equals("MKCOL") && _enableWrite) {
311     handleMkcol(req, res, out);
312       }
313       else if (req.getMethod().equals("DELETE") && _enableWrite) {
314     handleDelete(req, res, out);
315       }
316       else if (req.getMethod().equals("COPY") && _enableWrite) {
317     handleCopy(req, res, out, depth);
318       }
319       else if (req.getMethod().equals("MOVE") && _enableWrite) {
320     handleMove(req, res, out);
321       }
322       else if (req.getMethod().equals("PROPPATCH") && _enableWrite) {
323     handleProppatch(req, res, out, depth);
324       }
325       else if (! _enableWrite &&
326            "PUT".equals(req.getMethod()) ||
327            "MKCOL".equals(req.getMethod()) ||
328            "DELETE".equals(req.getMethod()) ||
329            "COPY".equals(req.getMethod()) ||
330            "MOVE".equals(req.getMethod()) ||
331            "PROPPATCH".equals(req.getMethod())) {
332     res.sendError(res.SC_FORBIDDEN);
333       }
334       else {
335     res.sendError(res.SC_NOT_IMPLEMENTED, "Method not implemented");
336       }
337     } finally {
338       out.close();
339     }
340   }
341
342   private void handlePropfind(HttpServletRequest JavaDoc req,
343                               HttpServletResponse JavaDoc res,
344                   WriteStream out,
345                               int depth)
346     throws ServletException JavaDoc, IOException JavaDoc
347   {
348     InputStream JavaDoc is = req.getInputStream();
349     PropfindHandler handler = new PropfindHandler();
350     XmlParser parser = new XmlParser();
351     parser.setContentHandler(handler);
352     
353     try {
354       parser.parse(is);
355     } catch (SAXException JavaDoc e) {
356       sendError(res, out, res.SC_BAD_REQUEST, "Bad Request for PROPFIND",
357                 String.valueOf(e));
358       return;
359     }
360
361     Application app = (Application) getServletContext();
362     Path appDir = app.getAppDir();
363
364     String JavaDoc pathInfo = req.getPathInfo();
365     String JavaDoc uriPwd = app.getContextPath() + req.getServletPath();
366     
367     if (pathInfo == null)
368       pathInfo = "/";
369     else
370       uriPwd = uriPwd + pathInfo;
371
372     if (_path.isDirectory(pathInfo, req, app) && ! uriPwd.endsWith("/"))
373       uriPwd = uriPwd + "/";
374
375     ServletContext JavaDoc rootApp = app.getContext("/");
376
377     ArrayList JavaDoc<AttributeName> properties = handler.getProperties();
378     boolean isPropname = handler.isPropname();
379
380     if (properties.size() == 0)
381       addAllProperties(properties, pathInfo, req, app);
382
383     startMultistatus(res, out);
384
385     printPathProperties(out, req, app, uriPwd, pathInfo,
386                         properties, isPropname, depth);
387     
388     out.println("</D:multistatus>");
389   }
390
391   /**
392    * Proppatch sets properties. This implementation does not allow
393    * any property setting.
394    */

395   private void handleProppatch(HttpServletRequest JavaDoc req,
396                                HttpServletResponse JavaDoc res,
397                    WriteStream out,
398                                int depth)
399     throws ServletException JavaDoc, IOException JavaDoc
400   {
401     InputStream JavaDoc is = req.getInputStream();
402     ProppatchHandler handler = new ProppatchHandler();
403     XmlParser parser = new XmlParser();
404     parser.setContentHandler(handler);
405     
406     try {
407       parser.parse(is);
408     } catch (SAXException JavaDoc e) {
409       sendError(res, out, res.SC_BAD_REQUEST, "Bad Request for PROPPATCH",
410                 "Bad Request: " + e);
411       return;
412     }
413     
414     Application app = (Application) getServletContext();
415     Path appDir = app.getAppDir();
416
417     String JavaDoc pathInfo = req.getPathInfo();
418     String JavaDoc uriPwd = app.getContextPath() + req.getServletPath();
419     
420     if (pathInfo == null)
421       pathInfo = "/";
422     else
423       uriPwd = uriPwd + pathInfo;
424
425     if (_path.isDirectory(pathInfo, req, app) && ! uriPwd.endsWith("/"))
426       uriPwd = uriPwd + "/";
427
428     ArrayList JavaDoc forbidden = new ArrayList JavaDoc();
429     
430     startMultistatus(res, out);
431
432     out.println("<D:response>");
433     out.println("<D:href>" + escapeXml(uriPwd) + "</D:href>");
434
435     ArrayList JavaDoc properties = new ArrayList JavaDoc();
436     ArrayList JavaDoc<ProppatchCommand> commands = handler.getCommands();
437
438     for (int i = 0; i < commands.size(); i++) {
439       ProppatchCommand command = commands.get(i);
440       int code = command.getCode();
441       AttributeName name = command.getName();
442       String JavaDoc value = command.getValue();
443       int status;
444
445       out.println("<D:propstat><D:prop><" + name.getName() + " xmlns:" +
446                   name.getPrefix() + "=\"" + name.getNamespace() + "\"/>");
447
448       if (code == ProppatchCommand.SET) {
449         _path.setAttribute(name, value, pathInfo, req, app);
450
451         out.println("<D:status>HTTP/1.1 200 OK</D:status>");
452       }
453       else if (code == ProppatchCommand.REMOVE) {
454         _path.removeAttribute(name, pathInfo, req, app);
455
456         out.println("<D:status>HTTP/1.1 200 OK</D:status>");
457       }
458       else
459         out.println("<D:status>HTTP/1.1 424 Failed</D:status>");
460
461       out.println("</D:prop></D:propstat>");
462     }
463
464     out.println("</D:response>");
465
466     out.println("</D:multistatus>");
467   }
468   
469   private void handlePut(HttpServletRequest JavaDoc req,
470                          HttpServletResponse JavaDoc res,
471              WriteStream out)
472     throws ServletException JavaDoc, IOException JavaDoc
473   {
474     ServletContext JavaDoc app = getServletContext();
475
476     String JavaDoc pathInfo = req.getPathInfo();
477     if (pathInfo == null)
478       pathInfo = "/";
479
480     if (! _path.isDirectory(getParent(pathInfo), req, app)) {
481       sendError(res, out, 409, "Conflict", "PUT requires a parent collection");
482       return;
483     }
484     else if (! _path.exists(pathInfo, req, app))
485       res.setStatus(201, "Created");
486     else
487       res.setStatus(204, "No Content");
488     
489     OutputStream JavaDoc os;
490
491     try {
492       os = _path.openWrite(pathInfo, req, app);
493     } catch (IOException JavaDoc e) {
494       log.log(Level.FINE, e.toString(), e);
495
496       sendError(res, out, 403, "Forbidden", "PUT forbidden");
497       return;
498     }
499     
500     WriteStream ws = Vfs.openWrite(os);
501     Path path =ws.getPath();
502     try {
503       InputStream JavaDoc is = req.getInputStream();
504       ws.writeStream(is);
505     } finally {
506       ws.close();
507     }
508   }
509
510   /**
511    * Creates a directory.
512    */

513   private void handleMkcol(HttpServletRequest JavaDoc req,
514                            HttpServletResponse JavaDoc res,
515                WriteStream out)
516     throws ServletException JavaDoc, IOException JavaDoc
517   {
518     res.setContentType("text/xml; charset=\"utf-8\"");
519     
520     ServletContext JavaDoc app = getServletContext();
521
522     String JavaDoc pathInfo = req.getPathInfo();
523     if (pathInfo == null)
524       pathInfo = "/";
525
526     if (_path.exists(pathInfo, req, app)) {
527       res.sendError(res.SC_METHOD_NOT_ALLOWED, "Collection already exists");
528       return;
529     }
530
531     if (! _path.isDirectory(getParent(pathInfo), req, app)) {
532       res.sendError(res.SC_CONFLICT, "MKCOL needs parent collection");
533       return;
534     }
535
536     InputStream JavaDoc is = req.getInputStream();
537     int ch = is.read();
538
539     if (ch >= 0) {
540       res.sendError(res.SC_UNSUPPORTED_MEDIA_TYPE, "MKCOL doesn't understand content-type");
541       return;
542     }
543
544     if (! _path.mkdir(pathInfo, req, app)) {
545       res.sendError(res.SC_FORBIDDEN, "MKCOL forbidden");
546       return;
547     }
548
549     res.setHeader("Location", req.getRequestURI());
550     sendError(res, out, res.SC_CREATED, null,
551               "Created collection " +
552               HTTPUtil.encodeString(req.getRequestURI()));
553   }
554
555   private void addAllProperties(ArrayList JavaDoc<AttributeName> properties,
556                 String JavaDoc pathInfo,
557                                 HttpServletRequest JavaDoc req,
558                 Application app)
559     throws IOException JavaDoc, ServletException JavaDoc
560   {
561     properties.add(new AttributeName("DAV:", "resourcetype", "D:resourcetype"));
562     properties.add(new AttributeName("DAV:", "getcontenttype", "D:getcontenttype"));
563     properties.add(new AttributeName("DAV:", "getcontentlength", "D:getcontentlength"));
564     properties.add(new AttributeName("DAV:", "creationdate", "D:creationdate"));
565     properties.add(new AttributeName("DAV:", "getlastmodified", "D:getlastmodified"));
566
567     Iterator JavaDoc<AttributeName> iter = _path.getAttributeNames(pathInfo, req, app);
568     while (iter.hasNext()) {
569       AttributeName name = iter.next();
570
571       if (! properties.contains(name))
572         properties.add(name);
573     }
574   }
575
576   private void printPathProperties(WriteStream out,
577                                    HttpServletRequest JavaDoc req,
578                                    ServletContext JavaDoc app,
579                                    String JavaDoc uri, String JavaDoc pathInfo,
580                                    ArrayList JavaDoc<AttributeName> properties,
581                                    boolean isPropname, int depth)
582     throws IOException JavaDoc, ServletException JavaDoc
583   {
584     out.println("<D:response>");
585     out.print("<D:href>");
586     out.print(escapeXml(uri));
587     out.println("</D:href>");
588
589     if (! _path.exists(pathInfo, req, app)) {
590       out.println("<D:propstat>");
591       out.println("<D:status>HTTP/1.1 404 Not Found</D:status>");
592       out.println("</D:propstat>");
593       out.println("</D:response>");
594       return;
595     }
596       
597     ArrayList JavaDoc<AttributeName> unknownProperties = new ArrayList JavaDoc<AttributeName>();
598       
599     out.println("<D:propstat>");
600
601     out.println("<D:prop>");
602
603     boolean isDirectory = _path.isDirectory(pathInfo, req, app);
604
605     for (int j = 0; j < properties.size(); j++) {
606       AttributeName prop = properties.get(j);
607       String JavaDoc localName = prop.getLocal();
608       String JavaDoc propUri = prop.getNamespace();
609       String JavaDoc qName = prop.getName();
610       String JavaDoc prefix = prop.getPrefix();
611
612       if (isPropname) {
613         if (propUri.equals("DAV:"))
614           out.println("<D:" + localName + "/>");
615         else {
616           String JavaDoc nsPrefix;
617
618           if (prefix.equals("D")) {
619             prefix = "caucho-D";
620             qName = "caucho-D:" + localName;
621           }
622
623           if (prefix.equals(""))
624             nsPrefix = "xmlns";
625           else
626             nsPrefix = "xmlns:" + prefix;
627           
628           out.println("<" + qName + " " + nsPrefix + "=\"" +
629                       propUri + "\"/>");
630         }
631         continue;
632       }
633
634       String JavaDoc value = _path.getAttribute(prop, pathInfo, req, app);
635       if (value != null) {
636         String JavaDoc nsPrefix;
637
638         if (prefix.equals("D")) {
639           prefix = "caucho-D";
640           qName = "caucho-D:" + localName;
641         }
642
643         if (prefix.equals(""))
644           nsPrefix = "xmlns";
645         else
646           nsPrefix = "xmlns:" + prefix;
647           
648         out.print("<" + qName + " " + nsPrefix + "=\"" +
649                   propUri + "\">");
650         out.print(value);
651         out.println("</" + prop.getName() + ">");
652         continue;
653       }
654
655       if (! propUri.equals("DAV:")) {
656         unknownProperties.add(prop);
657       }
658       else if (localName.equals("resourcetype")) {
659         if (isDirectory) {
660           out.print("<D:resourcetype>");
661           out.print("<D:collection/>");
662           out.println("</D:resourcetype>");
663         }
664         else {
665           out.println("<D:resourcetype/>");
666         }
667       }
668       else if (localName.equals("getcontentlength")) {
669         out.print("<D:getcontentlength>");
670         out.print(_path.getLength(pathInfo, req, app));
671         out.println("</D:getcontentlength>");
672       }
673       else if (localName.equals("getlastmodified")) {
674         out.print("<D:getlastmodified>");
675         out.print(_calendar.formatGMT(_path.getLastModified(pathInfo, req, app)));
676         out.println("</D:getlastmodified>");
677       }
678       else if (localName.equals("creationdate")) {
679         out.print("<D:creationdate>");
680
681         long time = _path.getLastModified(pathInfo, req, app);
682         
683         out.print(_calendar.formatGMT(time, "%Y-%m-%dT%H:%M:%SZ"));
684
685         out.println("</D:creationdate>");
686       }
687       else if (localName.equals("displayname")) {
688         out.print("<D:displayname>");
689
690         String JavaDoc name = pathInfo;
691         if (name.endsWith("/"))
692           name = name.substring(0, name.length() - 1);
693         int p = pathInfo.lastIndexOf('/');
694         if (p > 0 && p < pathInfo.length())
695           name = pathInfo.substring(p + 1);
696
697         out.print(escapeXml(name));
698         
699         out.println("</D:displayname>");
700       }
701       else if (localName.equals("getcontenttype")) {
702         String JavaDoc mimeType = app.getMimeType(uri);
703
704         if (mimeType != null) {
705           out.print("<D:getcontenttype>");
706           out.print(mimeType);
707           out.println("</D:getcontenttype>");
708         }
709         else {
710           out.println("<D:getcontenttype/>");
711         }
712       }
713       else
714         unknownProperties.add(prop);
715     }
716
717     out.println("</D:prop>");
718     out.println("<D:status>HTTP/1.1 200 OK</D:status>");
719     out.println("</D:propstat>");
720
721     if (unknownProperties.size() != 0) {
722       out.println("<D:propstat>");
723       out.println("<D:prop>");
724         
725       for (int j = 0; j < unknownProperties.size(); j++) {
726         AttributeName prop = (AttributeName) unknownProperties.get(j);
727
728         if (prop.getNamespace().equals("DAV:"))
729           out.println("<D:" + prop.getLocal() + "/>");
730         else {
731           String JavaDoc nsPrefix;
732           String JavaDoc prefix = prop.getPrefix();
733           String JavaDoc qName = prop.getName();
734
735           if (prefix.equals("D")) {
736             prefix = "caucho-D";
737             qName = "caucho-D:" + prop.getLocal();
738           }
739
740           if (prefix.equals(""))
741             nsPrefix = "xmlns";
742           else
743             nsPrefix = "xmlns:" + prefix;
744           
745           out.println("<" + qName + " " + nsPrefix + "=\"" +
746                       prop.getNamespace() + "\"/>");
747         }
748       }
749       out.println("</D:prop>");
750       out.println("<D:status>HTTP/1.1 404 Not Found</D:status>");
751       out.println("</D:propstat>");
752     }
753       
754     out.println("</D:response>");
755
756     if (depth > 0 && _path.isDirectory(pathInfo, req, app)) {
757       String JavaDoc []list = _path.list(pathInfo, req, app);
758       ArrayList JavaDoc<String JavaDoc> sortedList = new ArrayList JavaDoc<String JavaDoc>();
759
760       for (int i = 0; i < list.length; i++)
761         sortedList.add(list[i]);
762       Collections.sort(sortedList);
763       
764       for (int i = 0; i < sortedList.size(); i++) {
765         String JavaDoc filename = sortedList.get(i);
766         
767         String JavaDoc suburi;
768         if (uri.endsWith("/"))
769           suburi = uri + filename;
770         else
771           suburi = uri + "/" + filename;
772
773         String JavaDoc subpath;
774         if (pathInfo.endsWith("/"))
775           subpath = pathInfo + filename;
776         else
777           subpath = pathInfo + "/" + filename;
778
779         if (_path.isDirectory(subpath, req, app))
780           suburi = suburi + '/';
781
782         if (! _path.canRead(subpath, req, app) || filename.startsWith(".") ||
783             filename.equals("CVS") || filename.endsWith("~"))
784           continue;
785
786         printPathProperties(out, req, app, suburi, subpath,
787                             properties, isPropname, depth - 1);
788       }
789     }
790   }
791   
792   private void handleDelete(HttpServletRequest JavaDoc req,
793                             HttpServletResponse JavaDoc res,
794                 WriteStream out)
795     throws ServletException JavaDoc, IOException JavaDoc
796   {
797     ServletContext JavaDoc app = getServletContext();
798
799     String JavaDoc pathInfo = req.getPathInfo();
800     if (pathInfo == null)
801       pathInfo = "/";
802
803     String JavaDoc uri = req.getContextPath() + pathInfo;
804
805     if (_path.isFile(pathInfo, req, app)) {
806       if (! _path.remove(pathInfo, req, app))
807         res.sendError(403, "Forbidden");
808       else
809         res.setStatus(204, "No Content");
810     }
811     else if (_path.isDirectory(pathInfo, req, app)) {
812       if (deleteRecursive(req, res, out, uri, pathInfo, false)) {
813         out.println("<D:status>HTTP/1.0 403 Forbidden</D:status>");
814         out.println("</D:response>");
815         out.println("</D:multistatus>");
816       }
817       else
818         res.setStatus(204, "No Content");
819     }
820     else {
821       res.sendError(res.SC_NOT_FOUND);
822     }
823   }
824
825   private boolean deleteRecursive(HttpServletRequest JavaDoc req,
826                                   HttpServletResponse JavaDoc res,
827                                   WriteStream out,
828                                   String JavaDoc uri, String JavaDoc pathInfo,
829                                   boolean hasError)
830     throws IOException JavaDoc
831   {
832     ServletContext JavaDoc app = getServletContext();
833     boolean newError = false;
834     
835     if (_path.isDirectory(pathInfo, req, app)) {
836       String JavaDoc []list = _path.list(pathInfo, req, app);
837       for (int i = 0; i < list.length; i++) {
838         try {
839           String JavaDoc suburi = lookup(uri, list[i]);
840           String JavaDoc subpath = lookup(pathInfo, list[i]);
841             
842           hasError = deleteRecursive(req, res, out, suburi, subpath, hasError);
843         } catch (IOException JavaDoc e) {
844           log.log(Level.WARNING, e.toString(), e);
845         }
846       }
847
848       if (! _path.rmdir(pathInfo, req, app))
849         newError = true;
850     }
851     else if (! _path.remove(pathInfo, req, app))
852       newError = true;
853
854     if (newError) {
855       if (! hasError) {
856         startMultistatus(res, out);
857         out.println("<D:response>");
858       }
859       
860       out.println("<D:href>" + escapeXml(uri) + "</D:href>");
861
862       hasError = true;
863     }
864
865     return hasError;
866   }
867   
868   private void handleCopy(HttpServletRequest JavaDoc req,
869                           HttpServletResponse JavaDoc res,
870               WriteStream out,
871                           int depth)
872     throws ServletException JavaDoc, IOException JavaDoc
873   {
874     ServletContext JavaDoc app = getServletContext();
875     String JavaDoc pathInfo = req.getPathInfo();
876     if (pathInfo == null)
877       pathInfo = "/";
878
879     if (depth == 1)
880       depth = Integer.MAX_VALUE;
881
882     if (! _path.exists(pathInfo, req, app)) {
883       res.sendError(res.SC_NOT_FOUND);
884       return;
885     }
886
887     String JavaDoc destURI = getDestination(req);
888     if (destURI == null) {
889       res.sendError(403, "Forbidden");
890       return;
891     }
892
893     String JavaDoc prefix = req.getContextPath();
894     if (req.getServletPath() != null)
895       prefix += req.getServletPath();
896     if (! destURI.startsWith(prefix)) {
897       res.sendError(403, "Forbidden");
898       return;
899     }
900
901     String JavaDoc destPath = destURI.substring(prefix.length());
902
903     if (destPath.equals(pathInfo)) {
904       res.sendError(403, "Forbidden");
905       return;
906     }
907     else if (destPath.startsWith(pathInfo) &&
908              (pathInfo.endsWith("/") || destPath.startsWith(pathInfo + '/'))) {
909       res.sendError(403, "Forbidden");
910       return;
911     }
912     else if (pathInfo.startsWith(destPath) &&
913              (destPath.endsWith("/") || pathInfo.startsWith(destPath + '/'))) {
914       res.sendError(403, "Forbidden");
915       return;
916     }
917
918     String JavaDoc overwrite = req.getHeader("Overwrite");
919     if (overwrite == null)
920       overwrite = "T";
921
922     if (! _path.exists(destPath, req, app)) {
923       res.setStatus(res.SC_CREATED);
924     }
925     else if (! overwrite.equals("F")) {
926       removeRecursive(destPath, req);
927       res.setStatus(204, "No Content");
928     }
929     else {
930       res.sendError(412, "Overwrite not allowed for COPY");
931       return;
932     }
933
934     if (! _path.exists(getParent(destPath), req, app)) {
935       res.sendError(409, "COPY needs parent of destination");
936       return;
937     }
938
939     if (_path.isFile(pathInfo, req, app)) {
940       OutputStream JavaDoc os = _path.openWrite(destPath, req, app);
941       WriteStream ws = Vfs.openWrite(os);
942       try {
943         InputStream JavaDoc is = _path.openRead(pathInfo, req, app);
944         try {
945           ws.writeStream(is);
946         } finally {
947           is.close();
948         }
949       } finally {
950         ws.close();
951       }
952       return;
953     }
954     else {
955       copyRecursive(pathInfo, destPath, depth, req);
956       return;
957     }
958   }
959
960   private void removeRecursive(String JavaDoc pathInfo, HttpServletRequest JavaDoc req)
961     throws IOException JavaDoc
962   {
963     ServletContext JavaDoc app = getServletContext();
964     
965     if (_path.isDirectory(pathInfo, req, app)) {
966       String JavaDoc []list = _path.list(pathInfo, req, app);
967
968       for (int i = 0; i < list.length; i++) {
969         try {
970           removeRecursive(lookup(pathInfo, list[i]), req);
971         } catch (IOException JavaDoc e) {
972           log.log(Level.WARNING, e.toString(), e);
973         }
974       }
975     }
976
977     _path.remove(pathInfo, req, app);
978   }
979
980   private void copyRecursive(String JavaDoc srcPath, String JavaDoc destPath, int depth,
981                              HttpServletRequest JavaDoc req)
982     throws IOException JavaDoc
983   {
984     ServletContext JavaDoc app = getServletContext();
985     
986     if (_path.isDirectory(srcPath, req, app)) {
987       _path.mkdir(destPath, req, app);
988
989       if (depth == 0)
990         return;
991       
992       String JavaDoc []list = _path.list(srcPath, req, app);
993       for (int i = 0; i < list.length; i++) {
994         try {
995           copyRecursive(lookup(srcPath, list[i]),
996                         lookup(destPath, list[i]), depth - 1,
997                         req);
998         } catch (IOException JavaDoc e) {
999           log.log(Level.WARNING, e.toString(), e);
1000        }
1001      }
1002    }
1003    else {
1004      OutputStream JavaDoc os = _path.openWrite(destPath, req, app);
1005      WriteStream ws = Vfs.openWrite(os);
1006      try {
1007        InputStream JavaDoc is = _path.openRead(srcPath, req, app);
1008        try {
1009          ws.writeStream(is);
1010        } finally {
1011          is.close();
1012        }
1013      } finally {
1014        ws.close();
1015      }
1016    }
1017  }
1018  
1019  private void handleMove(HttpServletRequest JavaDoc req,
1020                          HttpServletResponse JavaDoc res,
1021              WriteStream out)
1022    throws ServletException JavaDoc, IOException JavaDoc
1023  {
1024    ServletContext JavaDoc app = getServletContext();
1025    
1026    String JavaDoc pathInfo = req.getPathInfo();
1027    if (pathInfo == null)
1028      pathInfo = "/";
1029
1030    int depth = Integer.MAX_VALUE;
1031
1032    if (! _path.exists(pathInfo, req, app)) {
1033      res.sendError(res.SC_NOT_FOUND);
1034      return;
1035    }
1036
1037    String JavaDoc destURI = getDestination(req);
1038    if (destURI == null) {
1039      res.sendError(403, "Forbidden");
1040      return;
1041    }
1042
1043    String JavaDoc prefix = req.getContextPath();
1044    if (req.getServletPath() != null)
1045      prefix += req.getServletPath();
1046    if (! destURI.startsWith(prefix)) {
1047      res.sendError(403, "Forbidden");
1048      return;
1049    }
1050
1051    String JavaDoc destPath = destURI.substring(prefix.length());
1052
1053    if (destPath.equals(pathInfo)) {
1054      res.sendError(403, "Forbidden");
1055      return;
1056    }
1057    else if (destPath.startsWith(pathInfo) &&
1058             (pathInfo.endsWith("/") || destPath.startsWith(pathInfo + '/'))) {
1059      res.sendError(403, "Forbidden");
1060      return;
1061    }
1062    else if (pathInfo.startsWith(destPath) &&
1063             (destPath.endsWith("/") || pathInfo.startsWith(destPath + '/'))) {
1064      res.sendError(403, "Forbidden");
1065      return;
1066    }
1067
1068    String JavaDoc overwrite = req.getHeader("Overwrite");
1069    if (overwrite == null)
1070      overwrite = "T";
1071
1072    if (! _path.exists(destPath, req, app)) {
1073      res.setStatus(res.SC_CREATED);
1074    }
1075    else if (! overwrite.equals("F")) {
1076      removeRecursive(destPath, req);
1077      res.setStatus(204, "No Content");
1078    }
1079    else {
1080      res.sendError(412, "Overwrite not allowed for MOVE");
1081      return;
1082    }
1083
1084    if (! _path.exists(getParent(destPath), req, app)) {
1085      res.sendError(409, "MOVE needs parent of destination");
1086      return;
1087    }
1088
1089    if (_path.rename(pathInfo, destPath, req, app)) {
1090      // try renaming directly
1091
res.setStatus(204, "No Content");
1092    }
1093    else if (_path.isFile(pathInfo, req, app)) {
1094      HashMap JavaDoc<AttributeName,String JavaDoc> props = getProperties(pathInfo, req, app);
1095      OutputStream JavaDoc os = _path.openWrite(destPath, req, app);
1096      WriteStream ws = Vfs.openWrite(os);
1097      
1098      try {
1099        InputStream JavaDoc is = _path.openRead(pathInfo, req, app);
1100        try {
1101          ws.writeStream(is);
1102        } finally {
1103          is.close();
1104        }
1105      } finally {
1106        ws.close();
1107      }
1108      setProperties(props, destPath, req, app);
1109
1110      _path.remove(pathInfo, req, app);
1111    }
1112    else {
1113      moveRecursive(pathInfo, destPath, req);
1114      res.setStatus(204, "No Content");
1115    }
1116  }
1117
1118  private String JavaDoc getDestination(HttpServletRequest JavaDoc request)
1119  {
1120    String JavaDoc dest = request.getHeader("Destination");
1121
1122    dest = java.net.URLDecoder.decode(dest);
1123
1124    if (dest.startsWith("/"))
1125      return dest;
1126
1127    String JavaDoc prefix = request.getScheme() + "://";
1128    String JavaDoc host = request.getHeader("Host");
1129    if (host != null)
1130      prefix = prefix + host.toLowerCase();
1131
1132    if (dest.startsWith(prefix))
1133      return dest.substring(prefix.length());
1134    else
1135      return null;
1136  }
1137
1138  private void moveRecursive(String JavaDoc srcPath, String JavaDoc destPath,
1139                             HttpServletRequest JavaDoc req)
1140    throws IOException JavaDoc
1141  {
1142    ServletContext JavaDoc app = getServletContext();
1143    
1144    if (_path.isDirectory(srcPath, req, app)) {
1145      _path.mkdir(destPath, req, app);
1146      
1147      String JavaDoc []list = _path.list(srcPath, req, app);
1148      for (int i = 0; i < list.length; i++) {
1149        try {
1150          moveRecursive(lookup(srcPath, list[i]),
1151                        lookup(destPath, list[i]),
1152                        req);
1153        } catch (IOException JavaDoc e) {
1154          log.log(Level.WARNING, e.toString(), e);
1155        }
1156      }
1157      
1158      _path.remove(srcPath, req, app);
1159    }
1160    else {
1161      HashMap JavaDoc<AttributeName,String JavaDoc> props = getProperties(srcPath, req, app);
1162      OutputStream JavaDoc os = _path.openWrite(destPath, req, app);
1163      WriteStream rs = Vfs.openWrite(os);
1164      
1165      try {
1166        InputStream JavaDoc is = _path.openRead(srcPath, req, app);
1167        try {
1168          rs.writeStream(is);
1169        } finally {
1170          is.close();
1171        }
1172      } finally {
1173        rs.close();
1174        os.close();
1175      }
1176      
1177      setProperties(props, destPath, req, app);
1178      _path.remove(srcPath, req, app);
1179    }
1180  }
1181
1182  /**
1183   * Grabs all the properties from a path.
1184   */

1185  private HashMap JavaDoc<AttributeName,String JavaDoc> getProperties(String JavaDoc pathInfo,
1186                              HttpServletRequest JavaDoc req,
1187                              ServletContext JavaDoc app)
1188    throws IOException JavaDoc
1189  {
1190    HashMap JavaDoc<AttributeName,String JavaDoc> properties = null;
1191
1192    Iterator JavaDoc<AttributeName> iter = _path.getAttributeNames(pathInfo, req, app);
1193    while (iter.hasNext()) {
1194      AttributeName name = iter.next();
1195      String JavaDoc value = _path.getAttribute(name, pathInfo, req, app);
1196
1197      if (properties == null)
1198        properties = new HashMap JavaDoc<AttributeName,String JavaDoc>();
1199      
1200      properties.put(name, value);
1201    }
1202
1203    return properties;
1204  }
1205
1206  /**
1207   * Sets all the properties for a path.
1208   */

1209  private void setProperties(HashMap JavaDoc<AttributeName,String JavaDoc> map,
1210                             String JavaDoc pathInfo,
1211                             HttpServletRequest JavaDoc req,
1212                             ServletContext JavaDoc app)
1213    throws IOException JavaDoc
1214  {
1215    if (map == null)
1216      return;
1217
1218    Iterator JavaDoc<AttributeName> iter = map.keySet().iterator();
1219    while (iter.hasNext()) {
1220      AttributeName name = iter.next();
1221      
1222      String JavaDoc value = map.get(name);
1223
1224      _path.setAttribute(name, value, pathInfo, req, app);
1225    }
1226  }
1227  
1228  private void handleGet(HttpServletRequest JavaDoc req,
1229                         HttpServletResponse JavaDoc res,
1230             WriteStream out)
1231    throws ServletException JavaDoc, IOException JavaDoc
1232  {
1233    ServletContext JavaDoc app = getServletContext();
1234
1235    String JavaDoc pathInfo = req.getPathInfo();
1236    if (pathInfo == null)
1237      pathInfo = "/";
1238
1239    String JavaDoc mimeType = app.getMimeType(pathInfo);
1240    res.setContentType(mimeType);
1241
1242    if (! _path.isFile(pathInfo, req, app) ||
1243        ! _path.canRead(pathInfo, req, app)) {
1244      res.sendError(res.SC_NOT_FOUND);
1245      return;
1246    }
1247
1248    long length = _path.getLength(pathInfo, req, app);
1249    res.setContentLength((int) length);
1250
1251    if ("HTTP/1.1".equals(req.getProtocol())) {
1252      res.setDateHeader("Last-Modified", _path.getLastModified(pathInfo, req, app));
1253      res.setHeader("Cache-Control", "private");
1254    }
1255    
1256    if (req.getMethod().equals("HEAD"))
1257      return;
1258
1259    OutputStream JavaDoc os = res.getOutputStream();
1260    InputStream JavaDoc is = _path.openRead(pathInfo, req, app);
1261    ReadStream rs = Vfs.openRead(is);
1262    try {
1263      rs.writeToStream(os);
1264    } finally {
1265      rs.close();
1266    }
1267  }
1268
1269  protected void startMultistatus(HttpServletResponse JavaDoc res,
1270                  WriteStream out)
1271    throws IOException JavaDoc
1272  {
1273    res.setStatus(207, "Multistatus");
1274    res.setContentType("text/xml; charset=\"utf-8\"");
1275
1276    out.println("<?xml version=\"1.0\"?>");
1277    out.println("<D:multistatus xmlns:D=\"DAV:\">");
1278  }
1279  
1280  protected void sendError(HttpServletResponse JavaDoc res,
1281               WriteStream out,
1282                           int status, String JavaDoc statusText, String JavaDoc message)
1283    throws IOException JavaDoc
1284  {
1285    if (statusText == null)
1286      res.setStatus(status);
1287    else
1288      res.setStatus(status, statusText);
1289
1290    res.setContentType("text/html");
1291    
1292    if (statusText != null) {
1293      out.print("<title>");
1294      out.print(statusText);
1295      out.println("</title>");
1296      out.print("<h1>");
1297      out.print(statusText);
1298      out.println("</h1>");
1299      out.println(message);
1300    }
1301    else {
1302      out.print("<title>");
1303      out.print(message);
1304      out.println("</title>");
1305      out.print("<h1>");
1306      out.print(message);
1307      out.println("</h1>");
1308    }
1309  }
1310
1311  private void handleDirectory(HttpServletRequest JavaDoc req,
1312                               HttpServletResponse JavaDoc res,
1313                   WriteStream out,
1314                               String JavaDoc pathInfo)
1315    throws IOException JavaDoc, ServletException JavaDoc
1316  {
1317    ServletContext JavaDoc app = getServletContext();
1318    
1319    res.setContentType("text/html");
1320    
1321    out.println("<title>Directory of " + pathInfo + "</title>");
1322    out.println("<h1>Directory of " + pathInfo + "</h1>");
1323
1324    String JavaDoc []list = _path.list(pathInfo, req, app);
1325    for (int i = 0; i < list.length; i++) {
1326      out.println("<a HREF=\"" + list[i] + "\">" + list[i] + "</a><br>");
1327    }
1328  }
1329
1330  private String JavaDoc escapeXml(String JavaDoc data)
1331  {
1332    CharBuffer cb = CharBuffer.allocate();
1333    for (int i = 0; i < data.length(); i++) {
1334      char ch = data.charAt(i);
1335
1336      switch (ch) {
1337      case '<':
1338        cb.append("&lt;");
1339        break;
1340      case '>':
1341        cb.append("&gt;");
1342        break;
1343      case '&':
1344        cb.append("&amp;");
1345        break;
1346      default:
1347        cb.append(ch);
1348        break;
1349      }
1350    }
1351
1352    return cb.close();
1353  }
1354
1355  protected String JavaDoc getParent(String JavaDoc pathInfo)
1356  {
1357    int p = pathInfo.lastIndexOf('/', pathInfo.length() - 2);
1358
1359    if (p < 0)
1360      return "/";
1361    else
1362      return pathInfo.substring(0, p);
1363  }
1364
1365  protected String JavaDoc lookup(String JavaDoc parent, String JavaDoc child)
1366  {
1367    if (parent.endsWith("/"))
1368      return parent + child;
1369    else
1370      return parent + '/' + child;
1371  }
1372
1373  public void destroy()
1374  {
1375    _path.destroy();
1376  }
1377
1378  static class PropfindHandler extends org.xml.sax.helpers.DefaultHandler JavaDoc {
1379    ArrayList JavaDoc<AttributeName> properties = new ArrayList JavaDoc<AttributeName>();
1380    boolean inProp;
1381    boolean isPropname;
1382    
1383    ArrayList JavaDoc<AttributeName> getProperties()
1384    {
1385      return properties;
1386    }
1387
1388    boolean isPropname()
1389    {
1390      return isPropname;
1391    }
1392
1393    public void startElement (String JavaDoc uri, String JavaDoc localName,
1394                  String JavaDoc qName, Attributes JavaDoc attributes)
1395      throws SAXException JavaDoc
1396    {
1397      if (localName.equals("prop"))
1398        inProp = true;
1399      else if (localName.equals("propname"))
1400        isPropname = true;
1401      else if (inProp) {
1402        if (qName.indexOf(':') > 0 && uri.equals(""))
1403          throw new SAXException JavaDoc("illegal empty namespace");
1404
1405        properties.add(new AttributeName(uri, localName, qName));
1406      }
1407    }
1408
1409    public void endElement (String JavaDoc uri, String JavaDoc localName, String JavaDoc qName)
1410    throws SAXException JavaDoc
1411    {
1412      if (localName.equals("prop"))
1413        inProp = false;
1414    }
1415  }
1416
1417  static class ProppatchHandler extends org.xml.sax.helpers.DefaultHandler JavaDoc {
1418    ArrayList JavaDoc<ProppatchCommand> _commands = new ArrayList JavaDoc<ProppatchCommand>();
1419    boolean _inProp;
1420    boolean _inSet;
1421    boolean _inRemove;
1422    boolean _isPropname;
1423    AttributeName _attributeName;
1424    CharBuffer _value;
1425    
1426    boolean isPropname()
1427    {
1428      return _isPropname;
1429    }
1430
1431    ArrayList JavaDoc<ProppatchCommand> getCommands()
1432    {
1433      return _commands;
1434    }
1435
1436    public void startElement(String JavaDoc uri, String JavaDoc localName,
1437                             String JavaDoc qName, Attributes JavaDoc attributes)
1438      throws SAXException JavaDoc
1439    {
1440      if (localName.equals("set"))
1441        _inSet = true;
1442      else if (localName.equals("remove"))
1443        _inRemove = true;
1444      else if (localName.equals("prop"))
1445        _inProp = true;
1446      else if (localName.equals("propname"))
1447        _isPropname = true;
1448      else if (! _inProp) {
1449      }
1450      else if (_attributeName == null) {
1451        _attributeName = new AttributeName(uri, localName, qName);
1452        _value = CharBuffer.allocate();
1453      }
1454      else {
1455        int p = qName.indexOf(':');
1456
1457        if (p > 0)
1458          _value.append("<" + qName + " xmlns:" + qName.substring(p + 1) +
1459                       "=\"" + uri + "\">");
1460        else
1461          _value.append("<" + qName + " xmlns=\"" + uri + "\">");
1462      }
1463    }
1464
1465    public void characters(char []buffer, int offset, int length)
1466    {
1467      if (_value != null)
1468        _value.append(buffer, offset, length);
1469    }
1470
1471    public void endElement (String JavaDoc uri, String JavaDoc localName, String JavaDoc qName)
1472    throws SAXException JavaDoc
1473    {
1474      if (localName.equals("prop"))
1475        _inProp = false;
1476      else if (localName.equals("set"))
1477        _inSet = false;
1478      else if (localName.equals("remove"))
1479        _inRemove = false;
1480      else if (_attributeName == null) {
1481      }
1482      else if (localName.equals(_attributeName.getLocal()) &&
1483               uri.equals(_attributeName.getNamespace())) {
1484        if (_inSet) {
1485          _commands.add(new ProppatchCommand(ProppatchCommand.SET,
1486                         _attributeName,
1487                         _value.close()));
1488        }
1489        else if (_inRemove) {
1490          _commands.add(new ProppatchCommand(ProppatchCommand.REMOVE,
1491                         _attributeName,
1492                         _value.close()));
1493        }
1494    
1495        _value = null;
1496        _attributeName = null;
1497      }
1498      else {
1499        _value.append("</" + qName + ">");
1500      }
1501    }
1502  }
1503  
1504  static class ProppatchCommand {
1505    public static int SET = 0;
1506    public static int REMOVE = 1;
1507    public static int CHANGE = 2;
1508    
1509    private int _code;
1510    private AttributeName _name;
1511    private String JavaDoc _value;
1512
1513    ProppatchCommand(int code, AttributeName name, String JavaDoc value)
1514    {
1515      _code = code;
1516      _name = name;
1517      _value = value;
1518    }
1519
1520    int getCode()
1521    {
1522      return _code;
1523    }
1524
1525    AttributeName getName()
1526    {
1527      return _name;
1528    }
1529
1530    String JavaDoc getValue()
1531    {
1532      return _value;
1533    }
1534  }
1535}
1536
Popular Tags