forked from I2P_Developers/i2p.i2p
2005-07-04 jrandom
* Within the tunnel, use xor(IV, msg[0:16]) as the flag to detect dups, rather than the IV by itself, preventing an attack that would let colluding internal adversaries tag a message to determine that they are in the same tunnel. Thanks dvorak for the catch! * Drop long inactive profiles on startup and shutdown * /configstats.jsp: web interface to pick what stats to log * Deliver more session tags to account for wider window sizes * Cache some intermediate values in our HMACSHA256 and BC's HMAC * Track the client send rate (stream.sendBps and client.sendBpsRaw) * UrlLauncher: adjust the browser selection order * I2PAppContext: hooks for dummy HMACSHA256 and a weak PRNG * StreamSinkClient: add support for sending an unlimited amount of data * Migrate the tests out of the default build jars 2005-06-22 Comwiz * Migrate the core tests to junit
This commit is contained in:
@@ -87,6 +87,7 @@ public class AddressBook {
|
||||
this.location = subscription.getLocation();
|
||||
|
||||
try {
|
||||
// EepGet get = new EepGet(I2PAppContext.getGlobalContext(), true, )
|
||||
URL url = new URL(subscription.getLocation());
|
||||
HttpURLConnection connection = (HttpURLConnection) url
|
||||
.openConnection();
|
||||
|
@@ -110,7 +110,7 @@ public abstract class I2PTunnelClientBase extends I2PTunnelTask implements Runna
|
||||
}
|
||||
}
|
||||
if (sockMgr == null) {
|
||||
_log.log(Log.CRIT, "Unable to create socket manager");
|
||||
_log.log(Log.CRIT, "Unable to create socket manager (our own? " + ownDest + ")");
|
||||
try { Thread.sleep(10*1000); } catch (InterruptedException ie) {}
|
||||
}
|
||||
}
|
||||
|
@@ -84,7 +84,7 @@ public class StreamSinkClient {
|
||||
Random rand = new Random();
|
||||
OutputStream out = sock.getOutputStream();
|
||||
long beforeSending = System.currentTimeMillis();
|
||||
for (int i = 0; i < _sendSize; i+= 32) {
|
||||
for (int i = 0; (_sendSize < 0) || (i < _sendSize); i+= 32) {
|
||||
rand.nextBytes(buf);
|
||||
out.write(buf);
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
@@ -117,7 +117,7 @@ public class StreamSinkClient {
|
||||
/**
|
||||
* Fire up the client. <code>Usage: StreamSinkClient [i2cpHost i2cpPort] sendSizeKB writeDelayMs serverDestFile</code> <br />
|
||||
* <ul>
|
||||
* <li><b>sendSizeKB</b>: how many KB to send</li>
|
||||
* <li><b>sendSizeKB</b>: how many KB to send, or -1 for unlimited</li>
|
||||
* <li><b>writeDelayMs</b>: how long to wait between each .write (0 for no delay)</li>
|
||||
* <li><b>serverDestFile</b>: file containing the StreamSinkServer's binary Destination</li>
|
||||
* </ul>
|
||||
|
@@ -0,0 +1,94 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.StringTokenizer;
|
||||
|
||||
import net.i2p.stat.StatManager;
|
||||
|
||||
/**
|
||||
* Handler to deal with form submissions from the stats config form and act
|
||||
* upon the values.
|
||||
*
|
||||
*/
|
||||
public class ConfigStatsHandler extends FormHandler {
|
||||
private String _filename;
|
||||
private List _stats;
|
||||
private boolean _explicitFilter;
|
||||
private String _explicitFilterValue;
|
||||
|
||||
public ConfigStatsHandler() {
|
||||
super();
|
||||
_stats = new ArrayList();
|
||||
}
|
||||
|
||||
protected void processForm() {
|
||||
saveChanges();
|
||||
}
|
||||
|
||||
public void setFilename(String filename) {
|
||||
_filename = (filename != null ? filename.trim() : null);
|
||||
}
|
||||
|
||||
public void setStatList(String stat) {
|
||||
if (stat != null) {
|
||||
if (stat.indexOf(',') != -1) {
|
||||
StringTokenizer tok = new StringTokenizer(stat, ",");
|
||||
while (tok.hasMoreTokens()) {
|
||||
String cur = tok.nextToken().trim();
|
||||
if ( (cur.length() > 0) && (!_stats.contains(cur)) )
|
||||
_stats.add(cur);
|
||||
}
|
||||
} else {
|
||||
stat = stat.trim();
|
||||
if ( (stat.length() > 0) && (!_stats.contains(stat)) )
|
||||
_stats.add(stat);
|
||||
}
|
||||
}
|
||||
}
|
||||
public void setStatList(String stats[]) {
|
||||
if (stats != null) {
|
||||
for (int i = 0; i < stats.length; i++) {
|
||||
String cur = stats[i].trim();
|
||||
if ( (cur.length() > 0) && (!_stats.contains(cur)) )
|
||||
_stats.add(cur);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setExplicitFilter(String foo) { _explicitFilter = true; }
|
||||
public void setExplicitFilterValue(String filter) { _explicitFilterValue = filter; }
|
||||
|
||||
/**
|
||||
* The user made changes to the config and wants to save them, so
|
||||
* lets go ahead and do so.
|
||||
*
|
||||
*/
|
||||
private void saveChanges() {
|
||||
if (_filename == null)
|
||||
_filename = StatManager.DEFAULT_STAT_FILE;
|
||||
_context.router().setConfigSetting(StatManager.PROP_STAT_FILE, _filename);
|
||||
|
||||
if (_explicitFilter) {
|
||||
_stats.clear();
|
||||
setStatList(_explicitFilterValue);
|
||||
}
|
||||
|
||||
StringBuffer stats = new StringBuffer();
|
||||
for (int i = 0; i < _stats.size(); i++) {
|
||||
stats.append((String)_stats.get(i));
|
||||
if (i + 1 < _stats.size())
|
||||
stats.append(',');
|
||||
}
|
||||
|
||||
_context.router().setConfigSetting(StatManager.PROP_STAT_FILTER, stats.toString());
|
||||
boolean ok = _context.router().saveConfig();
|
||||
if (ok)
|
||||
addFormNotice("Stat filter and location updated successfully to: " + stats.toString());
|
||||
else
|
||||
addFormError("Failed to update the stat filter and location");
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,125 @@
|
||||
package net.i2p.router.web;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.StringTokenizer;
|
||||
import net.i2p.stat.RateStat;
|
||||
import net.i2p.stat.FrequencyStat;
|
||||
import net.i2p.router.RouterContext;
|
||||
import net.i2p.util.Log;
|
||||
|
||||
public class ConfigStatsHelper {
|
||||
private RouterContext _context;
|
||||
private Log _log;
|
||||
private String _filter;
|
||||
private Set _filters;
|
||||
/** list of names of stats which are remaining, ordered by nested groups */
|
||||
private List _stats;
|
||||
private String _currentStatName;
|
||||
private String _currentStatDescription;
|
||||
private String _currentGroup;
|
||||
/** true if the current stat is the first in the group */
|
||||
private boolean _currentIsFirstInGroup;
|
||||
/** true if the stat is being logged */
|
||||
private boolean _currentIsLogged;
|
||||
|
||||
/**
|
||||
* Configure this bean to query a particular router context
|
||||
*
|
||||
* @param contextId begging few characters of the routerHash, or null to pick
|
||||
* the first one we come across.
|
||||
*/
|
||||
public void setContextId(String contextId) {
|
||||
try {
|
||||
_context = ContextHelper.getContext(contextId);
|
||||
_log = _context.logManager().getLog(ConfigStatsHelper.class);
|
||||
} catch (Throwable t) {
|
||||
t.printStackTrace();
|
||||
}
|
||||
|
||||
_stats = new ArrayList();
|
||||
Map groups = _context.statManager().getStatsByGroup();
|
||||
for (Iterator iter = groups.values().iterator(); iter.hasNext(); ) {
|
||||
Set stats = (Set)iter.next();
|
||||
for (Iterator statIter = stats.iterator(); statIter.hasNext(); )
|
||||
_stats.add(statIter.next());
|
||||
}
|
||||
_filter = _context.statManager().getStatFilter();
|
||||
if (_filter == null)
|
||||
_filter = "";
|
||||
|
||||
_filters = new HashSet();
|
||||
StringTokenizer tok = new StringTokenizer(_filter, ",");
|
||||
while (tok.hasMoreTokens())
|
||||
_filters.add(tok.nextToken().trim());
|
||||
}
|
||||
|
||||
public ConfigStatsHelper() {}
|
||||
|
||||
public String getFilename() { return _context.statManager().getStatFile(); }
|
||||
|
||||
/**
|
||||
* move the cursor to the next known stat, returning true if a valid
|
||||
* stat is available.
|
||||
*
|
||||
* @return true if a valid stat is available, otherwise false
|
||||
*/
|
||||
public boolean hasMoreStats() {
|
||||
if (_stats.size() <= 0)
|
||||
return false;
|
||||
_currentStatName = (String)_stats.remove(0);
|
||||
RateStat rs = _context.statManager().getRate(_currentStatName);
|
||||
if (rs != null) {
|
||||
_currentStatDescription = rs.getDescription();
|
||||
if (_currentGroup == null)
|
||||
_currentIsFirstInGroup = true;
|
||||
else if (!rs.getGroupName().equals(_currentGroup))
|
||||
_currentIsFirstInGroup = true;
|
||||
else
|
||||
_currentIsFirstInGroup = false;
|
||||
_currentGroup = rs.getGroupName();
|
||||
} else {
|
||||
FrequencyStat fs = _context.statManager().getFrequency(_currentStatName);
|
||||
if (fs != null) {
|
||||
_currentStatDescription = fs.getDescription();
|
||||
if (_currentGroup == null)
|
||||
_currentIsFirstInGroup = true;
|
||||
else if (!fs.getGroupName().equals(_currentGroup))
|
||||
_currentIsFirstInGroup = true;
|
||||
else
|
||||
_currentIsFirstInGroup = false;
|
||||
_currentGroup = fs.getGroupName();
|
||||
} else {
|
||||
if (_log.shouldLog(Log.ERROR))
|
||||
_log.error("Stat does not exist?! [" + _currentStatName + "]");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (_filters.contains("*") || _filters.contains(_currentStatName))
|
||||
_currentIsLogged = true;
|
||||
else
|
||||
_currentIsLogged = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
/** Is the current stat the first in the group? */
|
||||
public boolean groupRequired() {
|
||||
if (_currentIsFirstInGroup) {
|
||||
_currentIsFirstInGroup = false;
|
||||
return true;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
/** What group is the current stat in */
|
||||
public String getCurrentGroupName() { return _currentGroup; }
|
||||
public String getCurrentStatName() { return _currentStatName; }
|
||||
public String getCurrentStatDescription() { return _currentStatDescription; }
|
||||
public boolean getCurrentIsLogged() { return _currentIsLogged; }
|
||||
public String getExplicitFilter() { return _filter; }
|
||||
}
|
@@ -8,5 +8,7 @@
|
||||
%>Tunnels | <% } else { %><a href="configtunnels.jsp">Tunnels</a> | <% }
|
||||
if (request.getRequestURI().indexOf("configlogging.jsp") != -1) {
|
||||
%>Logging | <% } else { %><a href="configlogging.jsp">Logging</a> | <% }
|
||||
if (request.getRequestURI().indexOf("configstats.jsp") != -1) {
|
||||
%>Stats | <% } else { %><a href="configstats.jsp">Stats</a> | <% }
|
||||
if (request.getRequestURI().indexOf("configadvanced.jsp") != -1) {
|
||||
%>Advanced<% } else { %><a href="configadvanced.jsp">Advanced</a><% } %></h4>
|
||||
|
83
apps/routerconsole/jsp/configstats.jsp
Normal file
83
apps/routerconsole/jsp/configstats.jsp
Normal file
@@ -0,0 +1,83 @@
|
||||
<%@page contentType="text/html"%>
|
||||
<%@page pageEncoding="UTF-8"%>
|
||||
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
|
||||
|
||||
<html><head>
|
||||
<title>I2P Router Console - config stats</title>
|
||||
<link rel="stylesheet" href="default.css" type="text/css" />
|
||||
<script language="JavaScript">
|
||||
function toggleAll(category) {
|
||||
//alert("toggle all for " + category);
|
||||
var shouldCheck = false;
|
||||
var shouldCheckDetermined = false;
|
||||
var elements = document.statsForm.elements;
|
||||
for (var i = 0; i < elements.length; i++) {
|
||||
//alert("cur element: " + i);
|
||||
var curElement = elements.item(i);
|
||||
//alert("cur elem: " + curElement);
|
||||
var curName = curElement.name;
|
||||
//alert("cur name: " + curName);
|
||||
if (curName == 'statList') {
|
||||
if (shouldCheckDetermined == false) {
|
||||
shouldCheckDetermined = true;
|
||||
shouldCheck = !curElement.checked;
|
||||
}
|
||||
if (shouldCheck)
|
||||
curElement.checked = true;
|
||||
else
|
||||
curElement.checked = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</head><body>
|
||||
<%@include file="nav.jsp" %>
|
||||
<%@include file="summary.jsp" %>
|
||||
|
||||
<div class="main" id="main">
|
||||
<%@include file="confignav.jsp" %>
|
||||
|
||||
<jsp:useBean class="net.i2p.router.web.ConfigStatsHandler" id="formhandler" scope="request" />
|
||||
<jsp:setProperty name="formhandler" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
<jsp:setProperty name="formhandler" property="*" />
|
||||
<font color="red"><jsp:getProperty name="formhandler" property="errors" /></font>
|
||||
<i><jsp:getProperty name="formhandler" property="notices" /></i>
|
||||
|
||||
<jsp:useBean class="net.i2p.router.web.ConfigStatsHelper" id="statshelper" scope="request" />
|
||||
<jsp:setProperty name="statshelper" property="contextId" value="<%=(String)session.getAttribute("i2p.contextId")%>" />
|
||||
|
||||
<form id="statsForm" name="statsForm" action="configstats.jsp" method="POST">
|
||||
<% String prev = System.getProperty("net.i2p.router.web.ConfigStatsHandler.nonce");
|
||||
if (prev != null) System.setProperty("net.i2p.router.web.ConfigStatsHandler.noncePrev", prev);
|
||||
System.setProperty("net.i2p.router.web.ConfigStatsHandler.nonce", new java.util.Random().nextLong()+""); %>
|
||||
<input type="hidden" name="action" value="foo" />
|
||||
<input type="hidden" name="nonce" value="<%=System.getProperty("net.i2p.router.web.ConfigStatsHandler.nonce")%>" />
|
||||
Stat file: <input type="text" name="filename" value="<%=statshelper.getFilename()%>" /><br />
|
||||
Filter: (<a href="javascript:toggleAll('*')">toggle all</a>)<br />
|
||||
<table>
|
||||
<% while (statshelper.hasMoreStats()) {
|
||||
while (statshelper.groupRequired()) { %>
|
||||
<tr><td valign="top" align="left" colspan="2">
|
||||
<b><%=statshelper.getCurrentGroupName()%></b>
|
||||
<!--(<a href="javascript:toggleAll('<%=statshelper.getCurrentGroupName()%>')">toggle all</a>)-->
|
||||
</td></tr><%
|
||||
} // end iterating over required groups for the current stat %>
|
||||
<tr><td valign="top" align="left">
|
||||
<input type="checkbox" name="statList" value="<%=statshelper.getCurrentStatName()%>" <%
|
||||
if (statshelper.getCurrentIsLogged()) { %>checked="true" <% } %>/></td>
|
||||
<td valign="top" align="left"><b><%=statshelper.getCurrentStatName()%>:</b><br />
|
||||
<%=statshelper.getCurrentStatDescription()%></td></tr><%
|
||||
} // end iterating over all stats %>
|
||||
<tr><td colspan="2"><hr /></td></tr>
|
||||
<tr><td><input type="checkbox" name="explicitFilter" /></td>
|
||||
<td>Advanced filter:
|
||||
<input type="text" name="explicitFilterValue" value="<%=statshelper.getExplicitFilter()%>" size="40" /></td></tr>
|
||||
<tr><td colspan="2"><hr /></td></tr>
|
||||
<tr><td><input type="submit" name="shouldsave" value="Save changes" /> </td>
|
||||
<td><input type="reset" value="Cancel" /></td></tr>
|
||||
</form>
|
||||
</table>
|
||||
</div>
|
||||
|
||||
</body>
|
||||
</html>
|
@@ -72,8 +72,8 @@ public class Connection {
|
||||
private long _lifetimeDupMessageSent;
|
||||
private long _lifetimeDupMessageReceived;
|
||||
|
||||
public static final long MAX_RESEND_DELAY = 20*1000;
|
||||
public static final long MIN_RESEND_DELAY = 10*1000;
|
||||
public static final long MAX_RESEND_DELAY = 10*1000;
|
||||
public static final long MIN_RESEND_DELAY = 3*1000;
|
||||
|
||||
/** wait up to 5 minutes after disconnection so we can ack/close packets */
|
||||
public static int DISCONNECT_TIMEOUT = 5*60*1000;
|
||||
|
@@ -38,6 +38,10 @@ public class MessageOutputStream extends OutputStream {
|
||||
* size
|
||||
*/
|
||||
private volatile int _nextBufferSize;
|
||||
// rate calc helpers
|
||||
private long _sendPeriodBeginTime;
|
||||
private long _sendPeriodBytes;
|
||||
private int _sendBps;
|
||||
|
||||
public MessageOutputStream(I2PAppContext ctx, DataReceiver receiver) {
|
||||
this(ctx, receiver, Packet.MAX_PAYLOAD_SIZE);
|
||||
@@ -55,6 +59,10 @@ public class MessageOutputStream extends OutputStream {
|
||||
_writeTimeout = -1;
|
||||
_passiveFlushDelay = 500;
|
||||
_nextBufferSize = -1;
|
||||
_sendPeriodBeginTime = ctx.clock().now();
|
||||
_sendPeriodBytes = 0;
|
||||
_sendBps = 0;
|
||||
_context.statManager().createRateStat("stream.sendBps", "How fast we pump data through the stream", "Stream", new long[] { 60*1000, 5*60*1000, 60*60*1000 });
|
||||
_flusher = new Flusher();
|
||||
if (_log.shouldLog(Log.DEBUG))
|
||||
_log.debug("MessageOutputStream created");
|
||||
@@ -137,6 +145,21 @@ public class MessageOutputStream extends OutputStream {
|
||||
if ( (elapsed > 10*1000) && (_log.shouldLog(Log.DEBUG)) )
|
||||
_log.debug("wtf, took " + elapsed + "ms to write to the stream?", new Exception("foo"));
|
||||
throwAnyError();
|
||||
updateBps(len);
|
||||
}
|
||||
|
||||
private void updateBps(int len) {
|
||||
long now = _context.clock().now();
|
||||
int periods = (int)Math.floor((now - _sendPeriodBeginTime) / 1000d);
|
||||
if (periods > 0) {
|
||||
// first term decays on slow transmission
|
||||
_sendBps = (int)(((float)0.9f*((float)_sendBps/(float)periods)) + ((float)0.1f*((float)_sendPeriodBytes/(float)periods)));
|
||||
_sendPeriodBytes = len;
|
||||
_sendPeriodBeginTime = now;
|
||||
_context.statManager().addRateData("stream.sendBps", _sendBps, 0);
|
||||
} else {
|
||||
_sendPeriodBytes += len;
|
||||
}
|
||||
}
|
||||
|
||||
public void write(int b) throws IOException {
|
||||
|
@@ -119,6 +119,19 @@ public class PacketHandler {
|
||||
}
|
||||
|
||||
private void receiveKnownCon(Connection con, Packet packet) {
|
||||
if (packet.isFlagSet(Packet.FLAG_ECHO)) {
|
||||
if (packet.getSendStreamId() != null) {
|
||||
receivePing(packet);
|
||||
} else if (packet.getReceiveStreamId() != null) {
|
||||
receivePong(packet);
|
||||
} else {
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Echo packet received with no stream IDs: " + packet);
|
||||
}
|
||||
packet.releasePayload();
|
||||
return;
|
||||
}
|
||||
|
||||
// the packet is pointed at a stream ID we're receiving on
|
||||
if (isValidMatch(con.getSendStreamId(), packet.getReceiveStreamId())) {
|
||||
// the packet's receive stream ID also matches what we expect
|
||||
@@ -163,8 +176,19 @@ public class PacketHandler {
|
||||
} else {
|
||||
if (!con.getResetSent()) {
|
||||
// someone is sending us a packet on the wrong stream
|
||||
if (_log.shouldLog(Log.WARN))
|
||||
_log.warn("Received a packet on the wrong stream: " + packet + " connection: " + con);
|
||||
if (_log.shouldLog(Log.ERROR)) {
|
||||
Set cons = _manager.listConnections();
|
||||
StringBuffer buf = new StringBuffer(512);
|
||||
buf.append("Received a packet on the wrong stream: ");
|
||||
buf.append(packet);
|
||||
buf.append(" connection: ");
|
||||
buf.append(con);
|
||||
for (Iterator iter = cons.iterator(); iter.hasNext();) {
|
||||
Connection cur = (Connection)iter.next();
|
||||
buf.append(" ").append(cur);
|
||||
}
|
||||
_log.error(buf.toString(), new Exception("Wrong stream"));
|
||||
}
|
||||
}
|
||||
packet.releasePayload();
|
||||
}
|
||||
|
@@ -1,239 +1,239 @@
|
||||
/*
|
||||
* Created on Nov 9, 2004
|
||||
*
|
||||
* This file is part of susimail project, see http://susi.i2p/
|
||||
*
|
||||
* Copyright (C) 2004-2005 <susi23@mail.i2p>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* $Revision: 1.1 $
|
||||
*/
|
||||
package i2p.susi.webmail;
|
||||
|
||||
import i2p.susi.util.Config;
|
||||
import i2p.susi.util.ReadBuffer;
|
||||
import i2p.susi.webmail.encoding.Encoding;
|
||||
import i2p.susi.webmail.encoding.EncodingFactory;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* data structure to hold a single message, mostly used with folder view and sorting
|
||||
*
|
||||
* @author susi
|
||||
*/
|
||||
public class Mail {
|
||||
|
||||
public static final String DATEFORMAT = "date.format";
|
||||
|
||||
public static final String unknown = "unknown";
|
||||
|
||||
public int id, size;
|
||||
public String sender, reply, subject, dateString,
|
||||
formattedSender, formattedSubject, formattedDate,
|
||||
shortSender, shortSubject, quotedDate, uidl;
|
||||
public Date date;
|
||||
public ReadBuffer header, body;
|
||||
public MailPart part;
|
||||
Object[] to, cc;
|
||||
|
||||
public String error;
|
||||
|
||||
public boolean markForDeletion;
|
||||
|
||||
public boolean deleted;
|
||||
|
||||
public Mail() {
|
||||
id = 0;
|
||||
size = 0;
|
||||
formattedSender = unknown;
|
||||
formattedSubject = unknown;
|
||||
formattedDate = unknown;
|
||||
shortSender = unknown;
|
||||
shortSubject = unknown;
|
||||
quotedDate = unknown;
|
||||
error = "";
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param address
|
||||
* @return
|
||||
*/
|
||||
public static boolean validateAddress( String address )
|
||||
{
|
||||
if( address == null || address.length() == 0 )
|
||||
return false;
|
||||
|
||||
address = address.trim();
|
||||
|
||||
if( address.indexOf( "\n" ) != -1 ||
|
||||
address.indexOf( "\r" ) != -1 )
|
||||
return false;
|
||||
|
||||
String[] tokens = address.split( "[ \t]+" );
|
||||
|
||||
int addresses = 0;
|
||||
|
||||
for( int i = 0; i < tokens.length; i++ ) {
|
||||
if( tokens[i].matches( "^[^@< \t]+@[^> \t]+$" ) ||
|
||||
tokens[i].matches( "^<[^@< \t]+@[^> \t]+>$" ) )
|
||||
addresses++;
|
||||
}
|
||||
return addresses == 1;
|
||||
}
|
||||
/**
|
||||
* @param address
|
||||
* @return
|
||||
*/
|
||||
public static String getAddress(String address )
|
||||
{
|
||||
String[] tokens = address.split( "[ \t]+" );
|
||||
|
||||
for( int i = 0; i < tokens.length; i++ ) {
|
||||
if( tokens[i].matches( "^[^@< \t]+@[^> \t]+$" ) )
|
||||
return "<" + tokens[i] + ">";
|
||||
if( tokens[i].matches( "^<[^@< \t]+@[^> \t]+>$" ) )
|
||||
return tokens[i];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
public static boolean getRecipientsFromList( ArrayList recipients, String text, boolean ok )
|
||||
{
|
||||
if( text != null && text.length() > 0 ) {
|
||||
String[] ccs = text.split( "," );
|
||||
for( int i = 0; i < ccs.length; i++ ) {
|
||||
String recipient = ccs[i].trim();
|
||||
if( validateAddress( recipient ) ) {
|
||||
String str = getAddress( recipient );
|
||||
if( str != null && str.length() > 0 ) {
|
||||
recipients.add( str );
|
||||
}
|
||||
else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
public static void appendRecipients( StringBuffer buf, ArrayList recipients, String prefix )
|
||||
{
|
||||
for( Iterator it = recipients.iterator(); it.hasNext(); ) {
|
||||
buf.append( prefix );
|
||||
prefix ="\t";
|
||||
buf.append( (String)it.next() );
|
||||
buf.append( "\r\n" );
|
||||
}
|
||||
}
|
||||
public void parseHeaders()
|
||||
{
|
||||
DateFormat dateFormatter = new SimpleDateFormat( Config.getProperty( DATEFORMAT, "mm/dd/yyyy HH:mm:ss" ) );
|
||||
DateFormat mailDateFormatter = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.ENGLISH );
|
||||
|
||||
error = "";
|
||||
if( header != null ) {
|
||||
|
||||
boolean ok = true;
|
||||
|
||||
Encoding html = EncodingFactory.getEncoding( "HTML" );
|
||||
|
||||
if( html == null ) {
|
||||
error += "HTML encoder not found.<br>";
|
||||
ok = false;
|
||||
}
|
||||
|
||||
Encoding hl = EncodingFactory.getEncoding( "HEADERLINE" );
|
||||
|
||||
if( hl == null ) {
|
||||
error += "Header line encoder not found.<br>";
|
||||
ok = false;
|
||||
}
|
||||
|
||||
if( ok ) {
|
||||
|
||||
try {
|
||||
ReadBuffer decoded = hl.decode( header );
|
||||
BufferedReader reader = new BufferedReader( new InputStreamReader( new ByteArrayInputStream( decoded.content, decoded.offset, decoded.length ), "ISO-8859-1" ) );
|
||||
String line;
|
||||
while( ( line = reader.readLine() ) != null ) {
|
||||
if( line.length() == 0 )
|
||||
break;
|
||||
|
||||
if( line.startsWith( "From:" ) ) {
|
||||
sender = line.substring( 5 ).trim();
|
||||
formattedSender = getAddress( sender );
|
||||
shortSender = formattedSender.trim();
|
||||
if( shortSender.length() > 40 ) {
|
||||
shortSender = shortSender.substring( 0, 37 ).trim() + "...";
|
||||
}
|
||||
shortSender = html.encode( shortSender );
|
||||
}
|
||||
else if( line.startsWith( "Date:" ) ) {
|
||||
dateString = line.substring( 5 ).trim();
|
||||
try {
|
||||
date = mailDateFormatter.parse( dateString );
|
||||
formattedDate = dateFormatter.format( date );
|
||||
quotedDate = html.encode( dateString );
|
||||
}
|
||||
catch (ParseException e) {
|
||||
date = null;
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
else if( line.startsWith( "Subject:" ) ) {
|
||||
subject = line.substring( 8 ).trim();
|
||||
formattedSubject = subject;
|
||||
shortSubject = formattedSubject;
|
||||
if( formattedSubject.length() > 60 )
|
||||
shortSubject = formattedSubject.substring( 0, 57 ).trim() + "...";
|
||||
shortSubject = html.encode( shortSubject );
|
||||
}
|
||||
else if( line.toLowerCase().startsWith( "Reply-To:" ) ) {
|
||||
reply = Mail.getAddress( line.substring( 9 ).trim() );
|
||||
}
|
||||
else if( line.startsWith( "To:" ) ) {
|
||||
ArrayList list = new ArrayList();
|
||||
Mail.getRecipientsFromList( list, line.substring( 3 ).trim(), true );
|
||||
to = list.toArray();
|
||||
}
|
||||
else if( line.startsWith( "Cc:" ) ) {
|
||||
ArrayList list = new ArrayList();
|
||||
Mail.getRecipientsFromList( list, line.substring( 3 ).trim(), true );
|
||||
cc = list.toArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( Exception e ) {
|
||||
error += "Error parsing mail header: " + e.getClass().getName() + "<br>";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Created on Nov 9, 2004
|
||||
*
|
||||
* This file is part of susimail project, see http://susi.i2p/
|
||||
*
|
||||
* Copyright (C) 2004-2005 <susi23@mail.i2p>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* $Revision: 1.2 $
|
||||
*/
|
||||
package i2p.susi.webmail;
|
||||
|
||||
import i2p.susi.util.Config;
|
||||
import i2p.susi.util.ReadBuffer;
|
||||
import i2p.susi.webmail.encoding.Encoding;
|
||||
import i2p.susi.webmail.encoding.EncodingFactory;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.text.DateFormat;
|
||||
import java.text.ParseException;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Date;
|
||||
import java.util.Iterator;
|
||||
import java.util.Locale;
|
||||
|
||||
/**
|
||||
* data structure to hold a single message, mostly used with folder view and sorting
|
||||
*
|
||||
* @author susi
|
||||
*/
|
||||
public class Mail {
|
||||
|
||||
public static final String DATEFORMAT = "date.format";
|
||||
|
||||
public static final String unknown = "unknown";
|
||||
|
||||
public int id, size;
|
||||
public String sender, reply, subject, dateString,
|
||||
formattedSender, formattedSubject, formattedDate,
|
||||
shortSender, shortSubject, quotedDate, uidl;
|
||||
public Date date;
|
||||
public ReadBuffer header, body;
|
||||
public MailPart part;
|
||||
Object[] to, cc;
|
||||
|
||||
public String error;
|
||||
|
||||
public boolean markForDeletion;
|
||||
|
||||
public boolean deleted;
|
||||
|
||||
public Mail() {
|
||||
id = 0;
|
||||
size = 0;
|
||||
formattedSender = unknown;
|
||||
formattedSubject = unknown;
|
||||
formattedDate = unknown;
|
||||
shortSender = unknown;
|
||||
shortSubject = unknown;
|
||||
quotedDate = unknown;
|
||||
error = "";
|
||||
}
|
||||
/**
|
||||
*
|
||||
* @param address
|
||||
* @return
|
||||
*/
|
||||
public static boolean validateAddress( String address )
|
||||
{
|
||||
if( address == null || address.length() == 0 )
|
||||
return false;
|
||||
|
||||
address = address.trim();
|
||||
|
||||
if( address.indexOf( "\n" ) != -1 ||
|
||||
address.indexOf( "\r" ) != -1 )
|
||||
return false;
|
||||
|
||||
String[] tokens = address.split( "[ \t]+" );
|
||||
|
||||
int addresses = 0;
|
||||
|
||||
for( int i = 0; i < tokens.length; i++ ) {
|
||||
if( tokens[i].matches( "^[^@< \t]+@[^> \t]+$" ) ||
|
||||
tokens[i].matches( "^<[^@< \t]+@[^> \t]+>$" ) )
|
||||
addresses++;
|
||||
}
|
||||
return addresses == 1;
|
||||
}
|
||||
/**
|
||||
* @param address
|
||||
* @return
|
||||
*/
|
||||
public static String getAddress(String address )
|
||||
{
|
||||
String[] tokens = address.split( "[ \t]+" );
|
||||
|
||||
for( int i = 0; i < tokens.length; i++ ) {
|
||||
if( tokens[i].matches( "^[^@< \t]+@[^> \t]+$" ) )
|
||||
return "<" + tokens[i] + ">";
|
||||
if( tokens[i].matches( "^<[^@< \t]+@[^> \t]+>$" ) )
|
||||
return tokens[i];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
public static boolean getRecipientsFromList( ArrayList recipients, String text, boolean ok )
|
||||
{
|
||||
if( text != null && text.length() > 0 ) {
|
||||
String[] ccs = text.split( "," );
|
||||
for( int i = 0; i < ccs.length; i++ ) {
|
||||
String recipient = ccs[i].trim();
|
||||
if( validateAddress( recipient ) ) {
|
||||
String str = getAddress( recipient );
|
||||
if( str != null && str.length() > 0 ) {
|
||||
recipients.add( str );
|
||||
}
|
||||
else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
else {
|
||||
ok = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ok;
|
||||
}
|
||||
public static void appendRecipients( StringBuffer buf, ArrayList recipients, String prefix )
|
||||
{
|
||||
for( Iterator it = recipients.iterator(); it.hasNext(); ) {
|
||||
buf.append( prefix );
|
||||
prefix ="\t";
|
||||
buf.append( (String)it.next() );
|
||||
buf.append( "\r\n" );
|
||||
}
|
||||
}
|
||||
public void parseHeaders()
|
||||
{
|
||||
DateFormat dateFormatter = new SimpleDateFormat( Config.getProperty( DATEFORMAT, "mm/dd/yyyy HH:mm:ss" ) );
|
||||
DateFormat mailDateFormatter = new SimpleDateFormat("EEE, d MMM yyyy HH:mm:ss Z", Locale.ENGLISH );
|
||||
|
||||
error = "";
|
||||
if( header != null ) {
|
||||
|
||||
boolean ok = true;
|
||||
|
||||
Encoding html = EncodingFactory.getEncoding( "HTML" );
|
||||
|
||||
if( html == null ) {
|
||||
error += "HTML encoder not found.<br>";
|
||||
ok = false;
|
||||
}
|
||||
|
||||
Encoding hl = EncodingFactory.getEncoding( "HEADERLINE" );
|
||||
|
||||
if( hl == null ) {
|
||||
error += "Header line encoder not found.<br>";
|
||||
ok = false;
|
||||
}
|
||||
|
||||
if( ok ) {
|
||||
|
||||
try {
|
||||
ReadBuffer decoded = hl.decode( header );
|
||||
BufferedReader reader = new BufferedReader( new InputStreamReader( new ByteArrayInputStream( decoded.content, decoded.offset, decoded.length ), "ISO-8859-1" ) );
|
||||
String line;
|
||||
while( ( line = reader.readLine() ) != null ) {
|
||||
if( line.length() == 0 )
|
||||
break;
|
||||
|
||||
if( line.startsWith( "From:" ) ) {
|
||||
sender = line.substring( 5 ).trim();
|
||||
formattedSender = getAddress( sender );
|
||||
shortSender = formattedSender.trim();
|
||||
if( shortSender.length() > 40 ) {
|
||||
shortSender = shortSender.substring( 0, 37 ).trim() + "...";
|
||||
}
|
||||
shortSender = html.encode( shortSender );
|
||||
}
|
||||
else if( line.startsWith( "Date:" ) ) {
|
||||
dateString = line.substring( 5 ).trim();
|
||||
try {
|
||||
date = mailDateFormatter.parse( dateString );
|
||||
formattedDate = dateFormatter.format( date );
|
||||
quotedDate = html.encode( dateString );
|
||||
}
|
||||
catch (ParseException e) {
|
||||
date = null;
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
else if( line.startsWith( "Subject:" ) ) {
|
||||
subject = line.substring( 8 ).trim();
|
||||
formattedSubject = subject;
|
||||
shortSubject = formattedSubject;
|
||||
if( formattedSubject.length() > 60 )
|
||||
shortSubject = formattedSubject.substring( 0, 57 ).trim() + "...";
|
||||
shortSubject = html.encode( shortSubject );
|
||||
}
|
||||
else if( line.toLowerCase().startsWith( "Reply-To:" ) ) {
|
||||
reply = Mail.getAddress( line.substring( 9 ).trim() );
|
||||
}
|
||||
else if( line.startsWith( "To:" ) ) {
|
||||
ArrayList list = new ArrayList();
|
||||
Mail.getRecipientsFromList( list, line.substring( 3 ).trim(), true );
|
||||
to = list.toArray();
|
||||
}
|
||||
else if( line.startsWith( "Cc:" ) ) {
|
||||
ArrayList list = new ArrayList();
|
||||
Mail.getRecipientsFromList( list, line.substring( 3 ).trim(), true );
|
||||
cc = list.toArray();
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( Exception e ) {
|
||||
error += "Error parsing mail header: " + e.getClass().getName() + "<br>";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@@ -1,102 +1,102 @@
|
||||
/*
|
||||
* Created on Nov 23, 2004
|
||||
*
|
||||
* This file is part of susimail project, see http://susi.i2p/
|
||||
*
|
||||
* Copyright (C) 2004-2005 <susi23@mail.i2p>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* $Revision: 1.1 $
|
||||
*/
|
||||
package i2p.susi.webmail;
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
import i2p.susi.webmail.pop3.POP3MailBox;
|
||||
|
||||
/**
|
||||
* @author user
|
||||
*/
|
||||
public class MailCache {
|
||||
|
||||
public static final boolean FETCH_HEADER = true;
|
||||
public static final boolean FETCH_ALL = false;
|
||||
|
||||
private POP3MailBox mailbox;
|
||||
private String error;
|
||||
private Hashtable mails;
|
||||
private Object synchronizer;
|
||||
|
||||
MailCache( POP3MailBox mailbox ) {
|
||||
this.mailbox = mailbox;
|
||||
mails = new Hashtable();
|
||||
synchronizer = new Object();
|
||||
}
|
||||
/**
|
||||
* Fetch any needed data from pop3 server.
|
||||
*
|
||||
* @param id message id to get
|
||||
* @param headerOnly fetch only header lines?
|
||||
* @return
|
||||
*/
|
||||
public Mail getMail( String uidl, boolean headerOnly ) {
|
||||
|
||||
Mail mail = null, newMail = null;
|
||||
|
||||
if( mailbox != null ) {
|
||||
/*
|
||||
* synchronize update to hashtable
|
||||
*/
|
||||
synchronized( synchronizer ) {
|
||||
|
||||
mail = (Mail)mails.get( uidl );
|
||||
|
||||
if( mail == null ) {
|
||||
newMail = new Mail();
|
||||
mails.put( uidl, newMail );
|
||||
}
|
||||
}
|
||||
if( mail == null ) {
|
||||
mail = newMail;
|
||||
mail.uidl = uidl;
|
||||
mail.size = mailbox.getSize( uidl );
|
||||
}
|
||||
if( mail.size < 1024 )
|
||||
headerOnly = false;
|
||||
|
||||
boolean parseHeaders = mail.header == null;
|
||||
|
||||
if( headerOnly ) {
|
||||
if( mail.header == null )
|
||||
mail.header = mailbox.getHeader( uidl );
|
||||
}
|
||||
else {
|
||||
if( mail.body == null ) {
|
||||
mail.body = mailbox.getBody( uidl );
|
||||
if( mail.body != null ) {
|
||||
mail.header = mail.body;
|
||||
MailPart.parse( mail );
|
||||
}
|
||||
}
|
||||
}
|
||||
if( parseHeaders && mail.header != null )
|
||||
mail.parseHeaders();
|
||||
}
|
||||
if( mail != null && mail.deleted )
|
||||
mail = null;
|
||||
return mail;
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Created on Nov 23, 2004
|
||||
*
|
||||
* This file is part of susimail project, see http://susi.i2p/
|
||||
*
|
||||
* Copyright (C) 2004-2005 <susi23@mail.i2p>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the Free Software
|
||||
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
||||
*
|
||||
* $Revision: 1.2 $
|
||||
*/
|
||||
package i2p.susi.webmail;
|
||||
|
||||
import java.util.Hashtable;
|
||||
|
||||
import i2p.susi.webmail.pop3.POP3MailBox;
|
||||
|
||||
/**
|
||||
* @author user
|
||||
*/
|
||||
public class MailCache {
|
||||
|
||||
public static final boolean FETCH_HEADER = true;
|
||||
public static final boolean FETCH_ALL = false;
|
||||
|
||||
private POP3MailBox mailbox;
|
||||
private String error;
|
||||
private Hashtable mails;
|
||||
private Object synchronizer;
|
||||
|
||||
MailCache( POP3MailBox mailbox ) {
|
||||
this.mailbox = mailbox;
|
||||
mails = new Hashtable();
|
||||
synchronizer = new Object();
|
||||
}
|
||||
/**
|
||||
* Fetch any needed data from pop3 server.
|
||||
*
|
||||
* @param id message id to get
|
||||
* @param headerOnly fetch only header lines?
|
||||
* @return
|
||||
*/
|
||||
public Mail getMail( String uidl, boolean headerOnly ) {
|
||||
|
||||
Mail mail = null, newMail = null;
|
||||
|
||||
if( mailbox != null ) {
|
||||
/*
|
||||
* synchronize update to hashtable
|
||||
*/
|
||||
synchronized( synchronizer ) {
|
||||
|
||||
mail = (Mail)mails.get( uidl );
|
||||
|
||||
if( mail == null ) {
|
||||
newMail = new Mail();
|
||||
mails.put( uidl, newMail );
|
||||
}
|
||||
}
|
||||
if( mail == null ) {
|
||||
mail = newMail;
|
||||
mail.uidl = uidl;
|
||||
mail.size = mailbox.getSize( uidl );
|
||||
}
|
||||
if( mail.size < 1024 )
|
||||
headerOnly = false;
|
||||
|
||||
boolean parseHeaders = mail.header == null;
|
||||
|
||||
if( headerOnly ) {
|
||||
if( mail.header == null )
|
||||
mail.header = mailbox.getHeader( uidl );
|
||||
}
|
||||
else {
|
||||
if( mail.body == null ) {
|
||||
mail.body = mailbox.getBody( uidl );
|
||||
if( mail.body != null ) {
|
||||
mail.header = mail.body;
|
||||
MailPart.parse( mail );
|
||||
}
|
||||
}
|
||||
}
|
||||
if( parseHeaders && mail.header != null )
|
||||
mail.parseHeaders();
|
||||
}
|
||||
if( mail != null && mail.deleted )
|
||||
mail = null;
|
||||
return mail;
|
||||
}
|
||||
}
|
||||
|
@@ -92,11 +92,7 @@ public class UrlLauncher {
|
||||
|
||||
} else {
|
||||
|
||||
if (_shellCommand.executeSilentAndWaitTimed("konqueror " + url, 5))
|
||||
return true;
|
||||
|
||||
if (_shellCommand.executeSilentAndWaitTimed("galeon " + url, 5))
|
||||
return true;
|
||||
// fall through
|
||||
}
|
||||
|
||||
if (_shellCommand.executeSilentAndWaitTimed("opera -newpage " + url, 5))
|
||||
@@ -111,11 +107,18 @@ public class UrlLauncher {
|
||||
if (_shellCommand.executeSilentAndWaitTimed("netscape " + url, 5))
|
||||
return true;
|
||||
|
||||
if (_shellCommand.executeSilentAndWaitTimed("konqueror " + url, 5))
|
||||
return true;
|
||||
|
||||
if (_shellCommand.executeSilentAndWaitTimed("galeon " + url, 5))
|
||||
return true;
|
||||
|
||||
if (_shellCommand.executeSilentAndWaitTimed("links " + url, 5))
|
||||
return true;
|
||||
|
||||
if (_shellCommand.executeSilentAndWaitTimed("lynx " + url, 5))
|
||||
return true;
|
||||
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
Reference in New Issue
Block a user