wip on server side of feeds for plugin
This commit is contained in:
@@ -118,7 +118,7 @@ public class Core {
|
||||
private final ConnectionAcceptor connectionAcceptor
|
||||
private final ConnectionEstablisher connectionEstablisher
|
||||
private final HasherService hasherService
|
||||
private final DownloadManager downloadManager
|
||||
final DownloadManager downloadManager
|
||||
private final DirectoryWatcher directoryWatcher
|
||||
final FileManager fileManager
|
||||
final UploadManager uploadManager
|
||||
|
||||
@@ -242,4 +242,8 @@ public class DownloadManager {
|
||||
downloaders.values().each { it.stop() }
|
||||
Downloader.executorService.shutdownNow()
|
||||
}
|
||||
|
||||
public boolean isDownloading(InfoHash infoHash) {
|
||||
downloaders.containsKey(infoHash)
|
||||
}
|
||||
}
|
||||
|
||||
103
webui/src/main/java/com/muwire/webui/FeedManager.java
Normal file
103
webui/src/main/java/com/muwire/webui/FeedManager.java
Normal file
@@ -0,0 +1,103 @@
|
||||
package com.muwire.webui;
|
||||
|
||||
import java.io.File;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
import com.muwire.core.Core;
|
||||
import com.muwire.core.Persona;
|
||||
import com.muwire.core.filefeeds.Feed;
|
||||
import com.muwire.core.filefeeds.FeedFetchEvent;
|
||||
import com.muwire.core.filefeeds.FeedItem;
|
||||
import com.muwire.core.filefeeds.FeedItemFetchedEvent;
|
||||
import com.muwire.core.filefeeds.FeedLoadedEvent;
|
||||
import com.muwire.core.filefeeds.UIDownloadFeedItemEvent;
|
||||
import com.muwire.core.filefeeds.UIFeedConfigurationEvent;
|
||||
import com.muwire.core.filefeeds.UIFeedDeletedEvent;
|
||||
|
||||
public class FeedManager {
|
||||
|
||||
private final Core core;
|
||||
private final Map<Persona, RemoteFeed> remoteFeeds = new ConcurrentHashMap<>();
|
||||
|
||||
public FeedManager(Core core) {
|
||||
this.core = core;
|
||||
}
|
||||
|
||||
public Map<Persona, RemoteFeed> getRemoteFeeds() {
|
||||
return remoteFeeds;
|
||||
}
|
||||
|
||||
public void onFeedLoadedEvent(FeedLoadedEvent e) {
|
||||
remoteFeeds.put(e.getFeed().getPublisher(), new RemoteFeed(e.getFeed()));
|
||||
}
|
||||
|
||||
public void onUIFeedConfigurationEvent(UIFeedConfigurationEvent e) {
|
||||
if (!e.isNewFeed())
|
||||
return;
|
||||
remoteFeeds.put(e.getFeed().getPublisher(), new RemoteFeed(e.getFeed()));
|
||||
}
|
||||
|
||||
public void onFeedFetchEvent(FeedFetchEvent e) {
|
||||
RemoteFeed feed = remoteFeeds.get(e.getHost());
|
||||
if (feed == null)
|
||||
return; // hmm
|
||||
feed.getFeed().setStatus(e.getStatus());
|
||||
feed.revision++;
|
||||
}
|
||||
|
||||
public void onFeedItemFetchedEvent(FeedItemFetchedEvent e) {
|
||||
FeedItem item = e.getItem();
|
||||
RemoteFeed feed = remoteFeeds.get(item.getPublisher());
|
||||
if (feed == null)
|
||||
return; // hmm
|
||||
|
||||
if (feed.getFeed().isAutoDownload() &&
|
||||
!core.getFileManager().isShared(item.getInfoHash()) &&
|
||||
!core.getDownloadManager().isDownloading(item.getInfoHash())) {
|
||||
File target = new File(core.getMuOptions().getDownloadLocation(), item.getName());
|
||||
UIDownloadFeedItemEvent event = new UIDownloadFeedItemEvent();
|
||||
event.setItem(item);
|
||||
event.setTarget(target);
|
||||
event.setSequential(feed.getFeed().isSequential());
|
||||
core.getEventBus().publish(event);
|
||||
}
|
||||
}
|
||||
|
||||
void subscribe(Persona publisher) {
|
||||
Feed feed = new Feed(publisher);
|
||||
feed.setAutoDownload(core.getMuOptions().getDefaultFeedAutoDownload());
|
||||
feed.setItemsToKeep(core.getMuOptions().getDefaultFeedItemsToKeep());
|
||||
feed.setUpdateInterval(core.getMuOptions().getDefaultFeedUpdateInterval());
|
||||
feed.setSequential(core.getMuOptions().getDefaultFeedSequential());
|
||||
UIFeedConfigurationEvent event = new UIFeedConfigurationEvent();
|
||||
event.setFeed(feed);
|
||||
event.setNewFeed(true);
|
||||
core.getEventBus().publish(event);
|
||||
}
|
||||
|
||||
void unsubscribe(Persona publisher) {
|
||||
remoteFeeds.remove(publisher);
|
||||
UIFeedDeletedEvent event = new UIFeedDeletedEvent();
|
||||
event.setHost(publisher);
|
||||
core.getEventBus().publish(event);
|
||||
}
|
||||
|
||||
static class RemoteFeed {
|
||||
private final Feed feed;
|
||||
private volatile long revision;
|
||||
|
||||
RemoteFeed(Feed feed) {
|
||||
this.feed = feed;
|
||||
}
|
||||
|
||||
public Feed getFeed() {
|
||||
return feed;
|
||||
}
|
||||
|
||||
public long getRevision() {
|
||||
return revision;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
237
webui/src/main/java/com/muwire/webui/FeedServlet.java
Normal file
237
webui/src/main/java/com/muwire/webui/FeedServlet.java
Normal file
@@ -0,0 +1,237 @@
|
||||
package com.muwire.webui;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.text.Collator;
|
||||
import java.util.Comparator;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import javax.servlet.ServletConfig;
|
||||
import javax.servlet.ServletException;
|
||||
import javax.servlet.http.HttpServlet;
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
|
||||
import com.muwire.core.Core;
|
||||
import com.muwire.core.Persona;
|
||||
import com.muwire.core.filefeeds.Feed;
|
||||
import com.muwire.core.filefeeds.FeedItem;
|
||||
import com.muwire.core.util.DataUtil;
|
||||
import com.muwire.webui.FeedManager.RemoteFeed;
|
||||
|
||||
import net.i2p.data.Base64;
|
||||
import net.i2p.data.DataHelper;
|
||||
|
||||
public class FeedServlet extends HttpServlet {
|
||||
|
||||
private FeedManager feedManager;
|
||||
private Core core;
|
||||
|
||||
@Override
|
||||
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
String section = req.getParameter("section");
|
||||
if (section == null) {
|
||||
resp.sendError(403, "Bad section param");
|
||||
return;
|
||||
}
|
||||
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append("<?xml version='1.0' encoding='UTF-8'?>");
|
||||
|
||||
if (section.equals("status")) {
|
||||
List<WrappedFeed> feeds = feedManager.getRemoteFeeds().values().stream().
|
||||
map(rf -> new WrappedFeed(rf, core.getFeedManager().getFeedItems(rf.getFeed().getPublisher()).size())).
|
||||
collect(Collectors.toList());
|
||||
FEED_COMPARATORS.sort(feeds, req);
|
||||
sb.append("<Status>");
|
||||
feeds.forEach(f -> f.toXML(sb));
|
||||
sb.append("</Status>");
|
||||
} else if (section.equals("items")) {
|
||||
String publisherB64 = req.getParameter("publisher");
|
||||
if (publisherB64 == null) {
|
||||
resp.sendError(403, "Bad param");
|
||||
return;
|
||||
}
|
||||
|
||||
Persona publisher;
|
||||
try {
|
||||
publisher = new Persona(new ByteArrayInputStream(Base64.decode(publisherB64)));
|
||||
} catch (Exception bad) {
|
||||
resp.sendError(403, "Bad param");
|
||||
return;
|
||||
}
|
||||
|
||||
RemoteFeed feed = feedManager.getRemoteFeeds().get(publisher);
|
||||
if (feed == null)
|
||||
return; // hmm
|
||||
|
||||
List<WrappedFeedItem> items = core.getFeedManager().getFeedItems(publisher).stream().
|
||||
map(item -> {
|
||||
ResultStatus resultStatus = ResultStatus.AVAILABLE;
|
||||
if (core.getFileManager().isShared(item.getInfoHash()))
|
||||
resultStatus = ResultStatus.SHARED;
|
||||
else if (core.getDownloadManager().isDownloading(item.getInfoHash()))
|
||||
resultStatus = ResultStatus.DOWNLOADING;
|
||||
return new WrappedFeedItem(item, resultStatus);
|
||||
}).collect(Collectors.toList());
|
||||
|
||||
ITEM_COMPARATORS.sort(items, req);
|
||||
|
||||
sb.append("<Items>");
|
||||
items.forEach(i -> i.toXML(sb));
|
||||
sb.append("</Items>");
|
||||
} else {
|
||||
resp.sendError(403, "Bad section param");
|
||||
return;
|
||||
}
|
||||
|
||||
resp.setContentType("text/xml");
|
||||
resp.setCharacterEncoding("UTF-8");
|
||||
resp.setDateHeader("Expires", 0);
|
||||
resp.setHeader("Pragma", "no-cache");
|
||||
resp.setHeader("Cache-Control", "no-store, max-age=0, no-cache, must-revalidate");
|
||||
byte[] out = sb.toString().getBytes("UTF-8");
|
||||
resp.setContentLength(out.length);
|
||||
resp.getOutputStream().write(out);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
|
||||
String action = req.getParameter("action");
|
||||
if (action == null) {
|
||||
resp.sendError(403,"Bad param");
|
||||
return;
|
||||
}
|
||||
if (action.equals("subscribe")) {
|
||||
String personaB64 = req.getParameter("host");
|
||||
if (personaB64 == null) {
|
||||
resp.sendError(403,"Bad param");
|
||||
return;
|
||||
}
|
||||
Persona host;
|
||||
try {
|
||||
host = new Persona(new ByteArrayInputStream(Base64.decode(personaB64)));
|
||||
} catch (Exception bad) {
|
||||
resp.sendError(403,"Bad param");
|
||||
return;
|
||||
}
|
||||
feedManager.subscribe(host);
|
||||
Util.pause();
|
||||
} else if (action.equals("unsubscribe")) {
|
||||
String personaB64 = req.getParameter("host");
|
||||
if (personaB64 == null) {
|
||||
resp.sendError(403,"Bad param");
|
||||
return;
|
||||
}
|
||||
Persona host;
|
||||
try {
|
||||
host = new Persona(new ByteArrayInputStream(Base64.decode(personaB64)));
|
||||
} catch (Exception bad) {
|
||||
resp.sendError(403,"Bad param");
|
||||
return;
|
||||
}
|
||||
feedManager.unsubscribe(host);
|
||||
Util.pause();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void init(ServletConfig config) throws ServletException {
|
||||
feedManager = (FeedManager) config.getServletContext().getAttribute("feedManager");
|
||||
core = (Core) config.getServletContext().getAttribute("core");
|
||||
}
|
||||
|
||||
private static final Comparator<WrappedFeed> FEED_BY_PUBLISHER = (l, r) -> {
|
||||
return Collator.getInstance().compare(l.feed.getPublisher().getHumanReadableName(), r.feed.getPublisher().getHumanReadableName());
|
||||
};
|
||||
|
||||
private static final Comparator<WrappedFeed> FEED_BY_FILES = (l, r) -> {
|
||||
return Integer.compare(l.files, r.files);
|
||||
};
|
||||
|
||||
private static final Comparator<WrappedFeed> FEED_BY_LAST_UPDATED = (l, r) -> {
|
||||
return Long.compare(l.feed.getLastUpdated(), r.feed.getLastUpdated());
|
||||
};
|
||||
|
||||
private static final Comparator<WrappedFeed> FEED_BY_STATUS = (l, r) -> {
|
||||
return Collator.getInstance().compare(l.feed.getStatus().toString(), r.feed.getStatus().toString());
|
||||
};
|
||||
|
||||
private static final ColumnComparators<WrappedFeed> FEED_COMPARATORS = new ColumnComparators<>();
|
||||
static {
|
||||
FEED_COMPARATORS.add("publisher", FEED_BY_PUBLISHER);
|
||||
FEED_COMPARATORS.add("files", FEED_BY_FILES);
|
||||
FEED_COMPARATORS.add("status", FEED_BY_STATUS);
|
||||
FEED_COMPARATORS.add("lastUpdated", FEED_BY_LAST_UPDATED);
|
||||
}
|
||||
|
||||
private static final Comparator<WrappedFeedItem> ITEM_BY_NAME = (l, r) -> {
|
||||
return Collator.getInstance().compare(l.feedItem.getName(), r.feedItem.getName());
|
||||
};
|
||||
|
||||
private static final Comparator<WrappedFeedItem> ITEM_BY_SIZE = (l, r) -> {
|
||||
return Long.compare(l.feedItem.getSize(), r.feedItem.getSize());
|
||||
};
|
||||
|
||||
private static final Comparator<WrappedFeedItem> ITEM_BY_STATUS = (l, r) -> {
|
||||
return Collator.getInstance().compare(l.resultStatus.toString(), r.resultStatus.toString());
|
||||
};
|
||||
|
||||
private static final Comparator<WrappedFeedItem> ITEM_BY_TIMESTAMP = (l, r) -> {
|
||||
return Long.compare(l.feedItem.getTimestamp(), r.feedItem.getTimestamp());
|
||||
};
|
||||
|
||||
private static final ColumnComparators<WrappedFeedItem> ITEM_COMPARATORS = new ColumnComparators<>();
|
||||
static {
|
||||
ITEM_COMPARATORS.add("name", ITEM_BY_NAME);
|
||||
ITEM_COMPARATORS.add("size", ITEM_BY_SIZE);
|
||||
ITEM_COMPARATORS.add("status", ITEM_BY_STATUS);
|
||||
ITEM_COMPARATORS.add("timestamp", ITEM_BY_TIMESTAMP);
|
||||
}
|
||||
|
||||
private static class WrappedFeedItem {
|
||||
private final FeedItem feedItem;
|
||||
private final ResultStatus resultStatus;
|
||||
WrappedFeedItem(FeedItem feedItem, ResultStatus resultStatus) {
|
||||
this.feedItem = feedItem;
|
||||
this.resultStatus = resultStatus;
|
||||
}
|
||||
|
||||
void toXML(StringBuilder sb) {
|
||||
sb.append("<Item>");
|
||||
sb.append("<Name>").append(Util.escapeHTMLinXML(feedItem.getName())).append("</Name>");
|
||||
sb.append("<ResultStatus>").append(resultStatus).append("</ResultStatus>");
|
||||
sb.append("<Size>").append(DataHelper.formatSize2Decimal(feedItem.getSize(), false)).append("</Size>");
|
||||
sb.append("<Timestamp>").append(DataHelper.formatTime(feedItem.getTimestamp())).append("</Timestamp>");
|
||||
sb.append("<InfoHash>").append(Base64.encode(feedItem.getInfoHash().getRoot())).append("</InfoHash>");
|
||||
sb.append("<Certificates>").append(feedItem.getCertificates()).append("</Certificates>");
|
||||
if (feedItem.getComment() != null)
|
||||
sb.append("<Comment>").append(Util.escapeHTMLinXML(DataUtil.readi18nString(Base64.decode(feedItem.getComment())))).append("</Comment>");
|
||||
sb.append("</Item>");
|
||||
}
|
||||
}
|
||||
|
||||
private static class WrappedFeed {
|
||||
private final Feed feed;
|
||||
private final long revision;
|
||||
private final int files;
|
||||
WrappedFeed(RemoteFeed rf, int files) {
|
||||
this.feed = rf.getFeed();
|
||||
this.revision = rf.getRevision();
|
||||
this.files = files;
|
||||
}
|
||||
|
||||
void toXML(StringBuilder sb) {
|
||||
sb.append("<Feed>");
|
||||
sb.append("<Publisher>").append(Util.escapeHTMLinXML(feed.getPublisher().getHumanReadableName())).append("</Publisher>");
|
||||
sb.append("<PublisherB64>").append(feed.getPublisher().toBase64()).append("</PublisherB64>");
|
||||
sb.append("<Files>").append(files).append("</Files>");
|
||||
sb.append("<Revision>").append(revision).append("</Revision>");
|
||||
sb.append("<Status>").append(feed.getStatus().toString()).append("</Status>");
|
||||
sb.append("<Active>").append(feed.getStatus().isActive()).append("</Active>");
|
||||
sb.append("<LastUpdated>").append(DataHelper.formatTime(feed.getLastUpdated())).append("</LastUpdated>");
|
||||
sb.append("</Feed>");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -24,6 +24,10 @@ import com.muwire.core.connection.DisconnectionEvent;
|
||||
import com.muwire.core.download.DownloadStartedEvent;
|
||||
import com.muwire.core.filecert.CertificateFetchEvent;
|
||||
import com.muwire.core.filecert.CertificateFetchedEvent;
|
||||
import com.muwire.core.filefeeds.FeedFetchEvent;
|
||||
import com.muwire.core.filefeeds.FeedItemFetchedEvent;
|
||||
import com.muwire.core.filefeeds.FeedLoadedEvent;
|
||||
import com.muwire.core.filefeeds.UIFeedConfigurationEvent;
|
||||
import com.muwire.core.files.AllFilesLoadedEvent;
|
||||
import com.muwire.core.files.FileDownloadedEvent;
|
||||
import com.muwire.core.files.FileHashedEvent;
|
||||
@@ -163,6 +167,12 @@ public class MuWireClient {
|
||||
core.getEventBus().register(UploadEvent.class, uploadManager);
|
||||
core.getEventBus().register(UploadFinishedEvent.class, uploadManager);
|
||||
|
||||
FeedManager feedManager = new FeedManager(core);
|
||||
core.getEventBus().register(FeedLoadedEvent.class, feedManager);
|
||||
core.getEventBus().register(UIFeedConfigurationEvent.class, feedManager);
|
||||
core.getEventBus().register(FeedFetchEvent.class, feedManager);
|
||||
core.getEventBus().register(FeedItemFetchedEvent.class, feedManager);
|
||||
|
||||
servletContext.setAttribute("searchManager", searchManager);
|
||||
servletContext.setAttribute("downloadManager", downloadManager);
|
||||
servletContext.setAttribute("connectionCounter", connectionCounter);
|
||||
@@ -171,6 +181,7 @@ public class MuWireClient {
|
||||
servletContext.setAttribute("trustManager", trustManager);
|
||||
servletContext.setAttribute("certificateManager", certificateManager);
|
||||
servletContext.setAttribute("uploadManager", uploadManager);
|
||||
servletContext.setAttribute("feedManager", feedManager);
|
||||
}
|
||||
|
||||
public String getHome() {
|
||||
|
||||
@@ -103,11 +103,14 @@ public class SearchServlet extends HttpServlet {
|
||||
|
||||
List<Sender> senders = new ArrayList<>();
|
||||
results.getBySender().forEach( (persona, resultsFromSender) -> {
|
||||
UIResultEvent first = resultsFromSender.iterator().next();
|
||||
Sender sender = new Sender(persona,
|
||||
core.getTrustService().getLevel(persona.getDestination()),
|
||||
resultsFromSender.iterator().next().getBrowse(),
|
||||
first.getBrowse(),
|
||||
browseManager.isBrowsing(persona),
|
||||
resultsFromSender.size());
|
||||
resultsFromSender.size(),
|
||||
first.getFeed(),
|
||||
core.getFeedManager().getFeed(persona) != null);
|
||||
senders.add(sender);
|
||||
});
|
||||
|
||||
@@ -234,7 +237,9 @@ public class SearchServlet extends HttpServlet {
|
||||
browseManager.isBrowsing(event.getSender()),
|
||||
event.getComment(),
|
||||
event.getCertificates(),
|
||||
core.getTrustService().getLevel(event.getSender().getDestination()));
|
||||
core.getTrustService().getLevel(event.getSender().getDestination()),
|
||||
event.getFeed(),
|
||||
core.getFeedManager().getFeed(event.getSender()) != null);
|
||||
sendersForResult.add(senderForResult);
|
||||
});
|
||||
|
||||
@@ -284,13 +289,18 @@ public class SearchServlet extends HttpServlet {
|
||||
private final boolean browse;
|
||||
private final boolean browsing;
|
||||
private final int results;
|
||||
private final boolean feed;
|
||||
private final boolean subscribed;
|
||||
|
||||
Sender(Persona persona, TrustLevel trustLevel, boolean browse, boolean browsing, int results) {
|
||||
Sender(Persona persona, TrustLevel trustLevel, boolean browse, boolean browsing, int results,
|
||||
boolean feed, boolean subscribed) {
|
||||
this.persona = persona;
|
||||
this.trustLevel = trustLevel;
|
||||
this.browse = browse;
|
||||
this.browsing = browsing;
|
||||
this.results = results;
|
||||
this.feed = feed;
|
||||
this.subscribed = subscribed;
|
||||
}
|
||||
|
||||
void toXML(StringBuilder sb) {
|
||||
@@ -301,6 +311,8 @@ public class SearchServlet extends HttpServlet {
|
||||
sb.append("<Browse>").append(browse).append("</Browse>");
|
||||
sb.append("<Browsing>").append(browsing).append("</Browsing>");
|
||||
sb.append("<Results>").append(results).append("</Results>");
|
||||
sb.append("<Feed>").append(feed).append("</Feed>");
|
||||
sb.append("<Subscribed>").append(subscribed).append("</Subscribed>");
|
||||
sb.append("</Sender>");
|
||||
}
|
||||
}
|
||||
@@ -369,14 +381,19 @@ public class SearchServlet extends HttpServlet {
|
||||
private final String comment;
|
||||
private final int certificates;
|
||||
private final TrustLevel trustLevel;
|
||||
private final boolean feed;
|
||||
private final boolean subscribed;
|
||||
|
||||
SenderForResult(Persona sender, boolean browse, boolean browsing, String comment, int certificates, TrustLevel trustLevel) {
|
||||
SenderForResult(Persona sender, boolean browse, boolean browsing, String comment, int certificates, TrustLevel trustLevel,
|
||||
boolean feed, boolean subscribed) {
|
||||
this.sender = sender;
|
||||
this.browse = browse;
|
||||
this.trustLevel = trustLevel;
|
||||
this.browsing = browsing;
|
||||
this.comment = comment;
|
||||
this.certificates = certificates;
|
||||
this.feed = feed;
|
||||
this.subscribed = subscribed;
|
||||
}
|
||||
|
||||
void toXML(StringBuilder sb) {
|
||||
@@ -389,6 +406,8 @@ public class SearchServlet extends HttpServlet {
|
||||
if (comment != null)
|
||||
sb.append("<Comment>").append(Util.escapeHTMLinXML(comment)).append("</Comment>");
|
||||
sb.append("<Certificates>").append(certificates).append("</Certificates>");
|
||||
sb.append("<Feed>").append(feed).append("</Feed>");
|
||||
sb.append("<Subscribed>").append(subscribed).append("</Subscribed>");
|
||||
sb.append("</Sender>");
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user