001/*
002 *  Copyright 2017 Anyware Services
003 *
004 *  Licensed under the Apache License, Version 2.0 (the "License");
005 *  you may not use this file except in compliance with the License.
006 *  You may obtain a copy of the License at
007 *
008 *      http://www.apache.org/licenses/LICENSE-2.0
009 *
010 *  Unless required by applicable law or agreed to in writing, software
011 *  distributed under the License is distributed on an "AS IS" BASIS,
012 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 *  See the License for the specific language governing permissions and
014 *  limitations under the License.
015 */
016package org.ametys.plugins.workspaces.dav;
017
018import java.io.InputStream;
019import java.util.Date;
020import java.util.HashMap;
021import java.util.Map;
022
023import javax.jcr.Node;
024import javax.jcr.lock.Lock;
025import javax.jcr.lock.LockManager;
026import javax.servlet.http.HttpServletRequest;
027
028import org.apache.avalon.framework.parameters.Parameters;
029import org.apache.avalon.framework.service.ServiceException;
030import org.apache.avalon.framework.service.ServiceManager;
031import org.apache.avalon.framework.service.Serviceable;
032import org.apache.cocoon.acting.AbstractAction;
033import org.apache.cocoon.environment.ObjectModelHelper;
034import org.apache.cocoon.environment.Redirector;
035import org.apache.cocoon.environment.SourceResolver;
036import org.apache.cocoon.environment.http.HttpEnvironment;
037import org.apache.cocoon.environment.http.HttpResponse;
038
039import org.ametys.core.observation.Event;
040import org.ametys.core.observation.ObservationManager;
041import org.ametys.core.user.CurrentUserProvider;
042import org.ametys.core.user.UserIdentity;
043import org.ametys.plugins.explorer.ObservationConstants;
044import org.ametys.plugins.explorer.resources.ModifiableResource;
045import org.ametys.plugins.explorer.resources.ModifiableResourceCollection;
046import org.ametys.plugins.explorer.resources.actions.AddOrUpdateResourceHelper;
047import org.ametys.plugins.explorer.resources.actions.AddOrUpdateResourceHelper.ResourceOperationMode;
048import org.ametys.plugins.explorer.resources.actions.AddOrUpdateResourceHelper.ResourceOperationResult;
049import org.ametys.plugins.repository.AmetysObject;
050import org.ametys.plugins.repository.AmetysObjectResolver;
051import org.ametys.plugins.repository.RepositoryConstants;
052import org.ametys.plugins.repository.jcr.JCRAmetysObject;
053import org.ametys.plugins.repository.lock.LockHelper;
054import org.ametys.plugins.repository.version.VersionableAmetysObject;
055import org.ametys.plugins.workspaces.project.objects.Project;
056
057/**
058 * Action for WebDAV PUT method
059 *
060 */
061public class WebdavPutAction extends AbstractAction implements Serviceable
062{
063    /** The current user provider */
064    protected CurrentUserProvider _currentUserProvider;
065    /** Observer manager. */
066    protected ObservationManager _observationManager;
067
068    private AmetysObjectResolver _resolver;
069
070    private AddOrUpdateResourceHelper _addOrUpdateResourceHelper;
071
072    @Override
073    public void service(ServiceManager smanager) throws ServiceException
074    {
075        _currentUserProvider = (CurrentUserProvider) smanager.lookup(CurrentUserProvider.ROLE);
076        _observationManager = (ObservationManager) smanager.lookup(ObservationManager.ROLE);
077        _resolver = (AmetysObjectResolver) smanager.lookup(AmetysObjectResolver.ROLE);
078        _addOrUpdateResourceHelper = (AddOrUpdateResourceHelper) smanager.lookup(AddOrUpdateResourceHelper.ROLE);
079    }
080
081    public Map act(Redirector redirector, SourceResolver resolver, Map objectModel, String source, Parameters parameters) throws Exception
082    {
083        HttpServletRequest request = (HttpServletRequest) objectModel.get(HttpEnvironment.HTTP_REQUEST_OBJECT);
084
085        ModifiableResource resource = (ModifiableResource) request.getAttribute("resource");
086
087        if (resource != null)
088        {
089            // Resource exists, it is updated
090            if (resource.isLocked() && !LockHelper.isLockOwner(resource, _currentUserProvider.getUser()))
091            {
092                HttpResponse response = (HttpResponse) ObjectModelHelper.getResponse(objectModel);
093                response.setStatus(423);
094                return null;
095            }
096
097            if (resource instanceof JCRAmetysObject)
098            {
099                Node node = ((JCRAmetysObject) resource).getNode();
100
101                if (node.isLocked())
102                {
103                    LockManager lockManager = node.getSession().getWorkspace().getLockManager();
104
105                    Lock lock = lockManager.getLock(node.getPath());
106                    Node lockHolder = lock.getNode();
107
108                    lockManager.addLockToken(lockHolder.getProperty(RepositoryConstants.METADATA_LOCKTOKEN).getString());
109                }
110            }
111
112            InputStream is = request.getInputStream();
113
114            UserIdentity author = _currentUserProvider.getUser();
115            String mimeType = resource.getMimeType();
116            mimeType = mimeType == null ? "application/unknown" : mimeType;
117
118            resource.setData(is, mimeType, new Date(), author);
119
120            resource.saveChanges();
121            _checkpoint(resource);
122
123            Map<String, Object> eventParams = new HashMap<>();
124
125            eventParams.put(ObservationConstants.ARGS_ID, resource.getId());
126            eventParams.put(ObservationConstants.ARGS_NAME, resource.getName());
127            eventParams.put(ObservationConstants.ARGS_PATH, resource.getPath());
128            eventParams.put(ObservationConstants.ARGS_RESOURCE_PATH, resource.getResourcePath());
129            AmetysObject parent = resource.getParent();
130            if (parent != null)
131            {
132                eventParams.put(ObservationConstants.ARGS_PARENT_ID, parent.getId());
133                eventParams.put(ObservationConstants.ARGS_PARENT_PATH, parent.getPath());
134            }
135            _observationManager.notify(new Event(ObservationConstants.EVENT_RESOURCE_UPDATED, _currentUserProvider.getUser(), eventParams));
136        }
137        else
138        {
139            // Resource needs to be created.
140            String filePath = parameters.getParameter("path");
141            int lastSlashPos = filePath.lastIndexOf('/');
142            if (lastSlashPos > 0)
143            {
144                String folderPath = filePath.substring(0, lastSlashPos);
145                String fileName = filePath.substring(lastSlashPos + 1);
146                Project project = (Project) request.getAttribute("project");
147                AmetysObject folderAmetysObject = _resolver.resolveByPath(project.getPath() + "/ametys-internal:resources/" + folderPath);
148                if (folderAmetysObject instanceof ModifiableResourceCollection)
149                {
150                    ModifiableResourceCollection folder = (ModifiableResourceCollection) folderAmetysObject;
151                    _addOrUpdateResourceHelper.checkAddResourceRight(folder);
152                    
153                    InputStream is = request.getInputStream();
154                    ResourceOperationResult operationResult = _addOrUpdateResourceHelper.performResourceOperation(is, fileName, folder, ResourceOperationMode.ADD);
155                    if (operationResult.isSuccess())
156                    {
157                        resource = (ModifiableResource) operationResult.getResource();
158                    }
159                    else
160                    {
161                        getLogger().error("User '" + _currentUserProvider.getUser() + "' try create a ressource named '" + fileName + "' in parent '" + folder.getId() + "' but it failed with message : " + operationResult.getErrorMessage());
162                        HttpResponse response = (HttpResponse) ObjectModelHelper.getResponse(objectModel);
163                        int status = 500;
164                        switch(operationResult.getErrorMessage())
165                        {
166                            case "locked":
167                            case "locked-file":
168                                status = 423; // locked
169                                break;
170                            case "already-exist":
171                                status = 409; // conflict
172                                break;
173                            case "error":
174                            default:
175                                status = 500;
176                        }
177                        response.setStatus(status);
178                        return EMPTY_MAP;
179                    }
180                }
181                else
182                {
183                    getLogger().error("User '" + _currentUserProvider.getUser() + "' try create a ressource named '" + fileName + "' in parent '" + folderAmetysObject.getId() + "' but the parent is not a ModifiableResourceCollection");
184                    HttpResponse response = (HttpResponse) ObjectModelHelper.getResponse(objectModel);
185                    response.setStatus(409);
186                    return EMPTY_MAP;
187                }
188            }
189        }
190
191
192        return EMPTY_MAP;
193    }
194
195    private void _checkpoint(ModifiableResource resource)
196    {
197        if (resource instanceof VersionableAmetysObject)
198        {
199            // Create first version
200            ((VersionableAmetysObject) resource).checkpoint();
201        }
202    }
203}