99 ********************************************************************************/
1010package org .eclipse .openvsx ;
1111
12+ import java .io .ByteArrayInputStream ;
1213import java .io .EOFException ;
1314import java .io .IOException ;
1415import java .nio .file .Path ;
2627import com .fasterxml .jackson .databind .node .MissingNode ;
2728import com .fasterxml .jackson .dataformat .xml .XmlMapper ;
2829
30+ import org .apache .commons .io .FilenameUtils ;
2931import org .eclipse .openvsx .entities .ExtensionVersion ;
3032import org .eclipse .openvsx .entities .FileResource ;
3133import org .eclipse .openvsx .util .ArchiveUtil ;
3638import org .slf4j .Logger ;
3739import org .slf4j .LoggerFactory ;
3840import org .springframework .data .util .Pair ;
41+ import org .springframework .http .MediaType ;
42+ import org .xml .sax .SAXException ;
43+
44+ import javax .xml .parsers .DocumentBuilderFactory ;
45+ import javax .xml .parsers .ParserConfigurationException ;
3946
4047/**
4148 * Processes uploaded extension files and extracts their metadata.
@@ -282,17 +289,22 @@ private List<String> getEngines(JsonNode node) {
282289 }
283290
284291 public List <FileResource > getFileResources (ExtensionVersion extVersion ) {
285- var resources = new ArrayList <FileResource >();
292+ readInputStream ();
293+ var contentTypes = loadContentTypes ();
286294 var mappers = List .<Function <ExtensionVersion , FileResource >>of (
287295 this ::getManifest , this ::getReadme , this ::getChangelog , this ::getLicense , this ::getIcon
288296 );
289297
290- mappers .forEach (mapper -> Optional .of (extVersion ).map (mapper ).ifPresent (resources ::add ));
291- return resources ;
298+ return mappers .stream ()
299+ .map (mapper -> mapper .apply (extVersion ))
300+ .filter (Objects ::nonNull )
301+ .map (resource -> setContentType (resource , contentTypes ))
302+ .collect (Collectors .toList ());
292303 }
293304
294305 public void processEachResource (ExtensionVersion extVersion , Consumer <FileResource > processor ) {
295306 readInputStream ();
307+ var contentTypes = loadContentTypes ();
296308 zipFile .stream ()
297309 .filter (zipEntry -> !zipEntry .isDirectory ())
298310 .map (zipEntry -> {
@@ -311,6 +323,7 @@ public void processEachResource(ExtensionVersion extVersion, Consumer<FileResour
311323 resource .setName (zipEntry .getName ());
312324 resource .setType (FileResource .RESOURCE );
313325 resource .setContent (bytes );
326+ setContentType (resource , contentTypes );
314327 return resource ;
315328 })
316329 .filter (Objects ::nonNull )
@@ -393,9 +406,7 @@ public FileResource getLicense(ExtensionVersion extVersion) {
393406 var fileName = matcher .group ("file" );
394407 var bytes = ArchiveUtil .readEntry (zipFile , "extension/" + fileName );
395408 if (bytes != null ) {
396- var lastSegmentIndex = fileName .lastIndexOf ('/' );
397- var lastSegment = fileName .substring (lastSegmentIndex + 1 );
398- license .setName (lastSegment );
409+ license .setName (FilenameUtils .getName (fileName ));
399410 license .setContent (bytes );
400411 detectLicense (bytes , extVersion );
401412 return license ;
@@ -413,6 +424,44 @@ public FileResource getLicense(ExtensionVersion extVersion) {
413424 return license ;
414425 }
415426
427+ private Map <String , String > loadContentTypes () {
428+ var bytes = ArchiveUtil .readEntry (zipFile , "[Content_Types].xml" );
429+ var contentTypes = parseContentTypesXml (bytes );
430+ contentTypes .putIfAbsent (".vsix" , "application/zip" );
431+ return contentTypes ;
432+ }
433+
434+ private Map <String , String > parseContentTypesXml (byte [] content ) {
435+ var contentTypes = new HashMap <String , String >();
436+ try (var input = new ByteArrayInputStream (content )) {
437+ var document = DocumentBuilderFactory .newInstance ().newDocumentBuilder ().parse (input );
438+ var elements = document .getDocumentElement ().getElementsByTagName ("Default" );
439+ for (var i = 0 ; i < elements .getLength (); i ++) {
440+ var element = elements .item (i );
441+ var attributes = element .getAttributes ();
442+ var extension = attributes .getNamedItem ("Extension" ).getTextContent ();
443+ if (!extension .startsWith ("." )) {
444+ extension = "." + extension ;
445+ }
446+
447+ var contentType = attributes .getNamedItem ("ContentType" ).getTextContent ();
448+ contentTypes .put (extension , contentType );
449+ }
450+ } catch (IOException | ParserConfigurationException | SAXException e ) {
451+ logger .error ("failed to read content types" , e );
452+ contentTypes .clear ();
453+ }
454+
455+ return contentTypes ;
456+ }
457+
458+ private FileResource setContentType (FileResource resource , Map <String , String > contentTypes ) {
459+ var fileExtension = FilenameUtils .getExtension (resource .getName ());
460+ var contentType = contentTypes .getOrDefault (fileExtension , MediaType .APPLICATION_OCTET_STREAM_VALUE );
461+ resource .setContentType (contentType );
462+ return resource ;
463+ }
464+
416465 private void detectLicense (byte [] content , ExtensionVersion extVersion ) {
417466 if (Strings .isNullOrEmpty (extVersion .getLicense ())) {
418467 var detection = new LicenseDetection ();
@@ -425,9 +474,7 @@ private Pair<byte[], String> readFromAlternateNames(String[] names) {
425474 var entry = ArchiveUtil .getEntryIgnoreCase (zipFile , name );
426475 if (entry != null ) {
427476 var bytes = ArchiveUtil .readEntry (zipFile , entry );
428- var lastSegmentIndex = entry .getName ().lastIndexOf ('/' );
429- var lastSegment = entry .getName ().substring (lastSegmentIndex + 1 );
430- return Pair .of (bytes , lastSegment );
477+ return Pair .of (bytes , FilenameUtils .getName (entry .getName ()));
431478 }
432479 }
433480 return null ;
@@ -442,13 +489,10 @@ protected FileResource getIcon(ExtensionVersion extVersion) {
442489 var bytes = ArchiveUtil .readEntry (zipFile , "extension/" + iconPathStr );
443490 if (bytes == null )
444491 return null ;
492+
445493 var icon = new FileResource ();
446494 icon .setExtension (extVersion );
447- var fileNameIndex = iconPathStr .lastIndexOf ('/' );
448- if (fileNameIndex >= 0 )
449- icon .setName (iconPathStr .substring (fileNameIndex + 1 ));
450- else
451- icon .setName (iconPathStr );
495+ icon .setName (FilenameUtils .getName (iconPathStr ));
452496 icon .setType (FileResource .ICON );
453497 icon .setContent (bytes );
454498 return icon ;
0 commit comments