forked from I2P_Developers/i2p.i2p
SusiDNS: Add import feature (ticket #2447)
Box overlap issue remains todo, see ticket #2419
This commit is contained in:
@@ -97,6 +97,19 @@
|
||||
<replace file="WEB-INF/web-out.xml">
|
||||
<replacefilter token="<!-- precompiled servlets -->" value="${jspc.web.fragment}" />
|
||||
</replace>
|
||||
<!-- Add multipart config to servlets that need them -->
|
||||
<property name="__match1" value="<servlet-class>i2p.susi.dns.jsp." />
|
||||
<property name="__match2" value="_jsp</servlet-class>" />
|
||||
<property name="__class1" value="${__match1}addressbook${__match2}" />
|
||||
<property name="__multipart" value="
|
||||
<multipart-config>
|
||||
<max-file-size>67108864</max-file-size>
|
||||
<max-request-size>67108864</max-request-size>
|
||||
<file-size-threshold>262144</file-size-threshold>
|
||||
</multipart-config>" />
|
||||
<replace file="WEB-INF/web-out.xml">
|
||||
<replacefilter token="${__class1}" value="${__class1}${__multipart}" />
|
||||
</replace>
|
||||
</target>
|
||||
|
||||
<uptodate property="precompilejsp.uptodate" targetfile="WEB-INF/web-out.xml">
|
||||
|
||||
@@ -21,7 +21,11 @@
|
||||
|
||||
package i2p.susi.dns;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.io.Writer;
|
||||
import java.util.Arrays;
|
||||
import java.util.ArrayList;
|
||||
@@ -34,9 +38,11 @@ import java.util.Properties;
|
||||
import java.util.SortedMap;
|
||||
|
||||
import net.i2p.client.naming.NamingService;
|
||||
import net.i2p.client.naming.SingleFileNamingService;
|
||||
import net.i2p.data.DataFormatException;
|
||||
import net.i2p.data.DataHelper;
|
||||
import net.i2p.data.Destination;
|
||||
import net.i2p.servlet.RequestWrapper;
|
||||
|
||||
/**
|
||||
* Talk to the NamingService API instead of modifying the hosts.txt files directly,
|
||||
@@ -356,7 +362,7 @@ public class NamingServiceBean extends AddressbookBean
|
||||
action = null;
|
||||
|
||||
if( message.length() > 0 )
|
||||
message = "<p class=\"messages\">" + message + "</p>";
|
||||
message = styleMessage(message);
|
||||
return message;
|
||||
}
|
||||
|
||||
@@ -494,6 +500,90 @@ public class NamingServiceBean extends AddressbookBean
|
||||
// No post-filtering for hosts.txt naming services. It is what it is.
|
||||
}
|
||||
|
||||
/**
|
||||
* @return messages about this action
|
||||
* @since 0.9.40
|
||||
*/
|
||||
public String importFile(RequestWrapper wrequest) throws IOException {
|
||||
String message = "";
|
||||
InputStream in = wrequest.getInputStream("file");
|
||||
OutputStream out = null;
|
||||
File tmp = null;
|
||||
SingleFileNamingService sfns = null;
|
||||
try {
|
||||
// non-null but zero bytes if no file entered, don't know why
|
||||
if (in == null || in.available() <= 0) {
|
||||
return styleMessage(_t("You must enter a file"));
|
||||
}
|
||||
// copy to temp file
|
||||
tmp = new File(_context.getTempDir(), "susidns-import-" + _context.random().nextLong() + ".txt");
|
||||
out = new FileOutputStream(tmp);
|
||||
DataHelper.copy(in, out);
|
||||
in.close();
|
||||
in = null;
|
||||
out.close();
|
||||
out = null;
|
||||
// new SingleFileNamingService
|
||||
sfns = new SingleFileNamingService(_context, tmp.getAbsolutePath());
|
||||
// getEntries, copy over
|
||||
Map<String, Destination> entries = sfns.getEntries();
|
||||
int count = entries.size();
|
||||
if (count <= 0) {
|
||||
return styleMessage(_t("No entries found in file"));
|
||||
} else {
|
||||
NamingService service = getNamingService();
|
||||
int added = 0, dup = 0;
|
||||
Properties nsOptions = new Properties();
|
||||
nsOptions.setProperty("list", getFileName());
|
||||
String now = Long.toString(_context.clock().now());
|
||||
nsOptions.setProperty("m", now);
|
||||
String filename = wrequest.getFilename("file");
|
||||
if (filename != null)
|
||||
nsOptions.setProperty("s", _t("Imported from file {0}", filename));
|
||||
else
|
||||
nsOptions.setProperty("s", _t("Imported from file"));
|
||||
for (Map.Entry<String, Destination> e : entries.entrySet()) {
|
||||
String host = e.getKey();
|
||||
Destination dest = e.getValue();
|
||||
boolean ok = service.putIfAbsent(host, dest, nsOptions);
|
||||
if (ok)
|
||||
added++;
|
||||
else
|
||||
dup++;
|
||||
}
|
||||
StringBuilder buf = new StringBuilder(128);
|
||||
if (added > 0)
|
||||
buf.append(styleMessage(ngettext("Loaded {0} entry from file",
|
||||
"Loaded {0} entries from file",
|
||||
added)));
|
||||
if (dup > 0)
|
||||
buf.append(styleMessage(ngettext("Skipped {0} duplicate entry from file",
|
||||
"Skipped {0} duplicate entries from file",
|
||||
dup)));
|
||||
return buf.toString();
|
||||
}
|
||||
} catch (IOException ioe) {
|
||||
return styleMessage(_t("Import from file failed") + " - " + ioe);
|
||||
} finally {
|
||||
if (in != null)
|
||||
try { in.close(); } catch (IOException ioe) {}
|
||||
if (out != null)
|
||||
try { out.close(); } catch (IOException ioe) {}
|
||||
// shutdown SFNS
|
||||
if (sfns != null)
|
||||
sfns.shutdown();
|
||||
if (tmp != null)
|
||||
tmp.delete();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.40
|
||||
*/
|
||||
private static String styleMessage(String message) {
|
||||
return "<p class=\"messages\">" + message + "</p>";
|
||||
}
|
||||
|
||||
/**
|
||||
* @since 0.9.34
|
||||
*/
|
||||
|
||||
@@ -34,13 +34,18 @@
|
||||
response.setHeader("Referrer-Policy", "no-referrer");
|
||||
response.setHeader("Accept-Ranges", "none");
|
||||
|
||||
%>
|
||||
<%@page pageEncoding="UTF-8"%>
|
||||
<%@ page contentType="text/html"%>
|
||||
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
||||
%><%@page pageEncoding="UTF-8" contentType="text/html" import="net.i2p.servlet.RequestWrapper"
|
||||
%><%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core"%>
|
||||
<jsp:useBean id="version" class="i2p.susi.dns.VersionBean" scope="application" />
|
||||
<jsp:useBean id="book" class="i2p.susi.dns.NamingServiceBean" scope="session" />
|
||||
<jsp:useBean id="intl" class="i2p.susi.dns.Messages" scope="application" />
|
||||
<%
|
||||
String importMessages = null;
|
||||
if (intl._t("Import").equals(request.getParameter("action"))) {
|
||||
RequestWrapper wrequest = new RequestWrapper(request);
|
||||
importMessages = book.importFile(wrequest);
|
||||
}
|
||||
%>
|
||||
<jsp:setProperty name="book" property="*" />
|
||||
<jsp:setProperty name="book" property="resetDeletionMarks" value="1"/>
|
||||
<c:forEach items="${paramValues.checked}" var="checked">
|
||||
@@ -75,7 +80,11 @@
|
||||
<h4><%=intl._t("Storage")%>: ${book.displayName}</h4>
|
||||
</div>
|
||||
|
||||
<div id="messages">${book.messages}</div>
|
||||
<div id="messages">${book.messages}<%
|
||||
if (importMessages != null) {
|
||||
%><%=importMessages%><%
|
||||
}
|
||||
%></div>
|
||||
|
||||
${book.loadBookMessages}
|
||||
|
||||
@@ -254,6 +263,28 @@ ${book.loadBookMessages}
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<% if (!book.getBook().equals("published")) { %>
|
||||
<form method="POST" action="addressbook" enctype="multipart/form-data" accept-charset="UTF-8">
|
||||
<input type="hidden" name="book" value="${book.book}">
|
||||
<input type="hidden" name="serial" value="<%=susiNonce%>">
|
||||
<input type="hidden" name="begin" value="0">
|
||||
<input type="hidden" name="end" value="49">
|
||||
<div id="import">
|
||||
<h3><%=intl._t("Import from hosts.txt file")%></h3>
|
||||
<table>
|
||||
<tr>
|
||||
<td><b><%=intl._t("File")%></b></td>
|
||||
<td><input name="file" type="file" accept=".txt" value="" /></td>
|
||||
</tr>
|
||||
</table>
|
||||
<p class="buttons">
|
||||
<input class="cancel" type="reset" value="<%=intl._t("Cancel")%>" >
|
||||
<input class="download" type="submit" name="action" value="<%=intl._t("Import")%>" >
|
||||
</p>
|
||||
</div>
|
||||
</form>
|
||||
<% } %>
|
||||
|
||||
<div id="footer">
|
||||
<hr>
|
||||
<p class="footer">susidns v${version.version} © <a href="${version.url}" target="_top">susi</a> 2005</p>
|
||||
|
||||
@@ -346,7 +346,7 @@ public class SingleFileNamingService extends NamingService {
|
||||
}
|
||||
|
||||
/**
|
||||
* @param options As follows:
|
||||
* @param options null OK, or as follows:
|
||||
* Key "search": return only those matching substring
|
||||
* Key "startsWith": return only those starting with
|
||||
* ("[0-9]" allowed)
|
||||
@@ -413,7 +413,7 @@ public class SingleFileNamingService extends NamingService {
|
||||
/**
|
||||
* Overridden since we store base64 natively.
|
||||
*
|
||||
* @param options As follows:
|
||||
* @param options null OK, or as follows:
|
||||
* Key "search": return only those matching substring
|
||||
* Key "startsWith": return only those starting with
|
||||
* ("[0-9]" allowed)
|
||||
|
||||
@@ -751,14 +751,14 @@ div#book, #emptybook {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
div#add {
|
||||
div#add, div#import {
|
||||
border: 1px solid #2a5f29;
|
||||
padding: 0 0 10px;
|
||||
margin-top: 23px;
|
||||
background: #000;
|
||||
}
|
||||
|
||||
#add h3 {
|
||||
#add h3, #import h3 {
|
||||
margin-top: -6px;
|
||||
margin-left: -1px;
|
||||
margin-right: -1px;
|
||||
@@ -766,21 +766,23 @@ div#add {
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
#add table {
|
||||
#add table, #import table {
|
||||
width: 100%;
|
||||
width: calc(100% - 1px);
|
||||
margin: -10px 10px 0 0;
|
||||
}
|
||||
|
||||
#add td:first-child {
|
||||
#add td:first-child,
|
||||
#import table td:first-child {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#add td:last-child {
|
||||
#add td:last-child,
|
||||
#import td:last-child {
|
||||
width: 94%;
|
||||
}
|
||||
|
||||
#add p.buttons {
|
||||
#add p.buttons, #import p.buttons {
|
||||
margin-top: 5px;
|
||||
border-top: 1px solid #2a5f29;
|
||||
padding-top: 5px;
|
||||
|
||||
@@ -270,39 +270,40 @@ form[action="subscriptions"] #content {
|
||||
line-height: 130%;
|
||||
}
|
||||
|
||||
div#add {
|
||||
div#add, div#import {
|
||||
border: 1px solid #7778bf;
|
||||
margin-top: -1px;
|
||||
padding: 0 15px;
|
||||
background: #fafaff;
|
||||
}
|
||||
|
||||
.iframed #add {
|
||||
.iframed #add, .iframed #import {
|
||||
margin-top: 10px;
|
||||
}
|
||||
|
||||
#add h3 {
|
||||
#add h3, #import h3 {
|
||||
border-bottom: 1px solid #7778bf;
|
||||
margin: 0 -15px;
|
||||
padding: 5px 10px;
|
||||
}
|
||||
|
||||
#add table {
|
||||
#add table, #import table {
|
||||
width: 100%;
|
||||
margin: 5px 0;
|
||||
}
|
||||
|
||||
#add table td:first-child {
|
||||
#add table td:first-child,
|
||||
#import table td:first-child {
|
||||
width: 50px;
|
||||
white-space: nowrap;
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
#add td {
|
||||
#add td, #import td {
|
||||
padding: 3px;
|
||||
}
|
||||
|
||||
div#add p.buttons {
|
||||
div#add p.buttons, div#import p.buttons {
|
||||
border: 1px solid #7778bf;
|
||||
margin: 0 -16px -1px;
|
||||
background: linear-gradient(to bottom, #fff 50%, rgba(220,220,255,0.3)), repeating-linear-gradient(135deg, rgba(255,255,255,0.5) 2px, rgba(221, 221, 255, 0.3) 3px, #fff 5px), #fff !important;
|
||||
@@ -327,7 +328,7 @@ div.help {
|
||||
font-size: 10pt;
|
||||
}
|
||||
|
||||
div.help h3, #add h3 {
|
||||
div.help h3, #add h3, #import h3 {
|
||||
border: 1px solid #7778bf;
|
||||
padding: 5px 10px;
|
||||
margin: -1px -16px 0;
|
||||
|
||||
Reference in New Issue
Block a user