Skip to content

Commit

Permalink
Merge branch 'master' into release
Browse files Browse the repository at this point in the history
  • Loading branch information
abika committed May 23, 2016
2 parents dce72e3 + 3c713d3 commit 08fc25d
Show file tree
Hide file tree
Showing 49 changed files with 900 additions and 613 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,9 @@ applications on your computer!**
- XEP-0085: Chat state notifications
- XEP-0191: User blocking
- XEP-0066: File transfer over server
- XEP-0231: Image thumbnails for attachments
- XEP-0084: Avatar images
- XEP-0363: HTTP File Upload

## Support us

Expand Down
7 changes: 7 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -66,9 +66,16 @@ repositories {
mavenCentral()
}

task deleteDeps(type: Delete) {
delete fileTree(project.file('dist/lib')) {
include '*.jar'
}
}

task copyDeps(type: Copy) {
from(configurations.runtime)
into project.file('dist/lib')
dependsOn ':deleteDeps'
}

jar {
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/org/kontalk/Kontalk.java
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@
public final class Kontalk {
private static final Logger LOGGER = Logger.getLogger(Kontalk.class.getName());

public static final String VERSION = "3.1";
public static final String VERSION = "3.1.1";

private final Path mAppDir;
private ServerSocket mRunLock = null;
Expand Down
1 change: 1 addition & 0 deletions src/main/java/org/kontalk/client/AvatarSendReceiver.java
Original file line number Diff line number Diff line change
Expand Up @@ -197,6 +197,7 @@ public void processPacket(Stanza packet)
List<? extends ExtensionElement> itemsList = items.getItems();
if (itemsList.isEmpty()) {
LOGGER.warning("no items in itemlist");
return;
}

// there should be only one item
Expand Down
3 changes: 2 additions & 1 deletion src/main/java/org/kontalk/client/Client.java
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,8 @@ public boolean deleteAvatar() {
return mAvatarSendReceiver.delete();
} else {
LOGGER.warning("not supported by server");
return false;
// if not supported there should be no avatar set
return true;
}
}

Expand Down
3 changes: 2 additions & 1 deletion src/main/java/org/kontalk/client/PresenceListener.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

package org.kontalk.client;

import java.util.Optional;
import java.util.logging.Logger;
import org.apache.commons.lang.StringUtils;
import org.jivesoftware.smack.StanzaListener;
Expand Down Expand Up @@ -114,7 +115,7 @@ public void processPacket(Stanza packet) {

mHandler.onPresenceUpdate(jid,
bestPresence.getType(),
bestPresence.getStatus());
Optional.ofNullable(bestPresence.getStatus()));

if (pubKey != null) {
String fp = StringUtils.defaultString(pubKey.getFingerprint()).toLowerCase();
Expand Down
6 changes: 3 additions & 3 deletions src/main/java/org/kontalk/crypto/Coder.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,19 +44,19 @@ private Coder() {
* Encryption status of a message.
* Do not modify, only add! Ordinal used in database.
*/
public static enum Encryption {NOT, ENCRYPTED, DECRYPTED}
public enum Encryption {NOT, ENCRYPTED, DECRYPTED}

/**
* Signing status of a message.
* Do not modify, only add! Ordinal used in database.
*/
public static enum Signing {NOT, SIGNED, VERIFIED, UNKNOWN}
public enum Signing {NOT, SIGNED, VERIFIED, UNKNOWN}

/**
* Errors that can occur during de-/encryption and verification.
* Do not modify, only add! Ordinal used in database.
*/
public static enum Error {
public enum Error {
/** Some unknown error. */
UNKNOWN_ERROR,
/** Own personal key not found. Unused. */
Expand Down
7 changes: 6 additions & 1 deletion src/main/java/org/kontalk/crypto/Decryptor.java
Original file line number Diff line number Diff line change
Expand Up @@ -190,13 +190,18 @@ void decryptAttachment(Path baseDir) {
Path newPath = outPath.resolveSibling(outFile.getName() + "." +
MediaUtils.extensionForMIME(MediaUtils.mimeForFile(outPath)));
try {
outPath = Files.move(outFile.toPath(), newPath);
outPath = Files.move(outPath, newPath);
} catch (IOException ex) {
LOGGER.log(Level.WARNING, "can't rename file", ex);
}

inMessage.setDecryptedAttachment(outPath.toFile().getName());
LOGGER.info("success, decrypted file: "+outPath);

boolean succ = inFile.delete();
if (!succ) {
LOGGER.warning("can't delete obsolete decrypted attachment file");
}
}

/** Decrypt, verify and write input stream data to output stream. */
Expand Down
192 changes: 136 additions & 56 deletions src/main/java/org/kontalk/model/Avatar.java
Original file line number Diff line number Diff line change
Expand Up @@ -37,40 +37,34 @@
*
* @author Alexander Bikadorov {@literal <[email protected]>}
*/
public class Avatar {
public abstract class Avatar {
private static final Logger LOGGER = Logger.getLogger(Avatar.class.getName());

private static final String DIR = "avatars";
protected static final String FORMAT = "png";

static void createStorageDir(Path appDir) {
boolean created = appDir.resolve(DIR).toFile().mkdir();
if (created)
LOGGER.info("created avatar directory");
}

/** SHA1 hash of image data. */
private final String mID;
protected final File mFile;

protected BufferedImage mImage = null;

/** Saved contact avatar. Used when loading from database. */
Avatar(String id) {
this(id, null, null);
}

/** New contact avatar. */
public Avatar(String id, BufferedImage image) {
this(id, null, image);
}

private Avatar(String id, File file, BufferedImage image) {
mID = id;
mFile = file != null ?
file :
Model.appDir().resolve(DIR).resolve(id + "." + FORMAT).toFile();
mFile = file != null ? file : avatarFile(mID);
mImage = image;

if (mImage != null) {
// save new image
boolean succ = MediaUtils.writeImage(image, FORMAT, file);
boolean succ = MediaUtils.writeImage(mImage, FORMAT, mFile);
if (!succ)
LOGGER.warning("can't save avatar image: "+id);
LOGGER.warning("can't save avatar image: "+mID);
}
}

Expand All @@ -80,71 +74,159 @@ private Avatar(File file) {
mID = mImage != null ? id(mImage) : "";
}

private static BufferedImage image(File file) {
return MediaUtils.readImage(file).orElse(null);
}

public String getID() {
return mID;
}

public Optional<BufferedImage> loadImage() {
if (mImage == null)
mImage = image(this.mFile);
mImage = image(mFile);

return Optional.ofNullable(mImage);
}

void delete() {
boolean succ = this.mFile.delete();
if (succ)
LOGGER.warning("could not delete avatar file: "+this.mID);
boolean succ = mFile.delete();
if (!succ)
LOGGER.warning("could not delete avatar file: "+mID);
}

@Override
public boolean equals(Object o) {
if (this == o) return true;

if (!(o instanceof Avatar)) return false;

Avatar oAvatar = (Avatar) o;
protected boolean abstractEquals(Avatar oAvatar) {
return mID.equals(oAvatar.mID);
}

@Override
public int hashCode() {
protected int abstractHashCode() {
int hash = 7;
hash = 59 * hash + Objects.hashCode(this.mID);
return hash;
}

public static class DefaultAvatar extends Avatar {

/** Saved published contact avatar. */
static Optional<DefaultAvatar> load(String id) {
File file = avatarFile(id);
if (!file.isFile()) {
LOGGER.warning("no file: "+file);
return Optional.empty();
}

return Optional.of(new DefaultAvatar(id, file));
}

private DefaultAvatar(String id, File file) {
super(id, file, null);
}

/** New published contact avatar. */
public DefaultAvatar(String id, BufferedImage image) {
super(id, null, image);
}

@Override
public boolean equals(Object o) {
if (this == o)
return true;

if (!(o instanceof DefaultAvatar))
return false;

return this.abstractEquals((DefaultAvatar) o);
}

@Override
public int hashCode() {
int hash = 3 * this.abstractHashCode();
return hash;
}
}

public static class CustomAvatar extends Avatar {

// custom set avatars have always same ID for one contact,
// using this to distinguish them
private final long mLastModified;

static Optional<CustomAvatar> load(int contactID) {
String id = Integer.toString(contactID);
return avatarFile(id).isFile() ?
Optional.of(new CustomAvatar(id, null)) :
Optional.empty();
}

private CustomAvatar(String id, File file) {
super(id, file, null);
mLastModified = mFile.lastModified();
}

/** New custom contact avatar. */
public CustomAvatar(int contactID, BufferedImage image) {
super(Integer.toString(contactID), null, image);
mLastModified = mFile.lastModified();
}

@Override
public boolean equals(Object o) {
if (this == o)
return true;

if (!(o instanceof CustomAvatar))
return false;
CustomAvatar oAvatar = (CustomAvatar) o;

return this.abstractEquals(oAvatar) &&
mLastModified == oAvatar.mLastModified;
}

@Override
public int hashCode() {
int hash = 3 * this.abstractHashCode();
hash = 37 * hash + (int) (this.mLastModified ^ (this.mLastModified >>> 32));
return hash;
}
}

public static class UserAvatar extends Avatar {

private static final int MAX_SIZE = 150;
private static final String USER_FILENAME = "avatar";

private static UserAvatar INSTANCE = null;

private byte[] mImageData = null;

/** Saved user Avatar. */
UserAvatar(Path appDir) {
super(userFile(appDir));
public static Optional<UserAvatar> get() {
if (INSTANCE != null)
return Optional.of(INSTANCE);

File file = userFile();
return file.isFile() ?
Optional.of(INSTANCE = new UserAvatar(file)) :
Optional.empty();
}

/** New user Avatar. ID generated from image. */
private UserAvatar(BufferedImage image, Path appDir) {
super(id(image), userFile(appDir), image);
private UserAvatar(File file) {
super(file);
}

static UserAvatar create(BufferedImage image) {
image = MediaUtils.scale(image, MAX_SIZE, MAX_SIZE);
return new UserAvatar(image, Model.appDir());
public static UserAvatar set(BufferedImage image) {
return INSTANCE = new UserAvatar(MediaUtils.scale(image, MAX_SIZE, MAX_SIZE));
}

@Override
public Optional<BufferedImage> loadImage() {
return mFile.isFile() ?
Optional.ofNullable(image(mFile)) :
Optional.<BufferedImage>empty();
/** New user Avatar. ID generated from image. */
private UserAvatar(BufferedImage image) {
super(id(image), userFile(), image);
}

public static void remove() {
if (INSTANCE == null) {
LOGGER.warning("not set");
return;
}

INSTANCE.delete();
INSTANCE = null;
}

public Optional<byte[]> imageData() {
Expand All @@ -154,19 +236,13 @@ public Optional<byte[]> imageData() {
return Optional.ofNullable(mImageData);
}

private static File userFile(Path appDir) {
return appDir.resolve(USER_FILENAME + "." + FORMAT).toFile();
private static File userFile() {
return Model.appDir().resolve(USER_FILENAME + "." + FORMAT).toFile();
}
}

static void createStorageDir(Path appDir) {
boolean created = appDir.resolve(DIR).toFile().mkdir();
if (created)
LOGGER.info("created avatar directory");
}

static Avatar deleted() {
return new Avatar("");
private static File avatarFile(String id){
return Model.appDir().resolve(DIR).resolve(id + "." + FORMAT).toFile();
}

private static String id(BufferedImage image) {
Expand All @@ -184,5 +260,9 @@ private static byte[] imageData(BufferedImage image) {
}
return out.toByteArray();
}

private static BufferedImage image(File file) {
return MediaUtils.readImage(file).orElse(null);
}
}

Loading

0 comments on commit 08fc25d

Please sign in to comment.