Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (101.9 kB)

1
/*
2
 * Copyright 2007, 2008, 2009, 2010  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

    
23
import gr.ebs.gss.admin.client.ui.UsersTable;
24
import gr.ebs.gss.client.exceptions.DuplicateNameException;
25
import gr.ebs.gss.client.exceptions.GSSIOException;
26
import gr.ebs.gss.client.exceptions.InsufficientPermissionsException;
27
import gr.ebs.gss.client.exceptions.InvitationUsedException;
28
import gr.ebs.gss.client.exceptions.ObjectNotFoundException;
29
import gr.ebs.gss.client.exceptions.QuotaExceededException;
30
import gr.ebs.gss.server.domain.AuditInfo;
31
import gr.ebs.gss.server.domain.FileBody;
32
import gr.ebs.gss.server.domain.FileHeader;
33
import gr.ebs.gss.server.domain.FileTag;
34
import gr.ebs.gss.server.domain.FileUploadStatus;
35
import gr.ebs.gss.server.domain.Folder;
36
import gr.ebs.gss.server.domain.Group;
37
import gr.ebs.gss.server.domain.FileLock;
38
import gr.ebs.gss.server.domain.Invitation;
39
import gr.ebs.gss.server.domain.Nonce;
40
import gr.ebs.gss.server.domain.Permission;
41
import gr.ebs.gss.server.domain.User;
42
import gr.ebs.gss.server.domain.UserClass;
43
import gr.ebs.gss.server.domain.UserLogin;
44
import gr.ebs.gss.server.domain.WebDavNonce;
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.UnsupportedEncodingException;
55
import java.net.MalformedURLException;
56
import java.util.ArrayList;
57
import java.util.Date;
58
import java.util.Iterator;
59
import java.util.LinkedHashSet;
60
import java.util.List;
61
import java.util.Locale;
62
import java.util.Random;
63
import java.util.Set;
64
import java.util.StringTokenizer;
65

    
66
import javax.ejb.EJB;
67
import javax.ejb.EJBException;
68
import javax.ejb.EJBTransactionRolledbackException;
69
import javax.ejb.Stateless;
70
import javax.ejb.TransactionAttribute;
71
import javax.ejb.TransactionAttributeType;
72
import javax.jms.Connection;
73
import javax.jms.ConnectionFactory;
74
import javax.jms.JMSException;
75
import javax.jms.MapMessage;
76
import javax.jms.MessageProducer;
77
import javax.jms.Queue;
78
import javax.jms.QueueConnectionFactory;
79
import javax.jms.Session;
80
import javax.naming.Context;
81
import javax.naming.InitialContext;
82
import javax.naming.NamingException;
83
import javax.persistence.PersistenceException;
84

    
85
import org.apache.commons.lang.StringUtils;
86
import org.apache.commons.logging.Log;
87
import org.apache.commons.logging.LogFactory;
88
import org.apache.solr.client.solrj.SolrQuery;
89
import org.apache.solr.client.solrj.SolrServerException;
90
import org.apache.solr.client.solrj.impl.CommonsHttpSolrServer;
91
import org.apache.solr.client.solrj.request.ContentStreamUpdateRequest;
92
import org.apache.solr.client.solrj.response.QueryResponse;
93
import org.apache.solr.common.SolrDocument;
94
import org.apache.solr.common.SolrDocumentList;
95
import org.apache.solr.common.SolrException;
96
import org.apache.solr.common.SolrInputDocument;
97
import org.hibernate.exception.ConstraintViolationException;
98

    
99
import com.novell.ldap.LDAPAttribute;
100
import com.novell.ldap.LDAPAttributeSet;
101
import com.novell.ldap.LDAPConnection;
102
import com.novell.ldap.LDAPEntry;
103
import com.novell.ldap.LDAPException;
104

    
105
/**
106
 * The concrete implementation of the ExternalAPI interface.
107
 *
108
 * @author past
109
 */
110
@Stateless
111
public class ExternalAPIBean implements ExternalAPI, ExternalAPIRemote {
112
        /**
113
         * The default MIME type for files without an explicit one.
114
         */
115
        private static final String DEFAULT_MIME_TYPE = "application/octet-stream";
116

    
117
        /**
118
         * The size of the buffer that is used to temporarily store chunks of
119
         * uploaded files, while storing them to the file repository.
120
         */
121
        private static final int UPLOAD_BUFFER_SIZE = 1024 * 4;
122

    
123
        /**
124
         * The logger.
125
         */
126
        private static Log logger = LogFactory.getLog(ExternalAPIBean.class);
127

    
128
        /**
129
         * Injected reference to the GSSDAO data access facade.
130
         */
131
        @EJB
132
        private GSSDAO dao;
133

    
134

    
135
        /**
136
         * A cached random number generator for creating unique filenames.
137
         */
138
        private static Random random = new Random();
139

    
140
        /**
141
         * Mark the folder and all of its parent folders as modified from the specified user.
142
         */
143
        private void touchParentFolders(Folder folder, User user, Date date) {
144
                Folder f = folder;
145
                while (f != null) {
146
                        AuditInfo ai = f.getAuditInfo();
147
                        ai.setModifiedBy(user);
148
                        ai.setModificationDate(date);
149
                        f.setAuditInfo(ai);
150
                        f = f.getParent();
151
                }
152
        }
153

    
154
        private Long getRootFolderId(Long userId) throws ObjectNotFoundException {
155
                if (userId == null)
156
                        throw new ObjectNotFoundException("No user specified");
157
                return dao.getRootFolderId(userId);
158
        }
159
        
160
        @Override
161
        public Folder getRootFolder(Long userId) throws ObjectNotFoundException {
162
                if (userId == null)
163
                        throw new ObjectNotFoundException("No user specified");
164
                Folder folder = dao.getRootFolder(userId);
165
                return folder;
166
        }
167

    
168
        @Override
169
        public Folder getFolder(final Long userId, final Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
170
                if (userId == null)
171
                        throw new ObjectNotFoundException("No user specified");
172
                if (folderId == null)
173
                        throw new ObjectNotFoundException("No folder specified");
174
                final User user = dao.getEntityById(User.class, userId);
175
                final Folder folder = dao.getEntityById(Folder.class, folderId);
176
                // Check permissions
177
                if (!folder.hasReadPermission(user))
178
                        throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
179
                return expandFolder(folder);
180
        }
181

    
182
        @Override
183
        public User getUser(Long userId) throws ObjectNotFoundException {
184
                if (userId == null)
185
                        throw new ObjectNotFoundException("No user specified");
186
                return dao.getEntityById(User.class, userId);
187
        }
188

    
189
        @Override
190
        public UserDTO getUserDTO(final Long userId) throws ObjectNotFoundException {
191
                return getUser(userId).getDTO();
192
        }
193

    
194
        @Override
195
        public Group getGroup(final Long groupId) throws ObjectNotFoundException {
196
                if (groupId == null)
197
                        throw new ObjectNotFoundException("No group specified");
198
                final Group group = dao.getEntityById(Group.class, groupId);
199
                return group;
200
        }
201

    
202
        @Override
203
        public Group getGroup(Long userId, String name) throws ObjectNotFoundException {
204
                if (userId == null)
205
                        throw new ObjectNotFoundException("No user specified");
206
                if (name == null)
207
                        throw new ObjectNotFoundException("No group specified");
208
                User user = dao.getEntityById(User.class, userId);
209
                List<Group> groups = user.getGroupsSpecified();
210
                for (Group group: groups)
211
                        if (group.getName().equals(name))
212
                                return group;
213
                throw new ObjectNotFoundException("Group " + name + " not found");
214
        }
215

    
216
        @Override
217
        public List<Group> getGroups(final Long userId) throws ObjectNotFoundException {
218
                if (userId == null)
219
                        throw new ObjectNotFoundException("No user specified");
220
                final List<Group> groups = dao.getGroups(userId);
221
                return groups;
222
        }
223

    
224
        @Override
225
        public List<FileHeader> getFiles(Long userId, Long folderId, boolean ignoreDeleted)
226
                        throws ObjectNotFoundException, InsufficientPermissionsException {
227
                // Validate.
228
                if (userId == null)
229
                        throw new ObjectNotFoundException("No user specified");
230
                if (folderId == null)
231
                        throw new ObjectNotFoundException("No folder specified");
232
                User user = dao.getEntityById(User.class, userId);
233
                Folder folder = dao.getEntityById(Folder.class, folderId);
234
                if (!folder.hasReadPermission(user))
235
                        throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
236
                List<FileHeader> files = dao.getFiles(folderId, userId, ignoreDeleted);
237
                return files;
238
        }
239

    
240
        @Override
241
        public List<User> getUsers(final Long userId, final Long groupId) throws ObjectNotFoundException {
242
                // Validate.
243
                if (userId == null)
244
                        throw new ObjectNotFoundException("No user specified");
245
                if (groupId == null)
246
                        throw new ObjectNotFoundException("No group specified");
247

    
248
                // Do the actual work.
249
                final List<User> users = dao.getUsers(groupId);
250
                return users;
251
        }
252

    
253
        @Override
254
        public Folder createFolder(Long userId, Long parentId, String name)
255
                        throws DuplicateNameException, ObjectNotFoundException, InsufficientPermissionsException {
256
                // Validate.
257
                if (userId == null)
258
                        throw new ObjectNotFoundException("No user specified");
259
                if (StringUtils.isEmpty(name))
260
                        throw new ObjectNotFoundException("New folder name is empty");
261
                if (parentId == null)
262
                        throw new ObjectNotFoundException("No parent specified");
263
                if (dao.existsFolderOrFile(parentId, name))
264
                        throw new DuplicateNameException("A folder or file with the name '" +
265
                                                name + "' already exists at this level");
266

    
267
                User creator = dao.getEntityById(User.class, userId);
268

    
269
                Folder parent = null;
270
                try {
271
                        parent = dao.getEntityById(Folder.class, parentId);
272
                } catch (ObjectNotFoundException onfe) {
273
                        // Supply a more accurate problem description.
274
                        throw new ObjectNotFoundException("Parent folder not found");
275
                }
276
                if (!parent.hasWritePermission(creator))
277
                        throw new InsufficientPermissionsException("You don't have the permissions" +
278
                                        " to write to this folder");
279

    
280
                // Do the actual work.
281
                return createFolder(name, parent, creator);
282
        }
283

    
284
        /**
285
         * Create a new folder with the provided name, parent and owner.
286
         *
287
         * @param name
288
         * @param parent
289
         * @param creator
290
         * @return the new folder
291
         */
292
        private Folder createFolder(String name, Folder parent, User creator) {
293
                Folder folder = new Folder();
294
                folder.setName(name);
295
                if (parent != null) {
296
                        parent.addSubfolder(folder);
297
                        folder.setOwner(parent.getOwner());
298
                } else
299
                        folder.setOwner(creator);
300

    
301
                Date now = new Date();
302
                AuditInfo auditInfo = new AuditInfo();
303
                auditInfo.setCreatedBy(creator);
304
                auditInfo.setCreationDate(now);
305
                auditInfo.setModifiedBy(creator);
306
                auditInfo.setModificationDate(now);
307
                folder.setAuditInfo(auditInfo);
308
                touchParentFolders(folder, auditInfo.getModifiedBy(), auditInfo.getModificationDate());
309

    
310
                if (parent != null)
311
                        for (Permission p : parent.getPermissions()) {
312
                                Permission permission = new Permission();
313
                                permission.setGroup(p.getGroup());
314
                                permission.setUser(p.getUser());
315
                                permission.setRead(p.getRead());
316
                                permission.setWrite(p.getWrite());
317
                                permission.setModifyACL(p.getModifyACL());
318
                                folder.addPermission(permission);
319
                        }
320
                else {
321
                        Permission permission = new Permission();
322
                        permission.setUser(creator);
323
                        permission.setRead(true);
324
                        permission.setWrite(true);
325
                        permission.setModifyACL(true);
326
                        folder.addPermission(permission);
327
                }
328

    
329
                if(parent != null)
330
                        folder.setReadForAll(parent.isReadForAll());
331

    
332
                dao.create(folder);
333
                return folder;
334
        }
335

    
336
        @Override
337
        public void deleteFolder(final Long userId, final Long folderId) throws InsufficientPermissionsException, ObjectNotFoundException {
338
                // Validate.
339
                if (userId == null)
340
                        throw new ObjectNotFoundException("No user specified");
341
                if (folderId == null)
342
                        throw new ObjectNotFoundException("No folder specified");
343

    
344
                // Do the actual work.
345
                final Folder folder = dao.getEntityById(Folder.class, folderId);
346
                final Folder parent = folder.getParent();
347
                if (parent == null)
348
                        throw new ObjectNotFoundException("Deleting the root folder is not allowed");
349
                final User user = dao.getEntityById(User.class, userId);
350
                if (!folder.hasDeletePermission(user)) {
351
                        logger.info("User " + user.getId() + " cannot delete folder " + folder.getName() + "(" + folder.getId() + ")");
352
                        throw new InsufficientPermissionsException("User " + user.getId() + " cannot delete folder " + folder.getName() + "(" + folder.getId() + ")");
353
                }
354
                removeSubfolderFiles(folder);
355
                parent.removeSubfolder(folder);
356
                dao.delete(folder);
357
                touchParentFolders(parent, user, new Date());
358
        }
359

    
360
        /**
361
         * Traverses the folder and deletes all actual files (file system)
362
         * regardless of permissions
363
         *
364
         * @param folder
365
         */
366
        private void removeSubfolderFiles(Folder folder) {
367
                //remove files for all subfolders
368
                for (Folder subfolder:folder.getSubfolders())
369
                        removeSubfolderFiles(subfolder);
370
                //remove this folder's file bodies (actual files)
371
                for (FileHeader file:folder.getFiles()) {
372
                        for (FileBody body:file.getBodies())
373
                                deleteActualFile(body.getStoredFilePath());
374
                        indexFile(file.getId(), true);
375
                }
376
        }
377

    
378
        @Override
379
        @SuppressWarnings("unchecked")
380
        public List<Folder> getSubfolders(Long userId, Long folderId)
381
                        throws ObjectNotFoundException, InsufficientPermissionsException {
382
                if (userId == null)
383
                        throw new ObjectNotFoundException("No user specified");
384
                if (folderId == null)
385
                        throw new ObjectNotFoundException("No folder specified");
386
                User user = dao.getEntityById(User.class, userId);
387
                Folder folder = dao.getEntityById(Folder.class, folderId);
388
                if (!folder.hasReadPermission(user))
389
                        throw new InsufficientPermissionsException("You don't have the permissions to read this folder");
390
                List<Folder> result = new ArrayList<Folder>();
391
                if (folder.hasReadPermission(user))
392
                        for (Folder f : folder.getSubfolders())
393
                                if (f.hasReadPermission(user) && !f.isDeleted())
394
                                        result.add(f);
395
                return result;
396
        }
397

    
398
        @Override
399
        public Folder updateFolder(Long userId, Long folderId, String folderName,
400
                                Boolean readForAll,
401
                                Set<Permission> permissions)
402
                        throws InsufficientPermissionsException, ObjectNotFoundException,
403
                        DuplicateNameException {
404

    
405
                // Validate.
406
                if (userId == null)
407
                        throw new ObjectNotFoundException("No user specified");
408
                if (folderId == null)
409
                        throw new ObjectNotFoundException("No folder specified");
410

    
411
                Folder folder = dao.getEntityById(Folder.class, folderId);
412
                User user = dao.getEntityById(User.class, userId);
413
                if (folderName != null && !folder.hasWritePermission(user))
414
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
415
                if(permissions != null && !permissions.isEmpty() && !folder.hasModifyACLPermission(user))
416
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
417
                // Check permissions for making file public.
418
                if (readForAll != null && !user.equals(folder.getOwner()))
419
                                throw new InsufficientPermissionsException("Only the owner can make a folder public or not public");
420

    
421
                Folder parent = folder.getParent();
422
                if (folderName != null) {
423
                        if (parent != null)
424
                                if (!folder.getName().equals(folderName) && dao.existsFolderOrFile(parent.getId(), folderName))
425
                                        throw new DuplicateNameException("A folder or file with the name '" + folderName + "' already exists at this level");
426

    
427
                        // Do the actual modification.
428
                        folder.setName(folderName);
429
                }
430
                if (permissions != null)
431
                        setFolderPermissions(user, folder, permissions);
432
                if (readForAll != null)
433
                        setFolderReadForAll(user, folder, readForAll);
434
                folder.getAuditInfo().setModificationDate(new Date());
435
                folder.getAuditInfo().setModifiedBy(user);
436
                dao.update(folder);
437
                touchParentFolders(folder, user, new Date());
438
                // Re-index the folder contents if it was modified.
439
                if ((permissions != null && !permissions.isEmpty()) || readForAll != null) {
440
            indexFolder(folder);
441
        }
442

    
443
                return folder;
444
        }
445

    
446
    private void indexFolder(Folder folder) {
447
        for (FileHeader fh : folder.getFiles())
448
            indexFile(fh.getId(), false);
449
        for (Folder f : folder.getSubfolders())
450
            indexFolder(f);
451
    }
452

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

    
465
                // TODO: Check permissions
466

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

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

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

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

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

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

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

    
560

    
561

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

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

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

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

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

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

    
637
        @Override
638
        public Set<String> getUserTags(final Long userId) throws ObjectNotFoundException {
639
                return dao.getUserTags(userId);
640
        }
641

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

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

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

    
674
                if (modificationDate != null)
675
                        file.getAuditInfo().setModificationDate(modificationDate);
676
                else
677
                        file.getAuditInfo().setModificationDate(new Date());
678
                file.getAuditInfo().setModifiedBy(user);
679

    
680
                List<FileTag> tags = file.getFileTags();
681
                if (tagSet != null) {
682
                        Iterator<FileTag> i = tags.iterator();
683
                        while (i.hasNext()) {
684
                                FileTag tag = i.next();
685
                                i.remove();
686
                                tag.setFile(null);
687
                                user.removeTag(tag);
688
                                dao.delete(tag);
689
                        }
690
                        dao.flush();
691
                        StringTokenizer st = new StringTokenizer(tagSet, ",");
692
                        while (st.hasMoreTokens())
693
                                new FileTag(user, file, st.nextToken().trim());
694
                }
695
                if (versioned != null && !file.isVersioned() == versioned) {
696
                        if (file.isVersioned())
697
                                removeOldVersions(userId, fileId);
698
                        file.setVersioned(versioned);
699
                }
700
                if (readForAll != null && user.equals(file.getOwner()))
701
                        file.setReadForAll(readForAll);
702
                if (permissions != null && !permissions.isEmpty())
703
                        setFilePermissions(file, permissions);
704

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

    
720
                touchParentFolders(parent, user, new Date());
721

    
722
                // Re-index the file if it was modified.
723
                if (name != null || tagSet != null || (permissions != null && !permissions.isEmpty()) || readForAll != null)
724
                        indexFile(fileId, false);
725
        }
726

    
727
        @Override
728
        public InputStream getFileContents(Long userId, Long fileId)
729
                        throws ObjectNotFoundException, InsufficientPermissionsException {
730
                if (userId == null)
731
                        throw new ObjectNotFoundException("No user specified");
732
                if (fileId == null)
733
                        throw new ObjectNotFoundException("No file specified");
734

    
735
                FileHeader header = dao.getEntityById(FileHeader.class, fileId);
736
                User user = dao.getEntityById(User.class, userId);
737
                if (!header.hasReadPermission(user)) {
738
                        logger.info("User " + user.getId() + " cannot read file " + header.getName() + "(" + fileId + ")");
739
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
740
                }
741

    
742
                File f = new File(header.getCurrentBody().getStoredFilePath());
743
                try {
744
                        return new FileInputStream(f);
745
                } catch (FileNotFoundException e) {
746
                        logger.error("Could not locate the contents of file " + f.getAbsolutePath());
747
                        throw new ObjectNotFoundException("The file contents could not be located");
748
                }
749
        }
750

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

    
763
                final FileHeader header = dao.getEntityById(FileHeader.class, fileId);
764
                final FileBody body = dao.getEntityById(FileBody.class, bodyId);
765
                final User user = dao.getEntityById(User.class, userId);
766
                if (!header.hasReadPermission(user)) {
767
                        logger.info("User " + user.getId() + " cannot read file " + header.getName() + "(" + fileId + ")");
768
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
769
                }
770

    
771
                File f = new File(body.getStoredFilePath());
772
                try {
773
                        return new FileInputStream(f);
774
                } catch (FileNotFoundException e) {
775
                        logger.error("Could not locate the contents of file " + f.getAbsolutePath());
776
                        throw new ObjectNotFoundException("The file contents could not be located");
777
                }
778
        }
779

    
780
        @Override
781
        public FileHeader getFile(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
782
                if (userId == null)
783
                        throw new ObjectNotFoundException("No user specified");
784
                if (fileId == null)
785
                        throw new ObjectNotFoundException("No file specified");
786
                final User user = dao.getEntityById(User.class, userId);
787
                final FileHeader file = dao.getEntityById(FileHeader.class, fileId);
788
                if (!file.hasReadPermission(user) && !file.getFolder().hasReadPermission(user))
789
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
790
                return file;
791
        }
792

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

    
807
        @Override
808
        public Object getResourceAtPath(Long ownerId, String path, boolean ignoreDeleted)
809
                        throws ObjectNotFoundException {
810
                if (ownerId == null)
811
                        throw new ObjectNotFoundException("No user specified");
812
                if (StringUtils.isEmpty(path))
813
                        throw new ObjectNotFoundException("No path specified");
814

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

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

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

    
869
                FileHeader file = dao.getFile(folderId, name);
870
                return file;
871
        }
872

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

    
890
                Folder folder = dao.getFolder(parentId, name);
891
                return folder;
892
        }
893

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

    
905
        @Override
906
        public void copyFile(Long userId, Long fileId, String dest) throws ObjectNotFoundException, DuplicateNameException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
907
                if (userId == null)
908
                        throw new ObjectNotFoundException("No user specified");
909
                if (fileId == null)
910
                        throw new ObjectNotFoundException("No file specified");
911
                if (StringUtils.isEmpty(dest))
912
                        throw new ObjectNotFoundException("No destination specified");
913

    
914
                Object destination = getResourceAtPath(userId, getParentPath(dest), true);
915
                if (!(destination instanceof Folder))
916
                        throw new ObjectNotFoundException("Destination parent folder not found");
917
                Folder parent = (Folder) destination;
918
                copyFile(userId, fileId, parent.getId(), getLastElement(dest));
919
        }
920

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

    
932
                Object destination = getResourceAtPath(ownerId, getParentPath(dest), true);
933
                if (!(destination instanceof Folder))
934
                        throw new ObjectNotFoundException("Destination parent folder not found");
935
                Folder parent = (Folder) destination;
936
                copyFile(userId, fileId, parent.getId(), getLastElement(dest));
937
        }
938

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

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

    
976
                } catch (FileNotFoundException e) {
977
                        throw new ObjectNotFoundException("File contents not found for file " + contents.getAbsolutePath());
978
                }
979

    
980
        }
981

    
982
        @Override
983
        public void copyFolder(Long userId, Long folderId, String dest) throws ObjectNotFoundException, DuplicateNameException, InsufficientPermissionsException {
984
                if (userId == null)
985
                        throw new ObjectNotFoundException("No user specified");
986
                if (folderId == null)
987
                        throw new ObjectNotFoundException("No folder specified");
988
                if (StringUtils.isEmpty(dest))
989
                        throw new ObjectNotFoundException("No destination specified");
990

    
991
                Object destination = getResourceAtPath(userId, getParentPath(dest), true);
992
                if (!(destination instanceof Folder))
993
                        throw new ObjectNotFoundException("Destination folder not found");
994
                Folder parent = (Folder) destination;
995
                copyFolder(userId, folderId, parent.getId(), getLastElement(dest));
996
        }
997

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

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

    
1027
                Object destination = getResourceAtPath(ownerId, getParentPath(dest), true);
1028
                if (!(destination instanceof Folder))
1029
                        throw new ObjectNotFoundException("Destination folder not found");
1030
                Folder parent = (Folder) destination;
1031
                copyFolderStructure(userId, folderId, parent.getId(), getLastElement(dest));
1032
        }
1033

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

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

    
1069
        }
1070

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

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

    
1118
        @Override
1119
        public void moveFileToTrash(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
1120
                if (userId == null)
1121
                        throw new ObjectNotFoundException("No user specified");
1122
                if (fileId == null)
1123
                        throw new ObjectNotFoundException("No file specified");
1124

    
1125
                // Do the actual work.
1126
                FileHeader file = dao.getEntityById(FileHeader.class, fileId);
1127
                Folder parent = file.getFolder();
1128
                if (parent == null)
1129
                        throw new ObjectNotFoundException("The specified file has no parent folder");
1130
                User user = dao.getEntityById(User.class, userId);
1131
        trashFile(user, file);
1132
        touchParentFolders(parent, user, new Date());
1133
        }
1134

    
1135
    private void trashFile(User user, FileHeader file) throws InsufficientPermissionsException {
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
    }
1141

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

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

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

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

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

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

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

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

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

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

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

    
1290
                // Do the actual work.
1291
                final List<FileHeader> files = dao.getDeletedFiles(userId);
1292
                return files;
1293
        }
1294

    
1295
        @Override
1296
        public void removeFileFromTrash(Long userId, Long fileId)
1297
                        throws ObjectNotFoundException, InsufficientPermissionsException {
1298
                if (userId == null)
1299
                        throw new ObjectNotFoundException("No user specified");
1300
                if (fileId == null)
1301
                        throw new ObjectNotFoundException("No file specified");
1302

    
1303
                // Do the actual work.
1304
                FileHeader file = dao.getEntityById(FileHeader.class, fileId);
1305
                Folder parent = file.getFolder();
1306
                if (parent == null)
1307
                        throw new ObjectNotFoundException("The specified file has no parent folder");
1308
                User user = dao.getEntityById(User.class, userId);
1309
        untrashFile(user, file);
1310
                touchParentFolders(parent, user, new Date());
1311
        }
1312

    
1313
    private void untrashFile(User user, FileHeader file) throws InsufficientPermissionsException {
1314
        if (!file.hasDeletePermission(user))
1315
            throw new InsufficientPermissionsException("User " + user.getUsername() +
1316
                        " cannot restore file " + file.getName());
1317

    
1318
        file.setDeleted(false);
1319
    }
1320

    
1321
        @Override
1322
        public void moveFolderToTrash(Long userId, Long folderId) throws ObjectNotFoundException, InsufficientPermissionsException {
1323
        if (userId == null)
1324
            throw new ObjectNotFoundException("No user specified");
1325
        if (folderId == null)
1326
            throw new ObjectNotFoundException("No folder specified");
1327
        Folder folder = dao.getEntityById(Folder.class, folderId);
1328
        User user = dao.getEntityById(User.class, userId);
1329
        trashFolder(user, folder);
1330
        touchParentFolders(folder, user, new Date());
1331
        }
1332

    
1333
    private void trashFolder(User user, Folder folder) throws ObjectNotFoundException, InsufficientPermissionsException {
1334
        if (!folder.hasDeletePermission(user))
1335
            throw new InsufficientPermissionsException("You don't have the necessary permissions");
1336
        folder.setDeleted(true);
1337
        for (FileHeader file : folder.getFiles())
1338
            trashFile(user, file);
1339
        for (Folder subFolder : folder.getSubfolders())
1340
            trashFolder(user, subFolder);
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
        untrashFolder(user, folder);
1353
                touchParentFolders(folder, user, new Date());
1354
        }
1355

    
1356
    private void untrashFolder(User user, Folder folder) throws ObjectNotFoundException, InsufficientPermissionsException {
1357
        if (!folder.hasDeletePermission(user))
1358
            throw new InsufficientPermissionsException("User " + user.getUsername() +
1359
                        " cannot restore folder " + folder.getName());
1360
        folder.setDeleted(false);
1361
        for (FileHeader file : folder.getFiles())
1362
            untrashFile(user, file);
1363
        for (Folder subFolder : folder.getSubfolders())
1364
            untrashFolder(user, subFolder);
1365
    }
1366

    
1367
        @Override
1368
        public List<Folder> getDeletedRootFolders(Long userId) throws ObjectNotFoundException {
1369
                List<Folder> folders = dao.getDeletedRootFolders(userId);
1370
                return folders;
1371
        }
1372

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

    
1383
        @Override
1384
        public void restoreTrash(Long userId) throws ObjectNotFoundException, InsufficientPermissionsException {
1385
                List<Folder> deletedRootFolders = getDeletedRootFolders(userId);
1386
                for (Folder folder : deletedRootFolders)
1387
                        removeFolderFromTrash(userId, folder.getId());
1388
                List<FileHeader> deletedFiles = getDeletedFiles(userId);
1389
                for (FileHeader file : deletedFiles)
1390
                        removeFileFromTrash(userId, file.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<Permission> 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<Permission> result = new LinkedHashSet<Permission>();
1485
                for (Permission perm : perms)
1486
                        if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId()))
1487
                                result.add(perm);
1488
                for (Permission perm : perms)
1489
                        if (perm.getUser() != null && perm.getUser().getId().equals(folder.getOwner().getId())) {
1490
                        } else
1491
                                result.add(perm);
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<Permission> permissions) throws ObjectNotFoundException, InsufficientPermissionsException {
1507
                if (permissions != null && !permissions.isEmpty()) {
1508
                        User owner = folder.getOwner();
1509
                        Permission ownerPerm = null;
1510
                        for (Permission perm : permissions)
1511
                                if (perm.getUser() != null && perm.getUser().getId().equals(owner.getId())) {
1512
                                        ownerPerm = perm;
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 (Permission p : permissions) {
1522
                                // Skip 'empty' permission entries.
1523
                                if (!p.getRead() && !p.getWrite() && !p.getModifyACL()) continue;
1524
                                folder.addPermission(getPermission(p));
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(Permission perm) throws ObjectNotFoundException {
1539
                Permission res = new Permission();
1540
                if (perm.getGroup() != null)
1541
                        res.setGroup(dao.getEntityById(Group.class, perm.getGroup().getId()));
1542
                else if (perm.getUser() != null)
1543
                        if (perm.getUser().getId() == null)
1544
                                res.setUser(dao.getUser(perm.getUser().getUsername()));
1545
                        else
1546
                                res.setUser(dao.getEntityById(User.class, perm.getUser().getId()));
1547
                res.setRead(perm.hasRead());
1548
                res.setWrite(perm.hasWrite());
1549
                res.setModifyACL(perm.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<User> getUsersByUserNameLike(String username) {
1558
                List<User> users = dao.getUsersByUserNameLike(username);
1559
                return users;
1560

    
1561
        }
1562

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

    
1581
        }
1582

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

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

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

    
1622
        }
1623

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

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

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

    
1694
        @Override
1695
        public List<FileHeader> getSharedFilesNotInSharedFolders(Long userId) throws ObjectNotFoundException {
1696
                if (userId == null)
1697
                        throw new ObjectNotFoundException("No user specified");
1698
                List<FileHeader> files = dao.getSharedFilesNotInSharedFolders(userId);
1699
                return files;
1700
        }
1701

    
1702
        @Override
1703
        public List<FileHeader> getSharedFiles(Long userId) throws ObjectNotFoundException {
1704
                if (userId == null)
1705
                        throw new ObjectNotFoundException("No user specified");
1706
                List<FileHeader> files = dao.getSharedFiles(userId);
1707
                return files;
1708
        }
1709

    
1710
        @Override
1711
        public List<Folder> getSharedFolders(Long userId) throws ObjectNotFoundException {
1712
                if (userId == null)
1713
                        throw new ObjectNotFoundException("No user specified");
1714
                List<Folder> folders = dao.getSharedFolders(userId);
1715
                return folders;
1716
        }
1717

    
1718
        @Override
1719
        public List<FileHeader> getSharedFiles(Long ownerId, Long callingUserId) throws ObjectNotFoundException {
1720
                if (ownerId == null)
1721
                        throw new ObjectNotFoundException("No owner specified");
1722
                if (callingUserId == null)
1723
                        throw new ObjectNotFoundException("No calling user specified");
1724
                List<FileHeader> folders = dao.getSharedFiles(ownerId, callingUserId);
1725
                return folders;
1726
        }
1727

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

    
1743
        }
1744

    
1745
        @Override
1746
        public List<Folder> getSharedSubfolders(Long userId, Long folderId) throws ObjectNotFoundException {
1747
                if (userId == null)
1748
                        throw new ObjectNotFoundException("No user specified");
1749
                if (folderId == null)
1750
                        throw new ObjectNotFoundException("No folder specified");
1751
                User user = dao.getEntityById(User.class, userId);
1752
                Folder folder = dao.getEntityById(Folder.class, folderId);
1753
                List<Folder> result = new ArrayList<Folder>();
1754
                if (folder.isShared(user) || folder.isReadForAll())
1755
                        for (Folder f : folder.getSubfolders())
1756
                                if ((f.isShared(user) || f.isReadForAll()) && !f.isDeleted())
1757
                                        result.add(f);
1758
                return result;
1759
        }
1760

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

    
1781
        }
1782

    
1783
        @Override
1784
        public List<FileHeader> searchFiles(Long userId, String query) throws ObjectNotFoundException {
1785
        long startTime = System.currentTimeMillis();
1786
                if (userId == null)
1787
                        throw new ObjectNotFoundException("No user specified");
1788
                User user = getUser(userId);
1789
                if (query == null)
1790
                        throw new ObjectNotFoundException("No query specified");
1791
                List<FileHeader> files = search(user.getId(), query);
1792
                
1793
        long stopTime = System.currentTimeMillis();
1794
        logger.info("Total time: " + (stopTime - startTime));
1795
                return files;
1796
        }
1797

    
1798
        /**
1799
         * Performs the actuals search on the solr server and returns the results
1800
         *
1801
         * @param userId
1802
         * @param query
1803
         * @return a List of FileHeader objects
1804
         */
1805
        private List<FileHeader> search(Long userId, String query) {
1806
        final int maxRows = 100;
1807
                List<FileHeader> result = new ArrayList<FileHeader>();
1808
                try {
1809
                        CommonsHttpSolrServer solr = new CommonsHttpSolrServer(getConfiguration().getString("solr.url"));
1810
            List<Group> groups = dao.getGroupsContainingUser(userId);
1811
            String constructedQuery = escapeCharacters(normalizeSearchQuery(query)) + " AND (public: true OR ureaders: " + userId;
1812
            if (!groups.isEmpty()) {
1813
                constructedQuery += " OR (";
1814
                for (int i=0; i<groups.size(); i++) {
1815
                    Group g = groups.get(i);
1816
                    constructedQuery += "greaders :" + g.getId();
1817
                    if (i < groups.size() - 1)
1818
                        constructedQuery += " OR ";
1819
                }
1820
                constructedQuery += ")";
1821
            }
1822
            constructedQuery += ")";
1823
                        SolrQuery solrQuery = new SolrQuery(constructedQuery);
1824
            solrQuery.setRows(maxRows);
1825
            long startTime = System.currentTimeMillis();
1826
                        QueryResponse response = solr.query(solrQuery);
1827
                        SolrDocumentList results = response.getResults();
1828
            if (results.getNumFound() > maxRows) {
1829
                solrQuery.setRows(Integer.valueOf((int) results.getNumFound()));
1830
                response = solr.query(solrQuery);
1831
                results = response.getResults();
1832
            }
1833
            long stopTime = System.currentTimeMillis();
1834
            logger.info("Search time:" +  (stopTime - startTime));
1835
                        User user = getUser(userId);
1836
            startTime = System.currentTimeMillis();
1837
                        for (SolrDocument d : results) {
1838
                                Long id = Long.valueOf((String) d.getFieldValue("id"));
1839
                                try {
1840
                                        FileHeader f = dao.getEntityById(FileHeader.class, id);
1841
                                        result.add(f);
1842
                                } catch (ObjectNotFoundException e) {
1843
                                        logger.warn("Search result id " + id + " cannot be found", e);
1844
                                }
1845
                        }
1846
            stopTime = System.currentTimeMillis();
1847
            logger.info("File loads: " + (stopTime - startTime));
1848
                } catch (MalformedURLException e) {
1849
                        logger.error(e);
1850
                        throw new EJBException(e);
1851
                } catch (SolrServerException e) {
1852
                        logger.error(e);
1853
                        throw new EJBException(e);
1854
                } catch (ObjectNotFoundException e) {
1855
                        logger.error(e);
1856
                        throw new EJBException(e);
1857
                }
1858
                return result;
1859
        }
1860

    
1861
        @Override
1862
        public void copyFiles(Long userId, List<Long> fileIds, Long destId) throws ObjectNotFoundException, DuplicateNameException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
1863
                for(Long l : fileIds){
1864
                        FileHeader file = dao.getEntityById(FileHeader.class, l);
1865
                        copyFile(userId, l, destId, file.getName());
1866
                }
1867

    
1868

    
1869
        }
1870

    
1871
        @Override
1872
        public void moveFiles(Long userId, List<Long> fileIds, Long destId) throws InsufficientPermissionsException, ObjectNotFoundException, QuotaExceededException {
1873
                for(Long l : fileIds){
1874
                        FileHeader file = dao.getEntityById(FileHeader.class, l);
1875
                        moveFile(userId, l, destId, file.getName());
1876
                }
1877

    
1878
        }
1879

    
1880
        @Override
1881
        public Nonce createNonce(Long userId) throws ObjectNotFoundException {
1882
                if (userId == null)
1883
                        throw new ObjectNotFoundException("No user specified");
1884
                User user = dao.getEntityById(User.class, userId);
1885
                Nonce nonce = Nonce.createNonce(user.getId());
1886
                dao.create(nonce);
1887
                return nonce;
1888
        }
1889

    
1890
        @Override
1891
        public Nonce getNonce(String nonce, Long userId) throws ObjectNotFoundException {
1892
                if (userId == null)
1893
                        throw new ObjectNotFoundException("No user specified");
1894
                if (nonce == null)
1895
                        throw new ObjectNotFoundException("No nonce specified");
1896
                return dao.getNonce(nonce, userId);
1897
        }
1898

    
1899
        @Override
1900
        public void removeNonce(Long id) throws ObjectNotFoundException {
1901
                if (id == null)
1902
                        throw new ObjectNotFoundException("No nonce specified");
1903
                Nonce nonce = dao.getEntityById(Nonce.class, id);
1904
                dao.delete(nonce);
1905
        }
1906

    
1907
        @Override
1908
        public void activateUserNonce(Long userId, String nonce, Date nonceExpiryDate) throws ObjectNotFoundException {
1909
                if (userId == null)
1910
                        throw new ObjectNotFoundException("No user specified");
1911
                User user = dao.getEntityById(User.class, userId);
1912
                user.setNonce(nonce);
1913
                user.setNonceExpiryDate(nonceExpiryDate);
1914
        }
1915

    
1916
        @Override
1917
        public StatsDTO getUserStatistics(Long userId) throws ObjectNotFoundException {
1918
                if (userId == null)
1919
                        throw new ObjectNotFoundException("No user specified");
1920
                StatsDTO stats = new StatsDTO();
1921
                stats.setFileCount(dao.getFileCount(userId));
1922
                Long fileSize = dao.getFileSize(userId);
1923
                stats.setFileSize(fileSize);
1924
                Long quota = getQuota(userId);
1925
                Long quotaLeft = quota - fileSize;
1926
                stats.setQuotaLeftSize(quotaLeft);
1927
                return stats;
1928
        }
1929

    
1930
        @Override
1931
        public void restoreVersion(Long userId, Long fileId, int version) throws ObjectNotFoundException, InsufficientPermissionsException,  GSSIOException, QuotaExceededException {
1932
                if (userId == null)
1933
                        throw new ObjectNotFoundException("No user specified");
1934
                if (fileId == null)
1935
                        throw new ObjectNotFoundException("No file specified");
1936
                User user = dao.getEntityById(User.class, userId);
1937
                FileHeader header = dao.getEntityById(FileHeader.class, fileId);
1938
                if(!header.hasWritePermission(user))
1939
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
1940
                FileBody body = dao.getFileVersion(fileId, version);
1941
                final File fileContents = new File(body.getStoredFilePath());
1942

    
1943
                try {
1944
                        updateFileContents(userId, fileId, body.getMimeType(), new FileInputStream(fileContents) );
1945
                } catch (FileNotFoundException e) {
1946
                        throw new GSSIOException(e);
1947
                }
1948

    
1949
        }
1950

    
1951
        /* (non-Javadoc)
1952
         * @see gr.ebs.gss.server.ejb.ExternalAPI#removeOldVersions(java.lang.Long, java.lang.Long)
1953
         */
1954
        @Override
1955
        public void removeOldVersions(Long userId, Long fileId) throws ObjectNotFoundException, InsufficientPermissionsException {
1956
                if (userId == null)
1957
                        throw new ObjectNotFoundException("No user specified");
1958
                if (fileId == null)
1959
                        throw new ObjectNotFoundException("No file specified");
1960
                User user = dao.getEntityById(User.class, userId);
1961
                FileHeader header = dao.getEntityById(FileHeader.class, fileId);
1962
                if(!header.hasWritePermission(user))
1963
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
1964
                Iterator<FileBody> it = header.getBodies().iterator();
1965
                while(it.hasNext()){
1966
                        FileBody body = it.next();
1967
                        if(!body.equals(header.getCurrentBody())){
1968
                                deleteActualFile(body.getStoredFilePath());
1969
                                it.remove();
1970
                                dao.delete(body);
1971
                        }
1972
                }
1973
                header.getCurrentBody().setVersion(1);
1974

    
1975
                Folder parent = header.getFolder();
1976
                touchParentFolders(parent, user, new Date());
1977
        }
1978

    
1979
        /**
1980
         * Gets the quota left for specified user ID.
1981
         */
1982
        private Long getQuotaLeft(Long userId) throws ObjectNotFoundException{
1983
                Long fileSize = dao.getFileSize(userId);
1984
                Long quota = getQuota(userId);
1985
                return quota - fileSize;
1986
        }
1987

    
1988
        /**
1989
         * Gets the quota for specified user ID.
1990
         */
1991
        private Long getQuota(Long userId) throws ObjectNotFoundException{
1992
                UserClass uc = getUser(userId).getUserClass();
1993
                if (uc == null)
1994
                        uc = getDefaultUserClass();
1995
                return uc.getQuota();
1996
        }
1997

    
1998
        @Override
1999
    @TransactionAttribute(TransactionAttributeType.NEVER)
2000
        public String rebuildSolrIndex() {
2001
                try {
2002
            CommonsHttpSolrServer solr = new CommonsHttpSolrServer(getConfiguration().getString("solr.url"));
2003
                        solr.deleteByQuery("*:*");
2004
                        solr.commit();
2005
            logger.info("Deleted everything in solr");
2006

    
2007
                        List<Long> fileIds = dao.getAllFileIds();
2008
            logger.info("Total of " + fileIds.size() + " will be indexed");
2009
            int i = 0;
2010
                        for (Long id : fileIds) {
2011
                                postFileToSolr(solr, id);
2012
                i++;
2013
                if (i % 10 == 0) {
2014
                    solr.commit();
2015
                    logger.info("Sent commit to solr at file " + i);
2016
                }
2017
                        }
2018
                        solr.optimize();
2019
                        solr.commit();
2020
            logger.info("Finished indexing of " + i + " files");
2021
            return "Finished indexing of " + i + " files";
2022
                } catch (IOException e) {
2023
                        throw new EJBException(e);
2024
                } catch (SolrServerException e) {
2025
                        throw new EJBException(e);
2026
                }
2027
        }
2028

    
2029
        @Override
2030
    @TransactionAttribute(TransactionAttributeType.NEVER)
2031
        public String refreshSolrIndex() {
2032
                try {
2033
                        CommonsHttpSolrServer solr = new CommonsHttpSolrServer(getConfiguration().getString("solr.url"));
2034
                        
2035
                        List<Long> fileIds = dao.getAllFileIds();
2036
            logger.info("Total of " + fileIds.size() + " will be indexed");
2037
            int i = 0;
2038
                        for (Long id : fileIds) {
2039
                                postFileToSolr(solr, id);
2040
                i++;
2041
                        }
2042
            if (i % 10 == 0) {
2043
                solr.commit();
2044
                logger.info("Sent commit to solr at file " + i);
2045
            }
2046
                        solr.optimize();
2047
                        solr.commit();
2048
            logger.info("Finished indexing of " + i + " files");
2049
            return "Finished indexing of " + i + " files";
2050
                } catch (IOException e) {
2051
                        throw new EJBException(e);
2052
                } catch (SolrServerException e) {
2053
                        throw new EJBException(e);
2054
                }
2055
        }
2056

    
2057
        @Override
2058
        public FileHeader createFile(Long userId, Long folderId, String name, String mimeType, long fileSize, String filePath)
2059
                        throws DuplicateNameException, ObjectNotFoundException, GSSIOException,
2060
                        InsufficientPermissionsException, QuotaExceededException {
2061
                // Validate.
2062
                if (userId == null)
2063
                        throw new ObjectNotFoundException("No user specified");
2064
                if (folderId == null)
2065
                        throw new ObjectNotFoundException("No folder specified");
2066
                String contentType = mimeType;
2067
                if (StringUtils.isEmpty(mimeType))
2068
                        contentType = DEFAULT_MIME_TYPE;
2069
                if (StringUtils.isEmpty(name))
2070
                        throw new ObjectNotFoundException("No file name specified");
2071
                if (dao.existsFolderOrFile(folderId, name))
2072
                        throw new DuplicateNameException("A folder or file with the name '" + name +
2073
                                                "' already exists at this level");
2074

    
2075
                // Do the actual work.
2076
                Folder parent = null;
2077
                try {
2078
                        parent = dao.getEntityById(Folder.class, folderId);
2079
                } catch (final ObjectNotFoundException onfe) {
2080
                        // Supply a more accurate problem description.
2081
                        throw new ObjectNotFoundException("Parent folder not found");
2082
                }
2083
                final User owner = dao.getEntityById(User.class, userId);
2084
                if (!parent.hasWritePermission(owner))
2085
                        throw new InsufficientPermissionsException("You don't have the permissions to write to this folder");
2086
                final FileHeader file = new FileHeader();
2087
                file.setName(name);
2088
                parent.addFile(file);
2089
                // set file owner to folder owner
2090
                file.setOwner(parent.getOwner());
2091
                //set file's readForAll value according to parent folder readForAll value
2092
                file.setReadForAll(parent.isReadForAll());
2093

    
2094
                final Date now = new Date();
2095
                final AuditInfo auditInfo = new AuditInfo();
2096
                auditInfo.setCreatedBy(owner);
2097
                auditInfo.setCreationDate(now);
2098
                auditInfo.setModifiedBy(owner);
2099
                auditInfo.setModificationDate(now);
2100
                file.setAuditInfo(auditInfo);
2101
                // TODO set the proper versioning flag on creation
2102
                file.setVersioned(false);
2103

    
2104
                for (final Permission p : parent.getPermissions()) {
2105
                        final Permission permission = new Permission();
2106
                        permission.setGroup(p.getGroup());
2107
                        permission.setUser(p.getUser());
2108
                        permission.setRead(p.getRead());
2109
                        permission.setWrite(p.getWrite());
2110
                        permission.setModifyACL(p.getModifyACL());
2111
                        file.addPermission(permission);
2112
                }
2113

    
2114
                // Create the file body.
2115
                try {
2116
                        createFileBody(name, contentType, fileSize, filePath, file, auditInfo);
2117
                } catch (FileNotFoundException e) {
2118
                        throw new GSSIOException(e);
2119
                }
2120
                touchParentFolders(parent, owner, new Date());
2121
                dao.flush();
2122
                indexFile(file.getId(), false);
2123

    
2124
                return file;
2125
        }
2126

    
2127
        @Override
2128
        public FileHeader updateFileContents(Long userId, Long fileId, String mimeType, long fileSize, String filePath) throws ObjectNotFoundException, GSSIOException, InsufficientPermissionsException, QuotaExceededException {
2129
                if (userId == null)
2130
                        throw new ObjectNotFoundException("No user specified");
2131
                if (fileId == null)
2132
                        throw new ObjectNotFoundException("No file specified");
2133
                String contentType = mimeType;
2134

    
2135
                FileHeader file = dao.getEntityById(FileHeader.class, fileId);
2136

    
2137
                // if no mime type or the generic mime type is defined by the client, then try to identify it from the filename extension
2138
                if (StringUtils.isEmpty(mimeType) || "application/octet-stream".equals(mimeType)
2139
                                        || "application/download".equals(mimeType) || "application/force-download".equals(mimeType)
2140
                                        || "octet/stream".equals(mimeType) || "application/unknown".equals(mimeType))
2141
                        contentType = identifyMimeType(file.getName());
2142

    
2143
                final User owner = dao.getEntityById(User.class, userId);
2144
                if (!file.hasWritePermission(owner))
2145
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
2146
                final Date now = new Date();
2147
                final AuditInfo auditInfo = new AuditInfo();
2148
                auditInfo.setCreatedBy(owner);
2149
                auditInfo.setCreationDate(now);
2150
                auditInfo.setModifiedBy(owner);
2151
                auditInfo.setModificationDate(now);
2152
                try {
2153
                        createFileBody(file.getName(), contentType, fileSize, filePath, file, auditInfo);
2154
                } catch (FileNotFoundException e) {
2155
                        throw new GSSIOException(e);
2156
                }
2157
                Folder parent = file.getFolder();
2158
                touchParentFolders(parent, owner, new Date());
2159

    
2160
                indexFile(fileId, false);
2161
                return file;
2162
        }
2163

    
2164
        /**
2165
         * Helper method for identifying mime type by examining the filename extension
2166
         *
2167
         * @param filename
2168
         * @return the mime type
2169
         */
2170
        private String identifyMimeType(String filename) {
2171
                if (filename.indexOf('.') != -1) {
2172
                        String extension = filename.substring(filename.lastIndexOf('.')).toLowerCase(Locale.ENGLISH);
2173
                        if (".doc".equals(extension))
2174
                                return "application/msword";
2175
                        else if (".xls".equals(extension))
2176
                                return "application/vnd.ms-excel";
2177
                        else if (".ppt".equals(extension))
2178
                                return "application/vnd.ms-powerpoint";
2179
                        else if (".pdf".equals(extension))
2180
                                return "application/pdf";
2181
                        else if (".gif".equals(extension))
2182
                                return "image/gif";
2183
                        else if (".jpg".equals(extension) || ".jpeg".equals(extension) || ".jpe".equals(extension))
2184
                                return "image/jpeg";
2185
                        else if (".tiff".equals(extension) || ".tif".equals(extension))
2186
                                return "image/tiff";
2187
                        else if (".png".equals(extension))
2188
                                return "image/png";
2189
                        else if (".bmp".equals(extension))
2190
                                return "image/bmp";
2191
                }
2192
                // when all else fails assign the default mime type
2193
                return DEFAULT_MIME_TYPE;
2194
        }
2195

    
2196
        /**
2197
         * Helper method to create a new file body and attach it as the current body
2198
         * of the provided file header.
2199
         *
2200
         * @param name the original file name
2201
         * @param mimeType the content type
2202
         * @param fileSize the uploaded file size
2203
         * @param filePath the uploaded file full path
2204
         * @param header the file header that will be associated with the new body
2205
         * @param auditInfo the audit info
2206
         * @throws FileNotFoundException
2207
         * @throws QuotaExceededException
2208
         * @throws ObjectNotFoundException if the owner was not found
2209
         */
2210
        private void createFileBody(String name, String mimeType, long fileSize, String filePath,
2211
                                FileHeader header, AuditInfo auditInfo)
2212
                        throws FileNotFoundException, QuotaExceededException, ObjectNotFoundException {
2213

    
2214
                long currentTotalSize = 0;
2215
                if (!header.isVersioned() && header.getCurrentBody() != null && header.getBodies() != null)
2216
                        currentTotalSize = header.getTotalSize();
2217
                Long quotaLeft = getQuotaLeft(header.getOwner().getId());
2218
                if(quotaLeft < fileSize-currentTotalSize) {
2219
                        // quota exceeded -> delete the file
2220
                        deleteActualFile(filePath);
2221
                        throw new QuotaExceededException("Not enough free space available");
2222
                }
2223

    
2224
                FileBody body = new FileBody();
2225

    
2226
                // if no mime type or the generic mime type is defined by the client, then try to identify it from the filename extension
2227
                if (StringUtils.isEmpty(mimeType) || "application/octet-stream".equals(mimeType)
2228
                                        || "application/download".equals(mimeType) || "application/force-download".equals(mimeType)
2229
                                        || "octet/stream".equals(mimeType) || "application/unknown".equals(mimeType))
2230
                        body.setMimeType(identifyMimeType(name));
2231
                else
2232
                        body.setMimeType(mimeType);
2233
                body.setAuditInfo(auditInfo);
2234
                body.setFileSize(fileSize);
2235
                body.setOriginalFilename(name);
2236
                body.setStoredFilePath(filePath);
2237
                //CLEAR OLD VERSION IF FILE IS NOT VERSIONED AND GETS UPDATED
2238
                if(!header.isVersioned() && header.getCurrentBody() != null){
2239
                        header.setCurrentBody(null);
2240
                        if (header.getBodies() != null) {
2241
                                Iterator<FileBody> it = header.getBodies().iterator();
2242
                                while(it.hasNext()){
2243
                                        FileBody bo = it.next();
2244
                                        deleteActualFile(bo.getStoredFilePath());
2245
                                        it.remove();
2246
                                        dao.delete(bo);
2247
                                }
2248
                        }
2249
                }
2250

    
2251
                dao.flush();
2252
                header.addBody(body);
2253
                header.setAuditInfo(auditInfo);
2254

    
2255
                dao.create(body);
2256
        }
2257

    
2258

    
2259
        @Override
2260
        @TransactionAttribute(TransactionAttributeType.NOT_SUPPORTED)
2261
        public File uploadFile(InputStream stream, Long userId) throws IOException, ObjectNotFoundException {
2262
                if (userId == null)
2263
                        throw new ObjectNotFoundException("No user specified");
2264
                User owner = dao.getEntityById(User.class, userId);
2265
                if(owner == null)
2266
                        throw new ObjectNotFoundException("No user specified");
2267
                long start = 0, end = 0;
2268
                if (logger.isDebugEnabled())
2269
                        start = System.currentTimeMillis();
2270
                File result = new File(generateRepositoryFilePath());
2271
                try {
2272
                        final FileOutputStream output = new FileOutputStream(result);
2273
                        final byte[] buffer = new byte[UPLOAD_BUFFER_SIZE];
2274
                        int n = 0;
2275

    
2276
                        while (-1 != (n = stream.read(buffer)))
2277
                                output.write(buffer, 0, n);
2278
                        output.close();
2279
                        stream.close();
2280
                } catch (IOException e) {
2281
                        if (!result.delete())
2282
                                logger.warn("Could not delete " + result.getPath());
2283
                        throw e;
2284
                }
2285
                if (logger.isDebugEnabled()) {
2286
                        end = System.currentTimeMillis();
2287
                        logger.debug("Time to upload: " + (end - start) + " (msec)");
2288
                }
2289
                return result;
2290
        }
2291

    
2292

    
2293
        @Override
2294
        public void createFileUploadProgress(Long userId, String filename, Long bytesTransfered, Long fileSize) throws ObjectNotFoundException{
2295

    
2296
                if (userId == null)
2297
                        throw new ObjectNotFoundException("No user specified");
2298
                User user = dao.getEntityById(User.class, userId);
2299
                FileUploadStatus status = dao.getFileUploadStatus(userId, filename);
2300
                if(status == null){
2301
                        status = new FileUploadStatus();
2302
                        status.setOwner(user);
2303
                        status.setFilename(filename);
2304
                        status.setBytesUploaded(bytesTransfered);
2305
                        status.setFileSize(fileSize);
2306
                        dao.create(status);
2307
                }
2308
                else{
2309
                        status.setBytesUploaded(bytesTransfered);
2310
                        status.setFileSize(fileSize);
2311
                        dao.update(status);
2312
                }
2313

    
2314
        }
2315

    
2316
        @Override
2317
        public void removeFileUploadProgress(Long userId, String filename) throws ObjectNotFoundException{
2318
                if (userId == null)
2319
                        throw new ObjectNotFoundException("No user specified");
2320
                FileUploadStatus status = dao.getFileUploadStatus(userId, filename);
2321
                if(status != null)
2322
                        dao.delete(status);
2323
        }
2324

    
2325
        @Override
2326
        public FileUploadStatus getFileUploadStatus(Long userId, String fileName) {
2327
                return dao.getFileUploadStatus(userId, fileName);
2328
        }
2329

    
2330
        @Override
2331
        public FileBody getFileVersion(Long userId, Long fileId, int version)
2332
                        throws ObjectNotFoundException, InsufficientPermissionsException {
2333
                if (userId == null)
2334
                        throw new ObjectNotFoundException("No user specified");
2335
                if (fileId == null)
2336
                        throw new ObjectNotFoundException("No file specified");
2337
                if (version < 1)
2338
                        throw new ObjectNotFoundException("No valid version specified");
2339
                User user = dao.getEntityById(User.class, userId);
2340
                FileHeader file = dao.getEntityById(FileHeader.class, fileId);
2341
                if (!file.hasReadPermission(user) && !file.getFolder().hasReadPermission(user))
2342
                        throw new InsufficientPermissionsException("You don't have the necessary permissions");
2343
                FileBody body = dao.getFileVersion(fileId, version);
2344
                return body;
2345
        }
2346

    
2347
        @Override
2348
        public User updateUserPolicyAcceptance(Long userId, boolean isAccepted) throws ObjectNotFoundException {
2349
                if (userId == null)
2350
                        throw new ObjectNotFoundException("No user specified");
2351
                User user = dao.getEntityById(User.class, userId);
2352
                user.setAcceptedPolicy(isAccepted);
2353
                return user;
2354
        }
2355

    
2356
        @Override
2357
        public void updateAccounting(User user, Date date, long bandwidthDiff) {
2358
                dao.updateAccounting(user, date, bandwidthDiff);
2359
        }
2360

    
2361
        @Override
2362
        public boolean canReadFolder(Long userId, Long folderId) throws ObjectNotFoundException {
2363
                if (userId == null)
2364
                        throw new ObjectNotFoundException("No user specified");
2365
                if (folderId == null)
2366
                        throw new ObjectNotFoundException("No folder specified");
2367
                User user = dao.getEntityById(User.class, userId);
2368
                Folder folder = dao.getEntityById(Folder.class, folderId);
2369
                // Check permissions
2370
                if (!folder.hasReadPermission(user))
2371
                        return false;
2372
                return true;
2373
        }
2374

    
2375
        @Override
2376
        public String resetWebDAVPassword(Long userId) throws ObjectNotFoundException {
2377
                if (userId == null)
2378
                        throw new ObjectNotFoundException("No user specified");
2379
                User user = dao.getEntityById(User.class, userId);
2380
                user.generateWebDAVPassword();
2381
                return user.getWebDAVPassword();
2382
        }
2383

    
2384
        @Override
2385
        public Invitation findInvite(String code) {
2386
                if (code == null)
2387
                        return null;
2388
                return dao.findInvite(code);
2389
        }
2390

    
2391
        @Override
2392
        public void createLdapUser(String username, String firstname, String lastname, String email, String password) {
2393
                LDAPConnection lc = new LDAPConnection();
2394
        LDAPAttributeSet attributeSet = new LDAPAttributeSet();
2395
        attributeSet.add(new LDAPAttribute("objectClass", getConfiguration().getStringArray("objectClass")));
2396
        attributeSet.add(new LDAPAttribute("uid", username));
2397
        attributeSet.add(new LDAPAttribute("cn", new String[]{firstname + " " + lastname}));
2398
        attributeSet.add(new LDAPAttribute("sn", lastname));
2399
        attributeSet.add(new LDAPAttribute("givenName", firstname));
2400
        attributeSet.add(new LDAPAttribute("mail", email));
2401
        attributeSet.add(new LDAPAttribute("userPassword", password));
2402
        String dn = "uid=" + username + "," + getConfiguration().getString("baseDn");
2403
        LDAPEntry newEntry = new LDAPEntry(dn, attributeSet);
2404
        try {
2405
                lc.connect(getConfiguration().getString("ldapHost"), LDAPConnection.DEFAULT_PORT);
2406
                lc.bind(LDAPConnection.LDAP_V3, getConfiguration().getString("bindDn"),
2407
                                getConfiguration().getString("bindPassword").getBytes("UTF8"));
2408
                lc.add(newEntry);
2409
                logger.info("Successfully added LDAP account: " + dn);
2410
                lc.disconnect();
2411
        } catch(LDAPException e) {
2412
                throw new RuntimeException(e);
2413
        } catch(UnsupportedEncodingException e) {
2414
                throw new RuntimeException(e);
2415
        }
2416

    
2417
        }
2418

    
2419
        @Override
2420
        public UserClass upgradeUserClass(String username, String code) throws ObjectNotFoundException, InvitationUsedException {
2421
                User user = findUser(username);
2422
                if (user == null)
2423
                        throw new ObjectNotFoundException("The user was not found");
2424
                Invitation invite = findInvite(code);
2425
                if (invite.getUser() != null)
2426
                        throw new InvitationUsedException("This code has already been used");
2427
                invite.setUser(user);
2428
                UserClass couponClass = getCouponUserClass();
2429
                user.setUserClass(couponClass);
2430
                return couponClass;
2431
        }
2432

    
2433
        @Override
2434
        public UserClass getCouponUserClass() {
2435
                return dao.findCouponUserClass();
2436
        }
2437

    
2438
        /**
2439
         * Set the provided readForAll as the new readforAll value of the specified
2440
         * folder and sub-folders.
2441
         *
2442
         * @param user
2443
         * @param folder
2444
         * @param readForAll
2445
         * @throws ObjectNotFoundException
2446
         *
2447
         */
2448
        private void setFolderReadForAll(User user, Folder folder, Boolean readForAll){
2449
                if (readForAll != null && user.equals(folder.getOwner())){
2450
                        folder.setReadForAll(readForAll);
2451
                        dao.update(folder);
2452
                        for (FileHeader file : folder.getFiles())
2453
                                file.setReadForAll(readForAll);
2454
                        if(readForAll)
2455
                                //only update subfolders when readforall is true. otherwise all sub-folders stay untouched
2456
                                for (Folder sub : folder.getSubfolders())
2457
                                        setFolderReadForAll(user, sub, readForAll);
2458

    
2459
                }
2460

    
2461
        }
2462
                
2463
        /**
2464
         * Update the userLogin with the values from the supplied object.
2465
         */
2466
        
2467
        public void addUserLogin(UserLogin userLogin) {
2468
                dao.update(userLogin);                
2469

    
2470
        }
2471
                
2472
        /**
2473
         * Retrieves the current session user login and the user's last login
2474
         * 
2475
         * @param userId
2476
         * @return a list of last two user logins
2477
         * @throws ObjectNotFoundException 
2478
         */
2479
        
2480
        public List<UserLogin> getLastUserLogins(Long userId) throws ObjectNotFoundException{
2481
                List<UserLogin> userLoginResults = new ArrayList<UserLogin>();                
2482
                userLoginResults = dao.getLoginsForUser(userId);        
2483
                if(userLoginResults.size() == 0)
2484
                        throw new ObjectNotFoundException("No userlogin found for the user");
2485
                //if the user logins for the first time lastLoginDate = currentLoginDate
2486
                if(userLoginResults.size()==1)
2487
                        userLoginResults.add(userLoginResults.get(0));
2488
                return userLoginResults;
2489
        }
2490
        
2491

    
2492
        @Override
2493
        public void postFileToSolr(CommonsHttpSolrServer solr, Long id) {
2494
                try {
2495
                        FileHeader file = dao.getFileForIndexing(id);
2496
                        FileBody body = file.getCurrentBody();
2497
                        String mime = body.getMimeType();
2498
                        boolean multipart = true;
2499
                        if (!mime.equals("application/pdf") 
2500
                                                && !mime.equals("text/plain")
2501
                                                && !mime.equals("text/html")
2502
                                                && !mime.endsWith("msword")
2503
                                                && !mime.endsWith("ms-excel")
2504
                                                && !mime.endsWith("powerpoint")
2505
                                                || (body.getFileSize() > getConfiguration().getLong("solrDocumentUploadLimitInKB") * 1024))
2506
                                multipart = false;
2507

    
2508
                        if (!multipart)
2509
                                sendMetaDataOnly(solr, file);
2510
                        else {
2511
                ContentStreamUpdateRequest solrRequest = new ContentStreamUpdateRequest(getConfiguration().getString("solr.rich.update.path"));
2512
                                solrRequest.setParam("literal.id", file.getId().toString());
2513
                                solrRequest.setParam("literal.name", file.getName());
2514
                                for (FileTag t : file.getFileTags()) {
2515
                                        solrRequest.getParams().add("literal.tag", t.getTag());
2516
                                }
2517
                for (Permission p : file.getPermissions()) {
2518
                    if (p.getRead()) {
2519
                        if (p.getUser() != null)
2520
                            solrRequest.getParams().add("literal.ureaders", p.getUser().getId().toString());
2521
                        else if (p.getGroup() != null)
2522
                            solrRequest.getParams().add("literal.greaders", p.getGroup().getId().toString());
2523
                    }
2524
                }
2525
                solrRequest.setParam("literal.owner", file.getOwner().getId().toString());
2526
                solrRequest.setParam("literal.public", String.valueOf(file.isReadForAll()));
2527
                File fsFile = new File(body.getStoredFilePath());
2528
                                solrRequest.addFile(fsFile);
2529
                                try {
2530
                                        solr.request(solrRequest);
2531
                                }
2532
                                catch (SolrException e) {
2533
                                        logger.warn("File " + id + " failed with SolrException: " + e.getLocalizedMessage() + ". Retrying without the file");
2534
                                        //Let 's try without the file
2535
                                        sendMetaDataOnly(solr, file);
2536
                                }
2537
                                catch (NullPointerException e) {
2538
                                        logger.warn("File " + id + " failed with NullPointerException: " + e.getLocalizedMessage() + ". Retrying without the file");
2539
                                        //Let 's try without the file
2540
                                        sendMetaDataOnly(solr, file);
2541
                                }
2542
                                catch (SolrServerException e) {
2543
                                        logger.warn("File " + id + " failed with SolrServerException: " + e.getLocalizedMessage() + ". Retrying without the file");
2544
                                        //Let 's try without the file
2545
                                        sendMetaDataOnly(solr, file);
2546
                                }
2547
                        }
2548
                } catch (MalformedURLException e) {
2549
                        throw new EJBException(e);
2550
                } catch (ObjectNotFoundException e) {
2551
                        logger.error("Indexing of file id " + id + " failed.", e);
2552
                } catch (SolrServerException e) {
2553
                        throw new EJBException(e);
2554
                } catch (IOException e) {
2555
                        throw new EJBException(e);
2556
                }
2557
        }
2558

    
2559
        private void sendMetaDataOnly(CommonsHttpSolrServer solr, FileHeader file) throws SolrServerException, IOException {
2560
                SolrInputDocument solrDoc = new SolrInputDocument();
2561
                solrDoc.addField("id", file.getId().toString());
2562
                solrDoc.addField("name", file.getName());
2563
                for (FileTag t : file.getFileTags()) {
2564
                        solrDoc.addField("tag", t.getTag());
2565
                }
2566
        for (Permission p : file.getPermissions()) {
2567
            if (p.getRead()) {
2568
                if (p.getUser() != null)
2569
                    solrDoc.addField("ureaders", p.getUser().getId());
2570
                else if (p.getGroup() != null)
2571
                    solrDoc.addField("greaders", p.getGroup().getId());
2572
            }
2573
        }
2574
        solrDoc.addField("owner", file.getOwner().getId());
2575
        solrDoc.addField("public", file.isReadForAll());
2576
                solr.add(solrDoc);
2577
        }
2578

    
2579
        private String tokenizeFilename(String filename){
2580
                StringBuffer result = new StringBuffer();
2581
                StringTokenizer tokenizer = new StringTokenizer(filename,"._");
2582
                while(tokenizer.hasMoreTokens()){
2583
                        result.append(tokenizer.nextToken());
2584
                        result.append(" ");
2585
                }
2586
                result.append(filename);
2587
                return result.toString();
2588
        }
2589

    
2590
        private String normalizeSearchQuery(String query) {
2591
                if (query.contains("*"))
2592
                        return query.toLowerCase().replace('ά', 'α').replace('έ', 'ε').replace('ί', 'ι').replace('ή', 'η').replace('ύ', 'υ')
2593
                                        .replace('ό', 'ο').replace('ς', 'σ').replace('ώ', 'ω').replace('ϊ', 'ι').replace('ϋ', 'υ');
2594
                else
2595
                        return query;
2596
        }
2597
        
2598
        private String escapeCharacters(String text) {
2599
                return text.replaceAll(":", "\\\\:");
2600
        }
2601
        
2602
        /*** NEW METHODS IN ORDER TO AVOID LAZY loading exception in json render 
2603
         ****/
2604
        @Override
2605
        public Folder expandFolder(Folder folder) throws ObjectNotFoundException{
2606
                Folder result = dao.getEntityById(Folder.class, folder.getId());
2607
                result.getSubfolders().size();
2608
                result.getFiles().size();
2609
                result.getPermissions().size();
2610
                return result;
2611
}
2612

    
2613
        @Override
2614
        public FileHeader expandFile(FileHeader folder) throws ObjectNotFoundException{
2615
                FileHeader result = dao.getEntityById(FileHeader.class, folder.getId());
2616
                result.getFolder();
2617
                result.getPermissions().size();
2618
                result.getFileTags().size();
2619
                return result;
2620
        }
2621
        
2622
        @Override
2623
        public Group expandGroup(Group folder) throws ObjectNotFoundException{
2624
                Group result = dao.getEntityById(Group.class, folder.getId());
2625
                result.getMembers().size();
2626
                return result;
2627
        }
2628

    
2629
        /* (non-Javadoc)
2630
         * @see gr.ebs.gss.server.ejb.ExternalAPI#getUsersByUserNameLike(java.lang.String)
2631
         */
2632
        @Override
2633
        public User getUserByUserName(String username) {
2634
                User result = dao.getUserByUserName(username);
2635
                return result;
2636
        }
2637
        
2638
        /*WEBDAV CREATE EMPTY FILE*/
2639
        @Override
2640
        public FileHeader createEmptyFile(Long userId, Long folderId, String name)
2641
                        throws DuplicateNameException, ObjectNotFoundException, GSSIOException,
2642
                        InsufficientPermissionsException, QuotaExceededException {
2643
                // Validate.
2644
                if (userId == null)
2645
                        throw new ObjectNotFoundException("No user specified");
2646
                if (folderId == null)
2647
                        throw new ObjectNotFoundException("No folder specified");
2648
                String contentType = DEFAULT_MIME_TYPE;
2649
                if (StringUtils.isEmpty(name))
2650
                        throw new ObjectNotFoundException("No file name specified");
2651
                if (dao.existsFolderOrFile(folderId, name))
2652
                        throw new DuplicateNameException("A folder or file with the name '" + name +
2653
                                                "' already exists at this level");
2654

    
2655
                // Do the actual work.
2656
                Folder parent = null;
2657
                try {
2658
                        parent = dao.getEntityById(Folder.class, folderId);
2659
                } catch (final ObjectNotFoundException onfe) {
2660
                        // Supply a more accurate problem description.
2661
                        throw new ObjectNotFoundException("Parent folder not found");
2662
                }
2663
                final User owner = dao.getEntityById(User.class, userId);
2664
                if (!parent.hasWritePermission(owner))
2665
                        throw new InsufficientPermissionsException("You don't have the permissions to write to this folder");
2666
                final FileHeader file = new FileHeader();
2667
                file.setName(name);
2668
                parent.addFile(file);
2669
                // set file owner to folder owner
2670
                file.setOwner(parent.getOwner());
2671
                //set file's readForAll value according to parent folder readForAll value
2672
                file.setReadForAll(parent.isReadForAll());
2673

    
2674
                final Date now = new Date();
2675
                final AuditInfo auditInfo = new AuditInfo();
2676
                auditInfo.setCreatedBy(owner);
2677
                auditInfo.setCreationDate(now);
2678
                auditInfo.setModifiedBy(owner);
2679
                auditInfo.setModificationDate(now);
2680
                file.setAuditInfo(auditInfo);
2681
                // TODO set the proper versioning flag on creation
2682
                file.setVersioned(false);
2683

    
2684
                for (final Permission p : parent.getPermissions()) {
2685
                        final Permission permission = new Permission();
2686
                        permission.setGroup(p.getGroup());
2687
                        permission.setUser(p.getUser());
2688
                        permission.setRead(p.getRead());
2689
                        permission.setWrite(p.getWrite());
2690
                        permission.setModifyACL(p.getModifyACL());
2691
                        file.addPermission(permission);
2692
                }
2693
                // Create the file body.
2694
                try {
2695
                        createEmptyFileBody(name, contentType, 0,  file, auditInfo);
2696
                } catch (FileNotFoundException e) {
2697
                        throw new GSSIOException(e);
2698
                }
2699
                touchParentFolders(parent, owner, new Date());
2700
                dao.flush();
2701
                return file;
2702
        }
2703
        
2704
        private void createEmptyFileBody(String name, String mimeType, long fileSize, 
2705
                                FileHeader header, AuditInfo auditInfo)
2706
                        throws FileNotFoundException, QuotaExceededException, ObjectNotFoundException {
2707

    
2708
                long currentTotalSize = 0;
2709
                if (!header.isVersioned() && header.getCurrentBody() != null && header.getBodies() != null)
2710
                        currentTotalSize = header.getTotalSize();
2711
                Long quotaLeft = getQuotaLeft(header.getOwner().getId());
2712
                
2713

    
2714
                FileBody body = new FileBody();
2715

    
2716
                // if no mime type or the generic mime type is defined by the client, then try to identify it from the filename extension
2717
                if (StringUtils.isEmpty(mimeType) || "application/octet-stream".equals(mimeType)
2718
                                        || "application/download".equals(mimeType) || "application/force-download".equals(mimeType)
2719
                                        || "octet/stream".equals(mimeType) || "application/unknown".equals(mimeType))
2720
                        body.setMimeType(identifyMimeType(name));
2721
                else
2722
                        body.setMimeType(mimeType);
2723
                body.setAuditInfo(auditInfo);
2724
                body.setFileSize(fileSize);
2725
                body.setOriginalFilename(name);
2726
                body.setStoredFilePath(generateRepositoryFilePath());
2727
                //CLEAR OLD VERSION IF FILE IS NOT VERSIONED AND GETS UPDATED
2728
                if(!header.isVersioned() && header.getCurrentBody() != null){
2729
                        header.setCurrentBody(null);
2730
                        if (header.getBodies() != null) {
2731
                                Iterator<FileBody> it = header.getBodies().iterator();
2732
                                while(it.hasNext()){
2733
                                        FileBody bo = it.next();
2734
                                        deleteActualFile(bo.getStoredFilePath());
2735
                                        it.remove();
2736
                                        dao.delete(bo);
2737
                                }
2738
                        }
2739
                }
2740

    
2741
                dao.flush();
2742
                header.addBody(body);
2743
                header.setAuditInfo(auditInfo);
2744

    
2745
                dao.create(body);
2746
        }
2747
        /*** WEBDAV LOCK **/
2748
        @Override
2749
        public FileLock getLockById(String id) {
2750
                return dao.getLockById(id);
2751
        }
2752

    
2753
        @Override
2754
        public FileLock getLockByToken(String tokenId) {
2755
                return dao.getLockByToken(tokenId);
2756
        }
2757

    
2758
        @Override
2759
        public void removeLock(FileLock lock) {
2760
                dao.removeLock(lock);                
2761
        }
2762

    
2763
        @Override
2764
        public FileLock saveOrUpdateLock(FileLock lock) {
2765
                return dao.saveOrUpdateLock(lock);
2766
        }
2767
        
2768
        @Override
2769
        public WebDavNonce getWebDavNonce(String tokenId) {
2770
                return dao.getWebDavNonce(tokenId);
2771
        }
2772

    
2773
        @Override
2774
        public void removeWebDavNonce(WebDavNonce nonce) {
2775
                dao.removeWebDavNonce(nonce);                
2776
        }
2777

    
2778
        @Override
2779
        public WebDavNonce saveOrUpdateWebDavNonce(WebDavNonce nonce) {
2780
                return dao.saveOrUpdateWebDavNonce(nonce);
2781
        }
2782
        
2783
        
2784
        
2785
}