<modelVersion>4.0.0</modelVersion>
<groupId>net.pterodactylus</groupId>
<artifactId>sone</artifactId>
- <version>0.3.6-2</version>
+ <version>0.3.6-3</version>
<dependencies>
<dependency>
<groupId>net.pterodactylus</groupId>
/** The logger. */
private static final Logger logger = Logging.getLogger(SoneDownloader.class);
+ /** The maximum protocol version. */
+ private static final int MAX_PROTOCOL_VERSION = 0;
+
/** The core. */
private final Core core;
return null;
}
+ Integer protocolVersion = null;
+ String soneProtocolVersion = soneXml.getValue("protocol-version", null);
+ if (soneProtocolVersion != null) {
+ protocolVersion = Numbers.safeParseInteger(soneProtocolVersion);
+ }
+ if (protocolVersion == null) {
+ logger.log(Level.INFO, "No protocol version found, assuming 0.");
+ protocolVersion = 0;
+ }
+
+ if (protocolVersion < 0) {
+ logger.log(Level.WARNING, "Invalid protocol version: " + protocolVersion + "! Not parsing Sone.");
+ return null;
+ }
+
+ /* check for valid versions. */
+ if (protocolVersion > MAX_PROTOCOL_VERSION) {
+ logger.log(Level.WARNING, "Unknown protocol version: " + protocolVersion + "! Not parsing Sone.");
+ return null;
+ }
+
String soneTime = soneXml.getValue("time", null);
if (soneTime == null) {
/* TODO - mark Sone as bad. */
}
/** The version. */
- public static final Version VERSION = new Version(0, 3, 6, 2);
+ public static final Version VERSION = new Version(0, 3, 6, 3);
/** The logger. */
private static final Logger logger = Logging.getLogger(SonePlugin.class);
public ListNotification(String id, String key, Template template) {
super(id, template);
template.set(key, elements);
+ template.set("notification", this);
}
//
return null;
}
try {
- return linkParser.parse(new StringReader(text));
+ synchronized (linkParser) {
+ linkParser.setPostingSone(post.getSone());
+ return linkParser.parse(new StringReader(text));
+ }
} catch (IOException ioe1) {
/* ignore. */
}
} else if (member.equals("text")) {
String text = reply.getText();
try {
- return linkParser.parse(new StringReader(text));
+ synchronized (linkParser) {
+ linkParser.setPostingSone(reply.getSone());
+ return linkParser.parse(new StringReader(text));
+ }
} catch (IOException ioe1) {
/* ignore. */
}
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.util.logging.Logging;
import net.pterodactylus.util.template.TemplateFactory;
private enum LinkType {
/** Link is a KSK. */
- KSK(true),
+ KSK,
/** Link is a CHK. */
- CHK(true),
+ CHK,
/** Link is an SSK. */
- SSK(true),
+ SSK,
/** Link is a USK. */
- USK(true),
+ USK,
/** Link is HTTP. */
- HTTP(false),
+ HTTP,
/** Link is HTTPS. */
- HTTPS(false);
-
- /** Whether this link type links to freenet. */
- private final boolean anonymous;
-
- /**
- * Creates a new link type.
- *
- * @param anonymous
- * {@code true} if this link type links to freenet,
- * {@code false} otherwise
- */
- private LinkType(boolean anonymous) {
- this.anonymous = anonymous;
- }
-
- /**
- * Returns whether this link type links anonymously to within freenet.
- *
- * @return {@code true} if this link type links to within freenet,
- * {@code false} otherwise
- */
- public boolean isAnonymous() {
- return anonymous;
- }
+ HTTPS;
}
/** The template factory. */
private final TemplateFactory templateFactory;
+ /** The Sone that posted the currently parsed text. */
+ private Sone postingSone;
+
/**
* Creates a new freenet link parser.
*
}
//
+ // ACCESSORS
+ //
+
+ /**
+ * Sets the Sone that posted the text that will be parsed in the next call
+ * to {@link #parse(Reader)}. You need to synchronize calling this method
+ * and {@link #parse(Reader)}!
+ *
+ * @param sone
+ * The Sone that posted the text
+ */
+ public void setPostingSone(Sone sone) {
+ postingSone = sone;
+ }
+
+ //
// PART METHODS
//
* {@inheritDoc}
*/
@Override
- @SuppressWarnings("null")
public Part parse(Reader source) throws IOException {
PartContainer parts = new PartContainer();
BufferedReader bufferedReader = (source instanceof BufferedReader) ? (BufferedReader) source : new BufferedReader(source);
String name = link;
logger.log(Level.FINER, "Found link: %s", link);
logger.log(Level.FINEST, "Next: %d, CHK: %d, SSK: %d, USK: %d", new Object[] { next, nextChk, nextSsk, nextUsk });
- if (((linkType == LinkType.CHK) || (linkType == LinkType.SSK) || (linkType == LinkType.USK)) && (link.length() > 98) && (link.charAt(47) == ',') && (link.charAt(91) == ',') && (link.charAt(99) == '/')) {
- name = link.substring(0, 47) + "…" + link.substring(99);
+ if (linkType == LinkType.KSK) {
+ name = link.substring(4);
+ } else if ((linkType == LinkType.CHK) || (linkType == LinkType.SSK) || (linkType == LinkType.USK)) {
+ if (name.indexOf('/') > -1) {
+ if (!name.endsWith("/")) {
+ name = name.substring(name.lastIndexOf('/') + 1);
+ } else {
+ if (name.indexOf('/') != name.lastIndexOf('/')) {
+ name = name.substring(name.lastIndexOf('/', name.lastIndexOf('/') - 1));
+ } else {
+ /* shorten to 5 chars. */
+ name = name.substring(4, 9);
+ }
+ }
+ }
+ if (name.indexOf('?') > -1) {
+ name = name.substring(0, name.indexOf('?'));
+ }
+ boolean fromPostingSone = false;
+ if ((linkType == LinkType.SSK) || (linkType == LinkType.USK)) {
+ fromPostingSone = link.substring(4, 47).equals(postingSone.getId());
+ }
+ parts.add(fromPostingSone ? createTrustedFreenetLinkPart(link, name) : createFreenetLinkPart(link, name));
} else if ((linkType == LinkType.HTTP) || (linkType == LinkType.HTTPS)) {
name = link.substring(linkType == LinkType.HTTP ? 7 : 8);
int firstSlash = name.indexOf('/');
if (((name.indexOf('/') > -1) && (name.indexOf('.') < name.lastIndexOf('.', name.indexOf('/'))) || ((name.indexOf('/') == -1) && (name.indexOf('.') < name.lastIndexOf('.')))) && name.startsWith("www.")) {
name = name.substring(4);
}
+ if (name.indexOf('?') > -1) {
+ name = name.substring(0, name.indexOf('?'));
+ }
link = "?_CHECKED_HTTP_=" + link;
+ parts.add(createInternetLinkPart(link, name));
}
- parts.add(createLinkPart(linkType.isAnonymous(), link, name));
line = line.substring(nextSpace);
} else {
parts.add(createPlainTextPart(line.substring(0, next + 4)));
}
/**
- * Creates a new link part based on a template.
+ * Creates a new part based on a template that links to a site within the
+ * normal internet.
+ *
+ * @param link
+ * The target of the link
+ * @param name
+ * The name of the link
+ * @return The part that displays the link
+ */
+ private Part createInternetLinkPart(String link, String name) {
+ return new TemplatePart(templateFactory.createTemplate(new StringReader("<a class=\"internet\" href=\"/<% link|html>\" title=\"<% link|html>\"><% name|html></a>"))).set("link", link).set("name", name);
+ }
+
+ /**
+ * Creates a new part based on a template that links to a site within
+ * freenet.
+ *
+ * @param link
+ * The target of the link
+ * @param name
+ * The name of the link
+ * @return The part that displays the link
+ */
+ private Part createFreenetLinkPart(String link, String name) {
+ return new TemplatePart(templateFactory.createTemplate(new StringReader("<a class=\"freenet\" href=\"/<% link|html>\" title=\"<% link|html>\"><% name|html></a>"))).set("link", link).set("name", name);
+ }
+
+ /**
+ * Creates a new part based on a template that links to a page in the
+ * poster’s keyspace.
*
- * @param anonymous
- * {@code true} if this link points to within freenet,
- * {@code false} if it points to the internet
* @param link
* The target of the link
* @param name
* The name of the link
* @return The part that displays the link
*/
- private Part createLinkPart(boolean anonymous, String link, String name) {
- return new TemplatePart(templateFactory.createTemplate(new StringReader("<a <%if !anonymous>class=\"internet\" <%/if>href=\"<% link|html>\"><% name|html></a>"))).set("anonymous", anonymous).set("link", "/" + link).set("name", name);
+ private Part createTrustedFreenetLinkPart(String link, String name) {
+ return new TemplatePart(templateFactory.createTemplate(new StringReader("<a class=\"freenet-trusted\" href=\"/<% link|html>\" title=\"<% link|html>\"><% name|html></a>"))).set("link", link).set("name", name);
}
}
@Override
protected void processTemplate(Request request, Template template) throws RedirectException {
super.processTemplate(request, template);
- template.set("currentSone", getCurrentSone(request.getToadletContext()));
+ template.set("currentSone", getCurrentSone(request.getToadletContext(), false));
template.set("request", request);
}
*/
@Override
protected String getRedirectTarget(Page.Request request) {
- if (requiresLogin() && (getCurrentSone(request.getToadletContext()) == null)) {
+ if (requiresLogin() && (getCurrentSone(request.getToadletContext(), false) == null)) {
return "login.html";
}
return null;
protected JsonObject createJsonObject(Request request) {
/* load Sones. */
boolean loadAllSones = Boolean.parseBoolean(request.getHttpRequest().getParam("loadAllSones", "true"));
- Set<Sone> sones = new HashSet<Sone>(Collections.singleton(getCurrentSone(request.getToadletContext())));
+ Set<Sone> sones = new HashSet<Sone>(Collections.singleton(getCurrentSone(request.getToadletContext(), false)));
if (loadAllSones) {
sones.addAll(webInterface.getCore().getSones());
}
JsonArray jsonSones = new JsonArray();
for (Sone sone : sones) {
+ if (sone == null) {
+ continue;
+ }
JsonObject jsonSone = createJsonSone(sone);
jsonSones.add(jsonSone);
}
package net.pterodactylus.sone.web.ajax;
-import java.util.UUID;
-
import net.pterodactylus.sone.data.Sone;
import net.pterodactylus.sone.web.WebInterface;
import net.pterodactylus.sone.web.page.Page;
* session
*/
protected Session getCurrentSession(ToadletContext toadletContenxt) {
- return getCurrentSession(toadletContenxt, true);
+ return webInterface.getCurrentSession(toadletContenxt);
}
/**
* session
*/
protected Session getCurrentSession(ToadletContext toadletContenxt, boolean create) {
- Session session = webInterface.getSessionManager().useSession(toadletContenxt);
- if (create && (session == null)) {
- session = webInterface.getSessionManager().createSession(UUID.randomUUID().toString(), toadletContenxt);
- }
- return session;
+ return webInterface.getCurrentSession(toadletContenxt, create);
}
/**
* currently logged in
*/
protected Sone getCurrentSone(ToadletContext toadletContext) {
- Session session = getCurrentSession(toadletContext);
- if (session == null) {
- return null;
- }
- String soneId = (String) session.getAttribute("Sone.CurrentSone");
- if (soneId == null) {
- return null;
- }
- return webInterface.getCore().getLocalSone(soneId, false);
+ return webInterface.getCurrentSone(toadletContext);
+ }
+
+ /**
+ * Returns the currently logged in Sone.
+ *
+ * @param toadletContext
+ * The toadlet context
+ * @param create
+ * {@code true} to create a new session if no session exists,
+ * {@code false} to not create a new session
+ * @return The currently logged in Sone, or {@code null} if no Sone is
+ * currently logged in
+ */
+ protected Sone getCurrentSone(ToadletContext toadletContext, boolean create) {
+ return webInterface.getCurrentSone(toadletContext, create);
}
//
WebInterface.SelectBox.No=No
WebInterface.ClickToShow.Replies=Click here to show hidden replies.
+Notification.ClickHereToRead=Click here to read the full text of the notification.
Notification.FirstStart.Text=This seems to be the first time you start Sone. To start, create a new Sone from a web of trust identity and start following other Sones.
Notification.Startup.Text=Sone is currently starting up. It may take a while to retrieve all identities and Sones from the web of trust. If you are missing some elements, please be patient, they will probably reappear very soon.
Notification.ConfigNotRead.Text=The configuration file “sone.properties” could not be read, probably because it was not saved correctly. This can happen on versions prior to Sone 0.3.3 and there is nothing you can do about it.
Notification.Button.Dismiss=Dismiss
+Notification.NewSone.ShortText=New Sones have been discovered:
Notification.NewSone.Text=New Sones have been discovered:
+Notification.NewPost.ShortText=New posts have been discovered.
Notification.NewPost.Text=New posts have been discovered by the following Sones:
+Notification.NewReply.ShortText=New replies have been discovered.
Notification.NewReply.Text=New replies have been discovered by the following Sones:
Notification.SoneIsBeingRescued.Text=The following Sones are currently being rescued:
Notification.SoneRescued.Text=The following Sones have been rescued:
color: rgb(255, 0, 0);
}
+#sone a.internet:before {
+ content: '⚠ ';
+}
+
+#sone a.freenet:before {
+ content: '» ';
+}
+
+#sone a.freenet-trusted {
+ color: rgb(0, 128, 0);
+}
+
+#sone a.freenet-trusted:before {
+ content: '★ ';
+}
+
#sone a img {
border: none;
}
float: right;
}
-#sone #notification-area .notification > div {
- display: inline;
-}
-
#sone #notification-area .notification .post-count {
margin-left: 1ex;
}
return notification;
}
+/**
+ * Shows the details of the notification with the given ID.
+ *
+ * @param notificationId
+ * The ID of the notification
+ */
+function showNotificationDetails(notificationId) {
+ $("#sone .notification#" + notificationId + " .text").show();
+ $("#sone .notification#" + notificationId + " .short-text").hide();
+}
+
//
// EVERYTHING BELOW HERE IS EXECUTED AFTER LOADING THE PAGE
//
<sone>
<time><% currentSone.time></time>
+ <protocol-version>0</protocol-version>
<client>
<name>Sone</name>
<%include include/head.html>
+
+ <div class="page-id hidden">login</div>
+
<%if !sones.empty>
<h1><%= Page.Login.Page.Title|l10n|html></h1>
<form method="post">
-<div class="text">
+<div class="short-text">
+ <%= Notification.NewPost.ShortText|l10n|html>
+ <a href="javascript:showNotificationDetails('<%notification.id|html>'); return false;"><%= Notification.ClickHereToRead|l10n|html></a>
+</div>
+<div class="text hidden">
<%= Notification.NewPost.Text|l10n|html>
<%foreach posts post>
<a href="viewPost.html?post=<% post.id|html>"><% post.sone.niceName|html></a><%notlast>,<%/notlast><%last>.<%/last>
-<div class="text">
+<div class="short-text">
+ <%= Notification.NewReply.ShortText|l10n|html>
+ <a href="javascript:showNotificationDetails('<%notification.id|html>'); return false;"><%= Notification.ClickHereToRead|l10n|html></a>
+</div>
+<div class="text hidden">
<%= Notification.NewReply.Text|l10n|html>
<%foreach replies reply>
<a href="viewPost.html?post=<% reply.post.id|html>"><% reply.sone.niceName|html></a><%notlast>,<%/notlast><%last>.<%/last>
-<div class="text">
+<div class="short-text">
+ <%= Notification.NewSone.ShortText|l10n|html>
+ <a href="javascript:showNotificationDetails('<%notification.id|html>'); return false;"><%= Notification.ClickHereToRead|l10n|html></a>
+</div>
+<div class="text hidden">
<%= Notification.NewSone.Text|l10n|html>
<%foreach sones sone>
<a href="viewSone.html?sone=<% sone.id|html>" title="<% sone.requestUri|html>"><% sone.niceName|html></a><%notlast>,<%/notlast><%last>.<%/last>