]> git.basschouten.com Git - openhab-addons.git/blob
2ba0fd5fe0aca48e52256311af3297ded2c624a1
[openhab-addons.git] /
1 /**
2  * Copyright (c) 2010-2023 Contributors to the openHAB project
3  *
4  * See the NOTICE file(s) distributed with this work for additional
5  * information.
6  *
7  * This program and the accompanying materials are made available under the
8  * terms of the Eclipse Public License 2.0 which is available at
9  * http://www.eclipse.org/legal/epl-2.0
10  *
11  * SPDX-License-Identifier: EPL-2.0
12  */
13 package org.openhab.binding.folderwatcher.internal.api;
14
15 import static org.eclipse.jetty.http.HttpHeader.*;
16 import static org.eclipse.jetty.http.HttpMethod.*;
17
18 import java.io.StringReader;
19 import java.net.MalformedURLException;
20 import java.net.URL;
21 import java.time.Duration;
22 import java.util.ArrayList;
23 import java.util.HashMap;
24 import java.util.List;
25 import java.util.Map;
26 import java.util.concurrent.TimeUnit;
27
28 import javax.xml.parsers.DocumentBuilder;
29 import javax.xml.parsers.DocumentBuilderFactory;
30
31 import org.eclipse.jdt.annotation.NonNullByDefault;
32 import org.eclipse.jetty.client.HttpClient;
33 import org.eclipse.jetty.client.api.ContentResponse;
34 import org.eclipse.jetty.client.api.Request;
35 import org.openhab.binding.folderwatcher.internal.api.auth.AWS4SignerBase;
36 import org.openhab.binding.folderwatcher.internal.api.auth.AWS4SignerForAuthorizationHeader;
37 import org.openhab.core.io.net.http.HttpClientFactory;
38 import org.w3c.dom.Document;
39 import org.w3c.dom.NodeList;
40 import org.xml.sax.InputSource;
41
42 /**
43  * The {@link S3Actions} class contains AWS S3 API implementation.
44  *
45  * @author Alexandr Salamatov - Initial contribution
46  */
47 @NonNullByDefault
48 public class S3Actions {
49     private final HttpClient httpClient;
50     private static final Duration REQUEST_TIMEOUT = Duration.ofMinutes(1);
51     private static final String CONTENT_TYPE = "application/xml";
52     private URL bucketUri;
53     private String region;
54     private String awsAccessKey;
55     private String awsSecretKey;
56
57     public S3Actions(HttpClientFactory httpClientFactory, String bucketName, String region) {
58         this(httpClientFactory, bucketName, region, "", "");
59     }
60
61     public S3Actions(HttpClientFactory httpClientFactory, String bucketName, String region, String awsAccessKey,
62             String awsSecretKey) {
63         this.httpClient = httpClientFactory.getCommonHttpClient();
64         try {
65             this.bucketUri = new URL("http://" + bucketName + ".s3." + region + ".amazonaws.com");
66         } catch (MalformedURLException e) {
67             throw new RuntimeException("Unable to parse service endpoint: " + e.getMessage());
68         }
69         this.region = region;
70         this.awsAccessKey = awsAccessKey;
71         this.awsSecretKey = awsSecretKey;
72     }
73
74     public List<String> listBucket(String prefix) throws Exception {
75         Map<String, String> headers = new HashMap<String, String>();
76         Map<String, String> params = new HashMap<String, String>();
77         return listObjectsV2(prefix, headers, params);
78     }
79
80     private List<String> listObjectsV2(String prefix, Map<String, String> headers, Map<String, String> params)
81             throws Exception {
82         params.put("list-type", "2");
83         params.put("prefix", prefix);
84         if (!awsAccessKey.isEmpty() || !awsSecretKey.isEmpty()) {
85             headers.put("x-amz-content-sha256", AWS4SignerBase.EMPTY_BODY_SHA256);
86             AWS4SignerForAuthorizationHeader signer = new AWS4SignerForAuthorizationHeader(this.bucketUri, "GET", "s3",
87                     region);
88             String authorization = signer.computeSignature(headers, params, AWS4SignerBase.EMPTY_BODY_SHA256,
89                     awsAccessKey, awsSecretKey);
90             headers.put("Authorization", authorization);
91         }
92
93         headers.put(ACCEPT.toString(), CONTENT_TYPE);
94         Request request = httpClient.newRequest(this.bucketUri.toString()) //
95                 .method(GET) //
96                 .timeout(REQUEST_TIMEOUT.toNanos(), TimeUnit.NANOSECONDS); //
97
98         for (String headerKey : headers.keySet()) {
99             request.header(headerKey, headers.get(headerKey));
100         }
101         for (String paramKey : params.keySet()) {
102             request.param(paramKey, params.get(paramKey));
103         }
104
105         ContentResponse contentResponse = request.send();
106         if (contentResponse.getStatus() != 200) {
107             throw new Exception("HTTP Response is not 200");
108         }
109
110         DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory.newInstance();
111         DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder();
112         InputSource is = new InputSource(new StringReader(contentResponse.getContentAsString()));
113         Document doc = docBuilder.parse(is);
114         NodeList nameNodesList = doc.getElementsByTagName("Key");
115         List<String> returnList = new ArrayList<>();
116
117         if (nameNodesList.getLength() == 0) {
118             return returnList;
119         }
120
121         for (int i = 0; i < nameNodesList.getLength(); i++) {
122             returnList.add(nameNodesList.item(i).getFirstChild().getTextContent());
123         }
124
125         nameNodesList = doc.getElementsByTagName("IsTruncated");
126         if (nameNodesList.getLength() > 0) {
127             if ("true".equals(nameNodesList.item(0).getFirstChild().getTextContent())) {
128                 nameNodesList = doc.getElementsByTagName("NextContinuationToken");
129                 if (nameNodesList.getLength() > 0) {
130                     String continueToken = nameNodesList.item(0).getFirstChild().getTextContent();
131                     params.clear();
132                     headers.clear();
133                     params.put("continuation-token", continueToken);
134                     returnList.addAll(listObjectsV2(prefix, headers, params));
135                 }
136             }
137         }
138         return returnList;
139     }
140 }