2 * Copyright (c) 2010-2023 Contributors to the openHAB project
4 * See the NOTICE file(s) distributed with this work for additional
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
11 * SPDX-License-Identifier: EPL-2.0
13 package org.openhab.binding.folderwatcher.internal.api;
15 import static org.eclipse.jetty.http.HttpHeader.*;
16 import static org.eclipse.jetty.http.HttpMethod.*;
18 import java.io.StringReader;
19 import java.net.MalformedURLException;
21 import java.time.Duration;
22 import java.util.ArrayList;
23 import java.util.HashMap;
24 import java.util.List;
26 import java.util.concurrent.TimeUnit;
28 import javax.xml.parsers.DocumentBuilder;
29 import javax.xml.parsers.DocumentBuilderFactory;
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;
43 * The {@link S3Actions} class contains AWS S3 API implementation.
45 * @author Alexandr Salamatov - Initial contribution
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;
57 public S3Actions(HttpClientFactory httpClientFactory, String bucketName, String region) {
58 this(httpClientFactory, bucketName, region, "", "");
61 public S3Actions(HttpClientFactory httpClientFactory, String bucketName, String region, String awsAccessKey,
62 String awsSecretKey) {
63 this.httpClient = httpClientFactory.getCommonHttpClient();
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());
70 this.awsAccessKey = awsAccessKey;
71 this.awsSecretKey = awsSecretKey;
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);
80 private List<String> listObjectsV2(String prefix, Map<String, String> headers, Map<String, String> params)
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",
88 String authorization = signer.computeSignature(headers, params, AWS4SignerBase.EMPTY_BODY_SHA256,
89 awsAccessKey, awsSecretKey);
90 headers.put("Authorization", authorization);
93 headers.put(ACCEPT.toString(), CONTENT_TYPE);
94 Request request = httpClient.newRequest(this.bucketUri.toString()) //
96 .timeout(REQUEST_TIMEOUT.toNanos(), TimeUnit.NANOSECONDS); //
98 for (String headerKey : headers.keySet()) {
99 request.header(headerKey, headers.get(headerKey));
101 for (String paramKey : params.keySet()) {
102 request.param(paramKey, params.get(paramKey));
105 ContentResponse contentResponse = request.send();
106 if (contentResponse.getStatus() != 200) {
107 throw new Exception("HTTP Response is not 200");
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<>();
117 if (nameNodesList.getLength() == 0) {
121 for (int i = 0; i < nameNodesList.getLength(); i++) {
122 returnList.add(nameNodesList.item(i).getFirstChild().getTextContent());
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();
133 params.put("continuation-token", continueToken);
134 returnList.addAll(listObjectsV2(prefix, headers, params));