some serious new feature implementation:

- cookie-based persistent logins
- expanded activity reporting
- "top" and "fixed" left menus are now dynamically generated from XML config,
  not hard coded
- error reporting enhanced and protection increased
- "About Venice" page first draft
- new means of "framing" static content within the Venice "frame"
- base page now includes the "footer" itself, "content" pages don't anymore
- general cleanup of some heavyweight old containers, replaced with faster
  Collections framework containers
- probably more, there's a LOT of stuff in here
This commit is contained in:
Eric J. Bowersox
2001-04-09 03:20:58 +00:00
parent 3d32fe95c5
commit 63fedc9db6
77 changed files with 2817 additions and 558 deletions
@@ -78,5 +78,17 @@ public interface TopicContext
public abstract void delete() throws DataException, AccessError;
public abstract List getActivePosters(int skip, int limit) throws DataException, AccessError;
public abstract List getActivePosters(int limit) throws DataException, AccessError;
public abstract List getActivePosters() throws DataException, AccessError;
public abstract List getActiveReaders(int skip, int limit) throws DataException, AccessError;
public abstract List getActiveReaders(int limit) throws DataException, AccessError;
public abstract List getActiveReaders() throws DataException, AccessError;
} // end interface TopicContext
@@ -103,4 +103,8 @@ public interface UserContext extends SearchMode
public abstract void setTimeZone(TimeZone timezone) throws DataException;
public abstract String getAuthenticationToken() throws AccessError, DataException;
public abstract boolean authenticateWithToken(String token) throws DataException;
} // end interface UserContext
@@ -20,7 +20,6 @@ package com.silverwrist.venice.core.impl;
import java.sql.*;
import java.util.*;
import org.apache.log4j.*;
import com.silverwrist.util.collections.*;
import com.silverwrist.venice.db.*;
import com.silverwrist.venice.core.*;
@@ -70,7 +69,7 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable
*/
private DataPool datapool; // used for doing database lookups
private Vector cats; // the actual category segments
private LinkedList cats; // the actual category segments
private int symlink = -1; // if our category is actually a symlink
private boolean do_hide = true; // do we hide subcategories marked hide_dir?
@@ -82,7 +81,7 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable
CategoryDescriptorImpl(DataPool datapool, int catid, boolean do_hide) throws DataException
{
this.datapool = datapool;
cats = new Vector();
cats = new LinkedList();
this.do_hide = do_hide;
if (catid<0)
@@ -114,7 +113,7 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable
throws SQLException, DataException
{
this.datapool = datapool;
cats = new Vector();
cats = new LinkedList();
this.do_hide = do_hide;
if (catid<0)
@@ -127,19 +126,18 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable
protected CategoryDescriptorImpl(DataPool datapool, int id, int symlink, String name, boolean do_hide)
{
this.datapool = datapool;
this.cats = new Vector();
this.cats = new LinkedList();
this.symlink = symlink;
this.do_hide = do_hide;
this.cats.add(new CatSegment(id,name));
this.cats.trimToSize();
} // end constructor
protected CategoryDescriptorImpl(CategoryDescriptorImpl other, int copy_levels)
{
this.datapool = other.datapool;
this.cats = new Vector();
this.cats = new LinkedList();
this.symlink = ((copy_levels==other.cats.size()) ? other.symlink : -1);
this.do_hide = other.do_hide;
@@ -147,7 +145,6 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable
{ // copy the references to the objects directly
for (int i=0; i<copy_levels; i++)
this.cats.add(other.cats.get(i));
this.cats.trimToSize();
} // end if
@@ -156,7 +153,7 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable
protected CategoryDescriptorImpl(CategoryDescriptorImpl other, int id, int symlink, String name)
{
this.datapool = other.datapool;
this.cats = new Vector();
this.cats = new LinkedList();
this.symlink = symlink;
this.do_hide = other.do_hide;
@@ -164,7 +161,6 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable
for (int i=0; i<other.cats.size(); i++)
this.cats.add(other.cats.get(i));
this.cats.add(new CatSegment(id,name));
this.cats.trimToSize();
} // end constructor
@@ -209,13 +205,11 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable
} // end if
cats.add(0,new CatSegment(curr_catid,rs.getString("name")));
cats.addFirst(new CatSegment(curr_catid,rs.getString("name")));
curr_catid = rs.getInt("parent");
} // end while
cats.trimToSize(); // shrink vector down to size
} // end doFillFromTop
/*--------------------------------------------------------------------------------
@@ -226,7 +220,7 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable
public int getCategoryID()
{
if (cats.size()>0)
return ((CatSegment)(cats.lastElement())).getID();
return ((CatSegment)(cats.getLast())).getID();
else
return -1;
@@ -260,7 +254,7 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable
} // end if
Connection conn = null;
Vector rc = new Vector();
ArrayList rc = new ArrayList();
try
{ // get a connection and create a statement
conn = datapool.getConnection();
@@ -294,8 +288,7 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable
} // end finally
// wrap the vector in a ReadOnlyVector object
return new ReadOnlyVector(rc);
return Collections.unmodifiableList(rc);
} // end getSubCategories
@@ -393,7 +386,7 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable
static List getTopLevelCategoryList(DataPool datapool, boolean do_hide) throws DataException
{
Connection conn = null;
Vector rc = new Vector();
ArrayList rc = new ArrayList();
try
{ // get a connection and create a statement
conn = datapool.getConnection();
@@ -426,8 +419,7 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable
} // end finally
// wrap the vector in a ReadOnlyVector object
return new ReadOnlyVector(rc);
return Collections.unmodifiableList(rc);
} // end getTopLevelCategoryList
@@ -438,7 +430,7 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable
logger.debug("Category search: mode = " + String.valueOf(mode) + ", term '" + term + "', offset = "
+ String.valueOf(offset) + ", count = " + String.valueOf(count));
Vector rc = new Vector();
ArrayList rc = new ArrayList();
Connection conn = null; // pooled database connection
try
@@ -505,7 +497,7 @@ class CategoryDescriptorImpl implements CategoryDescriptor, Cloneable
} // end finally
return new ReadOnlyVector(rc);
return Collections.unmodifiableList(rc);
} // end searchForCategories
@@ -20,7 +20,6 @@ package com.silverwrist.venice.core.impl;
import java.sql.*;
import java.util.*;
import org.apache.log4j.*;
import com.silverwrist.util.collections.*;
import com.silverwrist.venice.db.*;
import com.silverwrist.venice.security.AuditRecord;
import com.silverwrist.venice.security.DefaultLevels;
@@ -233,7 +232,7 @@ class ConferenceCoreData implements ConferenceData
throw new DataException("This conference has been deleted.");
Connection conn = null;
Vector rc = new Vector();
ArrayList rc = new ArrayList();
try
{ // get a database connection from this object
@@ -263,7 +262,7 @@ class ConferenceCoreData implements ConferenceData
} // end finally
return new ReadOnlyVector(rc);
return Collections.unmodifiableList(rc);
} // end getAlias
@@ -273,7 +272,7 @@ class ConferenceCoreData implements ConferenceData
throw new DataException("This conference has been deleted.");
Connection conn = null;
Vector rc = new Vector();
ArrayList rc = new ArrayList();
try
{ // get a database connection from this object
@@ -312,7 +311,7 @@ class ConferenceCoreData implements ConferenceData
} // end finally
return new ReadOnlyVector(rc);
return Collections.unmodifiableList(rc);
} // end getHosts
@@ -1059,7 +1058,7 @@ class ConferenceCoreData implements ConferenceData
if (logger.isDebugEnabled())
logger.debug("Member list: conference = " + confid);
Vector rc = new Vector(); // return from this function
ArrayList rc = new ArrayList(); // return from this function
Connection conn = null; // pooled database connection
try
@@ -1104,7 +1103,7 @@ class ConferenceCoreData implements ConferenceData
} // end finally
return new ReadOnlyVector(rc);
return Collections.unmodifiableList(rc);
} // end getMemberList
@@ -20,7 +20,6 @@ package com.silverwrist.venice.core.impl;
import java.sql.*;
import java.util.*;
import org.apache.log4j.*;
import com.silverwrist.util.collections.*;
import com.silverwrist.util.rcache.ReferencedData;
import com.silverwrist.venice.db.*;
import com.silverwrist.venice.htmlcheck.*;
@@ -95,19 +94,21 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend
} // end constructor
public void doFix(Statement stmt, int uid) throws SQLException
public void doFix(Statement stmt, int uid, java.util.Date date) throws SQLException
{
StringBuffer sql = new StringBuffer();
if (do_insert)
{ // construct an SQL INSERT statement
sql.append("INSERT INTO topicsettings (topicid, uid, last_message) VALUES (").append(topicid);
sql.append(", ").append(uid).append(", ").append(top_message).append(");");
sql.append("INSERT INTO topicsettings (topicid, uid, last_message, last_read) VALUES (");
sql.append(topicid).append(", ").append(uid).append(", ").append(top_message).append(", '");
sql.append(SQLUtil.encodeDate(date)).append("');");
} // end if
else
{ // construct an SQL UPDATE statement
sql.append("UPDATE topicsettings SET last_message = ").append(top_message).append(" WHERE topicid = ");
sql.append(topicid).append(" AND uid = ").append(uid).append(';');
sql.append("UPDATE topicsettings SET last_message = ").append(top_message).append(", last_read = '");
sql.append(SQLUtil.encodeDate(date)).append("' WHERE topicid = ").append(topicid);
sql.append(" AND uid = ").append(uid).append(';');
} // end else
@@ -941,8 +942,17 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend
// now we need to reset our last post date
Connection conn = null;
try
{ // get a connection and feed it to the touchPost function
{ // get a connection
conn = datapool.getConnection();
// create a new record in topicsettings (we WERE the first to post in the topic after all!)
Statement stmt = conn.createStatement();
StringBuffer sql = new StringBuffer("INSERT INTO topicsettings (topicid, uid, last_post) VALUES (");
sql.append(new_topic_inf.getTopicID()).append(", ").append(sig.realUID()).append(", '");
sql.append(SQLUtil.encodeDate(new_topic_inf.getCreateDate())).append("');");
stmt.executeUpdate(sql.toString());
// update the conference last-post information
touchPost(conn,new_topic_inf.getCreateDate());
} // end try
@@ -1024,16 +1034,17 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend
ResultSet rs = stmt.executeQuery(sql.toString());
// use the results to build up a list of FixSeenHelpers
Vector tmp = new Vector();
ArrayList tmp = new ArrayList();
while (rs.next())
tmp.add(new FixSeenHelper(rs.getInt(1),rs.getInt(2),rs.getBoolean(3)));
// now iterate over the list and call doFix on each one
Iterator it = tmp.iterator();
java.util.Date now = new java.util.Date();
while (it.hasNext())
{ // just hit each one in turn
FixSeenHelper fsh = (FixSeenHelper)(it.next());
fsh.doFix(stmt,sig.realUID());
fsh.doFix(stmt,sig.realUID(),now);
} // end while
@@ -1074,7 +1085,7 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend
} // end if
Connection conn = null;
Vector rc = new Vector();
ArrayList rc = new ArrayList();
try
{ // retrieve a connection from the datapool
@@ -1115,7 +1126,7 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend
} // end finally
return new ReadOnlyVector(rc);
return Collections.unmodifiableList(rc);
} // end getActivePosters
@@ -1141,7 +1152,7 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend
} // end if
Connection conn = null;
Vector rc = new Vector();
ArrayList rc = new ArrayList();
try
{ // retrieve a connection from the datapool
@@ -1182,7 +1193,7 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend
} // end finally
return new ReadOnlyVector(rc);
return Collections.unmodifiableList(rc);
} // end getActiveReaders
@@ -1700,7 +1711,7 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend
{
if (logger.isDebugEnabled())
logger.debug("getSIGConferences for SIG # " + sig.realSIGID() + ", user #" + sig.realUID());
Vector rc = new Vector(); // return from this function
ArrayList rc = new ArrayList(); // return from this function
Connection conn = null; // pooled database connection
try
@@ -1751,7 +1762,7 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend
} // end finally
return new ReadOnlyVector(rc);
return Collections.unmodifiableList(rc);
} // end getSIGConferences
@@ -1863,7 +1874,7 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend
logger.debug("getUserHotlist for user #" + user.realUID());
Connection conn = null; // pooled database connection
Vector rc = new Vector(); // return from this function
ArrayList rc = new ArrayList(); // return from this function
try
{ // get a database connection
@@ -1926,7 +1937,7 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend
} // end finally
return new ReadOnlyVector(rc);
return Collections.unmodifiableList(rc);
} // end getUserHotlist
@@ -93,4 +93,8 @@ public interface EngineBackend
public abstract void unpublish(long postid);
public abstract String generateRandomAuthString();
public abstract boolean isValidRandomAuthString(String s);
} // end interface EngineBackend
@@ -21,7 +21,6 @@ import java.sql.*;
import java.util.*;
import org.apache.log4j.*;
import com.silverwrist.util.StringUtil;
import com.silverwrist.util.collections.*;
import com.silverwrist.util.rcache.*;
import com.silverwrist.venice.db.*;
import com.silverwrist.venice.core.*;
@@ -1493,7 +1492,7 @@ class SIGCoreData implements SIGData, SIGDataBackend
logger.debug("Member search: SIG = " + sigid + ", field = " + field + ", mode = " + mode + ", term '"
+ term + "', offset = " + offset + ", count = " + count);
Vector rc = new Vector(); // return from this function
ArrayList rc = new ArrayList(); // return from this function
Connection conn = null; // pooled database connection
try
@@ -1584,7 +1583,7 @@ class SIGCoreData implements SIGData, SIGDataBackend
} // end finally
return new ReadOnlyVector(rc);
return Collections.unmodifiableList(rc);
} // end searchForMembers
@@ -1689,7 +1688,7 @@ class SIGCoreData implements SIGData, SIGDataBackend
if (logger.isDebugEnabled())
logger.debug("Member list: SIG = " + sigid);
Vector rc = new Vector(); // return from this function
ArrayList rc = new ArrayList(); // return from this function
Connection conn = null; // pooled database connection
try
@@ -1737,7 +1736,7 @@ class SIGCoreData implements SIGData, SIGDataBackend
} // end finally
return new ReadOnlyVector(rc);
return Collections.unmodifiableList(rc);
} // end getMemberList
@@ -21,7 +21,6 @@ import java.sql.*;
import java.util.*;
import org.apache.log4j.*;
import com.silverwrist.util.StringUtil;
import com.silverwrist.util.collections.*;
import com.silverwrist.util.rcache.ReferencedData;
import com.silverwrist.venice.db.*;
import com.silverwrist.venice.security.AuditRecord;
@@ -163,7 +162,7 @@ class SIGUserContextImpl implements SIGContext, SIGBackend
{
if (logger.isDebugEnabled())
logger.debug("setMemberValues(" + String.valueOf(granted_level) + ", " + String.valueOf(member)
+ ", " + String.valueOf(locked));
+ ", " + String.valueOf(locked) + ")");
if (user.realBaseLevel()>granted_level)
this.level = user.realBaseLevel();
@@ -1484,7 +1483,7 @@ class SIGUserContextImpl implements SIGContext, SIGBackend
{
if (logger.isDebugEnabled())
logger.debug("getMemberSIGEntries for user #" + String.valueOf(user.realUID()));
Vector rc = new Vector(); // return from this function
ArrayList rc = new ArrayList(); // return from this function
Connection conn = null; // pooled database connection
try
@@ -1521,7 +1520,7 @@ class SIGUserContextImpl implements SIGContext, SIGBackend
} // end finally
return new ReadOnlyVector(rc);
return Collections.unmodifiableList(rc);
} // end getMemberSIGEntries
@@ -1611,7 +1610,7 @@ class SIGUserContextImpl implements SIGContext, SIGBackend
+ ", term '" + term + "', offset = " + String.valueOf(offset) + ", count = "
+ String.valueOf(count));
Vector rc = new Vector(); // return from this function
ArrayList rc = new ArrayList(); // return from this function
Connection conn = null; // pooled database connection
try
@@ -1688,7 +1687,7 @@ class SIGUserContextImpl implements SIGContext, SIGBackend
} // end finally
return new ReadOnlyVector(rc);
return Collections.unmodifiableList(rc);
} // end searchForSIGs
@@ -1780,7 +1779,7 @@ class SIGUserContextImpl implements SIGContext, SIGBackend
logger.debug("reading SIGs in category " + String.valueOf(catid) + ", offset = "
+ String.valueOf(offset) + ", count = " + String.valueOf(count));
Vector rc = new Vector(); // return from this function
ArrayList rc = new ArrayList(); // return from this function
Connection conn = null; // pooled database connection
try
@@ -1823,7 +1822,7 @@ class SIGUserContextImpl implements SIGContext, SIGBackend
} // end finally
return new ReadOnlyVector(rc);
return Collections.unmodifiableList(rc);
} // end getSIGsInCategory
@@ -23,7 +23,6 @@ import java.util.*;
import java.util.zip.*;
import org.apache.log4j.*;
import com.silverwrist.util.StringUtil;
import com.silverwrist.util.collections.*;
import com.silverwrist.venice.db.*;
import com.silverwrist.venice.security.AuditRecord;
import com.silverwrist.venice.security.Capability;
@@ -1092,7 +1091,7 @@ class TopicMessageUserContextImpl implements TopicMessageContext
logger.debug("loadMessageRange for conf # " + conf.realConfID() + ", topic #" + topicid + ", range ["
+ post_low + ", " + post_high + "]");
Vector rc = new Vector();
ArrayList rc = new ArrayList();
Connection conn = null; // pooled database connection
try
@@ -1138,7 +1137,7 @@ class TopicMessageUserContextImpl implements TopicMessageContext
} // end finally
return new ReadOnlyVector(rc); // wrap the return vector
return Collections.unmodifiableList(rc); // wrap the return vector
} // end loadMessageRange
@@ -20,7 +20,6 @@ package com.silverwrist.venice.core.impl;
import java.sql.*;
import java.util.*;
import org.apache.log4j.*;
import com.silverwrist.util.collections.*;
import com.silverwrist.venice.db.*;
import com.silverwrist.venice.htmlcheck.*;
import com.silverwrist.venice.security.AuditRecord;
@@ -511,7 +510,9 @@ class TopicUserContextImpl implements TopicContext
try
{ // start by trying to see if we can update topicsettings directly
StringBuffer sql = new StringBuffer("UPDATE topicsettings SET last_message = ");
sql.append(last_msg).append(" WHERE topicid = ").append(topicid).append(" AND uid = ");
sql.append(last_msg).append(", last_read = '");
java.util.Date now = new java.util.Date();
sql.append(SQLUtil.encodeDate(now)).append("' WHERE topicid = ").append(topicid).append(" AND uid = ");
sql.append(conf.realUID()).append(';');
if (logger.isDebugEnabled())
logger.debug("SQL: " + sql.toString());
@@ -540,8 +541,9 @@ class TopicUserContextImpl implements TopicContext
// OK, just insert a new row into topicsettings, why dontcha...
sql.setLength(0);
sql.append("INSERT INTO topicsettings (topicid, uid, last_message) VALUES (").append(topicid);
sql.append(", ").append(conf.realUID()).append(", ").append(last_msg).append(");");
sql.append("INSERT INTO topicsettings (topicid, uid, last_message, last_read) VALUES (");
sql.append(topicid).append(", ").append(conf.realUID()).append(", ").append(last_msg).append(", '");
sql.append(SQLUtil.encodeDate(now)).append("');");
if (logger.isDebugEnabled())
logger.debug("SQL: " + sql.toString());
stmt.executeUpdate(sql.toString());
@@ -691,7 +693,7 @@ class TopicUserContextImpl implements TopicContext
// slap a lock on all the tables we need to touch
stmt.executeUpdate("LOCK TABLES confs WRITE, topics WRITE, posts WRITE, postdata WRITE, "
+ "confsettings WRITE, topicsettings READ;");
+ "confsettings WRITE, topicsettings WRITE;");
try
{ // refresh our current status and recheck allowed status
@@ -755,6 +757,25 @@ class TopicUserContextImpl implements TopicContext
sql.append(real_text).append("');");
stmt.executeUpdate(sql.toString());
// mark that we posted to the topic
sql.setLength(0);
sql.append("UPDATE topicsettings SET last_post = '").append(SQLUtil.encodeDate(posted_date));
sql.append("' WHERE topicid = ").append(topicid).append(" AND uid = ").append(conf.realUID());
sql.append(';');
if (logger.isDebugEnabled())
logger.debug("SQL: " + sql.toString());
if (stmt.executeUpdate(sql.toString())<1)
{ // we had no topicsettings record, add one
sql.setLength(0);
sql.append("INSERT INTO topicsettings (topicid, uid, last_post) VALUES (").append(topicid);
sql.append(", ").append(conf.realUID()).append(", '").append(SQLUtil.encodeDate(posted_date));
sql.append("');");
if (logger.isDebugEnabled())
logger.debug("SQL: " + sql.toString());
stmt.executeUpdate(sql.toString());
} // end if
// mark that we posted to the conference
conf.touchUpdate(conn,posted_date);
conf.touchPost(conn,posted_date);
@@ -925,6 +946,126 @@ class TopicUserContextImpl implements TopicContext
} // end delete
public List getActivePosters(int skip, int limit) throws DataException
{
Connection conn = null;
ArrayList rc = new ArrayList();
try
{ // retrieve a connection from the datapool
conn = datapool.getConnection();
Statement stmt = conn.createStatement();
// create the SQL statement to retrieve all posters
StringBuffer sql =
new StringBuffer("SELECT s.uid, u.username, s.last_read, s.last_post FROM topicsettings s, "
+ "users u WHERE u.uid = s.uid AND s.topicid = ");
sql.append(topicid).append(" AND u.is_anon = 0 AND ISNULL(s.last_post) = 0 ORDER BY s.last_post DESC");
if ((skip>=0) && (limit>0))
sql.append(" LIMIT ").append(skip).append(", ").append(limit);
sql.append(';');
// execute the statement
ResultSet rs = stmt.executeQuery(sql.toString());
while (rs.next())
{ // return all the records as ActiveUser data elements
ActiveUser usr = new ActiveUserImpl(rs.getInt(1),rs.getString(2),SQLUtil.getFullDateTime(rs,3),
SQLUtil.getFullDateTime(rs,4));
rc.add(usr);
} // end while
} // end try
catch (SQLException e)
{ // this becomes a DataException
logger.error("DB error getting active poster list: " + e.getMessage(),e);
throw new DataException("unable to get active poster listing: " + e.getMessage(),e);
} // end catch
finally
{ // make sure we release the connection before we go
if (conn!=null)
datapool.releaseConnection(conn);
} // end finally
return Collections.unmodifiableList(rc);
} // end getActivePosters
public List getActivePosters(int limit) throws DataException, AccessError
{
return getActivePosters(0,limit);
} // end getActivePosters
public List getActivePosters() throws DataException, AccessError
{
return getActivePosters(-1,-1);
} // end getActivePosters
public List getActiveReaders(int skip, int limit) throws DataException, AccessError
{
Connection conn = null;
ArrayList rc = new ArrayList();
try
{ // retrieve a connection from the datapool
conn = datapool.getConnection();
Statement stmt = conn.createStatement();
// create the SQL statement to retrieve all readers
StringBuffer sql =
new StringBuffer("SELECT s.uid, u.username, s.last_read, s.last_post FROM topicsettings s, "
+ "users u WHERE u.uid = s.uid AND s.topicid = ");
sql.append(topicid).append(" AND u.is_anon = 0 AND ISNULL(s.last_read) = 0 ORDER BY s.last_read DESC");
if ((skip>=0) && (limit>0))
sql.append(" LIMIT ").append(skip).append(", ").append(limit);
sql.append(';');
// execute the statement
ResultSet rs = stmt.executeQuery(sql.toString());
while (rs.next())
{ // return all the records as ActiveUser data elements
ActiveUser usr = new ActiveUserImpl(rs.getInt(1),rs.getString(2),SQLUtil.getFullDateTime(rs,3),
SQLUtil.getFullDateTime(rs,4));
rc.add(usr);
} // end while
} // end try
catch (SQLException e)
{ // this becomes a DataException
logger.error("DB error getting active reader list: " + e.getMessage(),e);
throw new DataException("unable to get active reader listing: " + e.getMessage(),e);
} // end catch
finally
{ // make sure we release the connection before we go
if (conn!=null)
datapool.releaseConnection(conn);
} // end finally
return Collections.unmodifiableList(rc);
} // end getActiveReaders
public List getActiveReaders(int limit) throws DataException, AccessError
{
return getActiveReaders(0,limit);
} // end getActiveReaders
public List getActiveReaders() throws DataException, AccessError
{
return getActiveReaders(-1,-1);
} // end getActiveReaders
/*--------------------------------------------------------------------------------
* External operations usable only from within the package
*--------------------------------------------------------------------------------
@@ -936,7 +1077,7 @@ class TopicUserContextImpl implements TopicContext
if (logger.isDebugEnabled())
logger.debug("getTopicList for conf # " + String.valueOf(conf.realConfID()) + ", user #"
+ String.valueOf(conf.realUID()));
Vector rc = new Vector(); // return from this function
ArrayList rc = new ArrayList(); // return from this function
Connection conn = null; // pooled database connection
try
@@ -1088,7 +1229,7 @@ class TopicUserContextImpl implements TopicContext
} // end finally
return new ReadOnlyVector(rc);
return Collections.unmodifiableList(rc);
} // end getTopicList
@@ -22,7 +22,6 @@ import java.sql.*;
import org.apache.log4j.*;
import com.silverwrist.util.LocaleFactory;
import com.silverwrist.util.StringUtil;
import com.silverwrist.util.collections.*;
import com.silverwrist.util.rcache.ReferencedData;
import com.silverwrist.venice.*;
import com.silverwrist.venice.core.*;
@@ -41,6 +40,9 @@ class UserContextImpl implements UserContext, UserBackend
private static Category logger = Category.getInstance(UserContextImpl.class.getName());
private static final String AUTH_TOKEN_PREFIX = "VQAT:";
private static final char AUTH_TOKEN_SEP = '|';
/*--------------------------------------------------------------------------------
* Attributes
*--------------------------------------------------------------------------------
@@ -902,7 +904,7 @@ class UserContextImpl implements UserContext, UserBackend
public List getSideBoxList() throws DataException
{
Connection conn = null;
Vector rc = new Vector();
ArrayList rc = new ArrayList();
try
{ // retrieve a connection from the data pool
@@ -934,7 +936,7 @@ class UserContextImpl implements UserContext, UserBackend
} // end finally
return new ReadOnlyVector(rc);
return Collections.unmodifiableList(rc);
} // end getSideBoxList
@@ -1052,6 +1054,235 @@ class UserContextImpl implements UserContext, UserBackend
} // end setTimeZone
public String getAuthenticationToken() throws AccessError, DataException
{
if (!isLoggedIn())
{ // can't generate an authentication token if we're not authenticated!
logger.error("UserContext not authenticated, cannot generate auth token");
throw new AccessError("You cannot generate an authentication token without logging in.");
} // end if
// Generate a random authentication string and poke it into the database for this user.
String tokenauth = engine.generateRandomAuthString();
Connection conn = null;
try
{ // retrieve a connection from the data pool
conn = datapool.getConnection();
Statement stmt = conn.createStatement();
StringBuffer sql = new StringBuffer("UPDATE users SET tokenauth = '");
sql.append(tokenauth).append("' WHERE uid = ").append(uid).append(';');
stmt.executeUpdate(sql.toString());
} // end try
catch (SQLException e)
{ // turn SQLException into data exception
logger.error("DB error setting token authentication string: " + e.getMessage(),e);
throw new DataException("Unable to set authentication token: " + e.getMessage(),e);
} // end catch
finally
{ // make sure the connection is released before we go
if (conn!=null)
datapool.releaseConnection(conn);
} // end finally
// Build the full authentication token string value.
int checkvalue = uid ^ tokenauth.hashCode();
StringBuffer buf = new StringBuffer(AUTH_TOKEN_PREFIX);
buf.append(uid).append(AUTH_TOKEN_SEP).append(tokenauth).append(AUTH_TOKEN_SEP).append(checkvalue);
buf.append(AUTH_TOKEN_SEP);
return buf.toString();
} // end getAuthenticationToken
public boolean authenticateWithToken(String token) throws DataException
{
if (isLoggedIn())
{ // already authenticated, can't authenticate again
logger.error("UserContext already authenticated (with uid " + uid + ")");
throw new InternalStateError("context already authenticated");
} // end if
if (logger.isDebugEnabled())
logger.debug("decoding authtoken: " + token);
// Pick apart the authentication token value.
if (!(token.startsWith(AUTH_TOKEN_PREFIX)))
{ // token parse error
logger.error("Token parse error: prefix not valid");
return false;
} // end if
int xstart = AUTH_TOKEN_PREFIX.length();
int xend = token.indexOf(AUTH_TOKEN_SEP,xstart);
if (xend<0)
{ // could not find the UID separator
logger.error("Token parse error: UID sep not found");
return false;
} // end if
int pending_uid;
try
{ // get the user ID
pending_uid = Integer.parseInt(token.substring(xstart,xend));
} // end try
catch (NumberFormatException nfe)
{ // we couldn't parse the UID
logger.error("Token parse error: invalid UID value");
return false;
} // end catch
xstart = xend + 1;
xend = token.indexOf(AUTH_TOKEN_SEP,xstart);
if (xend<0)
{ // could not find the auth string separator
logger.error("Token parse error: auth string sep not found");
return false;
} // end if
String pending_auth = token.substring(xstart,xend);
if (!(engine.isValidRandomAuthString(pending_auth)))
{ // the auth string is not valid by the rules under which it was generated
logger.error("Token parse error: invalid auth string value");
return false;
} // end if
xstart = xend + 1;
xend = token.indexOf(AUTH_TOKEN_SEP,xstart);
if (xend<0)
{ // could not find the checkvalue separator
logger.error("Token parse error: checkvalue sep not found");
return false;
} // end if
int checkvalue;
try
{ // get the check value
checkvalue = Integer.parseInt(token.substring(xstart,xend));
} // end try
catch (NumberFormatException nfe)
{ // we couldn't parse the checkvalue
logger.error("Token parse error: invalid checkvalue");
return false;
} // end catch
if (checkvalue!=(pending_uid ^ pending_auth.hashCode()))
{ // the checkvalue does not match what it should - possible corrupted token
logger.error("Token parse error: checkvalue does not match");
return false;
} // end if
// At this point, we now have a UID and authentication string extracted from the token.
// Proceed to authenticate.
if (logger.isDebugEnabled())
logger.debug("Authenticating user ID#" + pending_uid);
Connection conn = null;
AuditRecord ar = null;
try
{ // look for a user record matching this user ID
conn = datapool.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("SELECT * FROM users WHERE uid = " + pending_uid + ";");
if (!(rs.next()))
{ // user not found
logger.error("...user not found");
ar = new AuditRecord(AuditRecord.LOGIN_FAIL,0,remote_addr,"Bad token UID: " + pending_uid);
return false;
} // end if
if (rs.getBoolean("is_anon"))
{ // can't log in as Anonymous Honyak
logger.error("...user is the Anonymous Honyak, can't explicitly login");
ar = new AuditRecord(AuditRecord.LOGIN_FAIL,pending_uid,remote_addr,"Anonymous user");
return false;
} // end if
if (rs.getBoolean("lockout"))
{ // account locked out
logger.error("...user is locked out by the Admin");
ar = new AuditRecord(AuditRecord.LOGIN_FAIL,pending_uid,remote_addr,"Account locked out");
return false;
} // end if
// compare the stored token auth value to what we have
if (!(pending_auth.equals(rs.getString("tokenauth"))))
{ // the auth string is bad - we can't log in
logger.warn("...invalid authentication string");
ar = new AuditRecord(AuditRecord.LOGIN_FAIL,pending_uid,remote_addr,"Bad auth-string");
return false;
} // end if
if (logger.isDebugEnabled())
logger.debug("...authenticated");
// we're authenticated - load the user data into the context
loadUserData(rs);
// update the "last access" time in the database
java.util.Date mydate = new java.util.Date();
stmt.executeUpdate("UPDATE users SET lastaccess = '" + SQLUtil.encodeDate(mydate)
+ "' WHERE uid = " + uid + ";");
// update the "last access" time in this object
last_access = mydate;
// an audit record indicating we logged in OK
ar = new AuditRecord(AuditRecord.LOGIN_OK,uid,remote_addr);
if (logger.isDebugEnabled())
logger.debug("...context loaded, we're ready :-)");
} // end try
catch (SQLException e)
{ // database error - this is a DataException
logger.error("DB error reading user data: " + e.getMessage(),e);
throw new DataException("unable to access user data: " + e.getMessage(),e);
} // end catch
finally
{ // make sure the connection is released before we go
try
{ // save off the audit record before we go, though
if ((ar!=null) && (conn!=null))
ar.store(conn);
} // end try
catch (SQLException e)
{ // we couldn't store the audit record!
logger.error("DB error saving audit record: " + e.getMessage(),e);
} // end catch
if (conn!=null)
datapool.releaseConnection(conn);
} // end if
return true; // token authentication worked!
} // end authenticateWithToken
/*--------------------------------------------------------------------------------
* Implementations from interface UserBackend
*--------------------------------------------------------------------------------
@@ -23,7 +23,6 @@ import org.apache.log4j.*;
import org.w3c.dom.*;
import com.silverwrist.util.StringUtil;
import com.silverwrist.util.DOMElementHelper;
import com.silverwrist.util.collections.*;
import com.silverwrist.util.rcache.*;
import com.silverwrist.venice.core.*;
import com.silverwrist.venice.db.*;
@@ -385,6 +384,10 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
private static Category logger = Category.getInstance(VeniceEngineImpl.class.getName());
private static final String AUTH_ALPHABET =
"0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz./";
private static final int AUTH_STRING_LEN = 32;
/*--------------------------------------------------------------------------------
* Attributes
*--------------------------------------------------------------------------------
@@ -406,7 +409,7 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
private int[] gp_ints; // global integer parameters
private MasterSideBox[] sideboxes; // master sidebox table
private Hashtable sidebox_ids = new Hashtable(); // maps sidebox IDs to MasterSideBox objects
private Vector cache_fp_posts = new Vector(); // all posts that have been published to front page
private LinkedList cache_fp_posts = new LinkedList(); // all posts that have been published to front page
private boolean cache_fp_posts_busy = false; // busy flag for above vector
/*--------------------------------------------------------------------------------
@@ -482,7 +485,7 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
this.config = config;
Vector dictionary_tmp;
ArrayList dictionary_tmp;
try
{ // first, verify that this is a valid configuration
Element root = config.getDocumentElement();
@@ -542,7 +545,7 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
} // end if
// Retrieve the list of dictionary files to load into the spellchecker.
dictionary_tmp = new Vector();
dictionary_tmp = new ArrayList();
NodeList dict_nodes = dict_sect.getChildNodes();
for (i=0; i<dict_nodes.getLength(); i++)
{ // scan the <dictionary> element looking for <file> elements
@@ -625,15 +628,13 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
logger.debug(max_value + " features loaded from database");
// load the master sidebox table
Vector sidebox_tmp = new Vector();
ArrayList sidebox_tmp = new ArrayList();
rs = stmt.executeQuery("SELECT * FROM refsidebox ORDER BY boxid;");
while (rs.next())
sidebox_tmp.add(new MasterSideBox(rs));
// store the real master sidebox table as an array
sideboxes = new MasterSideBox[sidebox_tmp.size()];
for (i=0; i<sidebox_tmp.size(); i++)
sideboxes[i] = (MasterSideBox)(sidebox_tmp.get(i));
sideboxes = (MasterSideBox[])(sidebox_tmp.toArray(new MasterSideBox[0]));
if (logger.isDebugEnabled())
logger.debug(sideboxes.length + " sidebox definitions loaded from database");
@@ -671,10 +672,7 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
UserNameRewriter username_rewriter = new UserNameRewriter(datapool);
// Create the LazyLexicon that holds our dictionary files, and add it to the SpellingRewriter.
String[] dictfiles = new String[dictionary_tmp.size()];
for (i=0; i<dictionary_tmp.size(); i++)
dictfiles[i] = (String)(dictionary_tmp.get(i));
LazyTreeLexicon lex = new LazyTreeLexicon(dictfiles);
LazyTreeLexicon lex = new LazyTreeLexicon((String[])(dictionary_tmp.toArray(new String[0])));
spell_rewriter.addDictionary(lex);
html_configs = new HTMLCheckerConfig[4]; // create the array
@@ -771,7 +769,7 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
{
checkInitialized();
Connection conn = null;
Vector rc = new Vector();
ArrayList rc = new ArrayList();
try
{ // do a SELECT on the refcountry table to load the master country list
@@ -803,7 +801,7 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
} // end finally
return new ReadOnlyVector(rc);
return Collections.unmodifiableList(rc);
} // end getCountryList
@@ -811,7 +809,7 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
{
checkInitialized();
Connection conn = null;
Vector rc = new Vector();
ArrayList rc = new ArrayList();
try
{ // do a SELECT on the refcountry table to load the master country list
@@ -843,7 +841,7 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
} // end finally
return new ReadOnlyVector(rc);
return Collections.unmodifiableList(rc);
} // end getLanguageList
@@ -1225,8 +1223,8 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
logger.debug("User search: field = " + field + ", mode = " + mode + ", term '" + term + "', offset = "
+ offset + ", count = " + count);
Vector rc = new Vector(); // return from this function
Connection conn = null; // pooled database connection
ArrayList rc = new ArrayList(); // return from this function
Connection conn = null; // pooled database connection
try
{ // get a database connection
@@ -1309,7 +1307,7 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
} // end finally
return new ReadOnlyVector(rc);
return Collections.unmodifiableList(rc);
} // end searchForUsers
@@ -1506,7 +1504,7 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
public List getPublishedMessages(boolean all) throws DataException
{
Vector rc = new Vector();
ArrayList rc = new ArrayList();
synchronized (this)
{ // Make sure the cache is in condition.
@@ -1542,7 +1540,7 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
if (all) // add the extra postings to the list
PublishedMessageImpl.backfillReturn(rc,datapool);
return new ReadOnlyVector(rc);
return Collections.unmodifiableList(rc);
} // end getPublishedMessages
@@ -1713,7 +1711,7 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
public List getSIGFeatureSet(BitSet enabled_features, int level, boolean read_privs)
{
checkInitialized();
Vector rc = new Vector();
ArrayList rc = new ArrayList();
for (int i=0; i<features.length; i++)
if (enabled_features.get(i) && features[i].featureAllowed(level,read_privs))
{ // this feature must be included in the eventual output set
@@ -1728,14 +1726,14 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
} // end if and for
if (insert_me) // insert at end by default
rc.addElement(elt);
rc.add(elt);
} // end if and for
if (logger.isDebugEnabled())
logger.debug("getSIGFeatureSet() loaded " + rc.size() + " elements");
return new ReadOnlyVector(rc); // wrap the vector for return
return Collections.unmodifiableList(rc); // wrap the vector for return
} // end getSIGFeatureSet
@@ -1934,9 +1932,9 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
{
if (pubmsg!=null)
{ // add the new message
cache_fp_posts.add(0,pubmsg);
cache_fp_posts.addFirst(pubmsg);
while (cache_fp_posts.size()>gp_ints[IP_NUMFRONTPAGEPOSTS])
cache_fp_posts.remove(cache_fp_posts.size()-1);
cache_fp_posts.removeLast();
} // end pubmsg
@@ -1962,4 +1960,28 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
} // end unpublish
public String generateRandomAuthString()
{
StringBuffer buf = new StringBuffer(AUTH_STRING_LEN);
for (int i=0; i<AUTH_STRING_LEN; i++)
buf.append(AUTH_ALPHABET.charAt(rng.nextInt(AUTH_ALPHABET.length())));
return buf.toString();
} // end generateRandomAuthString
public boolean isValidRandomAuthString(String s)
{
if (s.length()!=AUTH_STRING_LEN)
return false;
for (int i=0; i<AUTH_STRING_LEN; i++)
{ // verify each authentication character in turn
if (AUTH_ALPHABET.indexOf(s.charAt(i))<0)
return false;
} // end for
return true; // all tests passed - ship it!
} // end isValidRandomAuthString
} // end class VeniceEngineImpl