Statistics
| Branch: | Tag: | Revision:

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

History | View | Annotate | Download (100.2 kB)

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

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

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

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

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

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

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

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

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

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

    
147

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
440
                        // Do the actual modification.
441
                        folder.setName(folderName);
442
                }
443
                if (permissions != null)
444
                        setFolderPermissions(user, folder, permissions);
445
                if (readForAll != null && user.equals(folder.getOwner()))
446
                        if(!readForAll)
447
                                folder.setReadForAll(readForAll);
448
                        else{
449
                                List<FileHeader> files = dao.getFiles(folderId, userId, true);
450
                                for (FileHeader f : files)
451
                                        f.setReadForAll(readForAll);
452
                                folder.setReadForAll(readForAll);
453
                        }
454
                folder.getAuditInfo().setModificationDate(new Date());
455
                folder.getAuditInfo().setModifiedBy(user);
456
                dao.update(folder);
457
                touchParentFolders(folder, user, new Date());
458
                return folder.getDTO();
459
        }
460

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

    
473
                // TODO: Check permissions
474

    
475
                final User owner = dao.getEntityById(User.class, userId);
476

    
477
                // Do the actual work.
478
                owner.createGroup(name);
479
        }
480

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

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

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

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

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

    
569

    
570

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
988
        }
989

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

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

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

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

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

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

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

    
1077
        }
1078

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1344
        }
1345

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

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

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

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

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

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

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

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

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

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

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

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

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

    
1497
        }
1498

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

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

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

    
1567
        }
1568

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

    
1587
        }
1588

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

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

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

    
1628
        }
1629

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

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

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

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

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

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

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

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

    
1761
        }
1762

    
1763
        @Override
1764
        public List<FolderDTO> getSharedSubfolders(Long userId, Long folderId) throws ObjectNotFoundException {
1765
                if (userId == 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, userId);
1770
                Folder folder = dao.getEntityById(Folder.class, folderId);
1771
                List<FolderDTO> result = new ArrayList<FolderDTO>();
1772
                if (folder.isShared(user))
1773
                        for (Folder f : folder.getSubfolders())
1774
                                if (f.isShared(user) && !f.isDeleted())
1775
                                        result.add(f.getDTO());
1776
                return result;
1777
        }
1778

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

    
1799
        }
1800

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

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

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

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

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

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

    
1911

    
1912
        }
1913

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

    
1921
        }
1922

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

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

    
1953
        }
1954

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

    
1960
        }
1961

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

    
1967
        }
1968

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

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

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

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

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

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

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

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

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

    
2062
        }
2063

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

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

    
2083
        }
2084

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    
2328
                return file.getDTO();
2329
        }
2330

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

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

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

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

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

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

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

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

    
2429
                FileBody body = new FileBody();
2430

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

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

    
2460
                dao.create(body);
2461
        }
2462

    
2463

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

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

    
2497

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

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

    
2519
        }
2520

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

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

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

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

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

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

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

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

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

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

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

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

    
2660
        }
2661

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

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

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

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

    
2705
}