KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > slide > webdav > method > GetMethod


1 /*
2  * $Header: /home/cvs/jakarta-slide/src/webdav/server/org/apache/slide/webdav/method/GetMethod.java,v 1.51.2.3 2004/11/09 20:30:50 ozeigermann Exp $
3  * $Revision: 1.51.2.3 $
4  * $Date: 2004/11/09 20:30:50 $
5  *
6  * ====================================================================
7  *
8  * Copyright 1999-2002 The Apache Software Foundation
9  *
10  * Licensed under the Apache License, Version 2.0 (the "License");
11  * you may not use this file except in compliance with the License.
12  * You may obtain a copy of the License at
13  *
14  * http://www.apache.org/licenses/LICENSE-2.0
15  *
16  * Unless required by applicable law or agreed to in writing, software
17  * distributed under the License is distributed on an "AS IS" BASIS,
18  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
19  * See the License for the specific language governing permissions and
20  * limitations under the License.
21  *
22  */

23
24 package org.apache.slide.webdav.method;
25
26 import java.io.BufferedInputStream JavaDoc;
27 import java.io.IOException JavaDoc;
28 import java.io.InputStream JavaDoc;
29 import java.util.Date JavaDoc;
30 import java.util.Enumeration JavaDoc;
31 import java.util.StringTokenizer JavaDoc;
32 import java.util.Vector JavaDoc;
33
34 import javax.servlet.ServletOutputStream JavaDoc;
35 import javax.servlet.http.HttpServletRequest JavaDoc;
36 import javax.servlet.http.HttpServletResponse JavaDoc;
37
38 import org.apache.slide.common.NamespaceAccessToken;
39 import org.apache.slide.common.ServiceAccessException;
40 import org.apache.slide.common.SlideException;
41 import org.apache.slide.content.NodeRevisionDescriptor;
42 import org.apache.slide.content.NodeRevisionDescriptors;
43 import org.apache.slide.content.RevisionContentNotFoundException;
44 import org.apache.slide.content.RevisionDescriptorNotFoundException;
45 import org.apache.slide.content.RevisionNotFoundException;
46 import org.apache.slide.event.EventDispatcher;
47 import org.apache.slide.security.AccessDeniedException;
48 import org.apache.slide.structure.LinkedObjectNotFoundException;
49 import org.apache.slide.structure.ObjectNotFoundException;
50 import org.apache.slide.util.Configuration;
51 import org.apache.slide.webdav.WebdavException;
52 import org.apache.slide.webdav.WebdavServletConfig;
53 import org.apache.slide.webdav.event.WebdavEvent;
54 import org.apache.slide.webdav.util.DeltavConstants;
55 import org.apache.slide.webdav.util.DirectoryIndexGenerator;
56 import org.apache.slide.webdav.util.LabeledRevisionNotFoundException;
57 import org.apache.slide.webdav.util.PreconditionViolationException;
58 import org.apache.slide.webdav.util.VersioningHelper;
59 import org.apache.slide.webdav.util.ViolatedPrecondition;
60 import org.apache.slide.webdav.util.WebdavStatus;
61 import org.apache.slide.webdav.util.WebdavUtils;
62
63 /**
64  * GET method.
65  *
66  */

67 public class GetMethod extends AbstractWebdavMethod implements ReadMethod {
68
69
70     // -------------------------------------------------------------- Constants
71

72
73     protected final int BUFFER_SIZE = 2048;
74
75
76
77     /**
78      * MIME multipart separation string
79      */

80     protected static final String JavaDoc mimeSeparation = "SLIDE_MIME_BOUNDARY";
81
82
83     /**
84      * The input buffer size to use when serving resources.
85      */

86     protected int input = 2048;
87
88
89     /**
90      * The output buffer size to use when serving resources.
91      */

92     protected int output = 2048;
93
94
95     /**
96      * Print content.
97      */

98     protected boolean printContent = true;
99
100     /**
101      * The VersioningHelper used by this instance.
102      */

103     protected VersioningHelper vHelp = null;
104
105
106     // ----------------------------------------------------- Instance Variables
107

108
109     /**
110      * Resource to be retrieved.
111      */

112     protected String JavaDoc resourcePath;
113
114
115     // ----------------------------------------------------------- Constructors
116

117
118     /**
119      * Constructor.
120      *
121      * @param token the token for accessing the namespace
122      * @param config configuration of the WebDAV servlet
123      */

124     public GetMethod(NamespaceAccessToken token, WebdavServletConfig config) {
125         super(token, config);
126     }
127
128
129     // ------------------------------------------------------ Protected Methods
130

131
132     protected void displayDirectory() throws IOException JavaDoc {
133         String JavaDoc directoryBrowsing = config.getInitParameter( "directory-browsing" );
134         if( "true".equalsIgnoreCase(directoryBrowsing) ) {
135                 try {
136                     DirectoryIndexGenerator directoryIndexGenerator =
137                         new DirectoryIndexGenerator
138                         (token, config);
139                     directoryIndexGenerator.generate(req, resp, slideToken);
140                 } catch (AccessDeniedException e) {
141                     resp.sendError(WebdavStatus.SC_FORBIDDEN);
142                 } catch (ObjectNotFoundException e) {
143                     resp.sendError(WebdavStatus.SC_NOT_FOUND);
144                 } catch (LinkedObjectNotFoundException e) {
145                     resp.sendError(WebdavStatus.SC_NOT_FOUND);
146                 } catch (SlideException e) {
147                     resp.setStatus(WebdavStatus.SC_INTERNAL_SERVER_ERROR);
148                 }
149         } else {
150             resp.sendError(WebdavStatus.SC_FORBIDDEN);
151         }
152     }
153
154     /**
155      * Parse XML request.
156      */

157     protected void parseRequest()
158         throws WebdavException {
159         vHelp = VersioningHelper.getVersioningHelper(
160             slideToken, token, req, resp, getConfig() );
161         resourcePath = requestUri;
162         if (resourcePath == null) {
163             resourcePath = "/";
164         }
165
166         // evaluate "Label" header
167
if (Configuration.useVersionControl()) {
168             try {
169
170                 String JavaDoc labelHeader = WebdavUtils.fixTomcatHeader(requestHeaders.getLabel(), "UTF-8");
171                 resourcePath = vHelp.getLabeledResourceUri(resourcePath, labelHeader);
172             }
173             catch (LabeledRevisionNotFoundException e) {
174                 ViolatedPrecondition violatedPrecondition =
175                     new ViolatedPrecondition(DeltavConstants.C_MUST_SELECT_VERSION_IN_HISTORY,
176                                              WebdavStatus.SC_CONFLICT);
177                 try {
178                     sendPreconditionViolation(new PreconditionViolationException(violatedPrecondition,
179                                                                                  resourcePath));
180                 } catch (IOException JavaDoc ioe) {}
181                 throw new WebdavException( WebdavStatus.SC_CONFLICT );
182             }
183             catch (SlideException e) {
184                 int statusCode = getErrorCode( (Exception JavaDoc)e );
185                 sendError( statusCode, e );
186                 throw new WebdavException( statusCode );
187             }
188         }
189
190     }
191
192
193     /**
194      * Execute request.
195      *
196      * @exception WebdavException Can't access resource
197      */

198     protected void executeRequest()
199         throws WebdavException {
200         
201         // check lock-null resources
202
try {
203             if (isLockNull(resourcePath)) {
204                 int statusCode = WebdavStatus.SC_NOT_FOUND;
205                 sendError( statusCode, "lock-null resource", new Object JavaDoc[]{resourcePath} );
206                 throw new WebdavException( statusCode );
207             }
208         }
209         catch (ServiceAccessException e) {
210             int statusCode = getErrorCode((Exception JavaDoc)e);
211             sendError( statusCode, e );
212             throw new WebdavException( statusCode );
213         }
214
215         try {
216             // fire put event
217
if ( WebdavEvent.GET.isEnabled() ) EventDispatcher.getInstance().fireVetoableEvent(WebdavEvent.GET, new WebdavEvent(this));
218
219             // Then we must get object contents ...
220

221             structure.retrieve(slideToken, resourcePath);
222             NodeRevisionDescriptors revisionDescriptors =
223                 content.retrieve(slideToken, resourcePath);
224
225             if (revisionDescriptors.hasRevisions()) {
226
227                 // Retrieve latest revision descriptor
228
NodeRevisionDescriptor revisionDescriptor =
229                     content.retrieve(slideToken, revisionDescriptors);
230
231                 if (revisionDescriptor != null) {
232
233                     if (revisionDescriptor.propertyValueContains(
234                             NodeRevisionDescriptor.RESOURCE_TYPE,
235                             NodeRevisionDescriptor.COLLECTION_TYPE)) {
236                         displayDirectory();
237                     } else {
238                     
239                         ResourceInfo resourceInfo =
240                             new ResourceInfo(resourcePath, revisionDescriptor);
241     
242                         // Checking If headers
243
if (!checkIfHeaders(req, resp, resourceInfo))
244                             return;
245     
246                         ServletOutputStream JavaDoc os = resp.getOutputStream();
247                         InputStream JavaDoc is = null;
248     
249                         if (printContent) {
250                             is = content.retrieve
251                                 (slideToken, revisionDescriptors,
252                                  revisionDescriptor).streamContent();
253                         }
254     
255                         Vector JavaDoc ranges = parseRange(req, resp, resourceInfo);
256     
257                         // ETag header (tag must be quoted)
258
resp.setHeader("ETag", getETag(resourceInfo, true) );
259                         resp.setHeader
260                             ("Content-Language", revisionDescriptor.getContentLanguage());
261                         resp.addHeader
262                             ("Last-Modified",
263                              revisionDescriptor.getLastModified().toString());
264     
265                         // do this before setting content length, as Tomcat 5 seems to be picky
266
// about this
267
resp.setBufferSize(output);
268
269                         if ( ((ranges == null) || (ranges.isEmpty())) ) {
270                             // full content response
271
resp.setContentType
272                                 (revisionDescriptor.getContentType());
273                             resp.setContentLength
274                                 ((int) revisionDescriptor.getContentLength());
275     
276                             // Copy the input stream to our output stream
277
// (if requested)
278
if (printContent) {
279                                 copy(resourceInfo, is, os);
280                             }
281     
282                         } else {
283                             // Partial content response.
284

285                             resp.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT);
286     
287                             if (ranges.size() == 1) {
288     
289                                 Range range = (Range) ranges.elementAt(0);
290                                 resp.addHeader("Content-Range", "bytes "
291                                                    + range.start
292                                                    + "-" + range.end + "/"
293                                                    + range.fileLength);
294                                 resp.setContentLength((int) range.length);
295                                 resp.setContentType
296                                     (revisionDescriptor.getContentType());
297     
298                                 if (printContent) {
299                                     copy(resourceInfo, is, os, range);
300                                 }
301     
302                             } else {
303     
304                                 resp.setContentType
305                                     ("multipart/byteranges; boundary="
306                                          + mimeSeparation);
307     
308                                 if (printContent) {
309                                     copy(resourceInfo, is, os,
310                                          ranges.elements(),
311                                          revisionDescriptor.getContentType());
312                                 }
313                             }
314                         }
315                     }
316                 } else {
317                     // XXX if there is no revision descriptor, this should be a directory
318
displayDirectory();
319                     // resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
320
}
321             } else {
322                 // XXX if there is no revisions, this should be a directory
323
displayDirectory();
324                 // resp.setStatus(HttpServletResponse.SC_NO_CONTENT);
325
}
326         } catch (Exception JavaDoc e) {
327             int statusCode;
328             // XXX If there is some sort of IOException it has been issued by the copy methods
329
// which indicates the client aborted the connection
330
// like org.apache.catalina.connector.ClientAbortException thrown by Tomcat
331
if (e instanceof IOException JavaDoc) {
332                 // XXX like in WebdavUtils which reacts on a failure upon put, we use this return method,
333
// however, is it sensible?
334
statusCode = WebdavStatus.SC_PRECONDITION_FAILED;
335             } else {
336                 statusCode = getErrorCode( e );
337             }
338             sendError( statusCode, e );
339             throw new WebdavException( statusCode );
340         }
341     }
342
343
344     /**
345      * Get return status based on exception type.
346      */

347     protected int getErrorCode(Exception JavaDoc ex) {
348         try {
349             throw ex;
350         } catch (RevisionNotFoundException e) {
351             return WebdavStatus.SC_NOT_FOUND;
352         } catch (RevisionContentNotFoundException e) {
353             return WebdavStatus.SC_NOT_FOUND;
354         } catch (RevisionDescriptorNotFoundException e) {
355             return WebdavStatus.SC_NOT_FOUND;
356         } catch (LinkedObjectNotFoundException e) {
357             return WebdavStatus.SC_NOT_FOUND;
358         } catch (Exception JavaDoc e) {
359             return super.getErrorCode(e);
360         }
361     }
362
363
364
365     // -------------------------------------------------------- Private Methods
366

367
368
369
370     /**
371      * Copy the contents of the specified input stream to the specified
372      * output stream, and ensure that both streams are closed before returning
373      * (even in the face of an exception).
374      *
375      * @param istream The input stream to read from
376      * @param ostream The output stream to write to
377      *
378      * @exception IOException if an input/output error occurs
379      */

380     private void copy(ResourceInfo resourceInfo,
381                       InputStream JavaDoc resourceInputStream,
382                       ServletOutputStream JavaDoc ostream)
383         throws IOException JavaDoc {
384
385         IOException JavaDoc exception = null;
386
387         InputStream JavaDoc istream = new BufferedInputStream JavaDoc
388             (resourceInputStream, input);
389
390         // Copy the input stream to the output stream
391
exception = copyRange(istream, ostream);
392
393         // Clean up the input and output streams
394
try {
395             istream.close();
396         } catch (Throwable JavaDoc t) {
397             ;
398         }
399
400         try {
401             ostream.flush();
402         } catch (Throwable JavaDoc t) {
403             ;
404         }
405         try {
406             ostream.close();
407         } catch (Throwable JavaDoc t) {
408             ;
409         }
410
411         // Rethrow any exception that has occurred
412
if (exception != null)
413             throw exception;
414
415     }
416
417
418     /**
419      * Copy the contents of the specified input stream to the specified
420      * output stream, and ensure that both streams are closed before returning
421      * (even in the face of an exception).
422      *
423      * @param resourceInfo The ResourceInfo object
424      * @param ostream The output stream to write to
425      * @param range Range the client wanted to retrieve
426      * @exception IOException if an input/output error occurs
427      */

428     private void copy(ResourceInfo resourceInfo,
429                       InputStream JavaDoc resourceInputStream,
430                       ServletOutputStream JavaDoc ostream,
431                       Range range)
432         throws IOException JavaDoc {
433
434         IOException JavaDoc exception = null;
435
436         InputStream JavaDoc istream =
437             new BufferedInputStream JavaDoc(resourceInputStream, input);
438         exception = copyRange(istream, ostream, range.start, range.end);
439
440         // Clean up the input and output streams
441
try {
442             istream.close();
443         } catch (Throwable JavaDoc t) {
444             ;
445         }
446         try {
447             ostream.flush();
448         } catch (Throwable JavaDoc t) {
449             ;
450         }
451         try {
452             ostream.close();
453         } catch (Throwable JavaDoc t) {
454             ;
455         }
456
457         // Rethrow any exception that has occurred
458
if (exception != null)
459             throw exception;
460
461     }
462
463
464     /**
465      * Copy the contents of the specified input stream to the specified
466      * output stream, and ensure that both streams are closed before returning
467      * (even in the face of an exception).
468      *
469      * @param resourceInfo The ResourceInfo object
470      * @param ostream The output stream to write to
471      * @param ranges Enumeration of the ranges the client wanted to retrieve
472      * @param contentType Content type of the resource
473      * @exception IOException if an input/output error occurs
474      */

475     private void copy(ResourceInfo resourceInfo,
476                       InputStream JavaDoc resourceInputStream,
477                       ServletOutputStream JavaDoc ostream,
478                       Enumeration JavaDoc ranges, String JavaDoc contentType)
479         throws IOException JavaDoc {
480
481         IOException JavaDoc exception = null;
482
483         while ( (exception == null) && (ranges.hasMoreElements()) ) {
484
485             InputStream JavaDoc istream =
486                 new BufferedInputStream JavaDoc(resourceInputStream, input);
487
488             Range currentRange = (Range) ranges.nextElement();
489
490             // Writing MIME header.
491
ostream.println("--" + mimeSeparation);
492             if (contentType != null)
493                 ostream.println("Content-Type: " + contentType);
494             ostream.println("Content-Range: bytes " + currentRange.start
495                                 + "-" + currentRange.end + "/"
496                                 + currentRange.fileLength);
497             ostream.println();
498
499             // Printing content
500
exception = copyRange(istream, ostream, currentRange.start,
501                                   currentRange.end);
502
503             try {
504                 istream.close();
505             } catch (Throwable JavaDoc t) {
506                 ;
507             }
508
509         }
510
511         ostream.print("--" + mimeSeparation + "--");
512
513         // Clean up the output streams
514
try {
515             ostream.flush();
516         } catch (Throwable JavaDoc t) {
517             ;
518         }
519         try {
520             ostream.close();
521         } catch (Throwable JavaDoc t) {
522             ;
523         }
524
525         // Rethrow any exception that has occurred
526
if (exception != null)
527             throw exception;
528
529     }
530
531
532     /**
533      * Copy the contents of the specified input stream to the specified
534      * output stream, and ensure that both streams are closed before returning
535      * (even in the face of an exception).
536      *
537      * @param istream The input stream to read from
538      * @param ostream The output stream to write to
539      * @return Exception which occured during processing
540      */

541     private IOException JavaDoc copyRange(InputStream JavaDoc istream,
542                                   ServletOutputStream JavaDoc ostream) {
543
544         // Copy the input stream to the output stream
545
IOException JavaDoc exception = null;
546         byte buffer[] = new byte[input];
547         int len = buffer.length;
548         while (true) {
549             try {
550                 len = istream.read(buffer);
551                 if (len == -1)
552                     break;
553                 ostream.write(buffer, 0, len);
554             } catch (IOException JavaDoc e) {
555                 exception = e;
556                 len = -1;
557                 break;
558             }
559         }
560         return exception;
561
562     }
563
564
565     /**
566      * Copy the contents of the specified input stream to the specified
567      * output stream, and ensure that both streams are closed before returning
568      * (even in the face of an exception).
569      *
570      * @param istream The input stream to read from
571      * @param ostream The output stream to write to
572      * @param start Start of the range which will be copied
573      * @param end End of the range which will be copied
574      * @return Exception which occured during processing
575      */

576     private IOException JavaDoc copyRange(InputStream JavaDoc istream,
577                                   ServletOutputStream JavaDoc ostream,
578                                   long start, long end) {
579
580         try {
581             istream.skip(start);
582         } catch (IOException JavaDoc e) {
583             return e;
584         }
585
586         IOException JavaDoc exception = null;
587         long bytesToRead = end - start + 1;
588
589         byte buffer[] = new byte[input];
590         int len = buffer.length;
591         while ( (bytesToRead > 0) && (len >= buffer.length)) {
592             try {
593                 len = istream.read(buffer);
594                 if (bytesToRead >= len) {
595                     ostream.write(buffer, 0, len);
596                     bytesToRead -= len;
597                 } else {
598                     ostream.write(buffer, 0, (int) bytesToRead);
599                     bytesToRead = 0;
600                 }
601             } catch (IOException JavaDoc e) {
602                 exception = e;
603                 len = -1;
604             }
605             if (len < buffer.length)
606                 break;
607         }
608
609         return exception;
610
611     }
612
613
614     /**
615      * Parse the range header.
616      *
617      * @param request The servlet request we are processing
618      * @param response The servlet response we are creating
619      * @return Vector of ranges or <code>null</code> of no ranges are requested
620      * @throws WebdavException if the range request is wrong
621      */

622     private Vector JavaDoc parseRange(HttpServletRequest JavaDoc request,
623                               HttpServletResponse JavaDoc response,
624                               ResourceInfo resourceInfo)
625         throws IOException JavaDoc, WebdavException
626     {
627         
628         // Retrieving the range header (if any is specified)
629
String JavaDoc rangeHeader = request.getHeader("Range");
630         if (rangeHeader == null) {
631             return null;
632         }
633
634         // Checking If-Range
635
String JavaDoc headerValue = request.getHeader("If-Range");
636         if (headerValue != null) {
637             headerValue = headerValue.trim();
638             
639             long lastModified = resourceInfo.date;
640
641             Date JavaDoc date = parseHttpDate(headerValue);
642             if (date == null) {
643                 // If the ETag the client gave does not match the entity
644
// etag, then the entire entity is returned.
645
if (headerValue.startsWith("\"")) {
646                     if (!getETag(resourceInfo, true).equals(headerValue)) {
647                         return null;
648                     }
649                 } else {
650                     // HACK Some clients does not quote the ETag properly
651
if (!getETagValue(resourceInfo, true).equals(headerValue)) {
652                         return null;
653                     }
654                 }
655             } else {
656
657                 // If the timestamp of the entity the client got is older than
658
// the last modification date of the entity, the entire entity
659
// is returned.
660
if (lastModified > (date.getTime() + 1000))
661                     return null;
662             }
663         }
664
665         long fileLength = resourceInfo.length;
666
667         if (fileLength == 0) return null;
668
669         // bytes is the only range unit supported (and I don't see the point
670
// of adding new ones).
671
if (!rangeHeader.startsWith("bytes")) {
672             response.sendError
673                 (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
674             throw new WebdavException(
675                     HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
676         }
677         rangeHeader = rangeHeader.substring(6);
678
679
680         // Vector which will contain all the ranges which are successfully
681
// parsed.
682
Vector JavaDoc result = new Vector JavaDoc();
683         StringTokenizer JavaDoc commaTokenizer = new StringTokenizer JavaDoc(rangeHeader, ",");
684
685         // Parsing the range list
686
while (commaTokenizer.hasMoreTokens()) {
687             String JavaDoc rangeDefinition = commaTokenizer.nextToken();
688
689             Range currentRange = new Range();
690             currentRange.fileLength = fileLength;
691
692             int dashPos = rangeDefinition.indexOf('-');
693
694             if (dashPos == -1) {
695                 response.sendError
696                     (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
697                 throw new WebdavException(
698                         HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
699             }
700
701             if (dashPos == 0) {
702
703                 try {
704                     long offset = Long.parseLong(rangeDefinition);
705                     currentRange.start = fileLength + offset;
706                     currentRange.end = fileLength - 1;
707                 } catch (NumberFormatException JavaDoc e) {
708                     response.sendError(
709                         HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
710                     throw new WebdavException(
711                         HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
712                 }
713
714             } else {
715
716                 try {
717                     currentRange.start = Long.parseLong
718                         (rangeDefinition.substring(0, dashPos));
719                     if (dashPos < rangeDefinition.length() - 1)
720                         currentRange.end = Long.parseLong
721                             (rangeDefinition.substring
722                                  (dashPos + 1, rangeDefinition.length()));
723                     else
724                         currentRange.end = fileLength - 1;
725                 } catch (NumberFormatException JavaDoc e) {
726                     response.sendError(
727                         HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
728                     throw new WebdavException(
729                         HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
730                 }
731
732             }
733
734             currentRange.length = (currentRange.end - currentRange.start + 1);
735             if (!currentRange.validate()) {
736                 response.sendError
737                     (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
738                 throw new WebdavException(HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE);
739             }
740
741             result.addElement(currentRange);
742         }
743
744         return result;
745     }
746
747
748     // ------------------------------------------------------ Range Inner Class
749

750
751     private static class Range {
752
753         public long start;
754         public long end;
755         public long length;
756         public long fileLength;
757
758         /**
759          * Validate range.
760          */

761         public boolean validate() {
762             return ( (start >= 0) && (end >= 0) && (length > 0)
763                         && (start <= end) && (end < fileLength) && (fileLength >= length));
764         }
765
766     }
767 }
768
Popular Tags