]> source.dussan.org Git - archiva.git/blob
032f614fdbe1214fed3b31a479e7c6c7fe44409b
[archiva.git] /
1 package org.apache.maven.archiva.webdav;
2
3 /*
4  * Licensed to the Apache Software Foundation (ASF) under one
5  * or more contributor license agreements.  See the NOTICE file
6  * distributed with this work for additional information
7  * regarding copyright ownership.  The ASF licenses this file
8  * to you under the Apache License, Version 2.0 (the
9  * "License"); you may not use this file except in compliance
10  * with the License.  You may obtain a copy of the License at
11  *
12  *  http://www.apache.org/licenses/LICENSE-2.0
13  *
14  * Unless required by applicable law or agreed to in writing,
15  * software distributed under the License is distributed on an
16  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17  * KIND, either express or implied.  See the License for the
18  * specific language governing permissions and limitations
19  * under the License.
20  */
21
22 import java.io.File;
23 import java.io.FileNotFoundException;
24 import java.io.FileReader;
25 import java.io.IOException;
26 import java.util.ArrayList;
27 import java.util.HashMap;
28 import java.util.List;
29 import java.util.Map;
30
31 import javax.servlet.http.HttpServletResponse;
32
33 import org.apache.commons.io.FileUtils;
34 import org.apache.commons.lang.StringUtils;
35 import org.apache.jackrabbit.webdav.DavException;
36 import org.apache.jackrabbit.webdav.DavResource;
37 import org.apache.jackrabbit.webdav.DavResourceFactory;
38 import org.apache.jackrabbit.webdav.DavResourceLocator;
39 import org.apache.jackrabbit.webdav.DavServletRequest;
40 import org.apache.jackrabbit.webdav.DavServletResponse;
41 import org.apache.jackrabbit.webdav.DavSession;
42 import org.apache.jackrabbit.webdav.lock.LockManager;
43 import org.apache.jackrabbit.webdav.lock.SimpleLockManager;
44 import org.apache.maven.archiva.common.utils.PathUtil;
45 import org.apache.maven.archiva.configuration.ArchivaConfiguration;
46 import org.apache.maven.archiva.configuration.RepositoryGroupConfiguration;
47 import org.apache.maven.archiva.model.ArchivaRepositoryMetadata;
48 import org.apache.maven.archiva.model.ArtifactReference;
49 import org.apache.maven.archiva.model.ProjectReference;
50 import org.apache.maven.archiva.model.VersionedReference;
51 import org.apache.maven.archiva.policies.ProxyDownloadException;
52 import org.apache.maven.archiva.proxy.RepositoryProxyConnectors;
53 import org.apache.maven.archiva.repository.ManagedRepositoryContent;
54 import org.apache.maven.archiva.repository.RepositoryContentFactory;
55 import org.apache.maven.archiva.repository.RepositoryException;
56 import org.apache.maven.archiva.repository.RepositoryNotFoundException;
57 import org.apache.maven.archiva.repository.audit.AuditEvent;
58 import org.apache.maven.archiva.repository.audit.AuditListener;
59 import org.apache.maven.archiva.repository.audit.Auditable;
60 import org.apache.maven.archiva.repository.content.RepositoryRequest;
61 import org.apache.maven.archiva.repository.layout.LayoutException;
62 import org.apache.maven.archiva.repository.metadata.MetadataTools;
63 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataException;
64 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataMerge;
65 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataReader;
66 import org.apache.maven.archiva.repository.metadata.RepositoryMetadataWriter;
67 import org.apache.maven.archiva.repository.scanner.RepositoryContentConsumers;
68 import org.apache.maven.archiva.security.ArchivaXworkUser;
69 import org.apache.maven.archiva.security.ServletAuthenticator;
70 import org.apache.maven.archiva.webdav.util.MimeTypes;
71 import org.apache.maven.archiva.webdav.util.RepositoryPathUtil;
72 import org.apache.maven.archiva.webdav.util.WebdavMethodUtil;
73 import org.apache.maven.model.DistributionManagement;
74 import org.apache.maven.model.Model;
75 import org.apache.maven.model.Relocation;
76 import org.apache.maven.model.io.xpp3.MavenXpp3Reader;
77 import org.codehaus.plexus.digest.ChecksumFile;
78 import org.codehaus.plexus.digest.Digester;
79 import org.codehaus.plexus.digest.DigesterException;
80 import org.codehaus.plexus.redback.authentication.AuthenticationException;
81 import org.codehaus.plexus.redback.authentication.AuthenticationResult;
82 import org.codehaus.plexus.redback.authorization.AuthorizationException;
83 import org.codehaus.plexus.redback.authorization.UnauthorizedException;
84 import org.codehaus.plexus.redback.policy.AccountLockedException;
85 import org.codehaus.plexus.redback.policy.MustChangePasswordException;
86 import org.codehaus.plexus.redback.system.SecuritySession;
87 import org.codehaus.plexus.redback.system.SecuritySystemConstants;
88 import org.codehaus.plexus.redback.xwork.filter.authentication.HttpAuthenticator;
89 import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
90 import org.slf4j.Logger;
91 import org.slf4j.LoggerFactory;
92
93 import com.opensymphony.xwork.ActionContext;
94
95 /**
96  * @author <a href="mailto:james@atlassian.com">James William Dumay</a>
97  * @plexus.component role="org.apache.maven.archiva.webdav.ArchivaDavResourceFactory"
98  */
99 public class ArchivaDavResourceFactory
100     implements DavResourceFactory, Auditable
101 {   
102     private static final String PROXIED_SUFFIX = " (proxied)";
103
104     private static final String HTTP_PUT_METHOD = "PUT";
105
106     private Logger log = LoggerFactory.getLogger( ArchivaDavResourceFactory.class );
107
108     /**
109      * @plexus.requirement role="org.apache.maven.archiva.repository.audit.AuditListener"
110      */
111     private List<AuditListener> auditListeners = new ArrayList<AuditListener>();
112
113     /**
114      * @plexus.requirement
115      */
116     private RepositoryContentFactory repositoryFactory;
117
118     /**
119      * @plexus.requirement
120      */
121     private RepositoryRequest repositoryRequest;
122
123     /**
124      * @plexus.requirement role-hint="default"
125      */
126     private RepositoryProxyConnectors connectors;
127
128     /**
129      * @plexus.requirement
130      */
131     private MetadataTools metadataTools;
132
133     /**
134      * @plexus.requirement
135      */
136     private MimeTypes mimeTypes;
137
138     /**
139      * @plexus.requirement
140      */
141     private ArchivaConfiguration archivaConfiguration;
142
143     /**
144      * @plexus.requirement
145      */
146     private ServletAuthenticator servletAuth;
147
148     /**
149      * @plexus.requirement role-hint="basic"
150      */
151     private HttpAuthenticator httpAuth;
152
153     /**
154      * Lock Manager - use simple implementation from JackRabbit
155      */
156     private final LockManager lockManager = new SimpleLockManager();
157
158     /** 
159      * @plexus.requirement 
160      */
161     private RepositoryContentConsumers consumers;
162     
163     /**
164      * @plexus.requirement
165      */
166     private ChecksumFile checksum;
167         
168     /**
169      * @plexus.requirement role-hint="sha1"
170      */
171     private Digester digestSha1;
172
173     /**
174      * @plexus.requirement role-hint="md5";
175      */
176     private Digester digestMd5;
177     
178     /**
179      * @plexus.requirement
180      */
181     private ArchivaXworkUser archivaXworkUser;
182         
183     public DavResource createResource( final DavResourceLocator locator, final DavServletRequest request,
184                                        final DavServletResponse response )
185         throws DavException
186     {
187         checkLocatorIsInstanceOfRepositoryLocator( locator );
188         ArchivaDavResourceLocator archivaLocator = (ArchivaDavResourceLocator) locator;
189
190         RepositoryGroupConfiguration repoGroupConfig =
191             archivaConfiguration.getConfiguration().getRepositoryGroupsAsMap().get( archivaLocator.getRepositoryId() );
192         List<String> repositories = new ArrayList<String>();
193
194         boolean isGet = WebdavMethodUtil.isReadMethod( request.getMethod() );
195         boolean isPut = WebdavMethodUtil.isWriteMethod( request.getMethod() );
196
197         if ( repoGroupConfig != null )
198         {
199             if( WebdavMethodUtil.isWriteMethod( request.getMethod() ) )
200             {
201                 throw new DavException( HttpServletResponse.SC_METHOD_NOT_ALLOWED,
202                                         "Write method not allowed for repository groups." );
203             }
204             repositories.addAll( repoGroupConfig.getRepositories() );
205
206             // handle browse requests for virtual repos
207             if ( RepositoryPathUtil.getLogicalResource( locator.getResourcePath() ).endsWith( "/" ) )
208             {
209                 return getResource( request, repositories, archivaLocator );
210             }
211         }
212         else
213         {
214             repositories.add( archivaLocator.getRepositoryId() );
215         }
216
217         //MRM-419 - Windows Webdav support. Should not 404 if there is no content.
218         if (StringUtils.isEmpty(archivaLocator.getRepositoryId()))
219         {
220             throw new DavException(HttpServletResponse.SC_NO_CONTENT);
221         }
222
223         List<DavResource> availableResources = new ArrayList<DavResource>();
224         List<String> resourcesInAbsolutePath = new ArrayList<String>();
225         DavException e = null;
226         
227         for ( String repositoryId : repositories )
228         {
229             ManagedRepositoryContent managedRepository = null;
230
231             try
232             {
233                 managedRepository = getManagedRepository( repositoryId );
234             }
235             catch ( DavException de )
236             {
237                 throw new DavException( HttpServletResponse.SC_NOT_FOUND, "Invalid managed repository <" +
238                     repositoryId + ">" );
239             }
240             
241             DavResource resource = null;
242             
243             if ( !locator.getResourcePath().startsWith( ArchivaDavResource.HIDDEN_PATH_PREFIX ) )
244             {
245                 if ( managedRepository != null )
246                 {
247                     try
248                     {
249                         if( isAuthorized( request, repositoryId ) )
250                         {
251                             LogicalResource logicalResource =
252                                 new LogicalResource( RepositoryPathUtil.getLogicalResource( locator.getResourcePath() ) );
253
254                             if ( isGet )
255                             {
256                                 resource = doGet( managedRepository, request, archivaLocator, logicalResource );
257                             }
258
259                             if ( isPut )
260                             {
261                                 resource = doPut( managedRepository, request, archivaLocator, logicalResource );
262                             }
263                         }
264                     }
265                     catch ( DavException de ) 
266                     {
267                         e = de;
268                         continue;
269                     }
270
271                     if( resource == null )
272                     {
273                         e = new DavException( HttpServletResponse.SC_NOT_FOUND, "Resource does not exist" );
274                     }
275                     else
276                     {   
277                         availableResources.add( resource );
278
279                         String logicalResource = RepositoryPathUtil.getLogicalResource( locator.getResourcePath() );
280                         resourcesInAbsolutePath.add( managedRepository.getRepoRoot() + logicalResource );
281                     }
282                 }
283                 else
284                 {
285                     e = new DavException( HttpServletResponse.SC_NOT_FOUND, "Repository does not exist" );
286                 }
287             }
288         }        
289         
290         if ( availableResources.isEmpty() )
291         {
292             throw e;
293         }
294         
295         String requestedResource = request.getRequestURI();
296         
297         // MRM-872 : merge all available metadata
298         // merge metadata only when requested via the repo group        
299         if ( ( repositoryRequest.isMetadata( requestedResource ) || ( requestedResource.endsWith( "metadata.xml.sha1" ) || requestedResource.endsWith( "metadata.xml.md5" ) ) ) &&
300             repoGroupConfig != null )
301         {   
302             // this should only be at the project level not version level!
303             if( isProjectReference( requestedResource ) )
304             {
305                 String artifactId = StringUtils.substringBeforeLast( requestedResource.replace( '\\', '/' ), "/" );
306                 artifactId = StringUtils.substringAfterLast( artifactId, "/" );
307                 
308                 ArchivaDavResource res = ( ArchivaDavResource ) availableResources.get( 0 );
309                 String filePath = StringUtils.substringBeforeLast( res.getLocalResource().getAbsolutePath().replace( '\\', '/' ), "/" );                                
310                 filePath = filePath + "/maven-metadata-" + repoGroupConfig.getId() + ".xml";
311                 
312                 // for MRM-872 handle checksums of the merged metadata files 
313                 if( repositoryRequest.isSupportFile( requestedResource ) )
314                 {
315                     File metadataChecksum = new File( filePath + "." 
316                               + StringUtils.substringAfterLast( requestedResource, "." ) );                    
317                     if( metadataChecksum.exists() )
318                     {
319                         LogicalResource logicalResource =
320                             new LogicalResource( RepositoryPathUtil.getLogicalResource( locator.getResourcePath() ) );
321                                         
322                         ArchivaDavResource metadataChecksumResource =
323                             new ArchivaDavResource( metadataChecksum.getAbsolutePath(), logicalResource.getPath(), null,
324                                                     request.getRemoteAddr(), request.getDavSession(), archivaLocator, this,
325                                                     mimeTypes, auditListeners, consumers, archivaXworkUser );
326                         availableResources.add( 0, metadataChecksumResource );
327                     }
328                 }
329                 else
330                 {   // merge the metadata of all repos under group
331                     ArchivaRepositoryMetadata mergedMetadata = new ArchivaRepositoryMetadata();
332                     for ( String resourceAbsPath : resourcesInAbsolutePath )    
333                     {   
334                         try
335                         {   
336                             File metadataFile = new File( resourceAbsPath );
337                             ArchivaRepositoryMetadata repoMetadata = RepositoryMetadataReader.read( metadataFile );
338                             mergedMetadata = RepositoryMetadataMerge.merge( mergedMetadata, repoMetadata );
339                         }
340                         catch ( RepositoryMetadataException r )
341                         {
342                             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
343                                                     "Error occurred while reading metadata file." );
344                         }                
345                     }        
346                     
347                     try
348                     {   
349                         File resourceFile = writeMergedMetadataToFile( mergedMetadata, filePath );   
350                         
351                         LogicalResource logicalResource =
352                             new LogicalResource( RepositoryPathUtil.getLogicalResource( locator.getResourcePath() ) );
353                                         
354                         ArchivaDavResource metadataResource =
355                             new ArchivaDavResource( resourceFile.getAbsolutePath(), logicalResource.getPath(), null,
356                                                     request.getRemoteAddr(), request.getDavSession(), archivaLocator, this,
357                                                     mimeTypes, auditListeners, consumers, archivaXworkUser );
358                         availableResources.add( 0, metadataResource );
359                     }
360                     catch ( RepositoryMetadataException r )
361                     {                
362                         throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
363                                                 "Error occurred while writing metadata file." );
364                     }
365                     catch ( IOException ie )
366                     {
367                         throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
368                             "Error occurred while generating checksum files." );
369                     }
370                     catch ( DigesterException de )
371                     {
372                         throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
373                             "Error occurred while generating checksum files." );
374                     }
375                 }
376             }
377         }
378                 
379         DavResource resource = availableResources.get( 0 );               
380         setHeaders(response, locator, resource );
381
382         // compatibility with MRM-440 to ensure browsing the repository works ok
383         if ( resource.isCollection() && !request.getRequestURI().endsWith("/" ) )
384         {
385             throw new BrowserRedirectException( resource.getHref() );
386         }
387         resource.addLockManager(lockManager);
388         return resource;
389     }
390
391     public DavResource createResource( final DavResourceLocator locator, final DavSession davSession )
392         throws DavException
393     {
394         checkLocatorIsInstanceOfRepositoryLocator( locator );
395         ArchivaDavResourceLocator archivaLocator = (ArchivaDavResourceLocator) locator;
396
397         DavResource resource = null;
398         if ( !locator.getResourcePath().startsWith( ArchivaDavResource.HIDDEN_PATH_PREFIX ) )
399         {
400             ManagedRepositoryContent managedRepository = getManagedRepository( archivaLocator.getRepositoryId() );
401             String logicalResource = RepositoryPathUtil.getLogicalResource( locator.getResourcePath() );
402             File resourceFile = new File( managedRepository.getRepoRoot(), logicalResource );
403             resource =
404                 new ArchivaDavResource( resourceFile.getAbsolutePath(), logicalResource,
405                                         managedRepository.getRepository(), davSession, archivaLocator, this, mimeTypes,
406                                         auditListeners, consumers, archivaXworkUser );
407         }
408         resource.addLockManager(lockManager);
409         return resource;
410     }
411
412     private DavResource doGet( ManagedRepositoryContent managedRepository, DavServletRequest request,
413                                ArchivaDavResourceLocator locator, LogicalResource logicalResource )
414         throws DavException
415     {
416         File resourceFile = new File( managedRepository.getRepoRoot(), logicalResource.getPath() );
417         
418         //MRM-893, dont send back a file when user intentionally wants a directory
419         if ( locator.getHref( false ).endsWith( "/" ) )
420         {
421             if ( ! resourceFile.isDirectory() )
422             {
423                 //force a resource not found 
424                 return null;
425             }
426         }
427
428         ArchivaDavResource resource =
429             new ArchivaDavResource( resourceFile.getAbsolutePath(), logicalResource.getPath(),
430                                     managedRepository.getRepository(), request.getRemoteAddr(),
431                                     request.getDavSession(), locator, this, mimeTypes, auditListeners, consumers, archivaXworkUser );
432
433         if ( !resource.isCollection() )
434         {
435             boolean previouslyExisted = resourceFile.exists();
436
437             // At this point the incoming request can either be in default or
438             // legacy layout format.
439             boolean fromProxy = fetchContentFromProxies( managedRepository, request, logicalResource );
440
441             try
442             {
443                 // Perform an adjustment of the resource to the managed
444                 // repository expected path.
445                 String localResourcePath =
446                     repositoryRequest.toNativePath( logicalResource.getPath(), managedRepository );
447                 resourceFile = new File( managedRepository.getRepoRoot(), localResourcePath );
448             }
449             catch ( LayoutException e )
450             {
451                 if ( previouslyExisted )
452                 {
453                     return resource;
454                 }
455                 throw new DavException( HttpServletResponse.SC_NOT_FOUND, e );
456             }
457
458             // Attempt to fetch the resource from any defined proxy.
459             if ( fromProxy )
460             {
461                 String repositoryId = locator.getRepositoryId();
462                 String event = ( previouslyExisted ? AuditEvent.MODIFY_FILE : AuditEvent.CREATE_FILE ) + PROXIED_SUFFIX;
463                 triggerAuditEvent( request.getRemoteAddr(), repositoryId, logicalResource.getPath(), event );
464             }
465
466             if ( !resourceFile.exists() )
467             {
468                 resource = null;
469             }
470             else
471             {
472                 resource =
473                     new ArchivaDavResource( resourceFile.getAbsolutePath(), logicalResource.getPath(),
474                                             managedRepository.getRepository(), request.getRemoteAddr(),
475                                             request.getDavSession(), locator, this, mimeTypes, auditListeners,
476                                             consumers, archivaXworkUser );
477             }
478         }
479         return resource;
480     }
481
482     private DavResource doPut( ManagedRepositoryContent managedRepository, DavServletRequest request,
483                                ArchivaDavResourceLocator locator, LogicalResource logicalResource )
484         throws DavException
485     {
486         /*
487          * Create parent directories that don't exist when writing a file This actually makes this implementation not
488          * compliant to the WebDAV RFC - but we have enough knowledge about how the collection is being used to do this
489          * reasonably and some versions of Maven's WebDAV don't correctly create the collections themselves.
490          */
491
492         File rootDirectory = new File( managedRepository.getRepoRoot() );
493         File destDir = new File( rootDirectory, logicalResource.getPath() ).getParentFile();
494         if ( request.getMethod().equals(HTTP_PUT_METHOD) && !destDir.exists() )
495         {
496             destDir.mkdirs();
497             String relPath = PathUtil.getRelative( rootDirectory.getAbsolutePath(), destDir );
498             triggerAuditEvent( request.getRemoteAddr(), logicalResource.getPath(), relPath, AuditEvent.CREATE_DIR );
499         }
500
501         File resourceFile = new File( managedRepository.getRepoRoot(), logicalResource.getPath() );
502
503         return new ArchivaDavResource( resourceFile.getAbsolutePath(), logicalResource.getPath(),
504                                        managedRepository.getRepository(), request.getRemoteAddr(),
505                                        request.getDavSession(), locator, this, mimeTypes, auditListeners, consumers, archivaXworkUser );
506     }
507
508     private boolean fetchContentFromProxies( ManagedRepositoryContent managedRepository, DavServletRequest request,
509                                              LogicalResource resource )
510         throws DavException
511     {
512         if ( repositoryRequest.isSupportFile( resource.getPath() ) )
513         {
514             File proxiedFile = connectors.fetchFromProxies( managedRepository, resource.getPath() );
515
516             return ( proxiedFile != null );
517         }
518
519         // Is it a Metadata resource?
520         if ( repositoryRequest.isDefault( resource.getPath() ) && repositoryRequest.isMetadata( resource.getPath() ) )
521         {
522             return connectors.fetchMetatadaFromProxies(managedRepository, resource.getPath()) != null;
523         }
524
525         // Not any of the above? Then it's gotta be an artifact reference.
526         try
527         {
528             // Get the artifact reference in a layout neutral way.
529             ArtifactReference artifact = repositoryRequest.toArtifactReference( resource.getPath() );
530
531             if ( artifact != null )
532             {
533                 applyServerSideRelocation( managedRepository, artifact );
534
535                 File proxiedFile = connectors.fetchFromProxies( managedRepository, artifact );
536
537                 resource.setPath( managedRepository.toPath( artifact ) );
538
539                 return ( proxiedFile != null );
540             }
541         }
542         catch ( LayoutException e )
543         {
544             /* eat it */
545         }
546         catch ( ProxyDownloadException e )
547         {
548             log.error( e.getMessage(), e );
549             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR, "Unable to fetch artifact resource." );
550         }
551         return false;
552     }
553
554     /**
555      * A relocation capable client will request the POM prior to the artifact, and will then read meta-data and do
556      * client side relocation. A simplier client (like maven 1) will only request the artifact and not use the
557      * metadatas.
558      * <p>
559      * For such clients, archiva does server-side relocation by reading itself the &lt;relocation&gt; element in
560      * metadatas and serving the expected artifact.
561      */
562     protected void applyServerSideRelocation( ManagedRepositoryContent managedRepository, ArtifactReference artifact )
563         throws ProxyDownloadException
564     {
565         if ( "pom".equals( artifact.getType() ) )
566         {
567             return;
568         }
569
570         // Build the artifact POM reference
571         ArtifactReference pomReference = new ArtifactReference();
572         pomReference.setGroupId( artifact.getGroupId() );
573         pomReference.setArtifactId( artifact.getArtifactId() );
574         pomReference.setVersion( artifact.getVersion() );
575         pomReference.setType( "pom" );
576
577         // Get the artifact POM from proxied repositories if needed
578         connectors.fetchFromProxies( managedRepository, pomReference );
579
580         // Open and read the POM from the managed repo
581         File pom = managedRepository.toFile( pomReference );
582
583         if ( !pom.exists() )
584         {
585             return;
586         }
587
588         try
589         {
590             Model model = new MavenXpp3Reader().read( new FileReader( pom ) );
591             DistributionManagement dist = model.getDistributionManagement();
592             if ( dist != null )
593             {
594                 Relocation relocation = dist.getRelocation();
595                 if ( relocation != null )
596                 {
597                     // artifact is relocated : update the repositoryPath
598                     if ( relocation.getGroupId() != null )
599                     {
600                         artifact.setGroupId( relocation.getGroupId() );
601                     }
602                     if ( relocation.getArtifactId() != null )
603                     {
604                         artifact.setArtifactId( relocation.getArtifactId() );
605                     }
606                     if ( relocation.getVersion() != null )
607                     {
608                         artifact.setVersion( relocation.getVersion() );
609                     }
610                 }
611             }
612         }
613         catch ( FileNotFoundException e )
614         {
615             // Artifact has no POM in repo : ignore
616         }
617         catch ( IOException e )
618         {
619             // Unable to read POM : ignore.
620         }
621         catch ( XmlPullParserException e )
622         {
623             // Invalid POM : ignore
624         }
625     }
626
627     // TODO: remove?
628     private void triggerAuditEvent( String remoteIP, String repositoryId, String resource, String action )
629     {
630         String activePrincipal = archivaXworkUser.getActivePrincipal( ActionContext.getContext().getSession() );
631         AuditEvent event = new AuditEvent( repositoryId, activePrincipal, resource, action );
632         event.setRemoteIP( remoteIP );
633
634         for ( AuditListener listener : auditListeners )
635         {
636             listener.auditEvent( event );
637         }
638     }
639
640     public void addAuditListener( AuditListener listener )
641     {
642         this.auditListeners.add( listener );
643     }
644
645     public void clearAuditListeners()
646     {
647         this.auditListeners.clear();
648     }
649
650     public void removeAuditListener( AuditListener listener )
651     {
652         this.auditListeners.remove( listener );
653     }
654
655     private void setHeaders( DavServletResponse response, DavResourceLocator locator, DavResource resource )
656     {
657         // [MRM-503] - Metadata file need Pragma:no-cache response
658         // header.
659         if ( locator.getResourcePath().endsWith( "/maven-metadata.xml" ) )
660         {
661             response.addHeader( "Pragma", "no-cache" );
662             response.addHeader( "Cache-Control", "no-cache" );
663         }
664
665         //We need to specify this so connecting wagons can work correctly
666         response.addDateHeader("last-modified", resource.getModificationTime());
667
668         // TODO: [MRM-524] determine http caching options for other types of files (artifacts, sha1, md5, snapshots)
669     }
670
671     private ManagedRepositoryContent getManagedRepository( String respositoryId )
672         throws DavException
673     {
674         if ( respositoryId != null )
675         {
676             try
677             {
678                 return repositoryFactory.getManagedRepositoryContent( respositoryId );
679             }
680             catch ( RepositoryNotFoundException e )
681             {
682                 throw new DavException( HttpServletResponse.SC_NOT_FOUND, e );
683             }
684             catch ( RepositoryException e )
685             {
686                 throw new DavException( HttpServletResponse.SC_NOT_FOUND, e );
687             }
688         }
689         return null;
690     }
691
692     private void checkLocatorIsInstanceOfRepositoryLocator( DavResourceLocator locator )
693         throws DavException
694     {
695         if ( !( locator instanceof RepositoryLocator ) )
696         {
697             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
698                                     "Locator does not implement RepositoryLocator" );
699         }
700     }
701
702     class LogicalResource
703     {
704         private String path;
705
706         public LogicalResource( String path )
707         {
708             this.path = path;
709         }
710
711         public String getPath()
712         {
713             return path;
714         }
715
716         public void setPath( String path )
717         {
718             this.path = path;
719         }
720     }
721
722     protected boolean isAuthorized( DavServletRequest request, String repositoryId )
723         throws DavException
724     {
725         try
726         {
727             AuthenticationResult result = httpAuth.getAuthenticationResult( request, null );
728             SecuritySession securitySession = httpAuth.getSecuritySession();
729
730             return servletAuth.isAuthenticated( request, result ) &&
731                 servletAuth.isAuthorized( request, securitySession, repositoryId,
732                                           WebdavMethodUtil.isWriteMethod( request.getMethod() ) );
733         }
734         catch ( AuthenticationException e )
735         {
736             // safety check for MRM-911            
737             String guest = archivaXworkUser.getGuest();
738             try
739             {
740                 if( servletAuth.isAuthorized( guest, 
741                       ( ( ArchivaDavResourceLocator ) request.getRequestLocator() ).getRepositoryId() ) )
742                 {   
743                     return true;
744                 }
745             }
746             catch ( UnauthorizedException ae )
747             {
748                 throw new UnauthorizedDavException( repositoryId,
749                         "You are not authenticated and authorized to access any repository." );
750             }
751                         
752             throw new UnauthorizedDavException( repositoryId, "You are not authenticated" );
753         }
754         catch ( MustChangePasswordException e )
755         {
756             throw new UnauthorizedDavException( repositoryId, "You must change your password." );
757         }
758         catch ( AccountLockedException e )
759         {
760             throw new UnauthorizedDavException( repositoryId, "User account is locked." );
761         }
762         catch ( AuthorizationException e )
763         {
764             throw new DavException( HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
765                                     "Fatal Authorization Subsystem Error." );
766         }
767         catch ( UnauthorizedException e )
768         {
769             throw new UnauthorizedDavException( repositoryId, e.getMessage() );
770         }
771     }
772
773     private DavResource getResource( DavServletRequest request, List<String> repositories, ArchivaDavResourceLocator locator )
774         throws DavException
775     {
776         List<File> mergedRepositoryContents = new ArrayList<File>();
777         LogicalResource logicalResource =
778             new LogicalResource( RepositoryPathUtil.getLogicalResource( locator.getResourcePath() ) );
779
780         // flow:
781         // if the current user logged in has permission to any of the repositories, allow user to
782         // browse the repo group but displaying only the repositories which the user has permission to access.
783         // otherwise, prompt for authentication.
784
785         // put the current session in the session map which will be passed to ArchivaXworkUser
786         Map<String, Object> sessionMap = new HashMap<String, Object>();
787         if( request.getSession().getAttribute( SecuritySystemConstants.SECURITY_SESSION_KEY ) != null )
788         {
789             sessionMap.put( SecuritySystemConstants.SECURITY_SESSION_KEY,
790                             request.getSession().getAttribute( SecuritySystemConstants.SECURITY_SESSION_KEY ) );
791         }
792
793         String activePrincipal = archivaXworkUser.getActivePrincipal( sessionMap );
794         boolean allow = isAllowedToContinue( request, repositories, activePrincipal );
795
796         if( allow )
797         {
798             for( String repository : repositories )
799             {
800                 // for prompted authentication
801                 if( httpAuth.getSecuritySession() != null )
802                 {
803                     try
804                     {
805                         if( isAuthorized( request, repository ) )
806                         {
807                             getResource( locator, mergedRepositoryContents, logicalResource, repository );
808                         }
809                     }
810                     catch ( DavException e )
811                     {
812                         continue;
813                     }
814                 }
815                 else
816                 {
817                     // for the current user logged in
818                     try
819                     {
820                         if( servletAuth.isAuthorized( activePrincipal, repository ) )
821                         {
822                             getResource( locator, mergedRepositoryContents, logicalResource, repository );
823                         }
824                     }
825                     catch ( UnauthorizedException e )
826                     {
827                         continue;
828                     }
829                 }
830             }
831         }
832         else
833         {
834             throw new UnauthorizedDavException( locator.getRepositoryId(), "User not authorized." );
835         }
836
837         ArchivaVirtualDavResource resource =
838             new ArchivaVirtualDavResource( mergedRepositoryContents, logicalResource.getPath(), mimeTypes, locator, this );
839
840         // compatibility with MRM-440 to ensure browsing the repository group works ok
841         if ( resource.isCollection() && !request.getRequestURI().endsWith("/" ) )
842         {
843             throw new BrowserRedirectException( resource.getHref() );
844         }
845
846         return resource;
847     }
848
849     private void getResource( ArchivaDavResourceLocator locator, List<File> mergedRepositoryContents,
850                               LogicalResource logicalResource, String repository )
851         throws DavException
852     {
853         ManagedRepositoryContent managedRepository = null;
854
855         try
856         {
857             managedRepository = getManagedRepository( repository );
858         }
859         catch ( DavException de )
860         {
861             throw new DavException( HttpServletResponse.SC_NOT_FOUND, "Invalid managed repository <" +
862                 repository + ">" );
863         }
864
865         if ( !locator.getResourcePath().startsWith( ArchivaVirtualDavResource.HIDDEN_PATH_PREFIX ) )
866         {
867             if( managedRepository != null )
868             {
869                 File resourceFile = new File( managedRepository.getRepoRoot(), logicalResource.getPath() );
870                 if( resourceFile.exists() )
871                 {
872                     mergedRepositoryContents.add( resourceFile );
873                 }
874             }
875         }
876     }
877
878     /**
879      * Check if the current user is authorized to access any of the repos
880      *
881      * @param request
882      * @param repositories
883      * @param activePrincipal
884      * @return
885      */
886     private boolean isAllowedToContinue( DavServletRequest request, List<String> repositories, String activePrincipal )
887     {
888         boolean allow = false;
889
890
891         // if securitySession != null, it means that the user was prompted for authentication
892         if( httpAuth.getSecuritySession() != null )
893         {
894             for( String repository : repositories )
895             {
896                 try
897                 {
898                     if( isAuthorized( request, repository ) )
899                     {
900                         allow = true;
901                         break;
902                     }
903                 }
904                 catch( DavException e )
905                 {
906                     continue;
907                 }
908             }
909         }
910         else
911         {
912             for( String repository : repositories )
913             {
914                 try
915                 {
916                     if( servletAuth.isAuthorized( activePrincipal, repository ) )
917                     {
918                         allow = true;
919                         break;
920                     }
921                 }
922                 catch ( UnauthorizedException e )
923                 {
924                     continue;
925                 }
926             }
927         }
928
929         return allow;
930     }
931
932     private File writeMergedMetadataToFile( ArchivaRepositoryMetadata mergedMetadata, String outputFilename )
933         throws RepositoryMetadataException, DigesterException, IOException
934     {  
935         File outputFile = new File( outputFilename );        
936         if( outputFile.exists() )
937         {
938             FileUtils.deleteQuietly( outputFile );
939         }
940         
941         outputFile.getParentFile().mkdirs();
942         RepositoryMetadataWriter.write( mergedMetadata, outputFile );
943         
944         createChecksumFile( outputFilename, digestSha1 );
945         createChecksumFile( outputFilename, digestMd5 );
946         
947         return outputFile;
948     }
949     
950     private void createChecksumFile( String path, Digester digester )
951         throws DigesterException, IOException
952     {   
953         File checksumFile = new File( path + digester.getFilenameExtension() );        
954         if ( !checksumFile.exists() )
955         {
956             FileUtils.deleteQuietly( checksumFile );
957             checksum.createChecksum( new File( path ), digester );            
958         }
959         else if ( !checksumFile.isFile() )
960         {
961             log.error( "Checksum file is not a file." );
962         }
963     }
964     
965     private boolean isProjectReference( String requestedResource )
966     {  
967        try
968        {           
969            VersionedReference versionRef = metadataTools.toVersionedReference( requestedResource );           
970            return false;
971        }
972        catch ( RepositoryMetadataException re )
973        {
974            return true;
975        }
976     }
977 }