landed the code for doing post attachments (the infamous paperclip)

This commit is contained in:
Eric J. Bowersox
2001-02-08 22:43:58 +00:00
parent 66b7fea53b
commit 70774ead7d
20 changed files with 1075 additions and 25 deletions
@@ -112,4 +112,6 @@ public interface ConferenceContext
public abstract TopicContext addTopic(String title, String zp_pseud, String zp_text)
throws DataException, AccessError;
public abstract TopicMessageContext getMessageByPostID(long postid) throws DataException, AccessError;
} // end interface ConferenceContext
@@ -66,6 +66,8 @@ public interface TopicContext
public abstract List getMessages(int low, int high) throws DataException, AccessError;
public abstract TopicMessageContext getMessage(int number) throws DataException, AccessError;
public abstract TopicMessageContext postNewMessage(long parent, String pseud, String text)
throws DataException, AccessError;
@@ -17,6 +17,7 @@
*/
package com.silverwrist.venice.core;
import java.io.InputStream;
import java.util.Date;
public interface TopicMessageContext
@@ -49,6 +50,14 @@ public interface TopicMessageContext
public abstract boolean hasAttachment();
public abstract String getAttachmentType();
public abstract String getAttachmentFilename();
public abstract int getAttachmentLength();
public abstract InputStream getAttachmentData() throws AccessError, DataException;
public abstract boolean canHide();
public abstract boolean canScribble();
@@ -61,4 +70,7 @@ public interface TopicMessageContext
public abstract void nuke() throws DataException, AccessError;
public abstract void attachData(String m_type, String file, int length, InputStream data)
throws AccessError, DataException;
} // end interface TopicMessageContext
@@ -884,6 +884,20 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend
} // end addTopic
public TopicMessageContext getMessageByPostID(long postid) throws DataException, AccessError
{
if (!(getConferenceData().canReadConference(level)))
{ // the luser can't even read the conference...
logger.error("user not permitted to change membership");
throw new AccessError("You are not permitted to read this conference.");
} // end if
// call down to the static function level
return TopicMessageUserContextImpl.getMessage(engine,this,datapool,postid);
} // end getMessageByPostID
/*--------------------------------------------------------------------------------
* Implementations from interface UserBackend
*--------------------------------------------------------------------------------
@@ -17,9 +17,11 @@
*/
package com.silverwrist.venice.core.impl;
import java.io.*;
import java.sql.*;
import java.util.*;
import org.apache.log4j.*;
import com.silverwrist.util.StringUtil;
import com.silverwrist.venice.db.*;
import com.silverwrist.venice.security.AuditRecord;
import com.silverwrist.venice.core.*;
@@ -33,6 +35,8 @@ class TopicMessageUserContextImpl implements TopicMessageContext
private static Category logger = Category.getInstance(TopicMessageUserContextImpl.class.getName());
private static final int MAX_ATTACH = 1048576; // TODO: should be a configurable parameter
/*--------------------------------------------------------------------------------
* Attributes
*--------------------------------------------------------------------------------
@@ -329,6 +333,113 @@ class TopicMessageUserContextImpl implements TopicMessageContext
} // end hasAttachment
public String getAttachmentType()
{
return mimetype;
} // end getAttachmentType
public String getAttachmentFilename()
{
return ((mimetype!=null) ? filename : null);
} // end getAttachmentFilename
public int getAttachmentLength()
{
return ((mimetype!=null) ? datalen : 0);
} // end getAttachmentLength
public InputStream getAttachmentData() throws AccessError, DataException
{
if (nuked || (scribble_date!=null))
{ // this would be an exercise in futility!
logger.error("cannot attach to a nuked or scribbled message");
throw new AccessError("You cannot attach data to a message that no longer exists.");
} // end if
Connection conn = null;
InputStream rc = null;
try
{ // open up a database connection
conn = datapool.getConnection();
// make sure we have current data
refresh(conn);
if (nuked || (scribble_date!=null))
{ // this would be an exercise in futility!
logger.error("cannot attach to a nuked or scribbled message");
throw new AccessError("You cannot attach data to a message that no longer exists.");
} // end if
if (mimetype==null)
{ // there is no attachment data!
logger.error("no attachment data to get");
throw new AccessError("There is no attachment data for this message.");
} // end if
// Create the statement and the SQL we need to retrieve the attachment.
Statement stmt = conn.createStatement();
StringBuffer sql = new StringBuffer("SELECT data FROM postattach WHERE postid = ");
sql.append(postid).append(';');
// Execute the query!
ResultSet rs = stmt.executeQuery(sql.toString());
if (!(rs.next()))
{ // there is no attachment data!
logger.error("no attachment data to get");
throw new AccessError("There is no attachment data for this message.");
} // end if
// Since the InputStream we get from JDBC will probably go away when the connection is dropped, we
// need to make a temporary copy of it, to a ByteArrayOutputStream. (Attachments can only be
// 1 Mb in size, so this shouldn't be a big problem.)
InputStream sqldata = rs.getBinaryStream(1);
ByteArrayOutputStream copy = new ByteArrayOutputStream(datalen);
byte[] buffer = new byte[4096];
int rd = sqldata.read(buffer);
while (rd>=0)
{ // write, then read again
if (rd>0)
copy.write(buffer,0,rd);
rd = sqldata.read(buffer);
} // end while
// Close both our streams, making sure we create the stream we need for the return value.
sqldata.close();
rc = new ByteArrayInputStream(copy.toByteArray());
copy.close();
} // end try
catch (SQLException e)
{ // turn this into a DataException
logger.error("DB error retrieving attachment: " + e.getMessage(),e);
throw new DataException("unable to retrieve attachment data: " + e.getMessage(),e);
} // end catch
catch (IOException e)
{ // turn this into a DataException too
logger.error("I/O error copying attachment data: " + e.getMessage(),e);
throw new DataException("unable to retrieve attachment data: " + e.getMessage(),e);
} // end catch
finally
{ // make sure we release the connection before we go
if (conn!=null)
datapool.releaseConnection(conn);
} // end finally
return rc;
} // end getAttachmentData
public boolean canHide()
{
return ((creator_uid==conf.realUID()) || conf.userCanHide());
@@ -641,6 +752,125 @@ class TopicMessageUserContextImpl implements TopicMessageContext
} // end nuke
public void attachData(String m_type, String file, int length, InputStream data)
throws AccessError, DataException
{
if (mimetype!=null)
{ // the message already has an attachment
logger.error("tried to attach data to a message that already has it!");
throw new AccessError("This message already has an attachment.");
} // end if
if (creator_uid!=conf.realUID())
{ // you can't attach to this message!
logger.error("tried to attach data to a message that's not yours!");
throw new AccessError("You are not permitted to add an attachment to this message.");
} // end if
if (nuked || (scribble_date!=null))
{ // this would be an exercise in futility!
logger.error("cannot attach to a nuked or scribbled message");
throw new AccessError("You cannot attach data to a message that no longer exists.");
} // end if
if (StringUtil.isStringEmpty(m_type))
{ // no MIME type specified
logger.error("no MIME type specified for attachment");
throw new AccessError("MIME type of attachment data not specified.");
} // end if
if (StringUtil.isStringEmpty(file))
{ // no MIME type specified
logger.error("no filename specified for attachment");
throw new AccessError("Filename of attachment data not specified.");
} // end if
if (length<=0)
{ // a length of 0 or less is just WRONG
logger.error("non-positive length specified for attachment");
throw new AccessError("Invalid attachment length.");
} // end if
else if (length>MAX_ATTACH)
{ // the attachment is too damn long!
logger.error("attachment is too long (" + String.valueOf(length) + " bytes)");
throw new AccessError("The attachment is too long to store. Maximum available length is "
+ String.valueOf(MAX_ATTACH) + " bytes.");
} // end else if
Connection conn = null;
AuditRecord ar = null;
try
{ // open up a database connection
conn = datapool.getConnection();
// make sure we have the right status to upload
refresh(conn);
if (nuked || (scribble_date!=null))
{ // this would be an exercise in futility!
logger.error("cannot attach to a nuked or scribbled message");
throw new AccessError("You cannot attach data to a message that no longer exists.");
} // end if
// Build the SQL statement that inserts the attachment. Note the use of the "?" to specify the
// BLOB parameter (the attachment data itself); this comes later.
StringBuffer sql =
new StringBuffer("INSERT INTO postattach (postid, datalen, filename, mimetype, data) VALUES (");
sql.append(postid).append(", ").append(length).append(", '").append(SQLUtil.encodeString(file));
sql.append("', '").append(SQLUtil.encodeString(m_type)).append("', ?);");
// Prepare the statement, set the BLOB parameter, and execute it.
PreparedStatement stmt = conn.prepareStatement(sql.toString());
stmt.setBinaryStream(1,data,length);
stmt.executeUpdate();
// Save off the local attachment values.
datalen = length;
filename = file;
mimetype = m_type;
// Generate an audit record indicating what we did.
ar = new AuditRecord(AuditRecord.UPLOAD_ATTACHMENT,conf.realUID(),conf.userRemoteAddress(),
conf.realSIGID(),"conf=" + String.valueOf(conf.realConfID()) + ",post="
+ String.valueOf(postid),"len=" + String.valueOf(length) + ",type=" + m_type
+ ",name=" + file);
} // end try
catch (SQLException e)
{ // turn this into a DataException
logger.error("DB error saving attachment: " + e.getMessage(),e);
throw new DataException("unable to save attachment data: " + e.getMessage(),e);
} // end catch
finally
{ // make sure we release the connection 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 finally
} // end attachData
/*--------------------------------------------------------------------------------
* External static operations
*--------------------------------------------------------------------------------
@@ -703,4 +933,105 @@ class TopicMessageUserContextImpl implements TopicMessageContext
} // end loadMessageRange
static TopicMessageContext loadMessage(EngineBackend engine, ConferenceBackend conf, DataPool datapool,
int topicid, int message_num) throws DataException
{
if (logger.isDebugEnabled())
logger.debug("loadMessage for conf # " + String.valueOf(conf.realConfID()) + ", topic #"
+ String.valueOf(topicid) + ", message " + String.valueOf(message_num));
Connection conn = null; // pooled database connection
try
{ // get a database connection
conn = datapool.getConnection();
Statement stmt = conn.createStatement();
// run a query to get all the posts in a particular topic
StringBuffer sql =
new StringBuffer("SELECT p.postid, p.parent, p.num, p.linecount, p.creator_uid, p.posted, "
+ "p.hidden, p.scribble_uid, p.scribble_date, p.pseud, a.datalen, a.filename, "
+ "a.mimetype FROM posts p LEFT JOIN postattach a ON p.postid = a.postid "
+ "WHERE p.topicid = ");
sql.append(topicid).append(" AND p.num = ").append(message_num).append(';');
if (logger.isDebugEnabled())
logger.debug("SQL: " + sql.toString());
ResultSet rs = stmt.executeQuery(sql.toString());
if (rs.next()) // create an object reference and return it
return new TopicMessageUserContextImpl(engine,conf,datapool,rs.getLong(1),rs.getLong(2),rs.getInt(3),
rs.getInt(4),rs.getInt(5),SQLUtil.getFullDateTime(rs,6),
rs.getBoolean(7),rs.getInt(8),SQLUtil.getFullDateTime(rs,9),
rs.getString(10),rs.getInt(11),rs.getString(12),
rs.getString(13));
// indicates an error...
throw new DataException("Message not found.");
} // end try
catch (SQLException e)
{ // turn SQLException into data exception
logger.error("DB error reading message entry: " + e.getMessage(),e);
throw new DataException("unable to retrieve message: " + e.getMessage(),e);
} // end catch
finally
{ // make sure we release the connection before we go
if (conn!=null)
datapool.releaseConnection(conn);
} // end finally
} // end loadMessage
static TopicMessageContext getMessage(EngineBackend engine, ConferenceBackend conf, DataPool datapool,
long postid) throws DataException
{
if (logger.isDebugEnabled())
logger.debug("getMessage for conf # " + String.valueOf(conf.realConfID()) + ", post #"
+ String.valueOf(postid));
Connection conn = null; // pooled database connection
try
{ // get a database connection
conn = datapool.getConnection();
Statement stmt = conn.createStatement();
StringBuffer sql =
new StringBuffer("SELECT p.postid, p.parent, p.num, p.linecount, p.creator_uid, p.posted, "
+ "p.hidden, p.scribble_uid, p.scribble_date, p.pseud, a.datalen, a.filename, "
+ "a.mimetype FROM topics t, posts p LEFT JOIN postattach a ON p.postid = a.postid "
+ "WHERE t.topicid = p.topicid AND t.confid = ");
sql.append(conf.realConfID()).append(" AND p.postid = ").append(postid).append(';');
if (logger.isDebugEnabled())
logger.debug("SQL: " + sql.toString());
ResultSet rs = stmt.executeQuery(sql.toString());
if (rs.next()) // create an object reference and return it
return new TopicMessageUserContextImpl(engine,conf,datapool,rs.getLong(1),rs.getLong(2),rs.getInt(3),
rs.getInt(4),rs.getInt(5),SQLUtil.getFullDateTime(rs,6),
rs.getBoolean(7),rs.getInt(8),SQLUtil.getFullDateTime(rs,9),
rs.getString(10),rs.getInt(11),rs.getString(12),
rs.getString(13));
// indicates an error...
throw new DataException("Message not found.");
} // end try
catch (SQLException e)
{ // turn SQLException into data exception
logger.error("DB error reading message entries: " + e.getMessage(),e);
throw new DataException("unable to retrieve messages: " + e.getMessage(),e);
} // end catch
finally
{ // make sure we release the connection before we go
if (conn!=null)
datapool.releaseConnection(conn);
} // end finally
} // end getMessage
} // end class TopicMessageUserContextImpl
@@ -569,6 +569,20 @@ class TopicUserContextImpl implements TopicContext
} // end getMessages
public TopicMessageContext getMessage(int number) throws DataException, AccessError
{
if (!(conf.userCanRead()))
{ // they can't read messages in this topic!
logger.error("trying to read postings w/o permission!");
throw new AccessError("You do not have permission to read messages in this conference.");
} // end if
// pass down to one of our static functiions to return this
return TopicMessageUserContextImpl.loadMessage(engine,conf,datapool,topicid,number);
} // end getMessage
public TopicMessageContext postNewMessage(long parent, String pseud, String text)
throws DataException, AccessError
{