Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 0 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,6 @@ The easiest way to get this software is using [Maven](http://maven.apache.org/).

Otherwise, you can [download the jar file](https://github.com/sjamesr/jfreesane/releases/tag/jfreesane-0.98)
and put it in your project's CLASSPATH.
JFreeSane also depends on [Google Guava](http://code.google.com/p/guava-libraries/), an
excellent collection of Java libraries that you should be using in your project anyway.
You will need to download the Guava JAR file and put that in your classpath as well.

Once JFreeSane is available on your classpath, please read on for a tutorial on how to use it.

Expand Down
1 change: 0 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ repositories {
}

dependencies {
compile 'com.google.guava:guava:23.0'
testCompile 'junit:junit:4.12'
testCompile 'com.google.truth:truth:0.24'

Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,8 @@
package au.com.southsky.jfreesane;

import com.google.common.io.CharSource;

import javax.annotation.concurrent.ThreadSafe;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.Reader;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
Expand All @@ -21,8 +16,10 @@
* Represents the authentication configuration used by SANE clients. The SANE utilities like
* {@code scanimage} will read the {@code ~/.sane/pass} directory (if it exists), this class
* provides an implementation of that behavior.
*
* <p>
* Threadsafe.
*/
@ThreadSafe
public class SaneClientAuthentication extends SanePasswordProvider {
private static final Logger logger = Logger.getLogger(SaneClientAuthentication.class.getName());

Expand All @@ -35,41 +32,49 @@ public class SaneClientAuthentication extends SanePasswordProvider {
private final List<String> usernames;
private final List<String> passwords;

private final CharSource configurationSource;
private final Reader reader;
private final Path path;
private AtomicBoolean isInitialized = new AtomicBoolean(false);

public SaneClientAuthentication() {
this(DEFAULT_CONFIGURATION_PATH);
this(DEFAULT_CONFIGURATION_PATH, null);
}

public SaneClientAuthentication(final String path) {
this(Paths.get(path));
public SaneClientAuthentication(String path) {
this(Paths.get(path), null);
}

public SaneClientAuthentication(final Path path) {
this(
new CharSource() {
@Override
public Reader openStream() throws IOException {
return new InputStreamReader(Files.newInputStream(path), StandardCharsets.US_ASCII);
}
});
public SaneClientAuthentication(Path path) {
this(path, null);
}

/**
* Returns a new {@code SaneClientAuthentication} whose configuration is represented by the
* characters supplied by the given {@link CharSource}.
* characters supplied by the given {@link Reader}.
*/
public SaneClientAuthentication(CharSource configurationSource) {
this.configurationSource = configurationSource;
public SaneClientAuthentication(Reader reader) {
this(null, reader);
}

private SaneClientAuthentication(Path path, Reader reader) {
this.path = path;
this.reader = reader;
this.resources = new ArrayList<>(4);
this.usernames = new ArrayList<>(4);
this.passwords = new ArrayList<>(4);
}

private BufferedReader openConfig() throws IOException {
if (path != null) {
return Files.newBufferedReader(path);
} else {
return new BufferedReader(reader);
}
}

private synchronized void initializeIfRequired() {
if (isInitialized.compareAndSet(false, true)) {
try (BufferedReader r = configurationSource.openBufferedStream()) {
try (BufferedReader r = openConfig()) {
String line;

int lineNumber = 0;
Expand Down
41 changes: 29 additions & 12 deletions src/main/java/au/com/southsky/jfreesane/SaneImage.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,5 @@
package au.com.southsky.jfreesane;

import com.google.common.collect.Lists;
import com.google.common.collect.Ordering;

import java.awt.Point;
import java.awt.Transparency;
import java.awt.color.ColorSpace;
Expand All @@ -15,10 +12,13 @@
import java.awt.image.IndexColorModel;
import java.awt.image.Raster;
import java.awt.image.WritableRaster;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
* Represents a SANE image, which are composed of one or more {@link Frame frames}.
Expand All @@ -36,15 +36,32 @@ final class SaneImage {
private final int height;
private final int bytesPerLine;

private static int frameSortOrder(Frame frame) {
switch (frame.getType()) {
case RED:
return 0;
case GREEN:
return 1;
case BLUE:
return 2;
case RGB:
return 3;
case GRAY:
return 4;
default:
throw new IllegalArgumentException("unknown frame type " + frame.getType());
}
}

private SaneImage(
List<Frame> frames, int depthPerPixel, int width, int height, int bytesPerLine) {
// this ensures that in the 3-frame situation, they are always
// arranged in the following order: red, green, blue
this.frames =
Ordering.explicit(
FrameType.RED, FrameType.GREEN, FrameType.BLUE, FrameType.RGB, FrameType.GRAY)
.onResultOf(Frame::getType)
.immutableSortedCopy(frames);
frames
.stream()
.sorted(Comparator.comparing(SaneImage::frameSortOrder))
.collect(Collectors.toList());
this.depthPerPixel = depthPerPixel;
this.width = width;
this.height = height;
Expand Down Expand Up @@ -214,13 +231,13 @@ private DataBuffer asDataBuffer() {
}

public static class Builder {
private final List<Frame> frames = Lists.newArrayList();
private final List<Frame> frames = new ArrayList<>();
private final Set<FrameType> frameTypes = EnumSet.noneOf(FrameType.class);

private final WriteOnce<Integer> depthPerPixel = new WriteOnce<Integer>();
private final WriteOnce<Integer> width = new WriteOnce<Integer>();
private final WriteOnce<Integer> height = new WriteOnce<Integer>();
private final WriteOnce<Integer> bytesPerLine = new WriteOnce<Integer>();
private final WriteOnce<Integer> depthPerPixel = new WriteOnce<>();
private final WriteOnce<Integer> width = new WriteOnce<>();
private final WriteOnce<Integer> height = new WriteOnce<>();
private final WriteOnce<Integer> bytesPerLine = new WriteOnce<>();

public void addFrame(Frame frame) {
Preconditions.checkArgument(
Expand Down
20 changes: 13 additions & 7 deletions src/main/java/au/com/southsky/jfreesane/SaneSession.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package au.com.southsky.jfreesane;

import com.google.common.base.Splitter;
import java.awt.image.BufferedImage;
import java.io.BufferedInputStream;
import java.io.Closeable;
Expand Down Expand Up @@ -409,7 +408,8 @@ boolean authorize(String resource) throws IOException {
outputStream.write(SaneRpcCode.SANE_NET_AUTHORIZE);
outputStream.write(resource);
outputStream.write(passwordProvider.getUsername(resource));
writePassword(resource, passwordProvider.getPassword(resource));
// TODO(sjamesr): resource is not currently used, see writePassword.
writePassword(/* resource, */ passwordProvider.getPassword(resource));
outputStream.flush();

// Read dummy reply and discard (according to the spec, it is unused).
Expand All @@ -422,12 +422,17 @@ boolean authorize(String resource) throws IOException {

/**
* Write password to outputstream depending on resource provided by saned.
*
* @param resource as provided by sane in authorization request
* @param password
* @throws IOException
*/
private void writePassword(String resource, String password) throws IOException {
private void writePassword(/* String resource ,*/ String password) throws IOException {
outputStream.write(password);

// The code below always prints passwords in the clear, because Splitter.on takes
// a separator string, not a regular expression. We can't fix it now due to a bug
// in old versions of saned, which Linux distributions like Ubuntu still ship.
// TODO(sjamesr): revive this code when Ubuntu gets a new sane-backends release,
// see https://bugs.launchpad.net/ubuntu/+source/sane-backends/+bug/1858051.
// TODO(sjamesr): when reviving, remove Guava dependency.
/*
List<String> resourceParts = Splitter.on("\\$MD5\\$").splitToList(resource);
if (resourceParts.size() == 1) {
// Write in clean
Expand All @@ -436,6 +441,7 @@ private void writePassword(String resource, String password) throws IOException
outputStream.write(
"$MD5$" + SanePasswordEncoder.derivePassword(resourceParts.get(1), password));
}
*/
}

SaneOutputStream getOutputStream() {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
package au.com.southsky.jfreesane;

import au.com.southsky.jfreesane.SaneClientAuthentication.ClientCredential;
import com.google.common.io.CharSource;
import com.google.common.truth.Truth;
import org.junit.Test;

Expand Down Expand Up @@ -65,18 +64,13 @@ public void testCanAuthenticateTrueWithoutMD5() {
Truth.assertThat(sca.canAuthenticate(rcString)).isTrue();
}

private CharSource getTestConfigurationSource() {
private Reader getTestConfigurationSource() {
final StringBuilder users = new StringBuilder();
users.append("sane-user:password:pixma\n");
users.append("other-user:strongPassword:net\n");
users.append("user::mustek\n");
users.append("user1::bad-backend\n");
users.append("user2::bad-backend\n");
return new CharSource() {
@Override
public Reader openStream() {
return new StringReader(users.toString());
}
};
return new StringReader(users.toString());
}
}