Skip to content

Commit 5024f17

Browse files
Replace Guava depedency
-- Base64 encoding via Okio -- MD5 hashing via MessageDigest -- HmacSHA256 via Mac All of those algorithms are available at Android API >1
1 parent e639112 commit 5024f17

File tree

13 files changed

+124
-65
lines changed

13 files changed

+124
-65
lines changed

build.gradle

+1-3
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,10 @@ dependencies {
4242
compile 'com.squareup.retrofit2:converter-gson:2.3.0' // GSON integration
4343

4444
compile 'com.google.code.gson:gson:2.8.1' // JSON serialization to Java objects
45-
compile ('com.google.guava:guava:22.0-android', { // Useful core libraries for Java
46-
exclude group: 'com.google.code.findbugs', module: 'jsr305'
47-
})
4845

4946
compile 'io.reactivex.rxjava2:rxjava:2.1.2'
5047
compile "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlin_version" // Observable pattern for async methods
48+
compileOnly 'com.google.code.findbugs:jsr305:3.0.2'
5149
}
5250

5351
javadoc {

src/integTest/java/com/filestack/TestBasicFunctions.java

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
package com.filestack;
22

3-
import com.google.common.io.Files;
3+
import okio.Okio;
44
import org.junit.AfterClass;
55
import org.junit.Assert;
66
import org.junit.Rule;
@@ -86,7 +86,7 @@ public void testDownload() throws Exception {
8686
fileLink.download("/tmp/", downloadFile.getName());
8787

8888
Assert.assertTrue(downloadFile.isFile());
89-
byte[] bytes = Files.asByteSource(downloadFile).read();
89+
byte[] bytes = Okio.buffer(Okio.source(downloadFile)).readByteArray();
9090
String content = new String(bytes, "utf-16");
9191
Assert.assertEquals(uploadUuid, content);
9292
}

src/integTest/java/com/filestack/TestTransforms.java

+17-8
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,15 @@
44
import com.filestack.transforms.ImageTransform;
55
import com.filestack.transforms.tasks.AvTransformOptions;
66
import com.filestack.transforms.tasks.CropTask;
7-
import com.google.common.hash.Hashing;
8-
import com.google.common.io.Files;
7+
import okio.ByteString;
8+
import okio.Okio;
99
import org.junit.AfterClass;
1010
import org.junit.Assert;
1111
import org.junit.Test;
1212

1313
import java.io.File;
14+
import java.security.MessageDigest;
15+
import java.security.NoSuchAlgorithmException;
1416
import java.util.ArrayList;
1517

1618
public class TestTransforms {
@@ -39,9 +41,10 @@ public void testImageTransform() throws Exception {
3941
String cropPath = loader.getResource("com/filestack/sample_image_cropped.jpg").getPath();
4042
File cropFile = new File(cropPath);
4143

42-
String correct = Files.asByteSource(cropFile).hash(Hashing.sha256()).toString();
43-
byte[] bytes = transform.getContent().bytes();
44-
String output = Hashing.sha256().hashBytes(bytes).toString();
44+
byte[] bytes = Okio.buffer(Okio.source(cropFile)).readByteArray();
45+
String correct = ByteString.of(sha256(bytes)).hex();
46+
byte[] actualBytes = transform.getContent().bytes();
47+
String output = ByteString.of(sha256(actualBytes)).hex();
4548

4649
Assert.assertEquals(correct, output);
4750
}
@@ -70,9 +73,9 @@ public void testAvTransform() throws Exception {
7073
String mp3Path = loader.getResource("com/filestack/sample_music.mp3").getPath();
7174
File mp3File = new File(mp3Path);
7275

73-
String correct = Files.asByteSource(mp3File).hash(Hashing.sha256()).toString();
76+
String correct = ByteString.of(sha256(Okio.buffer(Okio.source(mp3File)).readByteArray())).hex();
7477
byte[] bytes = mp3FileLink.getContent().bytes();
75-
String output = Hashing.sha256().hashBytes(bytes).toString();
78+
String output = ByteString.of(bytes).hex();
7679

7780
Assert.assertEquals(correct, output);
7881
}
@@ -99,4 +102,10 @@ public static void cleanupFiles() {
99102
}
100103
}
101104
}
102-
}
105+
106+
private static byte[] sha256(byte[] bytes) throws NoSuchAlgorithmException {
107+
MessageDigest md = MessageDigest.getInstance("SHA-256");
108+
md.update(bytes);
109+
return md.digest();
110+
}
111+
}

src/main/java/com/filestack/Client.java

+1-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
import com.filestack.internal.Util;
77
import com.filestack.internal.responses.CloudStoreResponse;
88
import com.filestack.transforms.ImageTransform;
9-
import com.google.common.base.Strings;
109
import com.google.gson.Gson;
1110
import com.google.gson.JsonElement;
1211
import com.google.gson.JsonObject;
@@ -215,7 +214,7 @@ public Flowable<Progress<FileLink>> uploadAsync(String path, boolean intel, Stor
215214

216215
if (opts == null) {
217216
opts = new StorageOptions.Builder().filename(inputFile.getName()).build();
218-
} else if (Strings.isNullOrEmpty(opts.getFilename())) {
217+
} else if (Util.isNullOrEmpty(opts.getFilename())) {
219218
opts = opts.newBuilder().filename(inputFile.getName()).build();
220219
}
221220

src/main/java/com/filestack/Policy.java

+5-9
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
package com.filestack;
22

3-
import com.google.common.base.Charsets;
4-
import com.google.common.hash.HashFunction;
5-
import com.google.common.hash.Hashing;
6-
import com.google.common.io.BaseEncoding;
3+
import com.filestack.internal.Hash;
4+
import com.filestack.internal.Util;
75
import com.google.gson.Gson;
86

7+
import java.nio.charset.Charset;
98
import java.util.Date;
109

1110
/**
@@ -150,12 +149,9 @@ public Builder giveFullAccess() {
150149
*/
151150
public Policy build(String appSecret) {
152151
Gson gson = new Gson();
153-
HashFunction hashFunction = Hashing.hmacSha256(appSecret.getBytes(Charsets.UTF_8));
154-
155152
String jsonPolicy = gson.toJson(this);
156-
String encodedPolicy = BaseEncoding.base64Url().encode(jsonPolicy.getBytes(Charsets.UTF_8));
157-
String signature = hashFunction.hashString(encodedPolicy, Charsets.UTF_8).toString();
158-
153+
String encodedPolicy = Util.base64Url(jsonPolicy.getBytes());
154+
String signature = Hash.hmacSha256(appSecret.getBytes(Charset.forName("UTF-8")), encodedPolicy.getBytes(Charset.forName("UTF-8")));
159155
return new Policy(encodedPolicy, signature);
160156
}
161157
}

src/main/java/com/filestack/StorageOptions.java

+2-3
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import com.filestack.internal.Util;
44
import com.filestack.transforms.TransformTask;
5-
import com.google.common.base.Strings;
65
import com.google.gson.JsonObject;
76
import okhttp3.RequestBody;
87

@@ -64,14 +63,14 @@ public Map<String, RequestBody> getAsPartMap() {
6463

6564
// A name and MIME type must be set for uploads, so we set a default here but not in "build"
6665
// If we're not using the instance for an upload, we don't want to set these defaults
67-
if (Strings.isNullOrEmpty(filename)) {
66+
if (Util.isNullOrEmpty(filename)) {
6867
long timestamp = System.currentTimeMillis() / 1000L;
6968
filename = String.format(DEFAULT_FILENAME, timestamp);
7069
}
7170

7271
// There's too many variables in guessing MIME types, esp between platforms
7372
// Either the user sets it themselves or we use a default
74-
if (Strings.isNullOrEmpty(mimeType)) {
73+
if (Util.isNullOrEmpty(mimeType)) {
7574
mimeType = DEFAULT_MIME_TYPE;
7675
}
7776

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package com.filestack.internal;
2+
3+
import okio.ByteString;
4+
5+
import javax.annotation.Nullable;
6+
import javax.crypto.Mac;
7+
import javax.crypto.spec.SecretKeySpec;
8+
import java.security.InvalidKeyException;
9+
import java.security.MessageDigest;
10+
import java.security.NoSuchAlgorithmException;
11+
12+
public final class Hash {
13+
private Hash() {}
14+
15+
@Nullable
16+
public static byte[] md5(byte[] bytes) {
17+
return md5(bytes, 0, bytes.length);
18+
}
19+
20+
@Nullable
21+
public static byte[] md5(byte[] bytes, int offset, int length) {
22+
try {
23+
MessageDigest md = MessageDigest.getInstance("MD5");
24+
md.update(bytes, offset, length);
25+
return md.digest();
26+
} catch (NoSuchAlgorithmException e) {
27+
return null;
28+
}
29+
}
30+
31+
@Nullable
32+
public static String hmacSha256(byte[] key, byte[] message) {
33+
try {
34+
Mac mac = Mac.getInstance("HmacSHA256");
35+
mac.init(new SecretKeySpec(key, "HmacSHA256"));
36+
byte[] hash = mac.doFinal(message);
37+
return ByteString.of(hash).hex();
38+
} catch (InvalidKeyException e) {
39+
return null;
40+
} catch (NoSuchAlgorithmException e) {
41+
return null;
42+
}
43+
}
44+
}

src/main/java/com/filestack/internal/Networking.java

-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@
22

33
import java.util.concurrent.TimeUnit;
44

5-
import com.google.common.graph.Network;
65
import okhttp3.OkHttpClient;
76
import retrofit2.Retrofit;
87
import retrofit2.converter.gson.GsonConverterFactory;

src/main/java/com/filestack/internal/UploadTransferFunc.java

+3-12
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
package com.filestack.internal;
22

33
import com.filestack.internal.responses.UploadResponse;
4-
import com.google.common.hash.HashCode;
5-
import com.google.common.hash.Hashing;
6-
import com.google.common.io.BaseEncoding;
74
import io.reactivex.BackpressureStrategy;
85
import io.reactivex.Flowable;
96
import io.reactivex.FlowableEmitter;
@@ -48,20 +45,14 @@ public void subscribe(FlowableEmitter<Prog> e) throws Exception {
4845

4946
/** Get parameters from Filestack for the upload to S3. */
5047
private UploadResponse getUploadParams(int size) throws Exception {
51-
52-
// Deprecated because MD5 is insecure not because this is unmaintained
53-
@SuppressWarnings("deprecation")
54-
HashCode hc = Hashing.md5()
55-
.newHasher(size)
56-
.putBytes(container.data, container.sent, size)
57-
.hash();
58-
String md5 = BaseEncoding.base64().encode(hc.asBytes());
48+
byte[] md5 = Hash.md5(container.data, container.sent, size);
49+
String encodedMd5 = Util.base64(md5);
5950

6051
final HashMap<String, RequestBody> params = new HashMap<>();
6152
params.putAll(upload.baseParams);
6253
params.put("part", Util.createStringPart(Integer.toString(container.num)));
6354
params.put("size", Util.createStringPart(Integer.toString(size)));
64-
params.put("md5", Util.createStringPart(md5));
55+
params.put("md5", Util.createStringPart(encodedMd5));
6556
if (upload.intel) {
6657
params.put("offset", Util.createStringPart(Integer.toString(container.sent)));
6758
}

src/main/java/com/filestack/internal/Util.java

+22-6
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,20 @@
11
package com.filestack.internal;
22

33
import com.filestack.HttpException;
4-
import java.io.File;
5-
import java.io.FileNotFoundException;
6-
import java.io.IOException;
7-
import java.io.InputStream;
8-
import java.util.Properties;
94
import okhttp3.MultipartBody;
105
import okhttp3.RequestBody;
116
import okhttp3.ResponseBody;
127
import okio.Buffer;
8+
import okio.ByteString;
139
import retrofit2.Response;
1410

11+
import javax.annotation.Nullable;
12+
import java.io.File;
13+
import java.io.FileNotFoundException;
14+
import java.io.IOException;
15+
import java.io.InputStream;
16+
import java.util.Properties;
17+
1518
/**
1619
* Small helper functions that don't need their own class.
1720
*/
@@ -137,7 +140,7 @@ public static boolean isUnitTest() {
137140
}
138141

139142
/** Check if String is null or empty. */
140-
public static boolean isNullOrEmpty(String value) {
143+
public static boolean isNullOrEmpty(@Nullable String value) {
141144
return value == null || value.equals("");
142145
}
143146

@@ -149,4 +152,17 @@ public static void throwIfNullOrEmpty(String value, String message) {
149152
throw new IllegalArgumentException(message);
150153
}
151154
}
155+
156+
public static String base64(byte[] data) {
157+
return base64(data, 0, data.length);
158+
}
159+
160+
public static String base64(byte[] data, int offset, int length) {
161+
return ByteString.of(data, offset, length).base64();
162+
}
163+
164+
public static String base64Url(byte[] data) {
165+
return ByteString.of(data).base64Url();
166+
}
167+
152168
}

src/main/resources/META-INF/proguard/filestack.pro

-7
Original file line numberDiff line numberDiff line change
@@ -35,10 +35,3 @@
3535
}
3636
# Guarded by a NoClassDefFoundError try/catch and only used when on the classpath.
3737
-dontwarn kotlin.Unit
38-
39-
# Guava-specific rules
40-
-dontwarn afu.org.checkerframework.**
41-
-dontwarn org.checkerframework.**
42-
-dontwarn com.google.errorprone.**
43-
-dontwarn sun.misc.Unsafe
44-
-dontwarn java.lang.ClassValue

src/test/java/com/filestack/TestFileLink.java

+7-12
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,11 @@
33
import com.filestack.internal.BaseService;
44
import com.filestack.internal.CdnService;
55
import com.filestack.internal.Networking;
6-
import com.google.common.io.Files;
76
import okhttp3.MediaType;
87
import okhttp3.RequestBody;
98
import okhttp3.ResponseBody;
9+
import okio.BufferedSink;
10+
import okio.Okio;
1011
import org.junit.After;
1112
import org.junit.Assert;
1213
import org.junit.Before;
@@ -91,12 +92,10 @@ public void testDownloadCustomFilename() throws Exception {
9192

9293
@Test
9394
public void testOverwrite() throws Exception {
94-
String pathname = "/tmp/filestack_test_filelink_overwrite.txt";
95-
File file = new File(pathname);
96-
if (!file.createNewFile()) {
97-
Assert.fail("Unable to create resource");
98-
}
99-
Files.write("Test content".getBytes(), file);
95+
File file = File.createTempFile("filestack", ".txt");
96+
file.deleteOnExit();
97+
98+
Okio.buffer(Okio.sink(file)).writeUtf8("Test content").close();
10099

101100
Mockito
102101
.doReturn(Helpers.createRawCall("application/json", ""))
@@ -107,11 +106,7 @@ public void testOverwrite() throws Exception {
107106
Config config = new Config("apiKey", "policy", "signature");
108107
FileLink fileLink = new FileLink(config, "handle");
109108

110-
fileLink.overwrite(pathname);
111-
112-
if (!file.delete()) {
113-
Assert.fail("Unable to cleanup resource");
114-
}
109+
fileLink.overwrite(file.getAbsolutePath());
115110
}
116111

117112
@Test
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.filestack.internal
2+
3+
import okio.ByteString
4+
import org.junit.Assert.assertEquals
5+
import org.junit.Test
6+
7+
class HashTest {
8+
9+
@Test
10+
fun md5() {
11+
val result = Hash.md5("filestack".toByteArray())!!
12+
assertEquals("4d9248e9ad027c6f03a90897e329ca92", ByteString.of(*result).hex())
13+
}
14+
15+
@Test
16+
fun hmacSha256() {
17+
val result = Hash.hmacSha256("filestackKey".toByteArray(), "filestackMessage".toByteArray())
18+
assertEquals("3ea0712eaae796f0330814eff179dd39ab852924a7af023789ce39e3788af2f3", result)
19+
}
20+
}

0 commit comments

Comments
 (0)