001: // CvsDAVFileFrame.java
002: // $Id: CvsDAVDirectoryFrame.java,v 1.4 2005/01/06 12:03:19 ylafon Exp $
003: // (c) COPYRIGHT MIT and INRIA, 1996.
004: // Please first read the full copyright statement in file COPYRIGHT.html
005:
006: package org.w3c.jigedit.webdav.frames;
007:
008: import java.io.File;
009: import java.io.IOException;
010: import java.io.PrintStream;
011:
012: import java.net.MalformedURLException;
013: import java.net.URL;
014:
015: import org.w3c.tools.resources.Attribute;
016: import org.w3c.tools.resources.AttributeHolder;
017: import org.w3c.tools.resources.AttributeRegistry;
018: import org.w3c.tools.resources.BooleanAttribute;
019: import org.w3c.tools.resources.DirectoryResource;
020: import org.w3c.tools.resources.FileResource;
021: import org.w3c.tools.resources.FramedResource;
022: import org.w3c.tools.resources.InvalidResourceException;
023: import org.w3c.tools.resources.LookupState;
024: import org.w3c.tools.resources.LookupResult;
025: import org.w3c.tools.resources.MultipleLockException;
026: import org.w3c.tools.resources.ProtocolException;
027: import org.w3c.tools.resources.ReplyInterface;
028: import org.w3c.tools.resources.RequestInterface;
029: import org.w3c.tools.resources.Resource;
030: import org.w3c.tools.resources.ResourceException;
031: import org.w3c.tools.resources.ResourceFrame;
032: import org.w3c.tools.resources.ResourceReference;
033: import org.w3c.tools.resources.ServerInterface;
034:
035: import org.w3c.jigsaw.webdav.DAVFrame;
036: import org.w3c.jigsaw.webdav.DAVRequest;
037: import org.w3c.www.webdav.WEBDAV;
038:
039: import org.w3c.jigsaw.http.HTTPException;
040: import org.w3c.jigsaw.http.Reply;
041: import org.w3c.jigsaw.http.Request;
042:
043: import org.w3c.jigsaw.auth.AuthFilter;
044:
045: import org.w3c.www.http.HTTP;
046: import org.w3c.www.http.HttpEntityMessage;
047: import org.w3c.www.http.HttpMessage;
048: import org.w3c.www.http.HttpReplyMessage;
049: import org.w3c.www.http.HttpRequestMessage;
050:
051: import org.w3c.www.mime.MimeType;
052:
053: import org.w3c.jigsaw.html.HtmlGenerator;
054:
055: import org.w3c.cvs.CVS;
056: import org.w3c.cvs.CvsAddException;
057: import org.w3c.cvs.CvsDirectory;
058: import org.w3c.cvs.CvsException;
059: import org.w3c.cvs.UpToDateCheckFailedException;
060:
061: import org.w3c.jigedit.cvs.CvsFrame;
062: import org.w3c.jigedit.cvs.CvsModule;
063:
064: /**
065: * This subclass of HTTPFrame check cvs before performing a PUT request.
066: * If a CVS directory exists<BR>
067: * <ul>
068: * If the resource file exists<BR>
069: * <ul>
070: * If resource file not up to date Fail.<BR>
071: * Else perform PUT and commit it into cvs.<BR>
072: * </ul>
073: * Else perform PUT, add and commit it into cvs.<BR>
074: * </ul>
075: * Else perform PUT.
076: * @author Benoit Mahe <bmahe@sophia.inria.fr>
077: */
078:
079: public class CvsDAVDirectoryFrame extends DAVFrame {
080:
081: public static final boolean debug = true;
082:
083: /**
084: * Attribute index, tell if we must update the resource everytime it is
085: * acceded (not recommended as it generates many cvs commands)
086: */
087: private static int ATTR_AUTOUPDATE = -1;
088:
089: static {
090: Attribute a = null;
091: Class cls = null;
092:
093: try {
094: cls = Class.forName("org.w3c.jigedit.webdav.frames"
095: + ".CvsDAVDirectoryFrame");
096: } catch (Exception ex) {
097: ex.printStackTrace();
098: System.exit(1);
099: }
100: // The browsable flag:
101: a = new BooleanAttribute("autoupdate", Boolean.FALSE,
102: Attribute.EDITABLE);
103: ATTR_AUTOUPDATE = AttributeRegistry.registerAttribute(cls, a);
104: }
105:
106: protected static Reply error(Request request, int status,
107: String title, String msg) {
108: Reply error = request.makeReply(status);
109: HtmlGenerator g = CvsFrame.getHtmlGenerator(title);
110: g.append("<span class=\"title\">", title, "</span>\n");
111: g.append("<p>", msg);
112: error.setStream(g);
113: return error;
114: }
115:
116: protected File resDirectory = null;
117:
118: protected File getResourceDirectory() {
119: if (resDirectory == null) {
120: FramedResource fr = getResource();
121: if (fr instanceof DirectoryResource) {
122: resDirectory = ((DirectoryResource) fr).getDirectory();
123: }
124: }
125: return resDirectory;
126: }
127:
128: /**
129: * tell if we must always do an update.
130: */
131: public boolean isAutoUpdatable() {
132: return getBoolean(ATTR_AUTOUPDATE, false);
133: }
134:
135: protected synchronized CvsDirectory getCvsManager()
136: throws CvsException {
137: return CvsModule.getCvsManager(getResourceDirectory(),
138: getContext(), getServer().getProperties());
139: }
140:
141: protected boolean checkCvsManager() {
142: try {
143: return (getCvsManager() != null);
144: } catch (CvsException ex) {
145: return false;
146: }
147: }
148:
149: /**
150: * @exception CvsException if the CVS process failed
151: */
152: protected void add(Request request) throws CvsException {
153: String u = (String) request.getState(AuthFilter.STATE_AUTHUSER);
154: String env[] = { "USER=" + u, "LOGNAME=" + u };
155: String names[] = null;
156: MimeType mtype;
157: mtype = request.getContentType();
158: if (mtype != null
159: && (mtype.match(MimeType.TEXT) != MimeType.MATCH_SUBTYPE)
160: && (mtype.match(MimeType.APPLICATION_XHTML_XML) != MimeType.MATCH_SPECIFIC_SUBTYPE)) {
161: names = new String[2];
162: names[0] = "-kb";
163: names[1] = getFileResource().getFile().getName();
164: } else {
165: names = new String[1];
166: names[0] = getFileResource().getFile().getName();
167: }
168: CvsDirectory cvsdir = null;
169: cvsdir = getCvsManager();
170: cvsdir.add(names, env);
171: }
172:
173: /**
174: * @exception CvsException if the CVS process failed
175: */
176: protected void commit(Request request) throws CvsException {
177: commit(request, "Changed through Jigsaw.");
178: }
179:
180: /**
181: * @exception CvsException if the CVS process failed
182: */
183: protected void commit(Request request, String msg)
184: throws CvsException {
185: String u = (String) request.getState(AuthFilter.STATE_AUTHUSER);
186: String env[] = { "USER=" + u, "LOGNAME=" + u };
187: String comment = "(" + u + ") " + msg;
188: CvsDirectory cvsdir = null;
189: cvsdir = getCvsManager();
190: cvsdir.commit(getFileResource().getFile().getName(), comment,
191: env);
192: }
193:
194: /**
195: * @exception CvsException if the CVS process failed
196: */
197: protected void update() throws CvsException {
198: CvsDirectory cvsdir = getCvsManager();
199: cvsdir.update(getFileResource().getFile().getName());
200: }
201:
202: /**
203: * @exception CvsException if the CVS process failed
204: */
205: protected int status() throws CvsException {
206: CvsDirectory cvsdir = getCvsManager();
207: return cvsdir.status(getFileResource().getFile().getName());
208: }
209:
210: protected String statusToString(int status) {
211: return CvsDirectory.statusToString(status);
212: }
213:
214: /**
215: * Handle the MKCOL request.
216: * @param request the WEBDAV request
217: * @return a Reply instance
218: * @exception ProtocolException If processsing the request failed.
219: * @exception ResourceException If the resource got a fatal error.
220: */
221: public Reply mkcol(DAVRequest request) throws ProtocolException,
222: ResourceException {
223: Reply rep = super .mkcol(request);
224: if (rep.getStatus() == HTTP.CREATED) {
225: // we created a new dir, time to add it in cvs
226: String names[] = new String[1];
227: // as the result is HTTP.CREATED, we know for sure that dresource
228: // exists, no need to check.
229: String newcol = (String) request.getState(REMAINING_PATH);
230: names[0] = newcol;
231: try {
232: getCvsManager().add(names);
233: } catch (CvsException ex) {
234: getServer().errlog(this , ex.getMessage());
235: }
236: }
237: return rep;
238: }
239:
240: /**
241: * The WEBDAV DELETE method, actually the resource (file, directory)
242: * is moved into the trash directory which is not accessible via HTTP.
243: * @param request The request to handle.
244: * @exception ProtocolException If processsing the request failed.
245: * @exception ResourceException If the resource got a fatal error.
246: */
247:
248: public Reply delete(Request request) throws ProtocolException,
249: ResourceException {
250: /**
251: * We are currently refusing to handle direct DELETE requests (but
252: * honor internal requests following other actions, like a MOVE
253: * because is is quite dangerous with graphical WebDAV clients
254: * and because there is no clean way to remove directories from a CVS
255: * repository
256: */
257: String method = request.getMethod();
258: if (method.equals("DELETE")) {
259: Reply error = request.makeReply(HTTP.FORBIDDEN);
260: error.setContent("DELETE is forbidden on CVS controlled"
261: + " directories in this version of JigEdit");
262: throw new HTTPException(error);
263: }
264: return super .delete(request);
265: }
266:
267: private void updateStates(DAVRequest request) {
268: if (getCurrentLockDepth() == WEBDAV.DEPTH_INFINITY) {
269: // propagate the lock
270: request.setState(LOCKED_REREFENCE, getResourceReference());
271: request.setState(LOCK_OWNER, getCurrentLockOwner(null));
272: request.setState(LOCK_TOKEN, getCurrentLockToken(null));
273: request.setState(LOCK_USERNAME,
274: getCurrentLockUsername(null));
275: request.setState(LOCK_EXPIRE, new Long(
276: getTokenExpirationDate(null)));
277: request.setState(LOCK_TIMEOUT, getValue(ATTR_LOCK_TIMEOUT,
278: DEFAULT_LOCK_TIMEOUT));
279: }
280: }
281:
282: /**
283: * Lookup the target resource when associated with a DirectoryResource.
284: * @param ls The current lookup state
285: * @param lr The result
286: * @return true if lookup is done.
287: * @exception ProtocolException If an error relative to the protocol
288: * occurs
289: */
290: protected boolean lookupDirectory(LookupState ls, LookupResult lr)
291: throws ProtocolException {
292: // handle PUT and MKCOL
293: // refresh the timeout value
294: DAVRequest request = (DAVRequest) ls.getRequest();
295: if (request == null) {
296: return super .lookupDirectory(ls, lr);
297: }
298: if (isLocked(null)) {
299: updateStates(request);
300: }
301: if (ls.hasMoreComponents()) {
302: if (request.getMethod().equals("PUT")) {
303: String name = ls.peekNextComponent();
304: ResourceReference rr = dresource.lookup(name);
305: if ((rr == null) && dresource.getExtensibleFlag()
306: && getPutableFlag()) {
307: // the resource doesn't exists
308: if (ls.countRemainingComponents() == 1) {
309: rr = dresource.createResource(name, request);
310: } else {
311: rr = dresource.createDirectoryResource(name);
312: }
313: if (rr == null) {
314: Reply error = request
315: .makeReply(HTTP.UNSUPPORTED_MEDIA_TYPE);
316: error
317: .setContent("Failed to create resource "
318: + name
319: + " : "
320: + "Unable to create the appropriate file:"
321: + request.getURLPath()
322: + " this media type is not supported");
323: throw new HTTPException(error);
324: }
325: } else if (rr == null) {
326: Reply error = request.makeReply(HTTP.FORBIDDEN);
327: error
328: .setContent("You are not allowed to create resource "
329: + name
330: + " : "
331: + dresource.getIdentifier()
332: + " is not extensible.");
333: throw new HTTPException(error);
334: }
335: } else if (request.getMethod().equals("MKCOL")) {
336: String name = ls.peekNextComponent();
337: ResourceReference rr = dresource.lookup(name);
338: if (rr == null) {
339: if (ls.countRemainingComponents() == 1) {
340: request.setState(REMAINING_PATH, name);
341: return true;
342: } else {
343: Reply error = request.makeReply(HTTP.CONFLICT);
344: error.setContent("Can't create "
345: + ls.getRemainingPath(true));
346: throw new HTTPException(error);
347: }
348: }
349: }
350: }
351: // normal lookup
352: if (super .lookupOther(ls, lr)) {
353: if (!ls.isDirectory() && !ls.isInternal()) {
354: // The directory lookup URL doesn't end with a slash:
355: if (request == null) {
356: lr.setTarget(null);
357: return true;
358: } else if (!acceptRedirect(request)) {
359: return true;
360: }
361: URL url = null;
362: try {
363: if ((request != null)
364: && request.hasState(Request.ORIG_URL_STATE)) {
365: URL oldurl;
366: oldurl = (URL) request
367: .getState(Request.ORIG_URL_STATE);
368: url = new URL(oldurl, oldurl.getFile() + "/");
369: } else {
370: url = (ls.hasRequest() ? getURL(request)
371: : new URL(getServer().getURL(),
372: resource.getURLPath()));
373: }
374: } catch (MalformedURLException ex) {
375: getServer().errlog(this ,
376: "unable to build full URL.");
377: throw new HTTPException("Internal server error");
378: }
379: String msg = "Invalid requested URL: the directory resource "
380: + " you are trying to reach is available only through "
381: + " its full URL: <a href=\""
382: + url
383: + "\">"
384: + url + "</a>.";
385: if (getRelocateFlag()) {
386: // Emit an error (with reloc if allowed)
387: Reply reloc = request.makeReply(HTTP.FOUND);
388: reloc.setContent(msg);
389: reloc.setLocation(url);
390: lr.setTarget(null);
391: lr.setReply(reloc);
392: return true;
393: } else {
394: Reply error = request.makeReply(HTTP.NOT_FOUND);
395: error.setContent(msg);
396: lr.setTarget(null);
397: lr.setReply(error);
398: return true;
399: }
400: } else if (!ls.isInternal() && acceptRedirect(request)) {
401: request.setState(STATE_CONTENT_LOCATION, "true");
402: // return the index file.
403: String indexes[] = getIndexes();
404: if (indexes != null) {
405: for (int i = 0; i < indexes.length; i++) {
406: String index = indexes[i];
407: if (index != null && index.length() > 0) {
408: DirectoryResource dir = (DirectoryResource) resource;
409: ResourceReference rr = dir.lookup(index);
410: if (rr != null) {
411: try {
412: FramedResource rindex = (FramedResource) rr
413: .lock();
414: return rindex.lookup(ls, lr);
415: } catch (InvalidResourceException ex) {
416: } finally {
417: rr.unlock();
418: }
419: }
420: }
421: }
422: }
423: }
424: return true;
425: }
426: return false;
427: }
428:
429: }
|