9
9
********************************************************************************/
10
10
package org .eclipse .openvsx ;
11
11
12
+ import java .io .ByteArrayInputStream ;
12
13
import java .io .EOFException ;
13
14
import java .io .IOException ;
14
15
import java .nio .file .Path ;
26
27
import com .fasterxml .jackson .databind .node .MissingNode ;
27
28
import com .fasterxml .jackson .dataformat .xml .XmlMapper ;
28
29
30
+ import org .apache .commons .io .FilenameUtils ;
29
31
import org .eclipse .openvsx .entities .ExtensionVersion ;
30
32
import org .eclipse .openvsx .entities .FileResource ;
31
33
import org .eclipse .openvsx .util .ArchiveUtil ;
36
38
import org .slf4j .Logger ;
37
39
import org .slf4j .LoggerFactory ;
38
40
import 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 ;
39
46
40
47
/**
41
48
* Processes uploaded extension files and extracts their metadata.
@@ -282,17 +289,22 @@ private List<String> getEngines(JsonNode node) {
282
289
}
283
290
284
291
public List <FileResource > getFileResources (ExtensionVersion extVersion ) {
285
- var resources = new ArrayList <FileResource >();
292
+ readInputStream ();
293
+ var contentTypes = loadContentTypes ();
286
294
var mappers = List .<Function <ExtensionVersion , FileResource >>of (
287
295
this ::getManifest , this ::getReadme , this ::getChangelog , this ::getLicense , this ::getIcon
288
296
);
289
297
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 ());
292
303
}
293
304
294
305
public void processEachResource (ExtensionVersion extVersion , Consumer <FileResource > processor ) {
295
306
readInputStream ();
307
+ var contentTypes = loadContentTypes ();
296
308
zipFile .stream ()
297
309
.filter (zipEntry -> !zipEntry .isDirectory ())
298
310
.map (zipEntry -> {
@@ -311,6 +323,7 @@ public void processEachResource(ExtensionVersion extVersion, Consumer<FileResour
311
323
resource .setName (zipEntry .getName ());
312
324
resource .setType (FileResource .RESOURCE );
313
325
resource .setContent (bytes );
326
+ setContentType (resource , contentTypes );
314
327
return resource ;
315
328
})
316
329
.filter (Objects ::nonNull )
@@ -393,9 +406,7 @@ public FileResource getLicense(ExtensionVersion extVersion) {
393
406
var fileName = matcher .group ("file" );
394
407
var bytes = ArchiveUtil .readEntry (zipFile , "extension/" + fileName );
395
408
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 ));
399
410
license .setContent (bytes );
400
411
detectLicense (bytes , extVersion );
401
412
return license ;
@@ -413,6 +424,44 @@ public FileResource getLicense(ExtensionVersion extVersion) {
413
424
return license ;
414
425
}
415
426
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
+
416
465
private void detectLicense (byte [] content , ExtensionVersion extVersion ) {
417
466
if (Strings .isNullOrEmpty (extVersion .getLicense ())) {
418
467
var detection = new LicenseDetection ();
@@ -425,9 +474,7 @@ private Pair<byte[], String> readFromAlternateNames(String[] names) {
425
474
var entry = ArchiveUtil .getEntryIgnoreCase (zipFile , name );
426
475
if (entry != null ) {
427
476
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 ()));
431
478
}
432
479
}
433
480
return null ;
@@ -442,13 +489,10 @@ protected FileResource getIcon(ExtensionVersion extVersion) {
442
489
var bytes = ArchiveUtil .readEntry (zipFile , "extension/" + iconPathStr );
443
490
if (bytes == null )
444
491
return null ;
492
+
445
493
var icon = new FileResource ();
446
494
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 ));
452
496
icon .setType (FileResource .ICON );
453
497
icon .setContent (bytes );
454
498
return icon ;
0 commit comments