From: David ‘Bombe’ Roden Date: Sun, 9 Jun 2013 08:25:19 +0000 (+0200) Subject: Merge remote-tracking branch 'hernic/patch-1' into next X-Git-Tag: 0.8.5^2~5 X-Git-Url: https://git.pterodactylus.net/?a=commitdiff_plain;h=77804f0dda317541f8451a0649785e7cb192d696;hp=78df4874610a2e72eff60815bda1d944f206729b;p=Sone.git Merge remote-tracking branch 'hernic/patch-1' into next --- diff --git a/pom.xml b/pom.xml index 3b894ae..0e2cc60 100644 --- a/pom.xml +++ b/pom.xml @@ -7,7 +7,7 @@ net.pterodactylus utils - 0.12.2 + 0.12.3-SNAPSHOT junit diff --git a/src/main/java/net/pterodactylus/sone/core/Core.java b/src/main/java/net/pterodactylus/sone/core/Core.java index c391582..5843b30 100644 --- a/src/main/java/net/pterodactylus/sone/core/Core.java +++ b/src/main/java/net/pterodactylus/sone/core/Core.java @@ -59,6 +59,7 @@ import net.pterodactylus.util.config.ConfigurationException; import net.pterodactylus.util.logging.Logging; import net.pterodactylus.util.number.Numbers; import net.pterodactylus.util.service.AbstractService; +import net.pterodactylus.util.thread.NamedThreadFactory; import net.pterodactylus.util.thread.Ticker; import net.pterodactylus.util.validation.EqualityValidator; import net.pterodactylus.util.validation.IntegerRangeValidator; @@ -108,7 +109,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis private final ImageInserter imageInserter; /** Sone downloader thread-pool. */ - private final ExecutorService soneDownloaders = Executors.newFixedThreadPool(10); + private final ExecutorService soneDownloaders = Executors.newFixedThreadPool(10, new NamedThreadFactory("Sone Downloader %2$d")); /** The update checker. */ private final UpdateChecker updateChecker; @@ -1160,8 +1161,8 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis if (!storedPosts.contains(post)) { if (post.getTime() < getSoneFollowingTime(sone)) { knownPosts.add(post.getId()); + post.setKnown(true); } else if (!knownPosts.contains(post.getId())) { - sone.setKnown(false); coreListenerManager.fireNewPostFound(post); } } @@ -1185,8 +1186,8 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis if (!storedReplies.contains(reply)) { if (reply.getTime() < getSoneFollowingTime(sone)) { knownReplies.add(reply.getId()); + reply.setKnown(true); } else if (!knownReplies.contains(reply.getId())) { - reply.setKnown(false); coreListenerManager.fireNewReplyFound(reply); } } @@ -1579,6 +1580,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * @return The created post */ public Post createPost(Sone sone, Sone recipient, long time, String text) { + Validation.begin().isNotNull("Text", text).check().isGreater("Text Length", text.length(), 0).check(); if (!isLocalSone(sone)) { logger.log(Level.FINE, String.format("Tried to create post for non-local Sone: %s", sone)); return null; @@ -1719,6 +1721,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis * @return The created reply */ public PostReply createReply(Sone sone, Post post, long time, String text) { + Validation.begin().isNotNull("Text", text).check().isGreater("Text Length", text.trim().length(), 0).check(); if (!isLocalSone(sone)) { logger.log(Level.FINE, String.format("Tried to create reply for non-local Sone: %s", sone)); return null; @@ -2412,7 +2415,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis */ @Override public void identityUpdated(OwnIdentity ownIdentity, final Identity identity) { - new Thread(new Runnable() { + soneDownloaders.execute(new Runnable() { @Override @SuppressWarnings("synthetic-access") @@ -2423,7 +2426,7 @@ public class Core extends AbstractService implements IdentityListener, UpdateLis soneDownloader.addSone(sone); soneDownloader.fetchSone(sone); } - }).start(); + }); } /** diff --git a/src/main/java/net/pterodactylus/sone/fcp/FcpInterface.java b/src/main/java/net/pterodactylus/sone/fcp/FcpInterface.java index b3222be..c1ed953 100644 --- a/src/main/java/net/pterodactylus/sone/fcp/FcpInterface.java +++ b/src/main/java/net/pterodactylus/sone/fcp/FcpInterface.java @@ -27,7 +27,6 @@ import net.pterodactylus.sone.core.Core; import net.pterodactylus.sone.freenet.fcp.Command.AccessType; import net.pterodactylus.sone.freenet.fcp.Command.ErrorResponse; import net.pterodactylus.sone.freenet.fcp.Command.Response; -import net.pterodactylus.sone.freenet.fcp.FcpException; import net.pterodactylus.util.logging.Logging; import net.pterodactylus.util.validation.Validation; import freenet.pluginmanager.FredPluginFCP; @@ -172,9 +171,9 @@ public class FcpInterface { try { Response response = command.execute(parameters, data, AccessType.values()[accessType]); sendReply(pluginReplySender, identifier, response); - } catch (FcpException fe1) { + } catch (Exception e1) { logger.log(Level.WARNING, "Could not process FCP command “%s”.", command); - sendReply(pluginReplySender, identifier, new ErrorResponse("Error executing command: " + fe1.getMessage())); + sendReply(pluginReplySender, identifier, new ErrorResponse("Error executing command: " + e1.getMessage())); } } catch (PluginNotFoundException pnfe1) { logger.log(Level.WARNING, "Could not find destination plugin: " + pluginReplySender); diff --git a/src/main/java/net/pterodactylus/sone/template/ParserFilter.java b/src/main/java/net/pterodactylus/sone/template/ParserFilter.java index 75eff8b..b8af831 100644 --- a/src/main/java/net/pterodactylus/sone/template/ParserFilter.java +++ b/src/main/java/net/pterodactylus/sone/template/ParserFilter.java @@ -20,7 +20,9 @@ package net.pterodactylus.sone.template; import java.io.IOException; import java.io.StringReader; import java.io.StringWriter; +import java.io.UnsupportedEncodingException; import java.io.Writer; +import java.net.URLEncoder; import java.util.ArrayList; import java.util.List; import java.util.Map; @@ -213,7 +215,12 @@ public class ParserFilter implements Filter { * The part to render */ private void render(Writer writer, LinkPart linkPart) { - renderLink(writer, "/?_CHECKED_HTTP_=" + linkPart.getLink(), linkPart.getText(), linkPart.getTitle(), "internet"); + try { + renderLink(writer, "/external-link/?_CHECKED_HTTP_=" + URLEncoder.encode(linkPart.getLink(), "UTF-8"), linkPart.getText(), linkPart.getTitle(), "internet"); + } catch (UnsupportedEncodingException uee1) { + /* not possible for UTF-8. */ + throw new RuntimeException("The JVM does not support UTF-8 encoding!", uee1); + } } /** diff --git a/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java b/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java index baa3dc9..b3593ec 100644 --- a/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java +++ b/src/main/java/net/pterodactylus/sone/text/SoneTextParser.java @@ -55,28 +55,50 @@ public class SoneTextParser implements Parser { private enum LinkType { /** Link is a KSK. */ - KSK, + KSK("KSK@"), /** Link is a CHK. */ - CHK, + CHK("CHK@"), /** Link is an SSK. */ - SSK, + SSK("SSK@"), /** Link is a USK. */ - USK, + USK("USK@"), /** Link is HTTP. */ - HTTP, + HTTP("http://"), /** Link is HTTPS. */ - HTTPS, + HTTPS("https://"), /** Link is a Sone. */ - SONE, + SONE("sone://"), /** Link is a post. */ - POST, + POST("post://"); + + /** The scheme identifying this link type. */ + private final String scheme; + + /** + * Creates a new link type identified by the given scheme. + * + * @param scheme + * The scheme of the link type + */ + private LinkType(String scheme) { + this.scheme = scheme; + } + + /** + * Returns the scheme of this link type. + * + * @return The scheme of this link type + */ + public String getScheme() { + return scheme; + } } @@ -200,6 +222,20 @@ public class SoneTextParser implements Parser { } lineComplete = false; + Matcher matcher = whitespacePattern.matcher(line); + int nextSpace = matcher.find(0) ? matcher.start() : line.length(); + String link = line.substring(0, nextSpace); + String name = link; + logger.log(Level.FINER, String.format("Found link: %s", link)); + logger.log(Level.FINEST, String.format("CHK: %d, SSK: %d, USK: %d", nextChk, nextSsk, nextUsk)); + + /* if there is no text after the scheme, it’s not a link! */ + if (link.equals(linkType.getScheme())) { + parts.add(new PlainTextPart(linkType.getScheme())); + line = line.substring(linkType.getScheme().length()); + continue; + } + if (linkType == LinkType.SONE) { if (line.length() >= (7 + 43)) { String soneId = line.substring(7, 50); @@ -235,12 +271,6 @@ public class SoneTextParser implements Parser { } continue; } - Matcher matcher = whitespacePattern.matcher(line); - int nextSpace = matcher.find(0) ? matcher.start() : line.length(); - String link = line.substring(0, nextSpace); - String name = link; - logger.log(Level.FINER, String.format("Found link: %s", link)); - logger.log(Level.FINEST, String.format("CHK: %d, SSK: %d, USK: %d", nextChk, nextSsk, nextUsk)); if ((linkType == LinkType.KSK) || (linkType == LinkType.CHK) || (linkType == LinkType.SSK) || (linkType == LinkType.USK)) { FreenetURI uri; diff --git a/src/main/java/net/pterodactylus/sone/web/EditProfilePage.java b/src/main/java/net/pterodactylus/sone/web/EditProfilePage.java index a1b759b..51a72b8 100644 --- a/src/main/java/net/pterodactylus/sone/web/EditProfilePage.java +++ b/src/main/java/net/pterodactylus/sone/web/EditProfilePage.java @@ -77,7 +77,7 @@ public class EditProfilePage extends SoneTemplatePage { birthDay = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("birth-day", 256).trim()); birthMonth = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("birth-month", 256).trim()); birthYear = Numbers.safeParseInteger(request.getHttpRequest().getPartAsStringFailsafe("birth-year", 256).trim()); - avatarId = request.getHttpRequest().getPartAsStringFailsafe("avatar-id", 36); + avatarId = request.getHttpRequest().getPartAsStringFailsafe("avatarId", 36); profile.setFirstName(firstName.length() > 0 ? firstName : null); profile.setMiddleName(middleName.length() > 0 ? middleName : null); profile.setLastName(lastName.length() > 0 ? lastName : null); @@ -139,7 +139,7 @@ public class EditProfilePage extends SoneTemplatePage { templateContext.set("birthDay", birthDay); templateContext.set("birthMonth", birthMonth); templateContext.set("birthYear", birthYear); - templateContext.set("avatar-id", avatarId); + templateContext.set("avatarId", avatarId); templateContext.set("fields", fields); } diff --git a/src/main/java/net/pterodactylus/sone/web/ajax/JsonPage.java b/src/main/java/net/pterodactylus/sone/web/ajax/JsonPage.java index 22eeec8..910abd5 100644 --- a/src/main/java/net/pterodactylus/sone/web/ajax/JsonPage.java +++ b/src/main/java/net/pterodactylus/sone/web/ajax/JsonPage.java @@ -17,13 +17,17 @@ package net.pterodactylus.sone.web.ajax; +import java.io.ByteArrayOutputStream; import java.io.IOException; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; import java.net.URI; import net.pterodactylus.sone.data.Sone; import net.pterodactylus.sone.web.WebInterface; import net.pterodactylus.sone.web.page.FreenetPage; import net.pterodactylus.sone.web.page.FreenetRequest; +import net.pterodactylus.util.io.Closer; import net.pterodactylus.util.json.JsonObject; import net.pterodactylus.util.json.JsonUtils; import net.pterodactylus.util.web.Page; @@ -218,8 +222,12 @@ public abstract class JsonPage implements FreenetPage { return response.setStatusCode(403).setStatusText("Forbidden").setContentType("application/json").write(JsonUtils.format(new JsonObject().put("success", false).put("error", "auth-required"))); } } - JsonObject jsonObject = createJsonObject(request); - return response.setStatusCode(200).setStatusText("OK").setContentType("application/json").write(JsonUtils.format(jsonObject)); + try { + JsonObject jsonObject = createJsonObject(request); + return response.setStatusCode(200).setStatusText("OK").setContentType("application/json").write(JsonUtils.format(jsonObject)); + } catch (Exception e1) { + return response.setStatusCode(500).setStatusText(e1.getMessage()).setContentType("text/plain").write(dumpStackTrace(e1)); + } } /** @@ -230,4 +238,36 @@ public abstract class JsonPage implements FreenetPage { return false; } + // + // PRIVATE METHODS + // + + /** + * Returns a byte array containing the stack trace of the given throwable. + * + * @param t + * The throwable whose stack trace to dump into an array + * @return The array with the stack trace, or an empty array if the stack + * trace could not be dumped + */ + private static byte[] dumpStackTrace(Throwable t) { + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream(); + OutputStreamWriter writer = null; + PrintWriter printWriter = null; + try { + writer = new OutputStreamWriter(byteArrayOutputStream, "uTF-8"); + printWriter = new PrintWriter(writer); + t.printStackTrace(printWriter); + byteArrayOutputStream.flush(); + return byteArrayOutputStream.toByteArray(); + } catch (IOException ioe1) { + /* quite not possible. */ + return new byte[0]; + } finally { + Closer.close(printWriter); + Closer.close(writer); + Closer.close(byteArrayOutputStream); + } + } + } diff --git a/src/main/resources/i18n/sone.fr.properties b/src/main/resources/i18n/sone.fr.properties index 29a93ee..047a812 100644 --- a/src/main/resources/i18n/sone.fr.properties +++ b/src/main/resources/i18n/sone.fr.properties @@ -376,18 +376,18 @@ View.Time.InTheFuture=dans le futur View.Time.AFewSecondsAgo=au cours des dernières secondes passées View.Time.HalfAMinuteAgo=au cours des 30 dernières secondes View.Time.AMinuteAgo=au cours de la dernière minute -View.Time.XMinutesAgo=${min} il y a quelques minutes +View.Time.XMinutesAgo=il y a environs {min} minutes View.Time.HalfAnHourAgo=au cours de la dernière demi heure View.Time.AnHourAgo=il y a environ une heure -View.Time.XHoursAgo=${heure} au cours des dernières heures +View.Time.XHoursAgo=Il y a environ ${hour} heures View.Time.ADayAgo=il y a environ un jour -View.Time.XDaysAgo=${jour} il y a quelques jours +View.Time.XDaysAgo=il y a plus ou moins ${day} jours View.Time.AWeekAgo=il y a environ une semaine -View.Time.XWeeksAgo=${semaine} au cours des dernières semaines +View.Time.XWeeksAgo=au cours des dernières ${week}semaines View.Time.AMonthAgo=au cours du dernier mois -View.Time.XMonthsAgo=${mois} au cours des derniers mois +View.Time.XMonthsAgo=au cours des derniers ${month} mois View.Time.AYearAgo=au cours de la dernière année -View.Time.XYearsAgo=${année} au cours des dernières années +View.Time.XYearsAgo=au cours des dernières ${year} années WebInterface.DefaultText.StatusUpdate=Exprimez-vous WebInterface.DefaultText.Message=Écrire un message... diff --git a/src/main/resources/templates/editProfile.html b/src/main/resources/templates/editProfile.html index f0b8b56..ddc08a9 100644 --- a/src/main/resources/templates/editProfile.html +++ b/src/main/resources/templates/editProfile.html @@ -186,12 +186,12 @@
  • - checked="checked"<%/if>/> + checked="checked"<%/if>/> <%= Page.EditProfile.Avatar.Delete|l10n|html>
  • <%foreach currentSone.allImages image>
  • - checked="checked"<%/if>/> + checked="checked"<%/if>/>
    <% image|image-link max-width==48 max-height==48 mode==enlarge title=image.title>
  • <%/foreach> diff --git a/src/test/java/net/pterodactylus/sone/text/SoneTextParserTest.java b/src/test/java/net/pterodactylus/sone/text/SoneTextParserTest.java index 7baceaa..e1857a0 100644 --- a/src/test/java/net/pterodactylus/sone/text/SoneTextParserTest.java +++ b/src/test/java/net/pterodactylus/sone/text/SoneTextParserTest.java @@ -19,6 +19,7 @@ package net.pterodactylus.sone.text; import java.io.IOException; import java.io.StringReader; +import java.util.Arrays; import junit.framework.TestCase; import net.pterodactylus.sone.core.SoneProvider; @@ -106,6 +107,24 @@ public class SoneTextParserTest extends TestCase { assertEquals("Part Text", "Some text.\n\nLink to [Sone|DAxKQzS48mtaQc7sUVHIgx3fnWZPQBz0EueBreUVWrU] and stuff.", convertText(parts, PlainTextPart.class, SonePart.class)); } + /** + * Test for a bug discovered in Sone 0.8.4 where a plain “http://” would be + * parsed into a link. + * + * @throws IOException + * if an I/O error occurs + */ + @SuppressWarnings({ "synthetic-access", "static-method" }) + public void testEmpyHttpLinks() throws IOException { + SoneTextParser soneTextParser = new SoneTextParser(new TestSoneProvider(), null); + Iterable parts; + + /* check empty http links. */ + parts = soneTextParser.parse(null, new StringReader("Some text. Empty link: http:// – nice!")); + assertNotNull("Parts", parts); + assertEquals("Part Text", "Some text. Empty link: http:// – nice!", convertText(parts, PlainTextPart.class)); + } + // // PRIVATE METHODS // @@ -133,7 +152,7 @@ public class SoneTextParserTest extends TestCase { } } if (!classValid) { - assertEquals("Part’s Class", null, part.getClass()); + fail("Part’s Class (" + part.getClass() + ") is not one of " + Arrays.toString(validClasses)); } if (part instanceof PlainTextPart) { text.append(((PlainTextPart) part).getText());