diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/api/Util.java b/bbb-common-web/src/main/java/org/bigbluebutton/api/Util.java index c63746b264..52ea9a9e96 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/api/Util.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/api/Util.java @@ -2,11 +2,14 @@ package org.bigbluebutton.api; import java.io.File; import java.io.IOException; +import java.net.MalformedURLException; +import java.net.URL; import java.util.UUID; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.commons.codec.digest.DigestUtils; +import org.apache.commons.io.FilenameUtils; import org.apache.commons.lang3.StringUtils; public final class Util { @@ -50,8 +53,19 @@ public final class Util { return DigestUtils.sha1Hex(presFilename + uuid) + "-" + timestamp; } + public static String extractFilenameFromUrl(String preUploadedPresentation) throws MalformedURLException { + URL url = new URL(preUploadedPresentation); + String filename = FilenameUtils.getName(url.getPath()); + String extension = FilenameUtils.getExtension(url.getPath()); + if (extension == null || extension.isEmpty()) return null; + return filename; + } public static String createNewFilename(String presId, String fileExt) { - return presId + "." + fileExt; + if (!fileExt.isEmpty()) { + return presId + "." + fileExt; + } else { + return presId; + } } public static File createPresentationDir(String meetingId, String presentationDir, String presentationId) { diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/presentation/MimeTypeUtils.java b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/MimeTypeUtils.java index fa29c20541..5e7372476c 100644 --- a/bbb-common-web/src/main/java/org/bigbluebutton/presentation/MimeTypeUtils.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/MimeTypeUtils.java @@ -1,5 +1,7 @@ package org.bigbluebutton.presentation; +import org.bigbluebutton.api.domain.Extension; + import java.util.*; import static org.bigbluebutton.presentation.FileTypeConstants.*; @@ -43,7 +45,16 @@ public class MimeTypeUtils { put(FileTypeConstants.SVG, Arrays.asList(SVG)); } }; - + + public String getExtensionBasedOnMimeType(String mimeType) { + return EXTENSIONS_MIME.entrySet() + .stream() + .filter(entry -> entry.getValue().contains(mimeType)) + .map(Map.Entry::getKey) + .findFirst() + .orElse(null); + } + public Boolean extensionMatchMimeType(String mimeType, String finalExtension) { finalExtension = finalExtension.toLowerCase(); diff --git a/bbb-common-web/src/main/java/org/bigbluebutton/presentation/SupportedFileTypes.java b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/SupportedFileTypes.java index b485347845..4e68969451 100755 --- a/bbb-common-web/src/main/java/org/bigbluebutton/presentation/SupportedFileTypes.java +++ b/bbb-common-web/src/main/java/org/bigbluebutton/presentation/SupportedFileTypes.java @@ -108,14 +108,21 @@ public final class SupportedFileTypes { return ""; } + public static String detectFileExtensionBasedOnMimeType(File pres) { + String mimeType = detectMimeType(pres); + return mimeTypeUtils.getExtensionBasedOnMimeType(mimeType); + } + public static Boolean isPresentationMimeTypeValid(File pres, String fileExtension) { String mimeType = detectMimeType(pres); if (mimeType.equals("")) { + log.error("Not able to detect mimeType."); return false; } if (!mimeTypeUtils.getValidMimeTypes().contains(mimeType)) { + log.error("MimeType is not valid for this meeting, [{}]", mimeType); return false; } diff --git a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy index 50750820b2..28422102f9 100755 --- a/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy +++ b/bigbluebutton-web/grails-app/controllers/org/bigbluebutton/web/controllers/ApiController.groovy @@ -44,7 +44,7 @@ import org.bigbluebutton.web.services.turn.TurnEntry import org.bigbluebutton.web.services.turn.StunServer import org.bigbluebutton.web.services.turn.RemoteIceCandidate import org.json.JSONArray - +import org.apache.commons.io.FilenameUtils import javax.servlet.ServletRequest class ApiController { @@ -1383,17 +1383,27 @@ class ApiController { String[] pu = request.getParameterMap().get("preUploadedPresentation") + String[] puName = request.getParameterMap().get("preUploadedPresentationName") if (pu != null) { String preUploadedPresentation = pu[0] hasPresentationUrlInParameter = true def xmlString = new StringWriter() def xml = new MarkupBuilder(xmlString) + String filename + if (puName == null) { + filename = Util.extractFilenameFromUrl(preUploadedPresentation) + if (filename == null) { + filename = "untitled" + } + } else { + filename = puName[0] + } xml.document ( removable: "true", downloadable: "false", url: preUploadedPresentation, - filename: extractFilenameFromUrl(preUploadedPresentation), - name: extractFilenameFromUrl(preUploadedPresentation) + filename: filename, + isPreUploadedPresentationFromParameter: "true" ) def parsedXml = new XmlSlurper().parseText(xmlString.toString()) @@ -1454,6 +1464,7 @@ class ApiController { def Boolean isRemovable = true; def Boolean isDownloadable = false; def Boolean isInitialPresentation = false; + def Boolean isPreUploadedPresentationFromParameter = false if (document.name != null && "default".equals(document.name)) { if (presentationService.defaultUploadedPresentation) { @@ -1462,11 +1473,15 @@ class ApiController { } downloadAndProcessDocument(presentationService.defaultUploadedPresentation, conf.getInternalId(), document.current /* default presentation */, '', false, - true, isInitialPresentation); + true, isInitialPresentation, isPreUploadedPresentationFromParameter); } else { log.error "Default presentation could not be read, it is (" + presentationService.defaultUploadedPresentation + ")", "error" } } else { + if (!StringUtils.isEmpty(document.@isPreUploadedPresentationFromParameter.toString())) { + isPreUploadedPresentationFromParameter = java.lang.Boolean.parseBoolean( + document.@isPreUploadedPresentationFromParameter.toString()); + } // Extracting all properties inside the xml if (!StringUtils.isEmpty(document.@removable.toString())) { isRemovable = java.lang.Boolean.parseBoolean(document.@removable.toString()); @@ -1493,7 +1508,7 @@ class ApiController { fileName = document.@filename.toString(); } downloadAndProcessDocument(document.@url.toString(), conf.getInternalId(), isCurrent /* default presentation */, - fileName, isDownloadable, isRemovable, isInitialPresentation); + fileName, isDownloadable, isRemovable, isInitialPresentation, isPreUploadedPresentationFromParameter); } else if (!StringUtils.isEmpty(document.@name.toString())) { def b64 = new Base64() def decodedBytes = b64.decode(document.text().getBytes()) @@ -1507,10 +1522,6 @@ class ApiController { return true } - def extractFilenameFromUrl(String url) { - return url.split('/')[-1] - } - def processDocumentFromRawBytes(bytes, presOrigFilename, meetingId, current, isDownloadable, isRemovable, isInitialPresentation) { def uploadFailed = false @@ -1567,7 +1578,8 @@ class ApiController { } } - def downloadAndProcessDocument(address, meetingId, current, fileName, isDownloadable, isRemovable, isInitialPresentation) { + def downloadAndProcessDocument(address, meetingId, current, fileName, isDownloadable, isRemovable, + isInitialPresentation, isPreUploadedPresentationFromParameter) { log.debug("ApiController#downloadAndProcessDocument(${address}, ${meetingId}, ${fileName})"); String presOrigFilename; if (StringUtils.isEmpty(fileName)) { @@ -1592,7 +1604,7 @@ class ApiController { def pres = null def presId - if (presFilename == "" || filenameExt == "") { + if (presFilename == "" || (filenameExt == "" && !isPreUploadedPresentationFromParameter)) { log.debug("presentation is null by default") return } else { @@ -1609,6 +1621,22 @@ class ApiController { uploadFailReasons.add("failed_to_download_file") uploadFailed = true } + + if (isPreUploadedPresentationFromParameter && filenameExt.isEmpty()) { + String fileExtension = SupportedFileTypes.detectFileExtensionBasedOnMimeType(pres) + newFilename = Util.createNewFilename(presId, fileExtension) + newFilePath = uploadDir.absolutePath + File.separatorChar + newFilename + File destination = new File(newFilePath) + filenameExt = fileExtension + presFilename = Util.createNewFilename(presFilename, fileExtension) + if (pres.renameTo(destination)) { + log.info("Presentation coming from URL parameter is at ${destination.getAbsolutePath()}") + pres = destination + } else { + log.error("Error while renaming presentation from URL parameter to ${destination.getAbsolutePath()}, " + + "consider sending it through `/insertDocument`") + } + } } else { log.error("Null presentation directory meeting=[${meetingId}], presentationDir=[${presentationDir}], presId=[${presId}]") uploadFailReasons.add("null_presentation_dir")