Statistics
| Branch: | Tag: | Revision:

root / src / gr / ebs / gss / server / ejb / ExternalAPIBean.java @ 484428ad

History | View | Annotate | Download (101 kB)

1
/*
2
 * Copyright 2007, 2008, 2009 Electronic Business Systems Ltd.
3
 *
4
 * This file is part of GSS.
5
 *
6
 * GSS is free software: you can redistribute it and/or modify
7
 * it under the terms of the GNU General Public License as published by
8
 * the Free Software Foundation, either version 3 of the License, or
9
 * (at your option) any later version.
10
 *
11
 * GSS is distributed in the hope that it will be useful,
12
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14
 * GNU General Public License for more details.
15
 *
16
 * You should have received a copy of the GNU General Public License
17
 * along with GSS.  If not, see <http://www.gnu.org/licenses/>.
18
 */
19
package gr.ebs.gss.server.ejb;
20

    
21
import static gr.ebs.gss.server.configuration.GSSConfigurationFactory.getConfiguration;
22
import gr.ebs.gss.client.exceptions.DuplicateNameException;
23
import gr.ebs.gss.client.exceptions.GSSIOException;
24
import gr.ebs.gss.client.exceptions.InsufficientPermissionsException;
25
import gr.ebs.gss.client.exceptions.InvitationUsedException;
26
import gr.ebs.gss.client.exceptions.ObjectNotFoundException;
27
import gr.ebs.gss.client.exceptions.QuotaExceededException;
28
import gr.ebs.gss.server.domain.AuditInfo;
29
import gr.ebs.gss.server.domain.FileBody;
30
import gr.ebs.gss.server.domain.FileHeader;
31
import gr.ebs.gss.server.domain.FileTag;
32
import gr.ebs.gss.server.domain.FileUploadStatus;
33
import gr.ebs.gss.server.domain.Folder;
34
import gr.ebs.gss.server.domain.Group;
35
import gr.ebs.gss.server.domain.Invitation;
36
import gr.ebs.gss.server.domain.Nonce;
37
import gr.ebs.gss.server.domain.Permission;
38
import gr.ebs.gss.server.domain.User;
39
import gr.ebs.gss.server.domain.UserClass;
40
import gr.ebs.gss.server.domain.dto.FileBodyDTO;
41
import gr.ebs.gss.server.domain.dto.FileHeaderDTO;
42
import gr.ebs.gss.server.domain.dto.FolderDTO;
43
import gr.ebs.gss.server.domain.dto.GroupDTO;
44
import gr.ebs.gss.server.domain.dto.PermissionDTO;
45
import gr.ebs.gss.server.domain.dto.StatsDTO;
46
import gr.ebs.gss.server.domain.dto.UserDTO;
47

    
48
import java.io.File;
49
import java.io.FileInputStream;
50
import java.io.FileNotFoundException;
51
import java.io.FileOutputStream;
52
import java.io.IOException;
53
import java.io.InputStream;
54
import java.io.StringWriter;
55
import java.io.UnsupportedEncodingException;
56
import java.util.ArrayList;
57
import java.util.Date;
58
import java.util.Iterator;
59
import java.util.LinkedHashSet;
60
import java.util.LinkedList;
61
import java.util.List;
62
import java.util.Locale;
63
import java.util.Random;
64
import java.util.Set;
65
import java.util.StringTokenizer;
66

    
67
import javax.ejb.EJB;
68
import javax.ejb.EJBException;
69
import javax.ejb.EJBTransactionRolledbackException;
70
import javax.ejb.Stateless;
71
import javax.ejb.TransactionAttribute;
72
import javax.ejb.TransactionAttributeType;
73
import javax.jms.Connection;
74
import javax.jms.ConnectionFactory;
75
import javax.jms.JMSException;
76
import javax.jms.MapMessage;
77
import javax.jms.MessageProducer;
78
import javax.jms.Queue;
79
import javax.jms.QueueConnectionFactory;
80
import javax.jms.Session;
81
import javax.naming.Context;
82
import javax.naming.InitialContext;
83
import javax.naming.NamingException;
84
import javax.persistence.PersistenceException;
85
import javax.xml.parsers.DocumentBuilder;
86
import javax.xml.parsers.DocumentBuilderFactory;
87
import javax.xml.parsers.ParserConfigurationException;
88
import javax.xml.transform.OutputKeys;
89
import javax.xml.transform.Transformer;
90
import javax.xml.transform.TransformerConfigurationException;
91
import javax.xml.transform.TransformerException;
92
import javax.xml.transform.TransformerFactory;
93
import javax.xml.transform.dom.DOMSource;
94
import javax.xml.transform.stream.StreamResult;
95

    
96
import org.apache.commons.httpclient.HttpClient;
97
import org.apache.commons.httpclient.HttpException;
98
import org.apache.commons.httpclient.NameValuePair;
99
import org.apache.commons.httpclient.methods.GetMethod;
100
import org.apache.commons.httpclient.methods.PostMethod;
101
import org.apache.commons.httpclient.methods.StringRequestEntity;
102
import org.apache.commons.lang.StringUtils;
103
import org.apache.commons.logging.Log;
104
import org.apache.commons.logging.LogFactory;
105
import org.hibernate.exception.ConstraintViolationException;
106
import org.w3c.dom.DOMException;
107
import org.w3c.dom.Document;
108
import org.w3c.dom.Node;
109
import org.w3c.dom.NodeList;
110
import org.xml.sax.SAXException;
111

    
112
import com.novell.ldap.LDAPAttribute;
113
import com.novell.ldap.LDAPAttributeSet;
114
import com.novell.ldap.LDAPConnection;
115
import com.novell.ldap.LDAPEntry;
116
import com.novell.ldap.LDAPException;
117

    
118
/**
119
 * The concrete implementation of the ExternalAPI interface.
120
 *
121
 * @author past
122
 */
123
@Stateless
124
public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
125
        /**
126
         * The default MIME type for files without an explicit one.
127
         */
128
        private static final String DEFAULT_MIME_TYPE = "application/octet-stream";
129

    
130
        /**
131
         * The size of the buffer that is used to temporarily store chunks of
132
         * uploaded files, while storing them to the file repository.
133
         */
134
        private static final int UPLOAD_BUFFER_SIZE = 1024 * 4;
135

    
136
        /**
137
         * The logger.
138
         */
139
        private static Log logger = LogFactory.getLog(ExternalAPIBean.class);
140

    
141
        /**
142
         * Injected reference to the GSSDAO data access facade.
143
         */
144
        @EJB
145
        private GSSDAO dao;
146

    
147

    
148
        /**
149
         * A cached random number generator for creating unique filenames.
150
         */
151
        private static Random random = new Random();
152

    
153
        /**
154
         * Mark the folder and all of its parent folders as modified from the specified user.
155
         */
156
        private void touchParentFolders(Folder folder, User user, Date date) {
157
                Folder f = folder;
158
                while (f != null) {
159
                        AuditInfo ai = f.getAuditInfo();
160
                        ai.setModifiedBy(user);
161
                        ai.setModificationDate(date);
162
                        f.setAuditInfo(ai);
163
                        f = f.getParent();
164
                }
165
        }
166

    
167
        @Override
168
        public FolderDTO getRootFolder(Long userId) throws ObjectNotFoundException {
169
                if (userId == null)
170
                        throw new ObjectNotFoundException("No user specified");
171
                Folder folder = dao.getRootFolder(userId);
172
                return folder.getDTO();
173
        }
174

    
175
        @Override
176
        public FolderDTO getFolder(final Long userId, final Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
177
                if (userId == null)
178
                        throw new ObjectNotFoundException("No user specified");
179
                if (folderId == null)
180
                        throw new ObjectNotFoundException("No folder specified");
181
                final User user = dao.getEntityById(User.class, userId);
182
                final Folder folder = dao.getEntityById(Folder.class, folderId);
183
                // Check permissions
184
                if (!folder.hasReadPermission(user))
185
                        throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
186
                return folder.getDTO();
187
        }
188

    
189
        @Override
190
        public User getUser(Long userId) throws ObjectNotFoundException {
191
                if (userId == null)
192
                        throw new ObjectNotFoundException("No user specified");
193
                return dao.getEntityById(User.class, userId);
194
        }
195

    
196
        @Override
197
        public UserDTO getUserDTO(final Long userId) throws ObjectNotFoundException {
198
                return getUser(userId).getDTO();
199
        }
200

    
201
        @Override
202
        public GroupDTO getGroup(final Long groupId) throws ObjectNotFoundException {
203
                if (groupId == null)
204
                        throw new ObjectNotFoundException("No group specified");
205
                final Group group = dao.getEntityById(Group.class, groupId);
206
                return group.getDTO();
207
        }
208

    
209
        @Override
210
        public GroupDTO getGroup(Long userId, String name) throws ObjectNotFoundException {
211
                if (userId == null)
212
                        throw new ObjectNotFoundException("No user specified");
213
                if (name == null)
214
                        throw new ObjectNotFoundException("No group specified");
215
                User user = dao.getEntityById(User.class, userId);
216
                List<Group> groups = user.getGroupsSpecified();
217
                for (Group group: groups)
218
                        if (group.getName().equals(name))
219
                                return group.getDTO();
220
                throw new ObjectNotFoundException("Group " + name + " not found");
221
        }
222

    
223
        @Override
224
        public List<GroupDTO> getGroups(final Long userId) throws ObjectNotFoundException {
225
                if (userId == null)
226
                        throw new ObjectNotFoundException("No user specified");
227
                final List<Group> groups = dao.getGroups(userId);
228
                final List<GroupDTO> result = new ArrayList<GroupDTO>();
229
                for (final Group g : groups)
230
                        result.add(g.getDTO());
231
                return result;
232
        }
233

    
234
        @Override
235
        public List<FileHeaderDTO> getFiles(Long userId, Long folderId, boolean ignoreDeleted)
236
                        throws ObjectNotFoundException, InsufficientPermissionsException {
237
                // Validate.
238
                if (userId == null)
239
                        throw new ObjectNotFoundException("No user specified");
240
                if (folderId == null)
241
                        throw new ObjectNotFoundException("No folder specified");
242
                User user = dao.getEntityById(User.class, userId);
243
                Folder folder = dao.getEntityById(Folder.class, folderId);
244
                if (!folder.hasReadPermission(user))
245
                        throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
246
                // Do the actual work.
247
                List<FileHeaderDTO> result = new ArrayList<FileHeaderDTO>();
248
                List<FileHeader> files = dao.getFiles(folderId, userId, ignoreDeleted);
249
                for (FileHeader f : files)
250
                        result.add(f.getDTO());
251
                return result;
252
        }
253

    
254
        @Override
255
        public List<UserDTO> getUsers(final Long userId, final Long groupId) throws ObjectNotFoundException {
256
                // Validate.
257
                if (userId == null)
258
                        throw new ObjectNotFoundException("No user specified");
259
                if (groupId == null)
260
                        throw new ObjectNotFoundException("No group specified");
261

    
262
                // Do the actual work.
263
                final List<User> users = dao.getUsers(groupId);
264
                final List<UserDTO> result = new ArrayList<UserDTO>();
265
                for (final User u : users)
266
                        result.add(u.getDTO());
267
                return result;
268
        }
269

    
270
        @Override
271
        public FolderDTO createFolder(Long userId, Long parentId, String name)
272
                        throws DuplicateNameException, ObjectNotFoundException, InsufficientPermissionsException {
273
                // Validate.
274
                if (userId == null)
275
                        throw new ObjectNotFoundException("No user specified");
276
                if (StringUtils.isEmpty(name))
277
                        throw new ObjectNotFoundException("New folder name is empty");
278
                if (parentId == null)
279
                        throw new ObjectNotFoundException("No parent specified");
280
                if (dao.existsFolderOrFile(parentId, name))
281
                        throw new DuplicateNameException("A folder or file with the name '" +
282
                                                name + "' already exists at this level");
283

    
284
                User creator = dao.getEntityById(User.class, userId);
285

    
286
                Folder parent = null;
287
                try {
288
                        parent = dao.getEntityById(Folder.class, parentId);
289
                } catch (ObjectNotFoundException onfe) {
290
                        // Supply a more accurate problem description.
291
                        throw new ObjectNotFoundException("Parent folder not found");
292
                }
293
                if (!parent.hasWritePermission(creator))
294
                        throw new InsufficientPermissionsException("You don't have the permissions" +
295
                                        " to write to this folder");
296

    
297
                // Do the actual work.
298
                return createFolder(name, parent, creator);
299
        }
300

    
301
        /**
302
         * Create a new folder with the provided name, parent and owner.
303
         *
304
         * @param name
305
         * @param parent
306
         * @param creator
307
         * @return the new folder
308
         */
309
        private FolderDTO createFolder(String name, Folder parent, User creator) {
310
                Folder folder = new Folder();
311
                folder.setName(name);
312
                if (parent != null) {
313
                        parent.addSubfolder(folder);
314
                        folder.setOwner(parent.getOwner());
315
                } else
316
                        folder.setOwner(creator);
317

    
318
                Date now = new Date();
319
                AuditInfo auditInfo = new AuditInfo();
320
                auditInfo.setCreatedBy(creator);
321
                auditInfo.setCreationDate(now);
322
                auditInfo.setModifiedBy(creator);
323
                auditInfo.setModificationDate(now);
324
                folder.setAuditInfo(auditInfo);
325
                touchParentFolders(folder, auditInfo.getModifiedBy(), auditInfo.getModificationDate());
326

    
327
                if (parent != null)
328
                        for (Permission p : parent.getPermissions()) {
329
                                Permission permission = new Permission();
330
                                permission.setGroup(p.getGroup());
331
                                permission.setUser(p.getUser());
332
                                permission.setRead(p.getRead());
333
                                permission.setWrite(p.getWrite());
334
                                permission.setModifyACL(p.getModifyACL());
335
                                folder.addPermission(permission);
336
                        }
337
                else {
338
                        Permission permission = new Permission();
339
                        permission.setUser(creator);
340
                        permission.setRead(true);
341
                        permission.setWrite(true);
342
                        permission.setModifyACL(true);
343
                        folder.addPermission(permission);
344
                }
345

    
346
                if(parent != null)
347
                        folder.setReadForAll(parent.isReadForAll());
348

    
349
                dao.create(folder);
350
                return folder.getDTO();
351
        }
352

    
353
        @Override
354
        public void deleteFolder(final Long userId, final Long folderId) throws InsufficientPermissionsException, ObjectNotFoundException {
355
                // Validate.
356
                if (userId == null)
357
                        throw new ObjectNotFoundException("No user specified");
358
                if (folderId == null)
359
                        throw new ObjectNotFoundException("No folder specified");
360

    
361
                // Do the actual work.
362
                final Folder folder = dao.getEntityById(Folder.class, folderId);
363
                final Folder parent = folder.getParent();
364
                if (parent == null)
365
                        throw new ObjectNotFoundException("Deleting the root folder is not allowed");
366
                final User user = dao.getEntityById(User.class, userId);
367
                if (!folder.hasDeletePermission(user)) {
368
                        logger.info("User " + user.getId() + " cannot delete folder " + folder.getName() + "(" + folder.getId() + ")");
369
                        throw new InsufficientPermissionsException("User " + user.getId() + " cannot delete folder " + folder.getName() + "(" + folder.getId() + ")");
370
                }
371
                removeSubfolderFiles(folder);
372
                parent.removeSubfolder(folder);
373
                dao.delete(folder);
374
                touchParentFolders(parent, user, new Date());
375
        }
376

    
377
        /**
378
         * Traverses the folder and deletes all actual files (file system)
379
         * regardless of permissions
380
         *
381
         * @param folder
382
         */
383
        private void removeSubfolderFiles(Folder folder) {
384
                //remove files for all subfolders
385
                for (Folder subfolder:folder.getSubfolders())
386
                        removeSubfolderFiles(subfolder);
387
                //remove this folder's file bodies (actual files)
388
                for (FileHeader file:folder.getFiles()) {
389
                        for (FileBody body:file.getBodies())
390
                                deleteActualFile(body.getStoredFilePath());
391
                        indexFile(file.getId(), true);
392
                }
393
        }
394

    
395
        @Override
396
        @SuppressWarnings("unchecked")
397
        public List<FolderDTO> getSubfolders(Long userId, Long folderId)
398
                        throws ObjectNotFoundException, InsufficientPermissionsException {
399
                if (userId == null)
400
                        throw new ObjectNotFoundException("No user specified");
401
                if (folderId == null)
402
                        throw new ObjectNotFoundException("No folder specified");
403
                User user = dao.getEntityById(User.class, userId);
404
                Folder folder = dao.getEntityById(Folder.class, folderId);
405
                if (!folder.hasReadPermission(user))
406
                        throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
407
                List<FolderDTO> result = new ArrayList<FolderDTO>();
408
                if (folder.hasReadPermission(user))
409
                        for (Folder f : folder.getSubfolders())
410
                                if (f.hasReadPermission(user) && !f.isDeleted())
411
                                        result.add(f.getDTO());
412
                return result;
413
        }
414

    
415
        @Override
416
        public FolderDTO updateFolder(Long userId, Long folderId, String folderName,
417
                                Boolean readForAll,
418
                                Set<PermissionDTO> permissions)
419
                        throws InsufficientPermissionsException, ObjectNotFoundException,
420
                        DuplicateNameException {
421

    
422
                // Validate.
423
                if (userId == null)
424
                        throw new ObjectNotFoundException("No user specified");
425
                if (folderId == null)
426
                        throw new ObjectNotFoundException("No folder specified");
427

    
428
                Folder folder = dao.getEntityById(Folder.class, folderId);
429
                User user = dao.getEntityById(User.class, userId);
430
                if (folderName != null && !folder.hasWritePermission(user))
431
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
432
                if(permissions != null && !permissions.isEmpty() && !folder.hasModifyACLPermission(user))
433
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
434
                // Check permissions for making file public.
435
                if (readForAll != null && !user.equals(folder.getOwner()))
436
                                throw new InsufficientPermissionsException("Only the owner can make a folder public or not public");
437

    
438
                Folder parent = folder.getParent();
439
                if (folderName != null) {
440
                        if (parent != null)
441
                                if (!folder.getName().equals(folderName) && dao.existsFolderOrFile(parent.getId(), folderName))
442
                                        throw new DuplicateNameException("A folder or file with the name '" + folderName + "' already exists at this level");
443

    
444
                        // Do the actual modification.
445
                        folder.setName(folderName);
446
                }
447
                if (permissions != null)
448
                        setFolderPermissions(user, folder, permissions);
449
                if (readForAll != null)
450
                        setFolderReadForAll(user, folder, readForAll);
451
                folder.getAuditInfo().setModificationDate(new Date());
452
                folder.getAuditInfo().setModifiedBy(user);
453
                dao.update(folder);
454
                touchParentFolders(folder, user, new Date());
455
                return folder.getDTO();
456
        }
457

    
458
        @Override
459
        public void createGroup(final Long userId, final String name) throws ObjectNotFoundException, DuplicateNameException {
460
                // Validate.
461
                if (userId == null)
462
                        throw new ObjectNotFoundException("No user specified");
463
                if (StringUtils.isEmpty(name))
464
                        throw new ObjectNotFoundException("New group name is empty");
465
                if (name.indexOf('/')>=0)
466
                        throw new IllegalArgumentException("Character '/' is not allowed in group name");
467
                if (dao.existsGroup(userId, name))
468
                        throw new DuplicateNameException("A group with the name '" + name + "' already exists");
469

    
470
                // TODO: Check permissions
471

    
472
                final User owner = dao.getEntityById(User.class, userId);
473

    
474
                // Do the actual work.
475
                owner.createGroup(name);
476
        }
477

    
478
        @Override
479
        public void deleteGroup(final Long userId, final Long groupId) throws ObjectNotFoundException, InsufficientPermissionsException {
480
                // Validate.
481
                if (userId == null)
482
                        throw new ObjectNotFoundException("No user specified");
483
                if (groupId == null)
484
                        throw new ObjectNotFoundException("No group specified");
485

    
486
                // Do the actual work.
487
                final User owner = dao.getEntityById(User.class, userId);
488
                final Group group = dao.getEntityById(Group.class, groupId);
489
                final Date now = new Date();
490
                // Only delete the group if actually owned by the user.
491
                if (group.getOwner().equals(owner)) {
492
                        List<Folder> folders = dao.getFoldersPermittedForGroup(userId, groupId);
493
                        for (Folder f : folders){
494
                                f.getPermissions().removeAll(group.getPermissions());
495
                                touchFolder(f,owner,now);
496
                                for(FileHeader file : f.getFiles()){
497
                                        file.getPermissions().removeAll(group.getPermissions());
498
                                        touchFile(file,owner,now);
499
                                }
500
                        }
501
                        List<FileHeader> files = dao.getSharedFilesNotInSharedFolders(userId);
502
                        for(FileHeader h : files){
503
                                h.getPermissions().removeAll(group.getPermissions());
504
                                touchFile(h,owner,now);
505
                        }
506
                        owner.removeSpecifiedGroup(group);
507
                        dao.delete(group);
508
                }
509
                else throw new InsufficientPermissionsException("You are not the owner of this group");
510
        }
511

    
512
        @Override
513
        public FileHeaderDTO createFile(Long userId, Long folderId, String name, String mimeType, InputStream stream)
514
                        throws DuplicateNameException, ObjectNotFoundException, GSSIOException,
515
                        InsufficientPermissionsException, QuotaExceededException {
516
                File file = null;
517
                try {
518
                        file = uploadFile(stream, userId);
519
                } catch ( IOException ioe) {
520
                        // Supply a more accurate problem description.
521
                        throw new GSSIOException("Problem creating file",ioe);
522
                }
523
                return createFile(userId, folderId, name, mimeType, file.length(), file.getAbsolutePath());
524
        }
525

    
526
        /* (non-Javadoc)
527
         * @see gr.ebs.gss.server.ejb.ExternalAPIRemote#indexFile(java.lang.Long, boolean)
528
         */
529
        @Override
530
        public void indexFile(Long fileId, boolean delete) {
531
                Connection qConn = null;
532
                Session session = null;
533
                MessageProducer sender = null;
534
                try {
535
                        Context jndiCtx = new InitialContext();
536
                        ConnectionFactory factory = (QueueConnectionFactory) jndiCtx.lookup("java:/JmsXA");
537
                        Queue queue = (Queue) jndiCtx.lookup("queue/gss-indexingQueue");
538
                        qConn = factory.createConnection();
539
                        session = qConn.createSession(false, Session.AUTO_ACKNOWLEDGE);
540
                        sender = session.createProducer(queue);
541

    
542
                        MapMessage map = session.createMapMessage();
543
                        map.setObject("id", fileId);
544
                        map.setBoolean("delete", delete);
545
                        sender.send(map);
546
                }
547
                catch (NamingException e) {
548
                        logger.error("Index was not updated: ", e);
549
                }
550
                catch (JMSException e) {
551
                        logger.error("Index was not updated: ", e);
552
                }
553
                finally {
554
                        try {
555
                                if (sender != null)
556
                                        sender.close();
557
                                if (session != null)
558
                                        session.close();
559
                                if (qConn != null)
560
                                        qConn.close();
561
                        }
562
                        catch (JMSException e) {
563
                                logger.warn(e);
564
                        }
565
                }
566
        }
567

    
568

    
569

    
570
        /**
571
         * A helper method that generates a unique file path for a stored file. The
572
         * files are stored using random hash names that are distributed evenly in
573
         * a 2-level tree of subdirectories named after the first two hex characters
574
         * in the name. For example, file ab1234cd5769f will be stored in the path
575
         * /file-repository-root/a/b/ab1234cd5769f. The directories will be created
576
         * if they don't already exist.
577
         *
578
         * @return a unique new file path
579
         */
580
        private String generateRepositoryFilePath() {
581
                String filename = Long.toHexString(random.nextLong());
582
                String fileRepositoryPath = getConfiguration().getString("fileRepositoryPath","/tmp");
583
                File root = new File(fileRepositoryPath);
584
                if (!root.exists())
585
                        root.mkdirs();
586
                File firstFolder = new File(root + File.separator + filename.substring(0, 1));
587
                if (!firstFolder.exists())
588
                        firstFolder.mkdir();
589
                File secondFolder = new File(firstFolder + File.separator + filename.substring(1, 2));
590
                if (!secondFolder.exists())
591
                        secondFolder.mkdir();
592
                return secondFolder + File.separator + filename;
593
        }
594

    
595
        @Override
596
        public void deleteFile(final Long userId, final Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
597
                // Validate.
598
                if (userId == null)
599
                        throw new ObjectNotFoundException("No user specified");
600
                if (fileId == null)
601
                        throw new ObjectNotFoundException("No file specified");
602

    
603
                // Do the actual work.
604
                final FileHeader file = dao.getEntityById(FileHeader.class, fileId);
605
                final Folder parent = file.getFolder();
606
                if (parent == null)
607
                        throw new ObjectNotFoundException("The specified file has no parent folder");
608
                final User user = dao.getEntityById(User.class, userId);
609
                if (!file.hasDeletePermission(user))
610
                        throw new InsufficientPermissionsException("User " + user.getId() + " cannot delete file " + file.getName() + "(" + file.getId() + ")");
611
                for (final FileBody body : file.getBodies())
612
                        deleteActualFile(body.getStoredFilePath());
613
                dao.delete(file);
614
                touchParentFolders(parent, user, new Date());
615
                indexFile(fileId, true);
616
        }
617

    
618
        @Override
619
        public void deleteActualFile(String path) {
620
                if (path == null)
621
                        return;
622
                File file = new File(path);
623
                if (!file.delete())
624
                        logger.error("Could not delete file " + path);
625
        }
626

    
627
        @Override
628
        public void createTag(final Long userId, final Long fileHeaderId, final String tag) throws ObjectNotFoundException {
629
                if (userId == null)
630
                        throw new ObjectNotFoundException("No user specified");
631
                if (fileHeaderId == null)
632
                        throw new ObjectNotFoundException("No file specified");
633
                if (StringUtils.isEmpty(tag))
634
                        throw new ObjectNotFoundException("Tag is empty");
635

    
636
                final User user = dao.getEntityById(User.class, userId);
637
                final FileHeader fh = dao.getEntityById(FileHeader.class, fileHeaderId);
638
                final Folder parent = fh.getFolder();
639
                if (parent == null)
640
                        throw new ObjectNotFoundException("The specified file has no parent folder");
641
                user.addTag(fh, tag);
642
                touchParentFolders(parent, user, new Date());
643
        }
644

    
645
        @Override
646
        public Set<String> getUserTags(final Long userId) throws ObjectNotFoundException {
647
                return dao.getUserTags(userId);
648
        }
649

    
650
        @Override
651
        public void updateFile(Long userId, Long fileId, String name,
652
                                String tagSet, Date modificationDate, Boolean versioned,
653
                                Boolean readForAll,        Set<PermissionDTO> permissions)
654
                        throws DuplicateNameException, ObjectNotFoundException,        InsufficientPermissionsException {
655
                if (userId == null)
656
                        throw new ObjectNotFoundException("No user specified");
657
                if (fileId == null)
658
                        throw new ObjectNotFoundException("No file specified");
659
                FileHeader file = dao.getEntityById(FileHeader.class, fileId);
660
                final Folder parent = file.getFolder();
661
                if (parent == null)
662
                        throw new ObjectNotFoundException("The specified file has no parent folder");
663

    
664
                User user = dao.getEntityById(User.class, userId);
665
                // Check permissions for modifying the file metadata.
666
                if ((name != null || tagSet != null || modificationDate != null || versioned != null) && !file.hasWritePermission(user))
667
                        throw new InsufficientPermissionsException("User " + user.getId() +        " cannot update file " + file.getName() + "(" +        file.getId() + ")");
668
                // Check permissions for making file public.
669
                if (readForAll != null && !user.equals(file.getOwner()))
670
                                throw new InsufficientPermissionsException("Only the owner can make a file public or not public");
671
                // Check permissions for modifying the ACL.
672
                if(permissions != null && !permissions.isEmpty() &&        !file.hasModifyACLPermission(user))
673
                        throw new InsufficientPermissionsException("User " + user.getId() +        " cannot update the permissions on file " +        file.getName() + "(" + file.getId() + ")");
674

    
675
                if (name != null) {
676
                        // Do plain check for file already exists.
677
                        // Extreme concurrency case should be caught by constraint violation later.
678
                        if (dao.existsFolderOrFile(parent.getId(), name)) throw new DuplicateNameException("A file or folder with the name '" + name + "' already exists");
679
                        file.setName(name);
680
                }
681

    
682
                if (modificationDate != null)
683
                        file.getAuditInfo().setModificationDate(modificationDate);
684
                else
685
                        file.getAuditInfo().setModificationDate(new Date());
686
                file.getAuditInfo().setModifiedBy(user);
687

    
688
                List<FileTag> tags = file.getFileTags();
689
                if (tagSet != null) {
690
                        Iterator<FileTag> i = tags.iterator();
691
                        while (i.hasNext()) {
692
                                FileTag tag = i.next();
693
                                i.remove();
694
                                tag.setFile(null);
695
                                user.removeTag(tag);
696
                                dao.delete(tag);
697
                        }
698
                        dao.flush();
699
                        StringTokenizer st = new StringTokenizer(tagSet, ",");
700
                        while (st.hasMoreTokens())
701
                                new FileTag(user, file, st.nextToken().trim());
702
                }
703
                if (versioned != null && !file.isVersioned() == versioned) {
704
                        if (file.isVersioned())
705
                                removeOldVersions(userId, fileId);
706
                        file.setVersioned(versioned);
707
                }
708
                if (readForAll == null)
709
                        throw new ObjectNotFoundException("The 'public' value for the specific file hasn't been specified");
710
                if (user.equals(file.getOwner()))
711
                        file.setReadForAll(readForAll);
712
                if (permissions != null && !permissions.isEmpty())
713
                        setFilePermissions(file, permissions);
714

    
715
                /*
716
                 * Force constraint violation to manifest itself here.
717
                 * This should cover extreme concurrency cases that the simple check
718
                 * above hasn't caught.
719
                 */
720
                try {
721
                        dao.flush();
722
                }
723
                catch (EJBTransactionRolledbackException e) {
724
                        Throwable cause = e.getCause();
725
                        if (cause instanceof PersistenceException && cause.getCause() instanceof ConstraintViolationException)
726
                                throw new DuplicateNameException("A file or folder with the name '" + name + "' already exists");
727
                        throw e;
728
                }
729

    
730
                touchParentFolders(parent, user, new Date());
731

    
732
                // Re-index the file if it was modified.
733
                if (name != null || tagSet != null)
734
                        indexFile(fileId, false);
735
        }
736

    
737
        @Override
738
        public InputStream getFileContents(Long userId, Long fileId)
739
                        throws ObjectNotFoundException, InsufficientPermissionsException {
740
                if (userId == null)
741
                        throw new ObjectNotFoundException("No user specified");
742
                if (fileId == null)
743
                        throw new ObjectNotFoundException("No file specified");
744

    
745
                FileHeader header = dao.getEntityById(FileHeader.class, fileId);
746
                User user = dao.getEntityById(User.class, userId);
747
                if (!header.hasReadPermission(user)) {
748
                        logger.info("User " + user.getId() + " cannot read file " + header.getName() + "(" + fileId + ")");
749
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
750
                }
751

    
752
                File f = new File(header.getCurrentBody().getStoredFilePath());
753
                try {
754
                        return new FileInputStream(f);
755
                } catch (FileNotFoundException e) {
756
                        logger.error("Could not locate the contents of file " + f.getAbsolutePath());
757
                        throw new ObjectNotFoundException("The file contents could not be located");
758
                }
759
        }
760

    
761
        /* (non-Javadoc)
762
         * @see gr.ebs.gss.server.ejb.ExternalAPI#getFileContents(java.lang.Long, java.lang.Long, java.lang.Long)
763
         */
764
        @Override
765
        public InputStream getFileContents(Long userId, Long fileId, Long bodyId) throws ObjectNotFoundException, InsufficientPermissionsException {
766
                if (userId == null)
767
                        throw new ObjectNotFoundException("No user specified");
768
                if (fileId == null)
769
                        throw new ObjectNotFoundException("No file specified");
770
                if (bodyId == null)
771
                        throw new ObjectNotFoundException("No file specified");
772

    
773
                final FileHeader header = dao.getEntityById(FileHeader.class, fileId);
774
                final FileBody body = dao.getEntityById(FileBody.class, bodyId);
775
                final User user = dao.getEntityById(User.class, userId);
776
                if (!header.hasReadPermission(user)) {
777
                        logger.info("User " + user.getId() + " cannot read file " + header.getName() + "(" + fileId + ")");
778
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
779
                }
780

    
781
                File f = new File(body.getStoredFilePath());
782
                try {
783
                        return new FileInputStream(f);
784
                } catch (FileNotFoundException e) {
785
                        logger.error("Could not locate the contents of file " + f.getAbsolutePath());
786
                        throw new ObjectNotFoundException("The file contents could not be located");
787
                }
788
        }
789

    
790
        @Override
791
        public FileHeaderDTO getFile(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
792
                if (userId == null)
793
                        throw new ObjectNotFoundException("No user specified");
794
                if (fileId == null)
795
                        throw new ObjectNotFoundException("No file specified");
796
                final User user = dao.getEntityById(User.class, userId);
797
                final FileHeader file = dao.getEntityById(FileHeader.class, fileId);
798
                if (!file.hasReadPermission(user) && !file.getFolder().hasReadPermission(user))
799
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
800
                return file.getDTO();
801
        }
802

    
803
        @Override
804
        public FileBodyDTO getFileBody(Long userId, Long fileId, Long bodyId) throws ObjectNotFoundException, InsufficientPermissionsException {
805
                if (userId == null)
806
                        throw new ObjectNotFoundException("No user specified");
807
                if (fileId == null)
808
                        throw new ObjectNotFoundException("No file specified");
809
                User user = dao.getEntityById(User.class, userId);
810
                FileHeader file = dao.getEntityById(FileHeader.class, fileId);
811
                if (!file.hasReadPermission(user) && !file.getFolder().hasReadPermission(user))
812
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
813
                FileBody body = dao.getEntityById(FileBody.class, bodyId);
814
                return body.getDTO();
815
        }
816

    
817
        @Override
818
        public Object getResourceAtPath(Long ownerId, String path, boolean ignoreDeleted)
819
                        throws ObjectNotFoundException {
820
                if (ownerId == null)
821
                        throw new ObjectNotFoundException("No user specified");
822
                if (StringUtils.isEmpty(path))
823
                        throw new ObjectNotFoundException("No path specified");
824

    
825
                User owner = dao.getEntityById(User.class, ownerId);
826
                List<String> pathElements = new ArrayList<String>();
827
                StringTokenizer st = new StringTokenizer(path, "/");
828
                while (st.hasMoreTokens())
829
                        pathElements.add(st.nextToken());
830
                if (pathElements.size() < 1)
831
                        return getRootFolder(owner.getId());
832
                // Store the last element, since it requires special handling.
833
                String lastElement = pathElements.remove(pathElements.size() - 1);
834
                FolderDTO cursor = getRootFolder(owner.getId());
835
                // Traverse and verify the specified folder path.
836
                for (String pathElement : pathElements) {
837
                        cursor = getFolder(cursor.getId(), pathElement);
838
                        if (cursor.isDeleted())
839
                                throw new ObjectNotFoundException("Folder " + cursor.getPath() + " not found");
840
                }
841

    
842
                // Use the lastElement to retrieve the actual resource.
843
                Object resource = null;
844
                try {
845
                        FileHeaderDTO file = getFile(cursor.getId(), lastElement);
846
                        if (ignoreDeleted && file.isDeleted())
847
                                throw new ObjectNotFoundException("Resource not found");
848
                        resource = file;
849
                } catch (ObjectNotFoundException e) {
850
                        // Perhaps the requested resource is not a file, so
851
                        // check for folders as well.
852
                        FolderDTO folder = getFolder(cursor.getId(), lastElement);
853
                        if (ignoreDeleted && folder.isDeleted())
854
                                throw new ObjectNotFoundException("Resource not found");
855
                        resource = folder;
856
                }
857
                return resource;
858
        }
859

    
860
        /**
861
         * Retrieve a file for the specified user that has the specified name and
862
         * its parent folder has id equal to folderId.
863
         *
864
         * @param userId the ID of the current user
865
         * @param folderId the ID of the parent folder
866
         * @param name the name of the requested file
867
         * @return the file found
868
         * @throws ObjectNotFoundException if the specified folder or file was not
869
         *             found, with the exception message mentioning the precise
870
         *             problem
871
         */
872
        private FileHeaderDTO getFile(Long folderId, String name) throws ObjectNotFoundException {
873
                if (folderId == null)
874
                        throw new ObjectNotFoundException("No parent folder specified");
875
                if (StringUtils.isEmpty(name))
876
                        throw new ObjectNotFoundException("No file specified");
877

    
878
                FileHeader file = dao.getFile(folderId, name);
879
                return file.getDTO();
880
        }
881

    
882
        /**
883
         * Retrieve a folder for the specified user that has the specified name and
884
         * its parent folder has id equal to parentId.
885
         *
886
         * @param parentId the ID of the parent folder
887
         * @param name the name of the requested folder
888
         * @return the folder found
889
         * @throws ObjectNotFoundException if the specified folder or parent was not
890
         *             found, with the exception message mentioning the precise
891
         *             problem
892
         */
893
        private FolderDTO getFolder(Long parentId, String name) throws ObjectNotFoundException {
894
                if (parentId == null)
895
                        throw new ObjectNotFoundException("No parent folder specified");
896
                if (StringUtils.isEmpty(name))
897
                        throw new ObjectNotFoundException("No folder specified");
898

    
899
                Folder folder = dao.getFolder(parentId, name);
900
                return folder.getDTO();
901
        }
902

    
903
        private FileHeaderDTO updateFileContents(Long userId, Long fileId, String mimeType, InputStream resourceInputStream) throws ObjectNotFoundException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
904
                File file = null;
905
                try {
906
                        file = uploadFile(resourceInputStream, userId);
907
                } catch ( IOException ioe) {
908
                        // Supply a more accurate problem description.
909
                        throw new GSSIOException("Problem creating file",ioe);
910
                }
911
                return updateFileContents(userId, fileId, mimeType, file.length(), file.getAbsolutePath());
912
        }
913

    
914
        @Override
915
        public void copyFile(Long userId, Long fileId, String dest) throws ObjectNotFoundException, DuplicateNameException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
916
                if (userId == null)
917
                        throw new ObjectNotFoundException("No user specified");
918
                if (fileId == null)
919
                        throw new ObjectNotFoundException("No file specified");
920
                if (StringUtils.isEmpty(dest))
921
                        throw new ObjectNotFoundException("No destination specified");
922

    
923
                Object destination = getResourceAtPath(userId, getParentPath(dest), true);
924
                if (!(destination instanceof FolderDTO))
925
                        throw new ObjectNotFoundException("Destination parent folder not found");
926
                FolderDTO parent = (FolderDTO) destination;
927
                copyFile(userId, fileId, parent.getId(), getLastElement(dest));
928
        }
929

    
930
        @Override
931
        public void copyFileToPath(Long userId, Long ownerId, Long fileId, String dest) throws ObjectNotFoundException, DuplicateNameException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
932
                if (userId == null)
933
                        throw new ObjectNotFoundException("No user specified");
934
                if (ownerId == null)
935
                        throw new ObjectNotFoundException("No owner specified");
936
                if (fileId == null)
937
                        throw new ObjectNotFoundException("No file specified");
938
                if (StringUtils.isEmpty(dest))
939
                        throw new ObjectNotFoundException("No destination specified");
940

    
941
                Object destination = getResourceAtPath(ownerId, getParentPath(dest), true);
942
                if (!(destination instanceof FolderDTO))
943
                        throw new ObjectNotFoundException("Destination parent folder not found");
944
                FolderDTO parent = (FolderDTO) destination;
945
                copyFile(userId, fileId, parent.getId(), getLastElement(dest));
946
        }
947

    
948
        @Override
949
        public void copyFile(Long userId, Long fileId, Long destId, String destName) throws ObjectNotFoundException, DuplicateNameException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
950
                if (userId == null)
951
                        throw new ObjectNotFoundException("No user specified");
952
                if (fileId == null)
953
                        throw new ObjectNotFoundException("No file specified");
954
                if (destId == null)
955
                        throw new ObjectNotFoundException("No destination specified");
956
                if (StringUtils.isEmpty(destName))
957
                        throw new ObjectNotFoundException("No destination file name specified");
958

    
959
                FileHeader file = dao.getEntityById(FileHeader.class, fileId);
960
                Folder destination = dao.getEntityById(Folder.class, destId);
961
                User user = dao.getEntityById(User.class, userId);
962
                if (!file.hasReadPermission(user) || !destination.hasWritePermission(user))
963
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
964
                boolean versioned = file.isVersioned();
965
                int versionsNumber = file.getBodies().size();
966
                FileBody oldestBody = file.getBodies().get(0);
967
                assert oldestBody != null;
968
                File contents = new File(oldestBody.getStoredFilePath());
969
                try {
970
                        createFile(user.getId(), destination.getId(), destName, oldestBody.getMimeType(), new FileInputStream(contents));
971
                        FileHeader copiedFile = dao.getFile(destination.getId(), destName);
972
                        copiedFile.setVersioned(versioned);
973
                        dao.flush();
974
                        if (versionsNumber > 1)
975
                                for (int i = 1; i < versionsNumber; i++) {
976
                                        FileBody body = file.getBodies().get(i);
977
                                        assert body != null;
978
                                        contents = new File(body.getStoredFilePath());
979
                                        updateFileContents(user.getId(), copiedFile.getId(), body.getMimeType(), new FileInputStream(contents));
980
                                }
981
                        List<FileTag> tags = file.getFileTags();
982
                        for (FileTag tag : tags)
983
                                createTag(userId, copiedFile.getId(), tag.getTag());
984

    
985
                } catch (FileNotFoundException e) {
986
                        throw new ObjectNotFoundException("File contents not found for file " + contents.getAbsolutePath());
987
                }
988

    
989
        }
990

    
991
        @Override
992
        public void copyFolder(Long userId, Long folderId, String dest) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException {
993
                if (userId == null)
994
                        throw new ObjectNotFoundException("No user specified");
995
                if (folderId == null)
996
                        throw new ObjectNotFoundException("No folder specified");
997
                if (StringUtils.isEmpty(dest))
998
                        throw new ObjectNotFoundException("No destination specified");
999

    
1000
                Object destination = getResourceAtPath(userId, getParentPath(dest), true);
1001
                if (!(destination instanceof FolderDTO))
1002
                        throw new ObjectNotFoundException("Destination folder not found");
1003
                FolderDTO parent = (FolderDTO) destination;
1004
                copyFolder(userId, folderId, parent.getId(), getLastElement(dest));
1005
        }
1006

    
1007
        @Override
1008
        public void copyFolder(Long userId, Long folderId, Long destId, String destName) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException {
1009
                if (userId == null)
1010
                        throw new ObjectNotFoundException("No user specified");
1011
                if (folderId == null)
1012
                        throw new ObjectNotFoundException("No folder specified");
1013
                if (destId == null)
1014
                        throw new ObjectNotFoundException("No destination specified");
1015
                if (StringUtils.isEmpty(destName))
1016
                        throw new ObjectNotFoundException("No destination folder name specified");
1017
                Folder folder = dao.getEntityById(Folder.class, folderId);
1018
                Folder destination = dao.getEntityById(Folder.class, destId);
1019
                User user = dao.getEntityById(User.class, userId);
1020
                if (!destination.hasWritePermission(user) || !folder.hasReadPermission(user))
1021
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
1022
                createFolder(user.getId(), destination.getId(), destName);
1023
        }
1024

    
1025
        @Override
1026
        public void copyFolderStructureToPath(Long userId, Long ownerId, Long folderId, String dest) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException, GSSIOException, QuotaExceededException {
1027
                if (userId == null)
1028
                        throw new ObjectNotFoundException("No user specified");
1029
                if (ownerId == null)
1030
                        throw new ObjectNotFoundException("No owner specified");
1031
                if (folderId == null)
1032
                        throw new ObjectNotFoundException("No folder specified");
1033
                if (StringUtils.isEmpty(dest))
1034
                        throw new ObjectNotFoundException("No destination specified");
1035

    
1036
                Object destination = getResourceAtPath(ownerId, getParentPath(dest), true);
1037
                if (!(destination instanceof FolderDTO))
1038
                        throw new ObjectNotFoundException("Destination folder not found");
1039
                FolderDTO parent = (FolderDTO) destination;
1040
                copyFolderStructure(userId, folderId, parent.getId(), getLastElement(dest));
1041
        }
1042

    
1043
        @Override
1044
        public void copyFolderStructure(Long userId, Long folderId, Long destId, String destName) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException, GSSIOException, QuotaExceededException {
1045
                if (userId == null)
1046
                        throw new ObjectNotFoundException("No user specified");
1047
                if (folderId == null)
1048
                        throw new ObjectNotFoundException("No folder specified");
1049
                if (destId == null)
1050
                        throw new ObjectNotFoundException("No destination specified");
1051
                if (StringUtils.isEmpty(destName))
1052
                        throw new ObjectNotFoundException("No destination folder name specified");
1053

    
1054
                Folder folder = dao.getEntityById(Folder.class, folderId);
1055
                Folder destination = dao.getEntityById(Folder.class, destId);
1056
                final User user = dao.getEntityById(User.class, userId);
1057
                // XXX: quick fix need to copy only visible items to user (Source
1058
                // for bugs)
1059
                if (!folder.getOwner().getId().equals(userId) && !folder.hasReadPermission(user))
1060
                        return;
1061
                if(folder.isDeleted())//do not copy trashed folder and contents
1062
                        return;
1063
                if (!destination.hasWritePermission(user) || !folder.hasReadPermission(user))
1064
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
1065
                createFolder(user.getId(), destination.getId(), destName);
1066
                Folder createdFolder = dao.getFolder(destination.getId(), destName);
1067
                List<FileHeader> files = folder.getFiles();
1068
                if (files != null)
1069
                        for (FileHeader file : files)
1070
                                if(!file.isDeleted())
1071
                                        copyFile(userId, file.getId(), createdFolder.getId(), file.getName());
1072
                List<Folder> subFolders = folder.getSubfolders();
1073
                if (subFolders != null)
1074
                        for (Folder sub : subFolders)
1075
                                if(!sub.getId().equals(createdFolder.getId()))
1076
                                        copyFolderStructure(userId, sub.getId(), createdFolder.getId(), sub.getName());
1077

    
1078
        }
1079

    
1080
        /**
1081
         * For a provided path, remove the last element and return the rest, that is
1082
         * the path of the parent folder.
1083
         *
1084
         * @param path the specified path
1085
         * @return the path of the parent folder
1086
         * @throws ObjectNotFoundException if the provided string contains no path
1087
         *             delimiters
1088
         */
1089
        private String getParentPath(String path) throws ObjectNotFoundException {
1090
                int lastDelimiter = path.lastIndexOf('/');
1091
                if (lastDelimiter == 0)
1092
                        return "/";
1093
                if (lastDelimiter == -1)
1094
                        // No path found.
1095
                        throw new ObjectNotFoundException("There is no parent in the path: " + path);
1096
                else if (lastDelimiter < path.length() - 1)
1097
                        // Return the part before the delimiter.
1098
                        return path.substring(0, lastDelimiter);
1099
                else {
1100
                        // Remove the trailing delimiter and then recurse.
1101
                        String strippedTrail = path.substring(0, lastDelimiter);
1102
                        return getParentPath(strippedTrail);
1103
                }
1104
        }
1105

    
1106
        /**
1107
         * Get the last element in a path that denotes the file or folder name.
1108
         *
1109
         * @param path the provided path
1110
         * @return the last element in the path
1111
         */
1112
        private String getLastElement(String path) {
1113
                int lastDelimiter = path.lastIndexOf('/');
1114
                if (lastDelimiter == -1)
1115
                        // No path found.
1116
                        return path;
1117
                else if (lastDelimiter < path.length() - 1)
1118
                        // Return the part after the delimiter.
1119
                        return path.substring(lastDelimiter + 1);
1120
                else {
1121
                        // Remove the trailing delimiter and then recurse.
1122
                        String strippedTrail = path.substring(0, lastDelimiter);
1123
                        return getLastElement(strippedTrail);
1124
                }
1125
        }
1126

    
1127
        @Override
1128
        public void moveFileToTrash(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
1129
                if (userId == null)
1130
                        throw new ObjectNotFoundException("No user specified");
1131
                if (fileId == null)
1132
                        throw new ObjectNotFoundException("No file specified");
1133

    
1134
                // Do the actual work.
1135
                FileHeader file = dao.getEntityById(FileHeader.class, fileId);
1136
                Folder parent = file.getFolder();
1137
                if (parent == null)
1138
                        throw new ObjectNotFoundException("The specified file has no parent folder");
1139
                User user = dao.getEntityById(User.class, userId);
1140
                if (!file.hasDeletePermission(user))
1141
                        throw new InsufficientPermissionsException("User " + user.getId() + " cannot delete file " + file.getName() + "(" + file.getId() + ")");
1142

    
1143
                file.setDeleted(true);
1144
                dao.update(file);
1145
                touchParentFolders(parent, user, new Date());
1146
        }
1147

    
1148
        @Override
1149
        public void moveFileToPath(Long userId, Long ownerId, Long fileId, String dest) throws ObjectNotFoundException, InsufficientPermissionsException, QuotaExceededException {
1150
                if (userId == null)
1151
                        throw new ObjectNotFoundException("No user specified");
1152
                if (ownerId == null)
1153
                        throw new ObjectNotFoundException("No owner specified");
1154
                if (fileId == null)
1155
                        throw new ObjectNotFoundException("No file specified");
1156
                if (StringUtils.isEmpty(dest))
1157
                        throw new ObjectNotFoundException("No destination specified");
1158

    
1159
                Object destination = getResourceAtPath(ownerId, getParentPath(dest), true);
1160
                if (!(destination instanceof FolderDTO))
1161
                        throw new ObjectNotFoundException("Destination parent folder not found");
1162
                FolderDTO parent = (FolderDTO) destination;
1163
                moveFile(userId, fileId, parent.getId(), getLastElement(dest));
1164
        }
1165

    
1166
        @Override
1167
        public void moveFile(Long userId, Long fileId, Long destId, String destName) throws InsufficientPermissionsException, ObjectNotFoundException, QuotaExceededException {
1168
                if (userId == null)
1169
                        throw new ObjectNotFoundException("No user specified");
1170
                if (fileId == null)
1171
                        throw new ObjectNotFoundException("No file specified");
1172
                if (destId == null)
1173
                        throw new ObjectNotFoundException("No destination specified");
1174
                if (StringUtils.isEmpty(destName))
1175
                        throw new ObjectNotFoundException("No destination file name specified");
1176

    
1177
                FileHeader file = dao.getEntityById(FileHeader.class, fileId);
1178
                Folder source = file.getFolder();
1179
                Folder destination = dao.getEntityById(Folder.class, destId);
1180

    
1181
                User owner = dao.getEntityById(User.class, userId);
1182
                if (!file.hasDeletePermission(owner) || !destination.hasWritePermission(owner))
1183
                        throw new InsufficientPermissionsException("User " + owner.getId() + " cannot move file " + file.getName() + "(" + file.getId() + ")");
1184

    
1185
                // if the destination folder belongs to another user:
1186
                if (!file.getOwner().equals(destination.getOwner())) {
1187
                        // (a) check if the destination quota allows the move
1188
                        if(getQuotaLeft(destination.getOwner().getId()) < file.getTotalSize())
1189
                                throw new QuotaExceededException("Not enough free space available");
1190
                        User newOwner = destination.getOwner();
1191
                        // (b) if quota OK, change the owner of the file
1192
                        file.setOwner(newOwner);
1193
                        // if the file has no permission for the new owner, add it
1194
                        Permission ownerPermission = null;
1195
                        for (final Permission p : file.getPermissions())
1196
                                if (p.getUser() != null)
1197
                                        if (p.getUser().equals(newOwner)) {
1198
                                                ownerPermission = p;
1199
                                                break;
1200
                                        }
1201
                        if (ownerPermission == null) {
1202
                                ownerPermission = new Permission();
1203
                                ownerPermission.setUser(newOwner);
1204
                                file.addPermission(ownerPermission);
1205
                        }
1206
                        ownerPermission.setRead(true);
1207
                        ownerPermission.setWrite(true);
1208
                        ownerPermission.setModifyACL(true);
1209
                }
1210
                // move the file to the destination folder
1211
                file.setFolder(destination);
1212
                touchParentFolders(source, owner, new Date());
1213
                touchParentFolders(destination, owner, new Date());
1214
        }
1215

    
1216
        @Override
1217
        public void moveFolderToPath(Long userId, Long ownerId, Long folderId, String dest) throws ObjectNotFoundException, InsufficientPermissionsException, QuotaExceededException {
1218
                if (userId == null)
1219
                        throw new ObjectNotFoundException("No user specified");
1220
                if (ownerId == null)
1221
                        throw new ObjectNotFoundException("No owner specified");
1222
                if (folderId == null)
1223
                        throw new ObjectNotFoundException("No folder specified");
1224
                if (StringUtils.isEmpty(dest))
1225
                        throw new ObjectNotFoundException("No destination specified");
1226

    
1227
                Object destination = getResourceAtPath(ownerId, getParentPath(dest), true);
1228
                if (!(destination instanceof FolderDTO))
1229
                        throw new ObjectNotFoundException("Destination parent folder not found");
1230
                FolderDTO parent = (FolderDTO) destination;
1231
                moveFolder(userId, folderId, parent.getId(), getLastElement(dest));
1232
        }
1233

    
1234
        @Override
1235
        public void moveFolder(Long userId, Long folderId, Long destId, String destName)
1236
                        throws ObjectNotFoundException, InsufficientPermissionsException,
1237
                        QuotaExceededException {
1238
                Folder source = dao.getEntityById(Folder.class, folderId);
1239
                Folder destination = dao.getEntityById(Folder.class, destId);
1240
                User user = dao.getEntityById(User.class, userId);
1241
                User sourceOwner = source.getOwner();
1242
                User destinationOwner = destination.getOwner();
1243
                // Do not move trashed folders and contents.
1244
                if (source.isDeleted())
1245
                        return;
1246
                // Check permissions.
1247
                if (!destination.hasWritePermission(user)
1248
                                || !source.hasReadPermission(user)
1249
                                || !source.hasWritePermission(user))
1250
                        throw new InsufficientPermissionsException("You don't have the " +
1251
                                        "necessary permissions");
1252
                // Use the same timestamp for all subsequent modifications to make
1253
                // changes appear simultaneous.
1254
                Date now = new Date();
1255
                // If source and destination are not in the same user's namespace,
1256
                // change owners and check quota.
1257
                if (!sourceOwner.equals(destinationOwner)) {
1258
                        changeOwner(source, destinationOwner, user, now);
1259
                        if (getQuotaLeft(destinationOwner.getId()) < 0)
1260
                                throw new QuotaExceededException("Not enough free space " +
1261
                                                "available in destination folder");
1262
                }
1263
                // Perform the move.
1264
                Folder oldParent = source.getParent();
1265
                oldParent.removeSubfolder(source);
1266
                destination.addSubfolder(source);
1267
                // Mark the former parent and destination trees upwards as modified.
1268
                touchParentFolders(oldParent, user, now);
1269
                touchParentFolders(source, user, now);
1270
        }
1271

    
1272
        /**
1273
         * Recursively change the owner of the specified folder and all of its
1274
         * contents to the specified owner. Also mark them all as modified with the
1275
         * specified modifier and modificationDate.
1276
         */
1277
        private void changeOwner(Folder folder, User owner, User modifier, Date modificationDate) {
1278
                for (FileHeader file: folder.getFiles()) {
1279
                        file.setOwner(owner);
1280
                        file.getAuditInfo().setModificationDate(modificationDate);
1281
                        file.getAuditInfo().setModifiedBy(modifier);
1282
                }
1283
                for (Folder sub: folder.getSubfolders())
1284
                        changeOwner(sub, owner, modifier, modificationDate);
1285
                folder.setOwner(owner);
1286
                folder.getAuditInfo().setModificationDate(modificationDate);
1287
                folder.getAuditInfo().setModifiedBy(modifier);
1288
        }
1289

    
1290
        @Override
1291
        public List<FileHeaderDTO> getDeletedFiles(Long userId) throws ObjectNotFoundException {
1292
                // Validate.
1293
                if (userId == null)
1294
                        throw new ObjectNotFoundException("No user specified");
1295

    
1296
                // Do the actual work.
1297
                final List<FileHeaderDTO> result = new ArrayList<FileHeaderDTO>();
1298
                final List<FileHeader> files = dao.getDeletedFiles(userId);
1299
                for (final FileHeader f : files)
1300
                        result.add(f.getDTO());
1301
                return result;
1302
        }
1303

    
1304
        @Override
1305
        public void removeFileFromTrash(Long userId, Long fileId)
1306
                        throws ObjectNotFoundException, InsufficientPermissionsException {
1307
                if (userId == null)
1308
                        throw new ObjectNotFoundException("No user specified");
1309
                if (fileId == null)
1310
                        throw new ObjectNotFoundException("No file specified");
1311

    
1312
                // Do the actual work.
1313
                FileHeader file = dao.getEntityById(FileHeader.class, fileId);
1314
                Folder parent = file.getFolder();
1315
                if (parent == null)
1316
                        throw new ObjectNotFoundException("The specified file has no parent folder");
1317
                User user = dao.getEntityById(User.class, userId);
1318
                if (!file.hasDeletePermission(user))
1319
                        throw new InsufficientPermissionsException("User " + user.getUsername() +
1320
                                                " cannot restore file " + file.getName());
1321

    
1322
                file.setDeleted(false);
1323
                dao.update(file);
1324
                touchParentFolders(parent, user, new Date());
1325
        }
1326

    
1327
        @Override
1328
        public void moveFolderToTrash(Long userId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
1329
                if (userId == null)
1330
                        throw new ObjectNotFoundException("No user specified");
1331
                if (folderId == null)
1332
                        throw new ObjectNotFoundException("No folder specified");
1333
                Folder folder = dao.getEntityById(Folder.class, folderId);
1334
                User user = dao.getEntityById(User.class, userId);
1335
                if (!folder.hasDeletePermission(user))
1336
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
1337
                folder.setDeleted(true);
1338
                dao.update(folder);
1339
                touchParentFolders(folder, user, new Date());
1340
                for (FileHeader file : folder.getFiles())
1341
                        moveFileToTrash(userId, file.getId());
1342
                for (Folder subFolder : folder.getSubfolders())
1343
                        moveFolderToTrash(userId, subFolder.getId());
1344

    
1345
        }
1346

    
1347
        @Override
1348
        public void removeFolderFromTrash(Long userId, Long folderId)
1349
                        throws ObjectNotFoundException, InsufficientPermissionsException {
1350
                if (userId == null)
1351
                        throw new ObjectNotFoundException("No user specified");
1352
                if (folderId == null)
1353
                        throw new ObjectNotFoundException("No folder specified");
1354
                Folder folder = dao.getEntityById(Folder.class, folderId);
1355
                User user = dao.getEntityById(User.class, userId);
1356
                if (!folder.hasDeletePermission(user))
1357
                        throw new InsufficientPermissionsException("User " + user.getUsername() +
1358
                                                " cannot restore folder " + folder.getName());
1359
                folder.setDeleted(false);
1360
                for (FileHeader file : folder.getFiles())
1361
                        removeFileFromTrash(userId, file.getId());
1362
                for (Folder subFolder : folder.getSubfolders())
1363
                        removeFolderFromTrash(userId, subFolder.getId());
1364
                dao.update(folder);
1365
                touchParentFolders(folder, user, new Date());
1366
        }
1367

    
1368
        @Override
1369
        public List<FolderDTO> getDeletedRootFolders(Long userId) throws ObjectNotFoundException {
1370
                List<Folder> folders = dao.getDeletedRootFolders(userId);
1371
                List<FolderDTO> result = new ArrayList<FolderDTO>();
1372
                for (Folder folder : folders)
1373
                        result.add(folder.getDTO());
1374
                return result;
1375
        }
1376

    
1377
        @Override
1378
        public void emptyTrash(Long userId) throws ObjectNotFoundException, InsufficientPermissionsException {
1379
                List<FolderDTO> deletedRootFolders = getDeletedRootFolders(userId);
1380
                for (FolderDTO fdto : deletedRootFolders)
1381
                        deleteFolder(userId, fdto.getId());
1382
                List<FileHeaderDTO> deletedFiles = getDeletedFiles(userId);
1383
                for (FileHeaderDTO filedto : deletedFiles)
1384
                        deleteFile(userId, filedto.getId());
1385
        }
1386

    
1387
        @Override
1388
        public void restoreTrash(Long userId) throws ObjectNotFoundException, InsufficientPermissionsException {
1389
                List<FolderDTO> deletedRootFolders = getDeletedRootFolders(userId);
1390
                for (FolderDTO fdto : deletedRootFolders)
1391
                        removeFolderFromTrash(userId, fdto.getId());
1392
                List<FileHeaderDTO> deletedFiles = getDeletedFiles(userId);
1393
                for (FileHeaderDTO filedto : deletedFiles)
1394
                        removeFileFromTrash(userId, filedto.getId());
1395
        }
1396

    
1397
        @Override
1398
        public User createUser(String username, String name, String mail,
1399
                                String idp, String idpid) throws ObjectNotFoundException {
1400
                if (username == null)
1401
                        throw new ObjectNotFoundException("No username specified");
1402
                if (name == null)
1403
                        throw new ObjectNotFoundException("No name specified");
1404

    
1405
                User user = new User();
1406
                user.setUsername(username);
1407
                user.setName(name);
1408
                user.setEmail(mail);
1409
                user.setIdentityProvider(idp);
1410
                user.setIdentityProviderId(idpid);
1411
                Date now = new Date();
1412
                AuditInfo auditInfo = new AuditInfo();
1413
                auditInfo.setCreationDate(now);
1414
                auditInfo.setModificationDate(now);
1415
                user.setAuditInfo(auditInfo);
1416
                user.setActive(true);
1417
                user.generateAuthToken();
1418
                user.generateWebDAVPassword();
1419
                user.setUserClass(getDefaultUserClass());
1420
                dao.create(user);
1421
                // Make sure we get an ID in the user object.
1422
                dao.flush();
1423
                // Create the root folder for the user.
1424
                createFolder(user.getName(), null, user);
1425
                return user;
1426
        }
1427

    
1428
        /**
1429
         * Get the default user class, which is the one with the lowest quota.
1430
         */
1431
        private UserClass getDefaultUserClass() {
1432
                return getUserClasses().get(0);
1433
        }
1434

    
1435
        @Override
1436
        public List<UserClass> getUserClasses() {
1437
                List<UserClass> classes = dao.getUserClasses();
1438
                // Create a default user class for first-time use. Afterwards, the
1439
                // admin should modify or add to the userclass table.
1440
                if (classes.size() == 0) {
1441
                        UserClass defaultClass = new UserClass();
1442
                        defaultClass.setName("default");
1443
                        Long defaultQuota = getConfiguration().getLong("quota", new Long(52428800L));
1444
                        defaultClass.setQuota(defaultQuota);
1445
                        dao.create(defaultClass);
1446
                        classes.add(defaultClass);
1447
                }
1448
                return classes;
1449
        }
1450

    
1451
        @Override
1452
        public User findUserByEmail(String email) {
1453
                return dao.findUserByEmail(email);
1454
        }
1455

    
1456
        @Override
1457
        public void updateUser(User user) {
1458
                dao.update(user);
1459
        }
1460

    
1461
        @Override
1462
        public User findUser(String username) {
1463
                if (username == null)
1464
                        return null;
1465
                return dao.findUser(username);
1466
        }
1467

    
1468
        @Override
1469
        public User updateUserToken(Long userId) throws ObjectNotFoundException {
1470
                if (userId == null)
1471
                        throw new ObjectNotFoundException("No user specified");
1472
                User user = dao.getEntityById(User.class, userId);
1473
                user.generateAuthToken();
1474
                return user;
1475
        }
1476

    
1477
        @Override
1478
        public Set<PermissionDTO> getFolderPermissions(Long userId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
1479
                if (userId == null)
1480
                        throw new ObjectNotFoundException("No user specified");
1481
                if (folderId == null)
1482
                        throw new ObjectNotFoundException("No folder specified");
1483
                User user = dao.getEntityById(User.class, userId);
1484
                Folder folder = dao.getEntityById(Folder.class, folderId);
1485
                if(!folder.hasReadPermission(user))
1486
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
1487
                Set<Permission> perms = folder.getPermissions();
1488
                Set<PermissionDTO> result = new LinkedHashSet<PermissionDTO>();
1489
                for (Permission perm : perms)
1490
                        if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId()))
1491
                                result.add(perm.getDTO());
1492
                for (Permission perm : perms)
1493
                        if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId())) {
1494
                        } else
1495
                                result.add(perm.getDTO());
1496
                return result;
1497

    
1498
        }
1499

    
1500
        /**
1501
         * Set the provided permissions as the new permissions of the specified
1502
         * folder.
1503
         *
1504
         * @param user
1505
         * @param folder
1506
         * @param permissions
1507
         * @throws ObjectNotFoundException
1508
         * @throws InsufficientPermissionsException
1509
         */
1510
        private void setFolderPermissions(User user, Folder folder, Set<PermissionDTO> permissions) throws ObjectNotFoundException, InsufficientPermissionsException {
1511
                if (permissions != null && !permissions.isEmpty()) {
1512
                        User owner = folder.getOwner();
1513
                        PermissionDTO ownerPerm = null;
1514
                        for (PermissionDTO dto : permissions)
1515
                                if (dto.getUser() != null && dto.getUser().getId().equals(owner.getId())) {
1516
                                        ownerPerm = dto;
1517
                                        break;
1518
                                }
1519
                        if (ownerPerm == null || !ownerPerm.hasRead() || !ownerPerm.hasWrite() || !ownerPerm.hasModifyACL())
1520
                                throw new InsufficientPermissionsException("Can't remove permissions from owner");
1521
                        // Delete previous entries
1522
                        for (Permission perm: folder.getPermissions())
1523
                                dao.delete(perm);
1524
                        folder.getPermissions().clear();
1525
                        for (PermissionDTO dto : permissions) {
1526
                                // Skip 'empty' permission entries.
1527
                                if (!dto.getRead() && !dto.getWrite() && !dto.getModifyACL()) continue;
1528
                                folder.addPermission(getPermission(dto));
1529
                        }
1530
                        dao.update(folder);
1531
                        for (FileHeader file : folder.getFiles()) {
1532
                                setFilePermissions(file, permissions);
1533
                                Date now = new Date();
1534
                                file.getAuditInfo().setModificationDate(now);
1535
                                file.getAuditInfo().setModifiedBy(user);
1536
                        }
1537
                        for (Folder sub : folder.getSubfolders())
1538
                                setFolderPermissions(user, sub, permissions);
1539
                }
1540
        }
1541

    
1542
        private Permission getPermission(PermissionDTO dto) throws ObjectNotFoundException {
1543
                Permission res = new Permission();
1544
                if (dto.getGroup() != null)
1545
                        res.setGroup(dao.getEntityById(Group.class, dto.getGroup().getId()));
1546
                else if (dto.getUser() != null)
1547
                        if (dto.getUser().getId() == null)
1548
                                res.setUser(dao.getUser(dto.getUser().getUsername()));
1549
                        else
1550
                                res.setUser(dao.getEntityById(User.class, dto.getUser().getId()));
1551
                res.setRead(dto.hasRead());
1552
                res.setWrite(dto.hasWrite());
1553
                res.setModifyACL(dto.hasModifyACL());
1554
                return res;
1555
        }
1556

    
1557
        /* (non-Javadoc)
1558
         * @see gr.ebs.gss.server.ejb.ExternalAPI#getUsersByUserNameLike(java.lang.String)
1559
         */
1560
        @Override
1561
        public List<UserDTO> getUsersByUserNameLike(String username) {
1562
                List<User> users = dao.getUsersByUserNameLike(username);
1563
                List<UserDTO> result = new ArrayList<UserDTO>();
1564
                for (User u : users)
1565
                        result.add(u.getDTO());
1566
                return result;
1567

    
1568
        }
1569

    
1570
        @Override
1571
        public void addUserToGroup(Long userId, Long groupId, Long userToAddId) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException {
1572
                if (userId == null)
1573
                        throw new ObjectNotFoundException("No user specified");
1574
                if (groupId == null)
1575
                        throw new ObjectNotFoundException("No group specified");
1576
                if (userToAddId == null)
1577
                        throw new ObjectNotFoundException("No user to add specified");
1578
                User user = dao.getEntityById(User.class, userId);
1579
                Group group = dao.getEntityById(Group.class, groupId);
1580
                if (!group.getOwner().equals(user))
1581
                        throw new InsufficientPermissionsException();
1582
                User userToAdd = dao.getEntityById(User.class, userToAddId);
1583
                if (group.contains(userToAdd))
1584
                        throw new DuplicateNameException("User already exists in group");
1585
                group.getMembers().add(userToAdd);
1586
                dao.update(group);
1587

    
1588
        }
1589

    
1590
        @Override
1591
        public void invalidateUserToken(Long userId) throws ObjectNotFoundException {
1592
                if (userId == null)
1593
                        throw new ObjectNotFoundException("No user specified");
1594
                User user = dao.getEntityById(User.class, userId);
1595
                user.invalidateAuthToken();
1596
                return;
1597
        }
1598

    
1599
        @Override
1600
        public List<FolderDTO> getSharedRootFolders(Long userId) throws ObjectNotFoundException {
1601
                if (userId == null)
1602
                        throw new ObjectNotFoundException("No user specified");
1603
                List<Folder> folders = dao.getSharedRootFolders(userId);
1604
                List<FolderDTO> result = new ArrayList<FolderDTO>();
1605
                for (Folder f : folders) {
1606
                        FolderDTO dto = f.getDTO();
1607
                        dto.setSubfolders(getSharedSubfolders(userId, f.getId()));
1608
                        result.add(dto);
1609
                }
1610
                return result;
1611
        }
1612

    
1613
        @Override
1614
        public void removeMemberFromGroup(Long userId, Long groupId, Long memberId) throws ObjectNotFoundException, InsufficientPermissionsException {
1615
                if (userId == null)
1616
                        throw new ObjectNotFoundException("No user specified");
1617
                if (groupId == null)
1618
                        throw new ObjectNotFoundException("No group specified");
1619
                if (memberId == null)
1620
                        throw new ObjectNotFoundException("No member specified");
1621
                User owner = dao.getEntityById(User.class, userId);
1622
                Group group = dao.getEntityById(Group.class, groupId);
1623
                User member = dao.getEntityById(User.class, memberId);
1624
                if (!group.getOwner().equals(owner))
1625
                        throw new InsufficientPermissionsException("User is not the owner of the group");
1626
                group.removeMemberFromGroup(member);
1627
                dao.update(group);
1628

    
1629
        }
1630

    
1631
        @Override
1632
        public List<UserDTO> getUsersSharingFoldersForUser(Long userId) throws ObjectNotFoundException {
1633
                List<User> users = dao.getUsersSharingFoldersForUser(userId);
1634
                List<User> usersFiles = dao.getUsersSharingFilesForUser(userId);
1635
                List<UserDTO> res = new ArrayList<UserDTO>();
1636
                for (User u : users)
1637
                        res.add(u.getDTO());
1638
                for(User fu : usersFiles)
1639
                        if(!users.contains(fu))
1640
                                res.add(fu.getDTO());
1641
                return res;
1642
        }
1643

    
1644
        @Override
1645
        public Set<PermissionDTO> getFilePermissions(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
1646
                if (userId == null)
1647
                        throw new ObjectNotFoundException("No user specified");
1648
                if (fileId == null)
1649
                        throw new ObjectNotFoundException("No folder specified");
1650
                User user = dao.getEntityById(User.class, userId);
1651
                FileHeader folder = dao.getEntityById(FileHeader.class, fileId);
1652
                if(!folder.hasReadPermission(user))
1653
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
1654
                Set<Permission> perms = folder.getPermissions();
1655
                Set<PermissionDTO> result = new LinkedHashSet<PermissionDTO>();
1656
                for (Permission perm : perms)
1657
                        if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId()))
1658
                                result.add(perm.getDTO());
1659
                for (Permission perm : perms)
1660
                        if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId())) {
1661
                        } else
1662
                                result.add(perm.getDTO());
1663
                return result;
1664
        }
1665

    
1666
        /**
1667
         * Set the provided permissions as the new permissions of the specified
1668
         * file. This method sets the modification date/user attributes to the
1669
         * current values as a side effect.
1670
         *
1671
         * @param file
1672
         * @param permissions
1673
         * @throws ObjectNotFoundException
1674
         * @throws InsufficientPermissionsException
1675
         */
1676
        private void setFilePermissions(FileHeader file,
1677
                                Set<PermissionDTO> permissions)
1678
                        throws ObjectNotFoundException, InsufficientPermissionsException {
1679
                if (permissions != null && !permissions.isEmpty()) {
1680
                        PermissionDTO ownerPerm = null;
1681
                        for (PermissionDTO dto : permissions)
1682
                                if (dto.getUser() != null && dto.getUser().getId().equals(file.getOwner().getId())) {
1683
                                        ownerPerm = dto;
1684
                                        break;
1685
                                }
1686
                        if (ownerPerm == null || !ownerPerm.hasRead() || !ownerPerm.hasWrite() || !ownerPerm.hasModifyACL())
1687
                                throw new InsufficientPermissionsException("Can't remove permissions from owner");
1688
                        // Delete previous entries.
1689
                        for (Permission perm: file.getPermissions())
1690
                                dao.delete(perm);
1691
                        file.getPermissions().clear();
1692
                        for (PermissionDTO dto : permissions) {
1693
                                // Skip 'empty' permission entries.
1694
                                if (!dto.getRead() && !dto.getWrite() && !dto.getModifyACL()) continue;
1695
                                file.addPermission(getPermission(dto));
1696
                        }
1697
                        dao.flush();
1698
                }
1699
        }
1700

    
1701
        @Override
1702
        public List<FileHeaderDTO> getSharedFilesNotInSharedFolders(Long userId) throws ObjectNotFoundException {
1703
                if (userId == null)
1704
                        throw new ObjectNotFoundException("No user specified");
1705
                List<FileHeader> files = dao.getSharedFilesNotInSharedFolders(userId);
1706
                List<FileHeaderDTO> result = new ArrayList<FileHeaderDTO>();
1707
                for (FileHeader f : files)
1708
                        result.add(f.getDTO());
1709
                return result;
1710
        }
1711

    
1712
        @Override
1713
        public List<FileHeaderDTO> getSharedFiles(Long userId) throws ObjectNotFoundException {
1714
                if (userId == null)
1715
                        throw new ObjectNotFoundException("No user specified");
1716
                List<FileHeader> files = dao.getSharedFiles(userId);
1717
                List<FileHeaderDTO> result = new ArrayList<FileHeaderDTO>();
1718
                for (FileHeader f : files)
1719
                        result.add(f.getDTO());
1720
                return result;
1721
        }
1722

    
1723
        @Override
1724
        public List<FolderDTO> getSharedFolders(Long userId) throws ObjectNotFoundException {
1725
                if (userId == null)
1726
                        throw new ObjectNotFoundException("No user specified");
1727
                List<Folder> folders = dao.getSharedFolders(userId);
1728
                List<FolderDTO> result = new ArrayList<FolderDTO>();
1729
                for (Folder f : folders)
1730
                        result.add(f.getDTO());
1731
                return result;
1732
        }
1733

    
1734
        @Override
1735
        public List<FileHeaderDTO> getSharedFiles(Long ownerId, Long callingUserId) throws ObjectNotFoundException {
1736
                if (ownerId == null)
1737
                        throw new ObjectNotFoundException("No owner specified");
1738
                if (callingUserId == null)
1739
                        throw new ObjectNotFoundException("No calling user specified");
1740
                List<FileHeader> folders = dao.getSharedFiles(ownerId, callingUserId);
1741
                List<FileHeaderDTO> result = new ArrayList<FileHeaderDTO>();
1742
                for (FileHeader f : folders)
1743
                        result.add(f.getDTO());
1744
                return result;
1745
        }
1746

    
1747
        @Override
1748
        public List<FolderDTO> getSharedRootFolders(Long ownerId, Long callingUserId) throws ObjectNotFoundException {
1749
                if (ownerId == null)
1750
                        throw new ObjectNotFoundException("No owner specified");
1751
                if (callingUserId == null)
1752
                        throw new ObjectNotFoundException("No calling user specified");
1753
                List<Folder> folders = dao.getSharedRootFolders(ownerId, callingUserId);
1754
                List<FolderDTO> result = new ArrayList<FolderDTO>();
1755
                for (Folder f : folders) {
1756
                        FolderDTO dto = f.getDTO();
1757
                        dto.setSubfolders(getSharedSubfolders(ownerId, callingUserId, f.getId()));
1758
                        result.add(dto);
1759
                }
1760
                return result;
1761

    
1762
        }
1763

    
1764
        @Override
1765
        public List<FolderDTO> getSharedSubfolders(Long userId, Long folderId) throws ObjectNotFoundException {
1766
                if (userId == null)
1767
                        throw new ObjectNotFoundException("No user specified");
1768
                if (folderId == null)
1769
                        throw new ObjectNotFoundException("No folder specified");
1770
                User user = dao.getEntityById(User.class, userId);
1771
                Folder folder = dao.getEntityById(Folder.class, folderId);
1772
                List<FolderDTO> result = new ArrayList<FolderDTO>();
1773
                if (folder.isShared(user))
1774
                        for (Folder f : folder.getSubfolders())
1775
                                if (f.isShared(user) && !f.isDeleted())
1776
                                        result.add(f.getDTO());
1777
                return result;
1778
        }
1779

    
1780
        @Override
1781
        public List<FolderDTO> getSharedSubfolders(Long userId, Long callingUserId, Long folderId) throws ObjectNotFoundException {
1782
                if (userId == null)
1783
                        throw new ObjectNotFoundException("No user specified");
1784
                if (callingUserId == null)
1785
                        throw new ObjectNotFoundException("No user specified");
1786
                if (folderId == null)
1787
                        throw new ObjectNotFoundException("No folder specified");
1788
                User user = dao.getEntityById(User.class, callingUserId);
1789
                Folder folder = dao.getEntityById(Folder.class, folderId);
1790
                List<FolderDTO> result = new ArrayList<FolderDTO>();
1791
                if (folder.isSharedForOtherUser(user))
1792
                        for (Folder f : folder.getSubfolders())
1793
                                if (f.isSharedForOtherUser(user) && !f.isDeleted()){
1794
                                        FolderDTO dto = f.getDTO();
1795
                                        dto.setSubfolders(getSharedSubfolders(userId, callingUserId, dto.getId()));
1796
                                        result.add(dto);
1797
                                }
1798
                return result;
1799

    
1800
        }
1801

    
1802
        @Override
1803
        public List<FileHeaderDTO> searchFiles(Long userId, String query) throws ObjectNotFoundException {
1804
                if (userId == null)
1805
                        throw new ObjectNotFoundException("No user specified");
1806
                User user = getUser(userId);
1807
                if (query == null)
1808
                        throw new ObjectNotFoundException("No query specified");
1809
                List<FileHeader> files = search(user.getId(), query);
1810
                List<FileHeaderDTO> res = new ArrayList<FileHeaderDTO>();
1811
                for(FileHeader f : files)
1812
                        res.add(f.getDTO());
1813
                return res;
1814
        }
1815

    
1816
        /**
1817
         * Performs the actuals search on the solr server and returns the results
1818
         *
1819
         * We have to use the dismax query type (instead of the
1820
         * standard) because it allows for search time field boosting. This is because we can't use indexing
1821
         * time field boosting due to the patched rich indexing API that does not allow it
1822
         *
1823
         * @param userId
1824
         * @param query
1825
         * @return a List of FileHeader objects
1826
         */
1827
        private List<FileHeader> search(Long userId, String query) {
1828
                try {
1829
                        HttpClient httpClient = new HttpClient();
1830

    
1831
                        GetMethod method = new GetMethod(getConfiguration().getString("solrSelectUrl"));
1832
                        NameValuePair[] params = {new NameValuePair("qt", "dismax"),
1833
                                                                                new NameValuePair("q", query),
1834
                                                                                new NameValuePair("sort", "score desc"),
1835
                                                                                new NameValuePair("indent", "on")};
1836
                        method.setQueryString(params);
1837
                        int retryCount = 0;
1838
                        int statusCode = 0;
1839
                        String response = null;
1840
                        do {
1841
                                statusCode = httpClient.executeMethod(method);
1842
                                logger.debug("HTTP status: " + statusCode);
1843
                                response = method.getResponseBodyAsString();
1844
                                logger.debug(response);
1845
                                retryCount++;
1846
                                if (statusCode != 200 && retryCount < 3)
1847
                                        try {
1848
                                                Thread.sleep(3000); //Give Solr a little time to be available
1849
                                        } catch (InterruptedException e) {
1850
                                        }
1851
                        } while (statusCode != 200 && retryCount < 3);
1852
                        if (statusCode != 200)
1853
                                throw new EJBException("Search query return error:\n" + response);
1854

    
1855
                        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
1856
                        DocumentBuilder db = dbf.newDocumentBuilder();
1857
                        Document doc = db.parse(method.getResponseBodyAsStream());
1858
                        method.releaseConnection();
1859

    
1860
                        Node root = doc.getElementsByTagName("response").item(0);
1861
                        Node lst = root.getFirstChild().getNextSibling();
1862
                        Node status = lst.getFirstChild().getNextSibling();
1863
                        if (status.getAttributes().getNamedItem("name").getNodeValue().equals("status") &&
1864
                                status.getTextContent().equals("0")) {
1865
                                List<FileHeader> fileResult = new ArrayList<FileHeader>();
1866
                                Node result = lst.getNextSibling().getNextSibling();
1867
                                NodeList docs = result.getChildNodes();
1868
                                User user = getUser(userId);
1869
                                for (int i=1; i<docs.getLength(); i=i+2) {
1870
                                        Node d = docs.item(i);
1871
                                        NodeList docData = d.getChildNodes();
1872
                                        for (int j=1; j<docData.getLength(); j=j+2) {
1873
                                                Node dd = docData.item(j);
1874
                                                if (dd.getAttributes().item(0).getNodeName().equals("name") &&
1875
                                                        dd.getAttributes().item(0).getNodeValue().equals("id")) {
1876
                                                        Long fileId = Long.valueOf(dd.getTextContent());
1877
                                                        try {
1878
                                                                FileHeader file = dao.getEntityById(FileHeader.class, fileId);
1879
                                                                if (file.hasReadPermission(user)) {
1880
                                                                        fileResult.add(file);
1881
                                                                        logger.debug("File added " + fileId);
1882
                                                                }
1883
                                                        } catch (ObjectNotFoundException e) {
1884
                                                                logger.warn("Search result not found", e);
1885
                                                        }
1886
                                                }
1887
                                        }
1888
                                }
1889
                                return fileResult;
1890
                        }
1891
                        throw new EJBException();
1892
                } catch (HttpException e) {
1893
                        throw new EJBException(e);
1894
                } catch (IOException e) {
1895
                        throw new EJBException(e);
1896
                } catch (SAXException e) {
1897
                        throw new EJBException(e);
1898
                } catch (ParserConfigurationException e) {
1899
                        throw new EJBException(e);
1900
                } catch (ObjectNotFoundException e) {
1901
                        throw new EJBException(e);
1902
                }
1903
        }
1904

    
1905
        @Override
1906
        public void copyFiles(Long userId, List<Long> fileIds, Long destId) throws ObjectNotFoundException, DuplicateNameException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
1907
                for(Long l : fileIds){
1908
                        FileHeader file = dao.getEntityById(FileHeader.class, l);
1909
                        copyFile(userId, l, destId, file.getName());
1910
                }
1911

    
1912

    
1913
        }
1914

    
1915
        @Override
1916
        public void moveFiles(Long userId, List<Long> fileIds, Long destId) throws InsufficientPermissionsException, ObjectNotFoundException, QuotaExceededException {
1917
                for(Long l : fileIds){
1918
                        FileHeader file = dao.getEntityById(FileHeader.class, l);
1919
                        moveFile(userId, l, destId, file.getName());
1920
                }
1921

    
1922
        }
1923

    
1924
        @Override
1925
        public void deleteFiles(Long userId, List<Long> fileIds) throws ObjectNotFoundException, InsufficientPermissionsException {
1926
                if (userId == null)
1927
                        throw new ObjectNotFoundException("No user specified");
1928
                final User user = dao.getEntityById(User.class, userId);
1929
                List<String> filesToRemove = new ArrayList<String>();
1930
                //first delete database objects
1931
                for(Long fileId : fileIds){
1932
                        if (fileId == null)
1933
                                throw new ObjectNotFoundException("No file specified");
1934
                        final FileHeader file = dao.getEntityById(FileHeader.class, fileId);
1935
                        final Folder parent = file.getFolder();
1936
                        if (parent == null)
1937
                                throw new ObjectNotFoundException("The specified file has no parent folder");
1938
                        if (!file.hasDeletePermission(user))
1939
                                throw new InsufficientPermissionsException("User " + user.getId() + " cannot delete file " + file.getName() + "(" + file.getId() + ")");
1940

    
1941
                        parent.removeFile(file);
1942
                        for (final FileBody body : file.getBodies())
1943
                                filesToRemove.add(body.getStoredFilePath());
1944
                        dao.delete(file);
1945
                        touchParentFolders(parent, user, new Date());
1946
                }
1947
                //then remove physical files if everything is ok
1948
                for(String physicalFileName : filesToRemove)
1949
                        deleteActualFile(physicalFileName);
1950
                //then unindex deleted files
1951
                for(Long fileId : fileIds)
1952
                        indexFile(fileId, true);
1953

    
1954
        }
1955

    
1956
        @Override
1957
        public void moveFilesToTrash(Long userId, List<Long> fileIds) throws ObjectNotFoundException, InsufficientPermissionsException {
1958
                for(Long l : fileIds)
1959
                        moveFileToTrash(userId, l);
1960

    
1961
        }
1962

    
1963
        @Override
1964
        public void removeFilesFromTrash(Long userId, List<Long> fileIds) throws ObjectNotFoundException, InsufficientPermissionsException {
1965
                for(Long l : fileIds)
1966
                        removeFileFromTrash(userId, l);
1967

    
1968
        }
1969

    
1970
        @Override
1971
        public Nonce createNonce(Long userId) throws ObjectNotFoundException {
1972
                if (userId == null)
1973
                        throw new ObjectNotFoundException("No user specified");
1974
                User user = dao.getEntityById(User.class, userId);
1975
                Nonce nonce = Nonce.createNonce(user.getId());
1976
                dao.create(nonce);
1977
                return nonce;
1978
        }
1979

    
1980
        @Override
1981
        public Nonce getNonce(String nonce, Long userId) throws ObjectNotFoundException {
1982
                if (userId == null)
1983
                        throw new ObjectNotFoundException("No user specified");
1984
                if (nonce == null)
1985
                        throw new ObjectNotFoundException("No nonce specified");
1986
                return dao.getNonce(nonce, userId);
1987
        }
1988

    
1989
        @Override
1990
        public void removeNonce(Long id) throws ObjectNotFoundException {
1991
                if (id == null)
1992
                        throw new ObjectNotFoundException("No nonce specified");
1993
                Nonce nonce = dao.getEntityById(Nonce.class, id);
1994
                dao.delete(nonce);
1995
        }
1996

    
1997
        @Override
1998
        public void activateUserNonce(Long userId, String nonce, Date nonceExpiryDate) throws ObjectNotFoundException {
1999
                if (userId == null)
2000
                        throw new ObjectNotFoundException("No user specified");
2001
                User user = dao.getEntityById(User.class, userId);
2002
                user.setNonce(nonce);
2003
                user.setNonceExpiryDate(nonceExpiryDate);
2004
        }
2005

    
2006
        @Override
2007
        public StatsDTO getUserStatistics(Long userId) throws ObjectNotFoundException {
2008
                if (userId == null)
2009
                        throw new ObjectNotFoundException("No user specified");
2010
                StatsDTO stats = new StatsDTO();
2011
                stats.setFileCount(dao.getFileCount(userId));
2012
                Long fileSize = dao.getFileSize(userId);
2013
                stats.setFileSize(fileSize);
2014
                Long quota = getQuota(userId);
2015
                Long quotaLeft = quota - fileSize;
2016
                stats.setQuotaLeftSize(quotaLeft);
2017
                return stats;
2018
        }
2019

    
2020
        @Override
2021
        public List<FileBodyDTO> getVersions(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
2022
                if (userId == null)
2023
                        throw new ObjectNotFoundException("No user specified");
2024
                if (fileId == null)
2025
                        throw new ObjectNotFoundException("No file specified");
2026
                User user = dao.getEntityById(User.class, userId);
2027
                FileHeader header = dao.getEntityById(FileHeader.class, fileId);
2028
                if(!header.hasReadPermission(user))
2029
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
2030
                List<FileBodyDTO> result = new LinkedList<FileBodyDTO>();
2031
                for(int i = header.getBodies().size()-1 ; i>=0; i--)
2032
                        result.add(header.getBodies().get(i).getDTO());
2033
                return result;
2034
        }
2035

    
2036
        @Override
2037
        public void removeVersion(Long userId, Long fileId, Long bodyId) throws ObjectNotFoundException, InsufficientPermissionsException {
2038
                if (userId == null)
2039
                        throw new ObjectNotFoundException("No user specified");
2040
                if (fileId == null)
2041
                        throw new ObjectNotFoundException("No file specified");
2042
                if (bodyId == null)
2043
                        throw new ObjectNotFoundException("No body specified");
2044
                User user = dao.getEntityById(User.class, userId);
2045
                FileHeader header = dao.getEntityById(FileHeader.class, fileId);
2046
                if(!header.hasWritePermission(user))
2047
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
2048
                FileBody body = dao.getEntityById(FileBody.class, bodyId);
2049
                if(body.equals(header.getCurrentBody())){
2050

    
2051
                        if(header.getBodies().size() == 1)
2052
                                throw new InsufficientPermissionsException("You cant delete this version, Delete file instead!");
2053
                        for(FileBody b : header.getBodies())
2054
                                if(b.getVersion() == body.getVersion()-1)
2055
                                        header.setCurrentBody(b);
2056
                }
2057
                deleteActualFile(body.getStoredFilePath());
2058
                header.getBodies().remove(body);
2059

    
2060
                Folder parent = header.getFolder();
2061
                touchParentFolders(parent, user, new Date());
2062

    
2063
        }
2064

    
2065
        @Override
2066
        public void restoreVersion(Long userId, Long fileId, int version) throws ObjectNotFoundException, InsufficientPermissionsException,  GSSIOException, QuotaExceededException {
2067
                if (userId == null)
2068
                        throw new ObjectNotFoundException("No user specified");
2069
                if (fileId == null)
2070
                        throw new ObjectNotFoundException("No file specified");
2071
                User user = dao.getEntityById(User.class, userId);
2072
                FileHeader header = dao.getEntityById(FileHeader.class, fileId);
2073
                if(!header.hasWritePermission(user))
2074
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
2075
                FileBody body = dao.getFileVersion(fileId, version);
2076
                final File fileContents = new File(body.getStoredFilePath());
2077

    
2078
                try {
2079
                        updateFileContents(userId, fileId, body.getMimeType(), new FileInputStream(fileContents) );
2080
                } catch (FileNotFoundException e) {
2081
                        throw new GSSIOException(e);
2082
                }
2083

    
2084
        }
2085

    
2086
        /* (non-Javadoc)
2087
         * @see gr.ebs.gss.server.ejb.ExternalAPI#removeOldVersions(java.lang.Long, java.lang.Long)
2088
         */
2089
        @Override
2090
        public void removeOldVersions(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
2091
                if (userId == null)
2092
                        throw new ObjectNotFoundException("No user specified");
2093
                if (fileId == null)
2094
                        throw new ObjectNotFoundException("No file specified");
2095
                User user = dao.getEntityById(User.class, userId);
2096
                FileHeader header = dao.getEntityById(FileHeader.class, fileId);
2097
                if(!header.hasWritePermission(user))
2098
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
2099
                Iterator<FileBody> it = header.getBodies().iterator();
2100
                while(it.hasNext()){
2101
                        FileBody body = it.next();
2102
                        if(!body.equals(header.getCurrentBody())){
2103
                                deleteActualFile(body.getStoredFilePath());
2104
                                it.remove();
2105
                                dao.delete(body);
2106
                        }
2107
                }
2108
                header.getCurrentBody().setVersion(1);
2109

    
2110
                Folder parent = header.getFolder();
2111
                touchParentFolders(parent, user, new Date());
2112
        }
2113

    
2114
        /**
2115
         * Gets the quota left for specified user ID.
2116
         */
2117
        private Long getQuotaLeft(Long userId) throws ObjectNotFoundException{
2118
                Long fileSize = dao.getFileSize(userId);
2119
                Long quota = getQuota(userId);
2120
                return quota - fileSize;
2121
        }
2122

    
2123
        /**
2124
         * Gets the quota for specified user ID.
2125
         */
2126
        private Long getQuota(Long userId) throws ObjectNotFoundException{
2127
                UserClass uc = getUser(userId).getUserClass();
2128
                if (uc == null)
2129
                        uc = getDefaultUserClass();
2130
                return uc.getQuota();
2131
        }
2132

    
2133
        @Override
2134
        public void rebuildSolrIndex() {
2135
                MessageProducer sender = null;
2136
                Session session = null;
2137
                Connection qConn = null;
2138
                try {
2139
                        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
2140
                        DocumentBuilder db = dbf.newDocumentBuilder();
2141
                        Document doc = db.newDocument();
2142
                        Node root = doc.createElement("delete");
2143
                        doc.appendChild(root);
2144
                        Node queryNode = doc.createElement("query");
2145
                        root.appendChild(queryNode);
2146
                        queryNode.appendChild(doc.createTextNode("*:*"));
2147

    
2148
                        TransformerFactory fact = TransformerFactory.newInstance();
2149
                        Transformer trans = fact.newTransformer();
2150
                        trans.setOutputProperty(OutputKeys.INDENT, "yes");
2151
                        StringWriter sw = new StringWriter();
2152
                        StreamResult sr = new StreamResult(sw);
2153
                        DOMSource source = new DOMSource(doc);
2154
                        trans.transform(source, sr);
2155
                        logger.debug(sw.toString());
2156

    
2157
                        HttpClient httpClient = new HttpClient();
2158
                        PostMethod method = new PostMethod(getConfiguration().getString("solrUpdateUrl"));
2159
                        method.setRequestEntity(new StringRequestEntity(sw.toString()));
2160
                        int retryCount = 0;
2161
                        int statusCode = 0;
2162
                        String response = null;
2163
                        do {
2164
                                statusCode = httpClient.executeMethod(method);
2165
                                logger.debug("HTTP status: " + statusCode);
2166
                                response = method.getResponseBodyAsString();
2167
                                logger.debug(response);
2168
                                retryCount++;
2169
                                if (statusCode != 200 && retryCount < 3)
2170
                                        try {
2171
                                                Thread.sleep(10000); //Give Solr a little time to be available
2172
                                        } catch (InterruptedException e) {
2173
                                        }
2174
                        } while (statusCode != 200 && retryCount < 3);
2175
                        method.releaseConnection();
2176
                        if (statusCode != 200)
2177
                                throw new EJBException("Cannot clear Solr index. Solr response is:\n" + response);
2178
                        List<Long> fileIds = dao.getAllFileIds();
2179

    
2180
                        Context jndiCtx = new InitialContext();
2181
                        ConnectionFactory factory = (QueueConnectionFactory) jndiCtx.lookup("java:/JmsXA");
2182
                        Queue queue = (Queue) jndiCtx.lookup("queue/gss-indexingQueue");
2183
                        qConn = factory.createConnection();
2184
                        session = qConn.createSession(false, Session.AUTO_ACKNOWLEDGE);
2185
                        sender = session.createProducer(queue);
2186

    
2187
                        for (Long id : fileIds) {
2188
                                MapMessage map = session.createMapMessage();
2189
                                map.setObject("id", id);
2190
                                map.setBoolean("delete", false);
2191
                                sender.send(map);
2192
                        }
2193
                        sendOptimize(httpClient, 0);
2194
                } catch (DOMException e) {
2195
                        throw new EJBException(e);
2196
                } catch (TransformerConfigurationException e) {
2197
                        throw new EJBException(e);
2198
                } catch (IllegalArgumentException e) {
2199
                        throw new EJBException(e);
2200
                } catch (HttpException e) {
2201
                        throw new EJBException(e);
2202
                } catch (UnsupportedEncodingException e) {
2203
                        throw new EJBException(e);
2204
                } catch (ParserConfigurationException e) {
2205
                        throw new EJBException(e);
2206
                } catch (TransformerException e) {
2207
                        throw new EJBException(e);
2208
                } catch (IOException e) {
2209
                        throw new EJBException(e);
2210
                } catch (NamingException e) {
2211
                        throw new EJBException(e);
2212
                } catch (JMSException e) {
2213
                        throw new EJBException(e);
2214
                }
2215
                finally {
2216
                        try {
2217
                                if (sender != null)
2218
                                        sender.close();
2219
                                if (session != null)
2220
                                        session.close();
2221
                                if (qConn != null)
2222
                                        qConn.close();
2223
                        }
2224
                        catch (JMSException e) {
2225
                                logger.warn(e);
2226
                        }
2227
                }
2228
        }
2229

    
2230
        /**
2231
         * Sends a optimize message to the solr server
2232
         *
2233
         * @param httpClient
2234
         * @param retryCount If the commit fails, it is retried three times. This parameter is passed in the recursive
2235
         *                                         calls to stop the recursion
2236
         * @throws UnsupportedEncodingException
2237
         * @throws IOException
2238
         * @throws HttpException
2239
         */
2240
        private void sendOptimize(HttpClient httpClient, int retryCount) throws UnsupportedEncodingException, IOException, HttpException {
2241
                PostMethod method = null;
2242
                try {
2243
                        logger.debug("Optimize retry: " + retryCount);
2244
                        method = new PostMethod(getConfiguration().getString("solrUpdateUrl"));
2245
                        method.setRequestEntity(new StringRequestEntity("<optimize/>", "text/xml", "iso8859-1"));
2246
                        int statusCode = httpClient.executeMethod(method);
2247
                        logger.debug("HTTP status: " + statusCode);
2248
                        String response = method.getResponseBodyAsString();
2249
                        logger.debug(response);
2250
                        if (statusCode != 200 && retryCount < 2) {
2251
                                try {
2252
                                        Thread.sleep(10000); //Give Solr a little time to be available
2253
                                } catch (InterruptedException e) {
2254
                                }
2255
                                sendOptimize(httpClient, retryCount + 1);
2256
                        }
2257
                }
2258
                finally {
2259
                        if (method != null)
2260
                                method.releaseConnection();
2261
                }
2262
        }
2263

    
2264
        @Override
2265
        public FileHeaderDTO createFile(Long userId, Long folderId, String name, String mimeType, long fileSize, String filePath)
2266
                        throws DuplicateNameException, ObjectNotFoundException, GSSIOException,
2267
                        InsufficientPermissionsException, QuotaExceededException {
2268
                // Validate.
2269
                if (userId == null)
2270
                        throw new ObjectNotFoundException("No user specified");
2271
                if (folderId == null)
2272
                        throw new ObjectNotFoundException("No folder specified");
2273
                String contentType = mimeType;
2274
                if (StringUtils.isEmpty(mimeType))
2275
                        contentType = DEFAULT_MIME_TYPE;
2276
                if (StringUtils.isEmpty(name))
2277
                        throw new ObjectNotFoundException("No file name specified");
2278
                if (dao.existsFolderOrFile(folderId, name))
2279
                        throw new DuplicateNameException("A folder or file with the name '" + name +
2280
                                                "' already exists at this level");
2281

    
2282
                // Do the actual work.
2283
                Folder parent = null;
2284
                try {
2285
                        parent = dao.getEntityById(Folder.class, folderId);
2286
                } catch (final ObjectNotFoundException onfe) {
2287
                        // Supply a more accurate problem description.
2288
                        throw new ObjectNotFoundException("Parent folder not found");
2289
                }
2290
                final User owner = dao.getEntityById(User.class, userId);
2291
                if (!parent.hasWritePermission(owner))
2292
                        throw new InsufficientPermissionsException("You don't have the permissions to write to this folder");
2293
                final FileHeader file = new FileHeader();
2294
                file.setName(name);
2295
                parent.addFile(file);
2296
                // set file owner to folder owner
2297
                file.setOwner(parent.getOwner());
2298
                //set file's readForAll value according to parent folder readForAll value
2299
                file.setReadForAll(parent.isReadForAll());
2300

    
2301
                final Date now = new Date();
2302
                final AuditInfo auditInfo = new AuditInfo();
2303
                auditInfo.setCreatedBy(owner);
2304
                auditInfo.setCreationDate(now);
2305
                auditInfo.setModifiedBy(owner);
2306
                auditInfo.setModificationDate(now);
2307
                file.setAuditInfo(auditInfo);
2308
                // TODO set the proper versioning flag on creation
2309
                file.setVersioned(false);
2310

    
2311
                for (final Permission p : parent.getPermissions()) {
2312
                        final Permission permission = new Permission();
2313
                        permission.setGroup(p.getGroup());
2314
                        permission.setUser(p.getUser());
2315
                        permission.setRead(p.getRead());
2316
                        permission.setWrite(p.getWrite());
2317
                        permission.setModifyACL(p.getModifyACL());
2318
                        file.addPermission(permission);
2319
                }
2320

    
2321
                // Create the file body.
2322
                try {
2323
                        createFileBody(name, contentType, fileSize, filePath, file, auditInfo);
2324
                } catch (FileNotFoundException e) {
2325
                        throw new GSSIOException(e);
2326
                }
2327
                touchParentFolders(parent, owner, new Date());
2328
                dao.flush();
2329
                indexFile(file.getId(), false);
2330

    
2331
                return file.getDTO();
2332
        }
2333

    
2334
        @Override
2335
        public FileHeaderDTO updateFileContents(Long userId, Long fileId, String mimeType, long fileSize, String filePath) throws ObjectNotFoundException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
2336
                if (userId == null)
2337
                        throw new ObjectNotFoundException("No user specified");
2338
                if (fileId == null)
2339
                        throw new ObjectNotFoundException("No file specified");
2340
                String contentType = mimeType;
2341

    
2342
                FileHeader file = dao.getEntityById(FileHeader.class, fileId);
2343

    
2344
                // if no mime type or the generic mime type is defined by the client, then try to identify it from the filename extension
2345
                if (StringUtils.isEmpty(mimeType) || "application/octet-stream".equals(mimeType)
2346
                                        || "application/download".equals(mimeType) || "application/force-download".equals(mimeType)
2347
                                        || "octet/stream".equals(mimeType) || "application/unknown".equals(mimeType))
2348
                        contentType = identifyMimeType(file.getName());
2349

    
2350
                final User owner = dao.getEntityById(User.class, userId);
2351
                if (!file.hasWritePermission(owner))
2352
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
2353
                final Date now = new Date();
2354
                final AuditInfo auditInfo = new AuditInfo();
2355
                auditInfo.setCreatedBy(owner);
2356
                auditInfo.setCreationDate(now);
2357
                auditInfo.setModifiedBy(owner);
2358
                auditInfo.setModificationDate(now);
2359
                try {
2360
                        createFileBody(file.getName(), contentType, fileSize, filePath, file, auditInfo);
2361
                } catch (FileNotFoundException e) {
2362
                        throw new GSSIOException(e);
2363
                }
2364
                Folder parent = file.getFolder();
2365
                touchParentFolders(parent, owner, new Date());
2366

    
2367
                indexFile(fileId, false);
2368
                return file.getDTO();
2369
        }
2370

    
2371
        /**
2372
         * Helper method for identifying mime type by examining the filename extension
2373
         *
2374
         * @param filename
2375
         * @return the mime type
2376
         */
2377
        private String identifyMimeType(String filename) {
2378
                if (filename.indexOf('.') != -1) {
2379
                        String extension = filename.substring(filename.lastIndexOf('.')).toLowerCase(Locale.ENGLISH);
2380
                        if (".doc".equals(extension))
2381
                                return "application/msword";
2382
                        else if (".xls".equals(extension))
2383
                                return "application/vnd.ms-excel";
2384
                        else if (".ppt".equals(extension))
2385
                                return "application/vnd.ms-powerpoint";
2386
                        else if (".pdf".equals(extension))
2387
                                return "application/pdf";
2388
                        else if (".gif".equals(extension))
2389
                                return "image/gif";
2390
                        else if (".jpg".equals(extension) || ".jpeg".equals(extension) || ".jpe".equals(extension))
2391
                                return "image/jpeg";
2392
                        else if (".tiff".equals(extension) || ".tif".equals(extension))
2393
                                return "image/tiff";
2394
                        else if (".png".equals(extension))
2395
                                return "image/png";
2396
                        else if (".bmp".equals(extension))
2397
                                return "image/bmp";
2398
                }
2399
                // when all else fails assign the default mime type
2400
                return DEFAULT_MIME_TYPE;
2401
        }
2402

    
2403
        /**
2404
         * Helper method to create a new file body and attach it as the current body
2405
         * of the provided file header.
2406
         *
2407
         * @param name the original file name
2408
         * @param mimeType the content type
2409
         * @param fileSize the uploaded file size
2410
         * @param filePath the uploaded file full path
2411
         * @param header the file header that will be associated with the new body
2412
         * @param auditInfo the audit info
2413
         * @param owner the owner of the file
2414
         * @throws FileNotFoundException
2415
         * @throws QuotaExceededException
2416
         * @throws ObjectNotFoundException if the owner was not found
2417
         */
2418
        private void createFileBody(String name, String mimeType, long fileSize, String filePath,
2419
                                FileHeader header, AuditInfo auditInfo)
2420
                        throws FileNotFoundException, QuotaExceededException, ObjectNotFoundException {
2421

    
2422
                long currentTotalSize = 0;
2423
                if (!header.isVersioned() && header.getCurrentBody() != null && header.getBodies() != null)
2424
                        currentTotalSize = header.getTotalSize();
2425
                Long quotaLeft = getQuotaLeft(header.getOwner().getId());
2426
                if(quotaLeft < fileSize-currentTotalSize) {
2427
                        // quota exceeded -> delete the file
2428
                        deleteActualFile(filePath);
2429
                        throw new QuotaExceededException("Not enough free space available");
2430
                }
2431

    
2432
                FileBody body = new FileBody();
2433

    
2434
                // if no mime type or the generic mime type is defined by the client, then try to identify it from the filename extension
2435
                if (StringUtils.isEmpty(mimeType) || "application/octet-stream".equals(mimeType)
2436
                                        || "application/download".equals(mimeType) || "application/force-download".equals(mimeType)
2437
                                        || "octet/stream".equals(mimeType) || "application/unknown".equals(mimeType))
2438
                        body.setMimeType(identifyMimeType(name));
2439
                else
2440
                        body.setMimeType(mimeType);
2441
                body.setAuditInfo(auditInfo);
2442
                body.setFileSize(fileSize);
2443
                body.setOriginalFilename(name);
2444
                body.setStoredFilePath(filePath);
2445
                //CLEAR OLD VERSION IF FILE IS NOT VERSIONED AND GETS UPDATED
2446
                if(!header.isVersioned() && header.getCurrentBody() != null){
2447
                        header.setCurrentBody(null);
2448
                        if (header.getBodies() != null) {
2449
                                Iterator<FileBody> it = header.getBodies().iterator();
2450
                                while(it.hasNext()){
2451
                                        FileBody bo = it.next();
2452
                                        deleteActualFile(bo.getStoredFilePath());
2453
                                        it.remove();
2454
                                        dao.delete(bo);
2455
                                }
2456
                        }
2457
                }
2458

    
2459
                dao.flush();
2460
                header.addBody(body);
2461
                header.setAuditInfo(auditInfo);
2462

    
2463
                dao.create(body);
2464
        }
2465

    
2466

    
2467
        @Override
2468
        @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
2469
        public File uploadFile(InputStream stream, Long userId) throws IOException, ObjectNotFoundException {
2470
                if (userId == null)
2471
                        throw new ObjectNotFoundException("No user specified");
2472
                User owner = dao.getEntityById(User.class, userId);
2473
                if(owner == null)
2474
                        throw new ObjectNotFoundException("No user specified");
2475
                long start = 0, end = 0;
2476
                if (logger.isDebugEnabled())
2477
                        start = System.currentTimeMillis();
2478
                File result = new File(generateRepositoryFilePath());
2479
                try {
2480
                        final FileOutputStream output = new FileOutputStream(result);
2481
                        final byte[] buffer = new byte[UPLOAD_BUFFER_SIZE];
2482
                        int n = 0;
2483

    
2484
                        while (-1 != (n = stream.read(buffer)))
2485
                                output.write(buffer, 0, n);
2486
                        output.close();
2487
                        stream.close();
2488
                } catch (IOException e) {
2489
                        if (!result.delete())
2490
                                logger.warn("Could not delete " + result.getPath());
2491
                        throw e;
2492
                }
2493
                if (logger.isDebugEnabled()) {
2494
                        end = System.currentTimeMillis();
2495
                        logger.debug("Time to upload: " + (end - start) + " (msec)");
2496
                }
2497
                return result;
2498
        }
2499

    
2500

    
2501
        @Override
2502
        public void createFileUploadProgress(Long userId, String filename, Long bytesTransfered, Long fileSize) throws ObjectNotFoundException{
2503

    
2504
                if (userId == null)
2505
                        throw new ObjectNotFoundException("No user specified");
2506
                User user = dao.getEntityById(User.class, userId);
2507
                FileUploadStatus status = dao.getFileUploadStatus(userId, filename);
2508
                if(status == null){
2509
                        status = new FileUploadStatus();
2510
                        status.setOwner(user);
2511
                        status.setFilename(filename);
2512
                        status.setBytesUploaded(bytesTransfered);
2513
                        status.setFileSize(fileSize);
2514
                        dao.create(status);
2515
                }
2516
                else{
2517
                        status.setBytesUploaded(bytesTransfered);
2518
                        status.setFileSize(fileSize);
2519
                        dao.update(status);
2520
                }
2521

    
2522
        }
2523

    
2524
        @Override
2525
        public void removeFileUploadProgress(Long userId, String filename) throws ObjectNotFoundException{
2526
                if (userId == null)
2527
                        throw new ObjectNotFoundException("No user specified");
2528
                FileUploadStatus status = dao.getFileUploadStatus(userId, filename);
2529
                if(status != null)
2530
                        dao.delete(status);
2531
        }
2532

    
2533
        @Override
2534
        public FileUploadStatus getFileUploadStatus(Long userId, String fileName) {
2535
                return dao.getFileUploadStatus(userId, fileName);
2536
        }
2537

    
2538
        @Override
2539
        public FolderDTO getFolderWithSubfolders(Long userId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
2540
                if (userId == null)
2541
                        throw new ObjectNotFoundException("No user specified");
2542
                if (folderId == null)
2543
                        throw new ObjectNotFoundException("No folder specified");
2544
                final User user = dao.getEntityById(User.class, userId);
2545
                final Folder folder = dao.getEntityById(Folder.class, folderId);
2546
                // Check permissions
2547
                if (!folder.hasReadPermission(user))
2548
                        throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
2549
                List<FolderDTO> subfolders = new ArrayList<FolderDTO>();
2550
                if (folder.hasReadPermission(user))
2551
                        for (Folder f : folder.getSubfolders())
2552
                                if (f.hasReadPermission(user) && !f.isDeleted())
2553
                                        subfolders.add(f.getDTO());
2554
                FolderDTO result = folder.getDTO();
2555
                result.setSubfolders(subfolders);
2556
                return folder.getDTO();
2557
        }
2558

    
2559
        @Override
2560
        public FolderDTO getFolderWithSubfolders(Long userId, Long callingUserId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
2561
                if (userId == null)
2562
                        throw new ObjectNotFoundException("No user specified");
2563
                if (folderId == null)
2564
                        throw new ObjectNotFoundException("No folder specified");
2565
                User user = dao.getEntityById(User.class, callingUserId);
2566
                Folder folder = dao.getEntityById(Folder.class, folderId);
2567
                // Check permissions
2568
                if (!folder.hasReadPermission(user))
2569
                        throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
2570

    
2571
                FolderDTO result = folder.getDTO();
2572
                result.setSubfolders(getSharedSubfolders(userId, callingUserId, folder.getId()));
2573
                return result;
2574
        }
2575

    
2576
        @Override
2577
        public FileBodyDTO getFileVersion(Long userId, Long fileId, int version)
2578
                        throws ObjectNotFoundException, InsufficientPermissionsException {
2579
                if (userId == null)
2580
                        throw new ObjectNotFoundException("No user specified");
2581
                if (fileId == null)
2582
                        throw new ObjectNotFoundException("No file specified");
2583
                if (version < 1)
2584
                        throw new ObjectNotFoundException("No valid version specified");
2585
                User user = dao.getEntityById(User.class, userId);
2586
                FileHeader file = dao.getEntityById(FileHeader.class, fileId);
2587
                if (!file.hasReadPermission(user) && !file.getFolder().hasReadPermission(user))
2588
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
2589
                FileBody body = dao.getFileVersion(fileId, version);
2590
                return body.getDTO();
2591
        }
2592

    
2593
        @Override
2594
        public User updateUserPolicyAcceptance(Long userId, boolean isAccepted) throws ObjectNotFoundException {
2595
                if (userId == null)
2596
                        throw new ObjectNotFoundException("No user specified");
2597
                User user = dao.getEntityById(User.class, userId);
2598
                user.setAcceptedPolicy(isAccepted);
2599
                return user;
2600
        }
2601

    
2602
        @Override
2603
        public void updateAccounting(User user, Date date, long bandwidthDiff) {
2604
                dao.updateAccounting(user, date, bandwidthDiff);
2605
        }
2606

    
2607
        @Override
2608
        public boolean canReadFolder(Long userId, Long folderId) throws ObjectNotFoundException {
2609
                if (userId == null)
2610
                        throw new ObjectNotFoundException("No user specified");
2611
                if (folderId == null)
2612
                        throw new ObjectNotFoundException("No folder specified");
2613
                User user = dao.getEntityById(User.class, userId);
2614
                Folder folder = dao.getEntityById(Folder.class, folderId);
2615
                // Check permissions
2616
                if (!folder.hasReadPermission(user))
2617
                        return false;
2618
                return true;
2619
        }
2620

    
2621
        @Override
2622
        public String resetWebDAVPassword(Long userId) throws ObjectNotFoundException {
2623
                if (userId == null)
2624
                        throw new ObjectNotFoundException("No user specified");
2625
                User user = dao.getEntityById(User.class, userId);
2626
                user.generateWebDAVPassword();
2627
                return user.getWebDAVPassword();
2628
        }
2629

    
2630
        @Override
2631
        public Invitation findInvite(String code) {
2632
                if (code == null)
2633
                        return null;
2634
                return dao.findInvite(code);
2635
        }
2636

    
2637
        @Override
2638
        public void createLdapUser(String username, String firstname, String lastname, String email, String password) {
2639
                LDAPConnection lc = new LDAPConnection();
2640
        LDAPAttributeSet attributeSet = new LDAPAttributeSet();
2641
        attributeSet.add(new LDAPAttribute("objectClass", getConfiguration().getStringArray("objectClass")));
2642
        attributeSet.add(new LDAPAttribute("uid", username));
2643
        attributeSet.add(new LDAPAttribute("cn", new String[]{firstname + " " + lastname}));
2644
        attributeSet.add(new LDAPAttribute("sn", lastname));
2645
        attributeSet.add(new LDAPAttribute("givenName", firstname));
2646
        attributeSet.add(new LDAPAttribute("mail", email));
2647
        attributeSet.add(new LDAPAttribute("userPassword", password));
2648
        String dn = "uid=" + username + "," + getConfiguration().getString("baseDn");
2649
        LDAPEntry newEntry = new LDAPEntry(dn, attributeSet);
2650
        try {
2651
                lc.connect(getConfiguration().getString("ldapHost"), LDAPConnection.DEFAULT_PORT);
2652
                lc.bind(LDAPConnection.LDAP_V3, getConfiguration().getString("bindDn"),
2653
                                getConfiguration().getString("bindPassword").getBytes("UTF8"));
2654
                lc.add(newEntry);
2655
                logger.info("Successfully added LDAP account: " + dn);
2656
                lc.disconnect();
2657
        } catch(LDAPException e) {
2658
                throw new RuntimeException(e);
2659
        } catch(UnsupportedEncodingException e) {
2660
                throw new RuntimeException(e);
2661
        }
2662

    
2663
        }
2664

    
2665
        @Override
2666
        public UserClass upgradeUserClass(String username, String code) throws ObjectNotFoundException, InvitationUsedException {
2667
                User user = findUser(username);
2668
                if (user == null)
2669
                        throw new ObjectNotFoundException("The user was not found");
2670
                Invitation invite = findInvite(code);
2671
                if (invite.getUser() != null)
2672
                        throw new InvitationUsedException("This code has already been used");
2673
                invite.setUser(user);
2674
                UserClass couponClass = getCouponUserClass();
2675
                user.setUserClass(couponClass);
2676
                return couponClass;
2677
        }
2678

    
2679
        @Override
2680
        public UserClass getCouponUserClass() {
2681
                return dao.findCouponUserClass();
2682
        }
2683

    
2684
        /**
2685
         * Mark the folder as modified from the specified user and change it's modification date.
2686
         */
2687
        private void touchFolder(Folder f, User owner, Date now){
2688
                final AuditInfo auditInfo = new AuditInfo();
2689
                auditInfo.setCreatedBy(owner);
2690
                auditInfo.setCreationDate(now);
2691
                auditInfo.setModifiedBy(owner);
2692
                auditInfo.setModificationDate(now);
2693
                f.setAuditInfo(auditInfo);
2694
        }
2695

    
2696
        /**
2697
         * Mark the file as modified from the specified user and change it's modification date.
2698
         */
2699
        private void touchFile(FileHeader f, User owner, Date now){
2700
                final AuditInfo auditInfo = new AuditInfo();
2701
                auditInfo.setCreatedBy(owner);
2702
                auditInfo.setCreationDate(now);
2703
                auditInfo.setModifiedBy(owner);
2704
                auditInfo.setModificationDate(now);
2705
                f.setAuditInfo(auditInfo);
2706
        }
2707

    
2708
        /**
2709
         * Set the provided readForAll as the new readforAll value of the specified
2710
         * folder and sub-folders.
2711
         *
2712
         * @param user
2713
         * @param folder
2714
         * @param readForAll
2715
         * @throws ObjectNotFoundException
2716
         *
2717
         */
2718
        private void setFolderReadForAll(User user, Folder folder, Boolean readForAll){
2719
                if (readForAll != null && user.equals(folder.getOwner())){
2720
                        folder.setReadForAll(readForAll);
2721
                        dao.update(folder);
2722
                        for (FileHeader file : folder.getFiles())
2723
                                file.setReadForAll(readForAll);
2724
                        if(readForAll)
2725
                                //only update subfolders when readforall is true. otherwise all sub-folders stay untouched
2726
                                for (Folder sub : folder.getSubfolders())
2727
                                        setFolderReadForAll(user, sub, readForAll);
2728

    
2729
                }
2730

    
2731
        }
2732
}