001: /***************************************************************
002: * This file is part of the [fleXive](R) project.
003: *
004: * Copyright (c) 1999-2007
005: * UCS - unique computing solutions gmbh (http://www.ucs.at)
006: * All rights reserved
007: *
008: * The [fleXive](R) project is free software; you can redistribute
009: * it and/or modify it under the terms of the GNU General Public
010: * License as published by the Free Software Foundation;
011: * either version 2 of the License, or (at your option) any
012: * later version.
013: *
014: * The GNU General Public License can be found at
015: * http://www.gnu.org/copyleft/gpl.html.
016: * A copy is found in the textfile GPL.txt and important notices to the
017: * license from the author are found in LICENSE.txt distributed with
018: * these libraries.
019: *
020: * This library is distributed in the hope that it will be useful,
021: * but WITHOUT ANY WARRANTY; without even the implied warranty of
022: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
023: * GNU General Public License for more details.
024: *
025: * For further information about UCS - unique computing solutions gmbh,
026: * please see the company website: http://www.ucs.at
027: *
028: * For further information about [fleXive](R), please see the
029: * project website: http://www.flexive.org
030: *
031: *
032: * This copyright notice MUST APPEAR in all copies of the file!
033: ***************************************************************/package com.flexive.war.webdav;
034:
035: import javax.servlet.http.HttpServletRequest;
036: import javax.servlet.http.HttpServletResponse;
037: import java.io.IOException;
038:
039: /**
040: * Lock operation.
041: *
042: * @author Gregor Schober (gregor.schober@flexive.com), UCS - unique computing solutions gmbh (http://www.ucs.at)
043: * @version $Rev: 1 $
044: */
045: class OperationLock extends Operation {
046:
047: public OperationLock(HttpServletRequest req,
048: HttpServletResponse resp, boolean readonly) {
049: super (req, resp, readonly);
050: }
051:
052: void writeResponse() throws IOException {
053: if (readonly) {
054: response.sendError(FxWebDavStatus.SC_FORBIDDEN);
055: } else {
056: response.sendError(HttpServletResponse.SC_NOT_IMPLEMENTED);
057: }
058: }
059:
060: /**
061: * Repository of the locks put on single resources.
062: * <p/>
063: * Key : path <br>
064: * Value : LockInfo
065: */
066: //private Hashtable<String, LockInfo> resourceLocks = new Hashtable<String, LockInfo>();
067: /**
068: * Repository of the lock-null resources.
069: * <p/>
070: * Key : path of the collection containing the lock-null resource<br>
071: * Value : Vector of lock-null resource which are members of the
072: * collection. Each element of the Vector is the path associated with
073: * the lock-null resource.
074: */
075: //private Hashtable<String, Vector<String>> lockNullResources = new Hashtable<String, Vector<String>>();
076: /**
077: * Vector of the heritable locks.
078: * <p/>
079: * Key : path <br>
080: * Value : LockInfo
081: */
082: //private Vector<LockInfo> collectionLocks = new Vector<LockInfo>();
083: // Create a new lock.
084: //private static final int LOCK_CREATION = 0;
085: // Refresh lock
086: //private static final int LOCK_REFRESH = 1;
087: // Default lock timeout
088: //private static final int DEFAULT_TIMEOUT = 3600;
089: // Maximum lock timeout.
090: //private static final int MAX_TIMEOUT = 604800;
091: /*
092: **
093: * LOCK Method.
094: *
095: protected void doLock(HttpServletRequest req, HttpServletResponse resp)
096: throws ServletException, IOException {
097:
098: if (readOnly) {
099: resp.sendError(FxWebDavStatus.SC_FORBIDDEN);
100: return;
101: }
102:
103: if (isLocked(req)) {
104: resp.sendError(FxWebDavStatus.SC_LOCKED);
105: return;
106: }
107:
108: LockInfo lock = new LockInfo();
109:
110: // Parsing lock request
111:
112: // Parsing depth header
113:
114: String depthStr = req.getHeader("Depth");
115:
116: if (depthStr == null) {
117: lock.depth = INFINITY;
118: } else {
119: if (depthStr.equals("0")) {
120: lock.depth = 0;
121: } else {
122: lock.depth = INFINITY;
123: }
124: }
125:
126: // Parsing timeout header
127:
128: int lockDuration;
129: String lockDurationStr = req.getHeader("Timeout");
130: if (lockDurationStr == null) {
131: lockDuration = DEFAULT_TIMEOUT;
132: } else {
133: int commaPos = lockDurationStr.indexOf(",");
134: // If multiple timeouts, just use the first
135: if (commaPos != -1) {
136: lockDurationStr = lockDurationStr.substring(0, commaPos);
137: }
138: if (lockDurationStr.startsWith("Second-")) {
139: lockDuration = Integer.parseInt(lockDurationStr.substring(7));
140: } else {
141: if (lockDurationStr.equalsIgnoreCase("infinity")) {
142: lockDuration = MAX_TIMEOUT;
143: } else {
144: try {
145: lockDuration = Integer.parseInt(lockDurationStr);
146: } catch (NumberFormatException e) {
147: lockDuration = MAX_TIMEOUT;
148: }
149: }
150: }
151: if (lockDuration == 0) {
152: lockDuration = DEFAULT_TIMEOUT;
153: }
154: if (lockDuration > MAX_TIMEOUT) {
155: lockDuration = MAX_TIMEOUT;
156: }
157: }
158: lock.expiresAt = System.currentTimeMillis() + (lockDuration * 1000);
159:
160: int lockRequestType = LOCK_CREATION;
161:
162: Node lockInfoNode = null;
163:
164: DocumentBuilder documentBuilder = getDocumentBuilder();
165:
166: try {
167: Document document = documentBuilder.parse(new InputSource
168: (req.getInputStream()));
169:
170: // Get the root element of the document
171: lockInfoNode = document.getDocumentElement();
172: } catch (Exception e) {
173: lockRequestType = LOCK_REFRESH;
174: }
175:
176: if (lockInfoNode != null) {
177:
178: // Reading lock information
179:
180: NodeList childList = lockInfoNode.getChildNodes();
181: StringWriter strWriter;
182: DOMWriter domWriter;
183:
184: Node lockScopeNode = null;
185: Node lockTypeNode = null;
186: Node lockOwnerNode = null;
187:
188: for (int i = 0; i < childList.getLength(); i++) {
189: Node currentNode = childList.item(i);
190: switch (currentNode.getNodeType()) {
191: case Node.TEXT_NODE:
192: break;
193: case Node.ELEMENT_NODE:
194: String nodeName = currentNode.getNodeName();
195: if (nodeName.endsWith("lockscope")) {
196: lockScopeNode = currentNode;
197: }
198: if (nodeName.endsWith("locktype")) {
199: lockTypeNode = currentNode;
200: }
201: if (nodeName.endsWith("owner")) {
202: lockOwnerNode = currentNode;
203: }
204: break;
205: }
206: }
207:
208: if (lockScopeNode != null) {
209:
210: childList = lockScopeNode.getChildNodes();
211: for (int i = 0; i < childList.getLength(); i++) {
212: Node currentNode = childList.item(i);
213: switch (currentNode.getNodeType()) {
214: case Node.TEXT_NODE:
215: break;
216: case Node.ELEMENT_NODE:
217: String tempScope = currentNode.getNodeName();
218: if (tempScope.indexOf(':') != -1) {
219: lock.scope = tempScope.substring
220: (tempScope.indexOf(':') + 1);
221: } else {
222: lock.scope = tempScope;
223: }
224: break;
225: }
226: }
227:
228: if (lock.scope == null) {
229: // Bad request
230: resp.setStatus(FxWebDavStatus.SC_BAD_REQUEST);
231: }
232:
233: } else {
234: // Bad request
235: resp.setStatus(FxWebDavStatus.SC_BAD_REQUEST);
236: }
237:
238: if (lockTypeNode != null) {
239:
240: childList = lockTypeNode.getChildNodes();
241: for (int i = 0; i < childList.getLength(); i++) {
242: Node currentNode = childList.item(i);
243: switch (currentNode.getNodeType()) {
244: case Node.TEXT_NODE:
245: break;
246: case Node.ELEMENT_NODE:
247: String tempType = currentNode.getNodeName();
248: if (tempType.indexOf(':') != -1) {
249: lock.type =
250: tempType.substring(tempType.indexOf(':') + 1);
251: } else {
252: lock.type = tempType;
253: }
254: break;
255: }
256: }
257:
258: if (lock.type == null) {
259: // Bad request
260: resp.setStatus(FxWebDavStatus.SC_BAD_REQUEST);
261: }
262:
263: } else {
264: // Bad request
265: resp.setStatus(FxWebDavStatus.SC_BAD_REQUEST);
266: }
267:
268: if (lockOwnerNode != null) {
269:
270: childList = lockOwnerNode.getChildNodes();
271: for (int i = 0; i < childList.getLength(); i++) {
272: Node currentNode = childList.item(i);
273: switch (currentNode.getNodeType()) {
274: case Node.TEXT_NODE:
275: lock.owner += currentNode.getNodeValue();
276: break;
277: case Node.ELEMENT_NODE:
278: strWriter = new StringWriter();
279: domWriter = new DOMWriter(strWriter, true);
280: domWriter.setQualifiedNames(false);
281: domWriter.print(currentNode);
282: lock.owner += strWriter.toString();
283: break;
284: }
285: }
286:
287: if (lock.owner == null) {
288: // Bad request
289: resp.setStatus(FxWebDavStatus.SC_BAD_REQUEST);
290: }
291:
292: } else {
293: lock.owner = "";
294: }
295:
296: }
297:
298: String path = getRelativePath(req);
299:
300: lock.path = path;
301:
302: boolean exists = true;
303: Object object = null;
304: try {
305: object = resources.lookup(path);
306: } catch (NamingException e) {
307: exists = false;
308: }
309:
310: Enumeration locksList;
311:
312: if (lockRequestType == LOCK_CREATION) {
313:
314: // Generating lock id
315: String lockTokenStr = req.getServletPath() + "-" + lock.type + "-"
316: + lock.scope + "-" + req.getUserPrincipal() + "-"
317: + lock.depth + "-" + lock.owner + "-" + lock.tokens + "-"
318: + lock.expiresAt + "-" + System.currentTimeMillis() + "-"
319: + secret;
320: String lockToken = FxWebDavUtils.MD5encode(md5Helper.digest(lockTokenStr.getBytes()));
321:
322: if ((exists) && (object instanceof DirContext) &&
323: (lock.depth == INFINITY)) {
324:
325: // Locking a collection (and all its member resources)
326:
327: // Checking if a child resource of this collection is
328: // already locked
329: Vector<String> lockPaths = new Vector<String>();
330: locksList = collectionLocks.elements();
331: while (locksList.hasMoreElements()) {
332: LockInfo currentLock = (LockInfo) locksList.nextElement();
333: if (currentLock.hasExpired()) {
334: resourceLocks.remove(currentLock.path);
335: continue;
336: }
337: if ((currentLock.path.startsWith(lock.path)) &&
338: ((currentLock.isExclusive()) ||
339: (lock.isExclusive()))) {
340: // A child collection of this collection is locked
341: lockPaths.addElement(currentLock.path);
342: }
343: }
344: locksList = resourceLocks.elements();
345: while (locksList.hasMoreElements()) {
346: LockInfo currentLock = (LockInfo) locksList.nextElement();
347: if (currentLock.hasExpired()) {
348: resourceLocks.remove(currentLock.path);
349: continue;
350: }
351: if ((currentLock.path.startsWith(lock.path)) &&
352: ((currentLock.isExclusive()) ||
353: (lock.isExclusive()))) {
354: // A child resource of this collection is locked
355: lockPaths.addElement(currentLock.path);
356: }
357: }
358:
359: if (!lockPaths.isEmpty()) {
360:
361: // One of the child paths was locked
362: // We generate a multistatus error report
363:
364: Enumeration lockPathsList = lockPaths.elements();
365:
366: resp.setStatus(FxWebDavStatus.SC_CONFLICT);
367:
368: XMLWriter generatedXML = new XMLWriter();
369: generatedXML.writeXMLHeader();
370:
371: generatedXML.writeElement
372: (null, "multistatus" + generateNamespaceDeclarations(),
373: XMLWriter.OPENING);
374:
375: while (lockPathsList.hasMoreElements()) {
376: generatedXML.writeElement(null, "response",
377: XMLWriter.OPENING);
378: generatedXML.writeElement(null, "href",
379: XMLWriter.OPENING);
380: generatedXML
381: .writeText((String) lockPathsList.nextElement());
382: generatedXML.writeElement(null, "href",
383: XMLWriter.CLOSING);
384: generatedXML.writeElement(null, "status",
385: XMLWriter.OPENING);
386: generatedXML
387: .writeText("HTTP/1.1 " + FxWebDavStatus.SC_LOCKED
388: + " " + FxWebDavStatus
389: .getStatusText(FxWebDavStatus.SC_LOCKED));
390: generatedXML.writeElement(null, "status",
391: XMLWriter.CLOSING);
392:
393: generatedXML.writeElement(null, "response",
394: XMLWriter.CLOSING);
395: }
396:
397: generatedXML.writeElement(null, "multistatus",
398: XMLWriter.CLOSING);
399:
400: Writer writer = resp.getWriter();
401: writer.write(generatedXML.toString());
402: writer.close();
403:
404: return;
405:
406: }
407:
408: boolean addLock = true;
409:
410: // Checking if there is already a shared lock on this path
411: locksList = collectionLocks.elements();
412: while (locksList.hasMoreElements()) {
413:
414: LockInfo currentLock = (LockInfo) locksList.nextElement();
415: if (currentLock.path.equals(lock.path)) {
416:
417: if (currentLock.isExclusive()) {
418: resp.sendError(FxWebDavStatus.SC_LOCKED);
419: return;
420: } else {
421: if (lock.isExclusive()) {
422: resp.sendError(FxWebDavStatus.SC_LOCKED);
423: return;
424: }
425: }
426:
427: currentLock.tokens.addElement(lockToken);
428: lock = currentLock;
429: addLock = false;
430:
431: }
432:
433: }
434:
435: if (addLock) {
436: lock.tokens.addElement(lockToken);
437: collectionLocks.addElement(lock);
438: }
439:
440: } else {
441:
442: // Locking a single resource
443:
444: // Retrieving an already existing lock on that resource
445: LockInfo presentLock = resourceLocks.get(lock.path);
446: if (presentLock != null) {
447:
448: if ((presentLock.isExclusive()) || (lock.isExclusive())) {
449: // If either lock is exclusive, the lock can't be
450: // granted
451: resp.sendError(FxWebDavStatus.SC_PRECONDITION_FAILED);
452: return;
453: } else {
454: presentLock.tokens.addElement(lockToken);
455: lock = presentLock;
456: }
457:
458: } else {
459:
460: lock.tokens.addElement(lockToken);
461: resourceLocks.put(lock.path, lock);
462:
463: // Checking if a resource exists at this path
464: exists = true;
465: try {
466: resources.lookup(path);
467: } catch (NamingException e) {
468: exists = false;
469: }
470: if (!exists) {
471:
472: // "Creating" a lock-null resource
473: int slash = lock.path.lastIndexOf('/');
474: String parentPath = lock.path.substring(0, slash);
475:
476: Vector<String> lockNulls = lockNullResources.get(parentPath);
477: if (lockNulls == null) {
478: lockNulls = new Vector<String>();
479: lockNullResources.put(parentPath, lockNulls);
480: }
481:
482: lockNulls.addElement(lock.path);
483:
484: }
485: // Add the Lock-Token header as by RFC 2518 8.10.1
486: // - only do this for newly created locks
487: resp.addHeader("Lock-Token", "<opaquelocktoken:"
488: + lockToken + ">");
489: }
490:
491: }
492:
493: }
494:
495: if (lockRequestType == LOCK_REFRESH) {
496:
497: String ifHeader = req.getHeader("If");
498: if (ifHeader == null)
499: ifHeader = "";
500:
501: // Checking resource locks
502:
503: LockInfo toRenew = resourceLocks.get(path);
504: Enumeration tokenList;
505: if (lock != null) {
506:
507: // At least one of the tokens of the locks must have been given
508:
509: tokenList = toRenew.tokens.elements();
510: while (tokenList.hasMoreElements()) {
511: String token = (String) tokenList.nextElement();
512: if (ifHeader.indexOf(token) != -1) {
513: toRenew.expiresAt = lock.expiresAt;
514: lock = toRenew;
515: }
516: }
517:
518: }
519:
520: // Checking inheritable collection locks
521:
522: Enumeration collectionLocksList = collectionLocks.elements();
523: while (collectionLocksList.hasMoreElements()) {
524: toRenew = (LockInfo) collectionLocksList.nextElement();
525: if (path.equals(toRenew.path)) {
526:
527: tokenList = toRenew.tokens.elements();
528: while (tokenList.hasMoreElements()) {
529: String token = (String) tokenList.nextElement();
530: if (ifHeader.indexOf(token) != -1) {
531: toRenew.expiresAt = lock.expiresAt;
532: lock = toRenew;
533: }
534: }
535:
536: }
537: }
538:
539: }
540:
541: // Set the status, then generate the XML response containing
542: // the lock information
543: XMLWriter generatedXML = new XMLWriter();
544: generatedXML.writeXMLHeader();
545: generatedXML.writeElement(null, "prop" + generateNamespaceDeclarations(), XMLWriter.OPENING);
546:
547: generatedXML.writeElement(null, "lockdiscovery", XMLWriter.OPENING);
548:
549: lock.toXML(generatedXML);
550:
551: generatedXML.writeElement(null, "lockdiscovery", XMLWriter.CLOSING);
552:
553: generatedXML.writeElement(null, "prop", XMLWriter.CLOSING);
554:
555: resp.setStatus(FxWebDavStatus.SC_OK);
556: resp.setContentType("text/xml; charset=UTF-8");
557: Writer writer = resp.getWriter();
558: writer.write(generatedXML.toString());
559: writer.close();
560:
561: }
562:
563: */
564:
565: /**
566: * Check to see if a resource is currently write locked. The method
567: * will look at the "If" header to make sure the client
568: * has give the appropriate lock tokens.
569: *
570: * @param req Servlet request
571: * @return boolean true if the resource is locked (and no appropriate
572: * lock token has been found for at least one of the non-shared locks which
573: * are present on the resource).
574: *
575: private boolean isLocked(HttpServletRequest req) {
576:
577: String path = getRelativePath(req);
578:
579: String ifHeader = req.getHeader("If");
580: if (ifHeader == null)
581: ifHeader = "";
582:
583: String lockTokenHeader = req.getHeader("Lock-Token");
584: if (lockTokenHeader == null)
585: lockTokenHeader = "";
586:
587: return isLocked(path, ifHeader + lockTokenHeader);
588:
589: } */
590:
591: /**
592: * Check to see if a resource is currently write locked.
593: *
594: * @param path Path of the resource
595: * @param ifHeader "If" HTTP header which was included in the request
596: * @return boolean true if the resource is locked (and no appropriate
597: * lock token has been found for at least one of the non-shared locks which
598: * are present on the resource).
599: *
600: private boolean isLocked(String path, String ifHeader) {
601:
602: // Checking resource locks
603:
604: LockInfo lock = resourceLocks.get(path);
605: Enumeration tokenList;
606: if ((lock != null) && (lock.hasExpired())) {
607: resourceLocks.remove(path);
608: } else if (lock != null) {
609:
610: // At least one of the tokens of the locks must have been given
611:
612: tokenList = lock.tokens.elements();
613: boolean tokenMatch = false;
614: while (tokenList.hasMoreElements()) {
615: String token = (String) tokenList.nextElement();
616: if (ifHeader.indexOf(token) != -1)
617: tokenMatch = true;
618: }
619: if (!tokenMatch)
620: return true;
621:
622: }
623:
624: // Checking inheritable collection locks
625:
626: Enumeration collectionLocksList = collectionLocks.elements();
627: while (collectionLocksList.hasMoreElements()) {
628: lock = (LockInfo) collectionLocksList.nextElement();
629: if (lock.hasExpired()) {
630: collectionLocks.removeElement(lock);
631: } else if (path.startsWith(lock.path)) {
632:
633: tokenList = lock.tokens.elements();
634: boolean tokenMatch = false;
635: while (tokenList.hasMoreElements()) {
636: String token = (String) tokenList.nextElement();
637: if (ifHeader.indexOf(token) != -1)
638: tokenMatch = true;
639: }
640: if (!tokenMatch)
641: return true;
642:
643: }
644: }
645:
646: return false;
647:
648: }
649: */
650: }
|