/*
This file is part of Subsonic.
Subsonic is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
Subsonic is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with Subsonic. If not, see .
Copyright 2009 (C) Sindre Mehus
*/
package net.sourceforge.subsonic.controller;
import net.sourceforge.subsonic.*;
import net.sourceforge.subsonic.domain.*;
import net.sourceforge.subsonic.upload.*;
import net.sourceforge.subsonic.service.*;
import net.sourceforge.subsonic.util.*;
import org.apache.commons.fileupload.*;
import org.apache.commons.fileupload.servlet.*;
import org.apache.commons.io.*;
import org.apache.tools.zip.*;
import org.springframework.web.servlet.*;
import org.springframework.web.servlet.mvc.*;
import javax.servlet.http.*;
import java.io.*;
import java.util.*;
/**
* Controller which receives uploaded files.
*
* @author Sindre Mehus
*/
public class UploadController extends ParameterizableViewController {
private static final Logger LOG = Logger.getLogger(UploadController.class);
private SecurityService securityService;
private PlayerService playerService;
private StatusService statusService;
private SettingsService settingsService;
public static final String UPLOAD_STATUS = "uploadStatus";
@Override
protected ModelAndView handleRequestInternal(HttpServletRequest request, HttpServletResponse response) throws Exception {
Map map = new HashMap();
List uploadedFiles = new ArrayList();
List unzippedFiles = new ArrayList();
TransferStatus status = null;
try {
status = statusService.createUploadStatus(playerService.getPlayer(request, response, false, false));
status.setBytesTotal(request.getContentLength());
request.getSession().setAttribute(UPLOAD_STATUS, status);
// Check that we have a file upload request
if (!ServletFileUpload.isMultipartContent(request)) {
throw new Exception("Illegal request.");
}
File dir = null;
boolean unzip = false;
UploadListener listener = new UploadListenerImpl(status);
FileItemFactory factory = new MonitoredDiskFileItemFactory(listener);
ServletFileUpload upload = new ServletFileUpload(factory);
List> items = upload.parseRequest(request);
// First, look for "dir" and "unzip" parameters.
for (Object o : items) {
FileItem item = (FileItem) o;
if (item.isFormField() && "dir".equals(item.getFieldName())) {
dir = new File(item.getString());
} else if (item.isFormField() && "unzip".equals(item.getFieldName())) {
unzip = true;
}
}
if (dir == null) {
throw new Exception("Missing 'dir' parameter.");
}
// Look for file items.
for (Object o : items) {
FileItem item = (FileItem) o;
if (!item.isFormField()) {
String fileName = item.getName();
if (fileName.trim().length() > 0) {
File targetFile = new File(dir, new File(fileName).getName());
if (!securityService.isUploadAllowed(targetFile)) {
throw new Exception("Permission denied: " + StringUtil.toHtml(targetFile.getPath()));
}
if (!dir.exists()) {
dir.mkdirs();
}
item.write(targetFile);
uploadedFiles.add(targetFile);
LOG.info("Uploaded " + targetFile);
if (unzip && targetFile.getName().toLowerCase().endsWith(".zip")) {
unzip(targetFile, unzippedFiles);
}
}
}
}
} catch (Exception x) {
LOG.warn("Uploading failed.", x);
map.put("exception", x);
} finally {
if (status != null) {
statusService.removeUploadStatus(status);
request.getSession().removeAttribute(UPLOAD_STATUS);
User user = securityService.getCurrentUser(request);
securityService.updateUserByteCounts(user, 0L, 0L, status.getBytesTransfered());
}
}
map.put("uploadedFiles", uploadedFiles);
map.put("unzippedFiles", unzippedFiles);
ModelAndView result = super.handleRequestInternal(request, response);
result.addObject("model", map);
return result;
}
private void unzip(File file, List unzippedFiles) throws Exception {
LOG.info("Unzipping " + file);
ZipFile zipFile = new ZipFile(file);
try {
Enumeration> entries = zipFile.getEntries();
while (entries.hasMoreElements()) {
ZipEntry entry = (ZipEntry) entries.nextElement();
File entryFile = new File(file.getParentFile(), entry.getName());
if (!entry.isDirectory()) {
if (!securityService.isUploadAllowed(entryFile)) {
throw new Exception("Permission denied: " + StringUtil.toHtml(entryFile.getPath()));
}
entryFile.getParentFile().mkdirs();
InputStream inputStream = null;
OutputStream outputStream = null;
try {
inputStream = zipFile.getInputStream(entry);
outputStream = new FileOutputStream(entryFile);
byte[] buf = new byte[8192];
while (true) {
int n = inputStream.read(buf);
if (n == -1) {
break;
}
outputStream.write(buf, 0, n);
}
LOG.info("Unzipped " + entryFile);
unzippedFiles.add(entryFile);
} finally {
IOUtils.closeQuietly(inputStream);
IOUtils.closeQuietly(outputStream);
}
}
}
zipFile.close();
file.delete();
} finally {
zipFile.close();
}
}
public void setSecurityService(SecurityService securityService) {
this.securityService = securityService;
}
public void setPlayerService(PlayerService playerService) {
this.playerService = playerService;
}
public void setStatusService(StatusService statusService) {
this.statusService = statusService;
}
public void setSettingsService(SettingsService settingsService) {
this.settingsService = settingsService;
}
/**
* Receives callbacks as the file upload progresses.
*/
private class UploadListenerImpl implements UploadListener {
private TransferStatus status;
private long start;
private UploadListenerImpl(TransferStatus status) {
this.status = status;
start = System.currentTimeMillis();
}
public void start(String fileName) {
status.setFile(new File(fileName));
}
public void bytesRead(long bytesRead) {
// Throttle bitrate.
long byteCount = status.getBytesTransfered() + bytesRead;
long bitCount = byteCount * 8L;
float elapsedMillis = Math.max(1, System.currentTimeMillis() - start);
float elapsedSeconds = elapsedMillis / 1000.0F;
long maxBitsPerSecond = getBitrateLimit();
status.setBytesTransfered(byteCount);
if (maxBitsPerSecond > 0) {
float sleepMillis = 1000.0F * (bitCount / maxBitsPerSecond - elapsedSeconds);
if (sleepMillis > 0) {
try {
Thread.sleep((long) sleepMillis);
} catch (InterruptedException x) {
LOG.warn("Failed to sleep.", x);
}
}
}
}
private long getBitrateLimit() {
return 1024L * settingsService.getUploadBitrateLimit() / Math.max(1, statusService.getAllUploadStatuses().size());
}
}
}