Statistics
| Branch: | Tag: | Revision:

root / src / gr / ebs / gss / server / ejb / ExternalAPIBean.java @ 99d0d40c

History | View | Annotate | Download (100.9 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
                dao.create(folder);
346
                return folder.getDTO();
347
        }
348

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

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

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

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

    
411
        @Override
412
        public FolderDTO updateFolder(Long userId, Long folderId, String folderName,
413
                                Boolean readForAll,
414
                                Set<PermissionDTO> permissions)
415
                        throws InsufficientPermissionsException, ObjectNotFoundException,
416
                        DuplicateNameException {
417

    
418
                // Validate.
419
                if (userId == null)
420
                        throw new ObjectNotFoundException("No user specified");
421
                if (folderId == null)
422
                        throw new ObjectNotFoundException("No folder specified");
423

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

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

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

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

    
466
                // TODO: Check permissions
467

    
468
                final User owner = dao.getEntityById(User.class, userId);
469

    
470
                // Do the actual work.
471
                owner.createGroup(name);
472
        }
473

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

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

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

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

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

    
564

    
565

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

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

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

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

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

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

    
641
        @Override
642
        public Set<String> getUserTags(final Long userId) throws ObjectNotFoundException {
643
                return dao.getUserTags(userId);
644
        }
645

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

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

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

    
678
                if (modificationDate != null)
679
                        file.getAuditInfo().setModificationDate(modificationDate);
680
                else
681
                        file.getAuditInfo().setModificationDate(new Date());
682
                file.getAuditInfo().setModifiedBy(user);
683

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

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

    
726
                touchParentFolders(parent, user, new Date());
727

    
728
                // Re-index the file if it was modified.
729
                if (name != null || tagSet != null)
730
                        indexFile(fileId, false);
731
        }
732

    
733
        @Override
734
        public InputStream getFileContents(Long userId, Long fileId)
735
                        throws ObjectNotFoundException, InsufficientPermissionsException {
736
                if (userId == null)
737
                        throw new ObjectNotFoundException("No user specified");
738
                if (fileId == null)
739
                        throw new ObjectNotFoundException("No file specified");
740

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

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

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

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

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

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

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

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

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

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

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

    
874
                FileHeader file = dao.getFile(folderId, name);
875
                return file.getDTO();
876
        }
877

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

    
895
                Folder folder = dao.getFolder(parentId, name);
896
                return folder.getDTO();
897
        }
898

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

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

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

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

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

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

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

    
981
                } catch (FileNotFoundException e) {
982
                        throw new ObjectNotFoundException("File contents not found for file " + contents.getAbsolutePath());
983
                }
984

    
985
        }
986

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

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

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

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

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

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

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

    
1074
        }
1075

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

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

    
1123
        @Override
1124
        public void moveFileToTrash(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
1125
                if (userId == null)
1126
                        throw new ObjectNotFoundException("No user specified");
1127
                if (fileId == null)
1128
                        throw new ObjectNotFoundException("No file specified");
1129

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

    
1139
                file.setDeleted(true);
1140
                dao.update(file);
1141
                touchParentFolders(parent, user, new Date());
1142
        }
1143

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

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

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

    
1173
                FileHeader file = dao.getEntityById(FileHeader.class, fileId);
1174
                Folder source = file.getFolder();
1175
                Folder destination = dao.getEntityById(Folder.class, destId);
1176

    
1177
                User owner = dao.getEntityById(User.class, userId);
1178
                if (!file.hasDeletePermission(owner) || !destination.hasWritePermission(owner))
1179
                        throw new InsufficientPermissionsException("User " + owner.getId() + " cannot move file " + file.getName() + "(" + file.getId() + ")");
1180

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

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

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

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

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

    
1286
        @Override
1287
        public List<FileHeaderDTO> getDeletedFiles(Long userId) throws ObjectNotFoundException {
1288
                // Validate.
1289
                if (userId == null)
1290
                        throw new ObjectNotFoundException("No user specified");
1291

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

    
1300
        @Override
1301
        public void removeFileFromTrash(Long userId, Long fileId)
1302
                        throws ObjectNotFoundException, InsufficientPermissionsException {
1303
                if (userId == null)
1304
                        throw new ObjectNotFoundException("No user specified");
1305
                if (fileId == null)
1306
                        throw new ObjectNotFoundException("No file specified");
1307

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

    
1318
                file.setDeleted(false);
1319
                dao.update(file);
1320
                touchParentFolders(parent, user, new Date());
1321
        }
1322

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

    
1341
        }
1342

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

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

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

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

    
1393
        @Override
1394
        public User createUser(String username, String name, String mail,
1395
                                String idp, String idpid) throws ObjectNotFoundException {
1396
                if (username == null)
1397
                        throw new ObjectNotFoundException("No username specified");
1398
                if (name == null)
1399
                        throw new ObjectNotFoundException("No name specified");
1400

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

    
1424
        /**
1425
         * Get the default user class, which is the one with the lowest quota.
1426
         */
1427
        private UserClass getDefaultUserClass() {
1428
                return getUserClasses().get(0);
1429
        }
1430

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

    
1447
        @Override
1448
        public User findUserByEmail(String email) {
1449
                return dao.findUserByEmail(email);
1450
        }
1451

    
1452
        @Override
1453
        public void updateUser(User user) {
1454
                dao.update(user);
1455
        }
1456

    
1457
        @Override
1458
        public User findUser(String username) {
1459
                if (username == null)
1460
                        return null;
1461
                return dao.findUser(username);
1462
        }
1463

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

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

    
1494
        }
1495

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

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

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

    
1564
        }
1565

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

    
1584
        }
1585

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

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

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

    
1625
        }
1626

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

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

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

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

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

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

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

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

    
1758
        }
1759

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

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

    
1796
        }
1797

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

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

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

    
1851
                        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
1852
                        DocumentBuilder db = dbf.newDocumentBuilder();
1853
                        Document doc = db.parse(method.getResponseBodyAsStream());
1854
                        method.releaseConnection();
1855

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

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

    
1908

    
1909
        }
1910

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

    
1918
        }
1919

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

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

    
1950
        }
1951

    
1952
        @Override
1953
        public void moveFilesToTrash(Long userId, List<Long> fileIds) throws ObjectNotFoundException, InsufficientPermissionsException {
1954
                for(Long l : fileIds)
1955
                        moveFileToTrash(userId, l);
1956

    
1957
        }
1958

    
1959
        @Override
1960
        public void removeFilesFromTrash(Long userId, List<Long> fileIds) throws ObjectNotFoundException, InsufficientPermissionsException {
1961
                for(Long l : fileIds)
1962
                        removeFileFromTrash(userId, l);
1963

    
1964
        }
1965

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

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

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

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

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

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

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

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

    
2056
                Folder parent = header.getFolder();
2057
                touchParentFolders(parent, user, new Date());
2058

    
2059
        }
2060

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

    
2074
                try {
2075
                        updateFileContents(userId, fileId, body.getMimeType(), new FileInputStream(fileContents) );
2076
                } catch (FileNotFoundException e) {
2077
                        throw new GSSIOException(e);
2078
                }
2079

    
2080
        }
2081

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

    
2106
                Folder parent = header.getFolder();
2107
                touchParentFolders(parent, user, new Date());
2108
        }
2109

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2327
                return file.getDTO();
2328
        }
2329

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

    
2338
                FileHeader file = dao.getEntityById(FileHeader.class, fileId);
2339

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

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

    
2363
                indexFile(fileId, false);
2364
                return file.getDTO();
2365
        }
2366

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

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

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

    
2428
                FileBody body = new FileBody();
2429

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

    
2455
                dao.flush();
2456
                header.addBody(body);
2457
                header.setAuditInfo(auditInfo);
2458

    
2459
                dao.create(body);
2460
        }
2461

    
2462

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

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

    
2496

    
2497
        @Override
2498
        public void createFileUploadProgress(Long userId, String filename, Long bytesTransfered, Long fileSize) throws ObjectNotFoundException{
2499

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

    
2518
        }
2519

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

    
2529
        @Override
2530
        public FileUploadStatus getFileUploadStatus(Long userId, String fileName) {
2531
                return dao.getFileUploadStatus(userId, fileName);
2532
        }
2533

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

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

    
2567
                FolderDTO result = folder.getDTO();
2568
                result.setSubfolders(getSharedSubfolders(userId, callingUserId, folder.getId()));
2569
                return result;
2570
        }
2571

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

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

    
2598
        @Override
2599
        public void updateAccounting(User user, Date date, long bandwidthDiff) {
2600
                dao.updateAccounting(user, date, bandwidthDiff);
2601
        }
2602

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

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

    
2626
        @Override
2627
        public Invitation findInvite(String code) {
2628
                if (code == null)
2629
                        return null;
2630
                return dao.findInvite(code);
2631
        }
2632

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

    
2659
        }
2660

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

    
2675
        @Override
2676
        public UserClass getCouponUserClass() {
2677
                return dao.findCouponUserClass();
2678
        }
2679

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

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

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

    
2725
                }
2726

    
2727
        }
2728
}