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

26
TODO
View File

@ -25,17 +25,27 @@ Lots!
statements in the case of joined queries (no need to SELECT table.column statements in the case of joined queries (no need to SELECT table.column
AS name). AS name).
- Getting conferencing in there - but still not there yet. We need topic and - Functions still to do on conferencing "posts" page:
post implementations, and UI. Hide/Show Topic
Next & Keep New (make it actually Keep New)
Freeze/Unfreeze Topic
Archive/Unarchive Topic
Delete Topic
Make number of "viewable" posts per page a config option
Display the message locator (i.e. <Playground.56.123>) above each message
Hide/Show Post
Scribble Post
Nuke Post
Put the HTML Guide in (for all pages w/post boxes)
- Slippage during posting is still untested.
- Functions still to do on conferencing "topics" page:
Manage Conference
Add Conference To Hotlist
- Implement conference hotlist for users. - Implement conference hotlist for users.
- The HTML checker is back together and almost all integrated into the
Venice engine, but I still need to initialize the dictionary. It's going
to require configuration entries in the Venice XML config file, and the
use of LazyLexicon (to load the dictionary in the background while people
log in).
- Not everybody likes purple. Provide a way to change the default colors. - Not everybody likes purple. Provide a way to change the default colors.
Probably via entries in render-config.xml. Of course, if we go to a Probably via entries in render-config.xml. Of course, if we go to a
different rendering system eventually, we won't need this. different rendering system eventually, we won't need this.

View File

@ -150,6 +150,14 @@
<servlet-class>com.silverwrist.venice.servlets.PostMessage</servlet-class> <servlet-class>com.silverwrist.venice.servlets.PostMessage</servlet-class>
</servlet> </servlet>
<servlet>
<servlet-name>attachment</servlet-name>
<description>
Andles downloading and uploading attachments.
</description>
<servlet-class>com.silverwrist.venice.servlets.Attachment</servlet-class>
</servlet>
<!-- the following are test servlets, they should go away --> <!-- the following are test servlets, they should go away -->
<servlet> <servlet>
@ -213,6 +221,11 @@
<url-pattern>/post</url-pattern> <url-pattern>/post</url-pattern>
</servlet-mapping> </servlet-mapping>
<servlet-mapping>
<servlet-name>attachment</servlet-name>
<url-pattern>/attachment</url-pattern>
</servlet-mapping>
<!-- the following are test servlets, they should go away --> <!-- the following are test servlets, they should go away -->
<servlet-mapping> <servlet-mapping>
<servlet-name>testformdata</servlet-name> <servlet-name>testformdata</servlet-name>

View File

@ -420,6 +420,7 @@ INSERT INTO refaudit (type, descr) VALUES
(311, 'Hide Message'), (311, 'Hide Message'),
(312, 'Scribble Message'), (312, 'Scribble Message'),
(313, 'Nuke Message'), (313, 'Nuke Message'),
(314, 'Upload Message Attachment'),
(9999999, 'DUMMY'); (9999999, 'DUMMY');
# The ISO 3166 two-letter country codes. Source is # The ISO 3166 two-letter country codes. Source is

View File

@ -7,7 +7,7 @@
* WARRANTY OF ANY KIND, either express or implied. See the License for the specific * WARRANTY OF ANY KIND, either express or implied. See the License for the specific
* language governing rights and limitations under the License. * language governing rights and limitations under the License.
* *
* The Original Code is the Venice Web Community System. * The Original Code is the Venice Web Communities System.
* *
* The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>, * The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
* for Silverwrist Design Studios. Portions created by Eric J. Bowersox are * for Silverwrist Design Studios. Portions created by Eric J. Bowersox are
@ -32,9 +32,10 @@ import javax.servlet.*;
public class ServletMultipartHandler public class ServletMultipartHandler
{ {
private MimeMultipart multipart; // holds all the multipart data /*--------------------------------------------------------------------------------
private Hashtable param_byname; // parameters by name * Internal wrapper around the ServletRequest that implements DataSource
private Vector param_order; // parameters in order *--------------------------------------------------------------------------------
*/
class ServletDataSource implements DataSource class ServletDataSource implements DataSource
{ {
@ -75,6 +76,11 @@ public class ServletMultipartHandler
} // end class ServletDataSource } // end class ServletDataSource
/*--------------------------------------------------------------------------------
* Internal class representing a data value
*--------------------------------------------------------------------------------
*/
static class MultipartDataValue implements Blob static class MultipartDataValue implements Blob
{ {
private byte[] actual_data; // the actual data we contain private byte[] actual_data; // the actual data we contain
@ -83,7 +89,7 @@ public class ServletMultipartHandler
{ {
InputStream in = part.getInputStream(); InputStream in = part.getInputStream();
ByteArrayOutputStream out = new ByteArrayOutputStream(); ByteArrayOutputStream out = new ByteArrayOutputStream();
byte[] copybuf = new byte[1024]; byte[] copybuf = new byte[4096];
int ct = in.read(copybuf); int ct = in.read(copybuf);
while (ct>=0) while (ct>=0)
{ // do a simple read and write { // do a simple read and write
@ -133,6 +139,11 @@ public class ServletMultipartHandler
} // end class MultipartDataValue } // end class MultipartDataValue
/*--------------------------------------------------------------------------------
* Internal class representing a request parameter
*--------------------------------------------------------------------------------
*/
class MultipartParameter class MultipartParameter
{ {
private MimeBodyPart part; // the actual body part data private MimeBodyPart part; // the actual body part data
@ -178,7 +189,8 @@ public class ServletMultipartHandler
public String getValue() public String getValue()
{ {
if (filename!=null) if (filename!=null)
return filename; return filename; // "value" for file parts is the filename
try try
{ // Retrieve the part's actual content and convert it to a String. (Since non-file { // Retrieve the part's actual content and convert it to a String. (Since non-file
// fields are of type text/plain, the Object "val" should actually be a String, in // fields are of type text/plain, the Object "val" should actually be a String, in
@ -228,7 +240,7 @@ public class ServletMultipartHandler
public MultipartDataValue getContent() throws ServletMultipartException public MultipartDataValue getContent() throws ServletMultipartException
{ {
if (filename==null) if (filename==null)
return null; return null; // not a file parameter
if (cached_value==null) if (cached_value==null)
{ // we don't have the value cached yet { // we don't have the value cached yet
@ -256,6 +268,20 @@ public class ServletMultipartHandler
} // end class MultipartParameter } // end class MultipartParameter
/*--------------------------------------------------------------------------------
* Attributes
*--------------------------------------------------------------------------------
*/
private MimeMultipart multipart; // holds all the multipart data
private Hashtable param_byname; // parameters by name
private Vector param_order; // parameters in order
/*--------------------------------------------------------------------------------
* Constructor
*--------------------------------------------------------------------------------
*/
public ServletMultipartHandler(ServletRequest request) throws ServletMultipartException public ServletMultipartHandler(ServletRequest request) throws ServletMultipartException
{ {
if (!canHandle(request)) if (!canHandle(request))
@ -286,6 +312,11 @@ public class ServletMultipartHandler
} // end constructor } // end constructor
/*--------------------------------------------------------------------------------
* External static operations
*--------------------------------------------------------------------------------
*/
/** /**
* Returns <CODE>true</CODE> if the given <CODE>ServletRequest</CODE> can be handled by * Returns <CODE>true</CODE> if the given <CODE>ServletRequest</CODE> can be handled by
* the <CODE>ServletMultipartHandler</CODE>, <CODE>false</CODE> if not. * the <CODE>ServletMultipartHandler</CODE>, <CODE>false</CODE> if not.
@ -301,6 +332,11 @@ public class ServletMultipartHandler
} // end canHandle } // end canHandle
/*--------------------------------------------------------------------------------
* External operations
*--------------------------------------------------------------------------------
*/
public Enumeration getNames() public Enumeration getNames()
{ {
Vector tmp_vector = new Vector(); Vector tmp_vector = new Vector();

View File

@ -112,4 +112,6 @@ public interface ConferenceContext
public abstract TopicContext addTopic(String title, String zp_pseud, String zp_text) public abstract TopicContext addTopic(String title, String zp_pseud, String zp_text)
throws DataException, AccessError; throws DataException, AccessError;
public abstract TopicMessageContext getMessageByPostID(long postid) throws DataException, AccessError;
} // end interface ConferenceContext } // end interface ConferenceContext

View File

@ -66,6 +66,8 @@ public interface TopicContext
public abstract List getMessages(int low, int high) throws DataException, AccessError; 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) public abstract TopicMessageContext postNewMessage(long parent, String pseud, String text)
throws DataException, AccessError; throws DataException, AccessError;

View File

@ -17,6 +17,7 @@
*/ */
package com.silverwrist.venice.core; package com.silverwrist.venice.core;
import java.io.InputStream;
import java.util.Date; import java.util.Date;
public interface TopicMessageContext public interface TopicMessageContext
@ -49,6 +50,14 @@ public interface TopicMessageContext
public abstract boolean hasAttachment(); 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 canHide();
public abstract boolean canScribble(); public abstract boolean canScribble();
@ -61,4 +70,7 @@ public interface TopicMessageContext
public abstract void nuke() throws DataException, AccessError; 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 } // end interface TopicMessageContext

View File

@ -884,6 +884,20 @@ class ConferenceUserContextImpl implements ConferenceContext, ConferenceBackend
} // end addTopic } // 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 * Implementations from interface UserBackend
*-------------------------------------------------------------------------------- *--------------------------------------------------------------------------------

View File

@ -17,9 +17,11 @@
*/ */
package com.silverwrist.venice.core.impl; package com.silverwrist.venice.core.impl;
import java.io.*;
import java.sql.*; import java.sql.*;
import java.util.*; import java.util.*;
import org.apache.log4j.*; import org.apache.log4j.*;
import com.silverwrist.util.StringUtil;
import com.silverwrist.venice.db.*; import com.silverwrist.venice.db.*;
import com.silverwrist.venice.security.AuditRecord; import com.silverwrist.venice.security.AuditRecord;
import com.silverwrist.venice.core.*; import com.silverwrist.venice.core.*;
@ -33,6 +35,8 @@ class TopicMessageUserContextImpl implements TopicMessageContext
private static Category logger = Category.getInstance(TopicMessageUserContextImpl.class.getName()); private static Category logger = Category.getInstance(TopicMessageUserContextImpl.class.getName());
private static final int MAX_ATTACH = 1048576; // TODO: should be a configurable parameter
/*-------------------------------------------------------------------------------- /*--------------------------------------------------------------------------------
* Attributes * Attributes
*-------------------------------------------------------------------------------- *--------------------------------------------------------------------------------
@ -329,6 +333,113 @@ class TopicMessageUserContextImpl implements TopicMessageContext
} // end hasAttachment } // 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() public boolean canHide()
{ {
return ((creator_uid==conf.realUID()) || conf.userCanHide()); return ((creator_uid==conf.realUID()) || conf.userCanHide());
@ -641,6 +752,125 @@ class TopicMessageUserContextImpl implements TopicMessageContext
} // end nuke } // 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 * External static operations
*-------------------------------------------------------------------------------- *--------------------------------------------------------------------------------
@ -703,4 +933,105 @@ class TopicMessageUserContextImpl implements TopicMessageContext
} // end loadMessageRange } // 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 } // end class TopicMessageUserContextImpl

View File

@ -569,6 +569,20 @@ class TopicUserContextImpl implements TopicContext
} // end getMessages } // 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) public TopicMessageContext postNewMessage(long parent, String pseud, String text)
throws DataException, AccessError throws DataException, AccessError
{ {

View File

@ -58,5 +58,6 @@ public interface Audit
public static final int HIDE_MESSAGE = 311; public static final int HIDE_MESSAGE = 311;
public static final int SCRIBBLE_MESSAGE = 312; public static final int SCRIBBLE_MESSAGE = 312;
public static final int NUKE_MESSAGE = 313; public static final int NUKE_MESSAGE = 313;
public static final int UPLOAD_ATTACHMENT = 314;
} // end interface Audit } // end interface Audit

View File

@ -0,0 +1,427 @@
/*
* The contents of this file are subject to the Mozilla Public License Version 1.1
* (the "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at <http://www.mozilla.org/MPL/>.
*
* Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
* WARRANTY OF ANY KIND, either express or implied. See the License for the specific
* language governing rights and limitations under the License.
*
* The Original Code is the Venice Web Communities System.
*
* The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
* for Silverwrist Design Studios. Portions created by Eric J. Bowersox are
* Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved.
*
* Contributor(s):
*/
package com.silverwrist.venice.servlets;
import java.io.*;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import org.apache.log4j.*;
import com.silverwrist.util.StringUtil;
import com.silverwrist.util.ServletMultipartHandler;
import com.silverwrist.util.ServletMultipartException;
import com.silverwrist.venice.ValidationException;
import com.silverwrist.venice.core.*;
import com.silverwrist.venice.servlets.format.*;
public class Attachment extends VeniceServlet
{
/*--------------------------------------------------------------------------------
* Static data members
*--------------------------------------------------------------------------------
*/
private static Category logger = Category.getInstance(Attachment.class.getName());
/*--------------------------------------------------------------------------------
* Internal functions
*--------------------------------------------------------------------------------
*/
private static SIGContext getSIGParameter(String str, UserContext user)
throws ValidationException, DataException
{
if (str==null)
{ // no SIG parameter - bail out now!
logger.error("SIG parameter not specified!");
throw new ValidationException("No SIG specified.");
} // end if
try
{ // turn the string into a SIGID, and thence to a SIGContext
int sigid = Integer.parseInt(str);
return user.getSIGContext(sigid);
} // end try
catch (NumberFormatException nfe)
{ // error in Integer.parseInt
logger.error("Cannot convert SIG parameter '" + str + "'!");
throw new ValidationException("Invalid SIG parameter.");
} // end catch
} // end getSIGParameter
private static SIGContext getSIGParameter(ServletRequest request, UserContext user)
throws ValidationException, DataException
{
return getSIGParameter(request.getParameter("sig"),user);
} // end getSIGParameter
private static SIGContext getSIGParameter(ServletMultipartHandler mphandler, UserContext user)
throws ValidationException, DataException
{
if (mphandler.isFileParam("sig"))
throw new ValidationException("Internal Error: SIG should be a normal param");
return getSIGParameter(mphandler.getValue("sig"),user);
} // end getSIGParameter
private static ConferenceContext getConferenceParameter(String str, SIGContext sig)
throws ValidationException, DataException, AccessError
{
if (str==null)
{ // no conference parameter - bail out now!
logger.error("Conference parameter not specified!");
throw new ValidationException("No conference specified.");
} // end if
try
{ // turn the string into a ConfID, and thence to a ConferenceContext
int confid = Integer.parseInt(str);
return sig.getConferenceContext(confid);
} // end try
catch (NumberFormatException nfe)
{ // error in Integer.parseInt
logger.error("Cannot convert conference parameter '" + str + "'!");
throw new ValidationException("Invalid conference parameter.");
} // end catch
} // end getConferenceParameter
private static ConferenceContext getConferenceParameter(ServletRequest request, SIGContext sig)
throws ValidationException, DataException, AccessError
{
return getConferenceParameter(request.getParameter("conf"),sig);
} // end getConferenceParameter
private static ConferenceContext getConferenceParameter(ServletMultipartHandler mphandler, SIGContext sig)
throws ValidationException, DataException, AccessError
{
if (mphandler.isFileParam("conf"))
throw new ValidationException("Internal Error: conference should be a normal param");
return getConferenceParameter(mphandler.getValue("conf"),sig);
} // end getConferenceParameter
private static TopicMessageContext getMessageParameter(String str, ConferenceContext conf)
throws ValidationException, DataException, AccessError
{
if (str==null)
{ // no conference parameter - bail out now!
logger.error("Message parameter not specified!");
throw new ValidationException("No message specified.");
} // end if
try
{ // turn the string into a postid, and thence to a TopicMessageContext
long postid = Long.parseLong(str);
return conf.getMessageByPostID(postid);
} // end try
catch (NumberFormatException nfe)
{ // error in Integer.parseInt
logger.error("Cannot convert message parameter '" + str + "'!");
throw new ValidationException("Invalid message parameter.");
} // end catch
} // end getMessageParameter
private static TopicMessageContext getMessageParameter(ServletRequest request, ConferenceContext conf)
throws ValidationException, DataException, AccessError
{
return getMessageParameter(request.getParameter("msg"),conf);
} // end getMessageParameter
private static TopicMessageContext getMessageParameter(ServletMultipartHandler mphandler,
ConferenceContext conf)
throws ValidationException, DataException, AccessError
{
if (mphandler.isFileParam("msg"))
throw new ValidationException("Internal Error: message should be a normal param");
return getMessageParameter(mphandler.getValue("msg"),conf);
} // end getMessageParameter
/*--------------------------------------------------------------------------------
* Overrides from class HttpServlet
*--------------------------------------------------------------------------------
*/
public String getServletInfo()
{
String rc = "Attachment servlet - Handles uploading and downloading attachments\n"
+ "Part of the Venice Web Communities System\n";
return rc;
} // end getServletInfo
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
UserContext user = getUserContext(request);
RenderData rdat = createRenderData(request,response);
String page_title = null;
Object content = null;
SIGContext sig = null; // SIG context
ConferenceContext conf = null; // conference context
TopicMessageContext msg = null; // message context
try
{ // this outer try is to catch ValidationException
try
{ // all commands require a SIG parameter
sig = getSIGParameter(request,user);
changeMenuSIG(request,sig);
if (logger.isDebugEnabled())
logger.debug("found SIG #" + String.valueOf(sig.getSIGID()));
} // end try
catch (DataException de)
{ // error looking up the SIG
page_title = "Database Error";
content = new ErrorBox(page_title,"Database error finding SIG: " + de.getMessage(),"top");
} // end catch
if (content==null)
{ // we got the SIG parameter OK
try
{ // all commands require a conference parameter
conf = getConferenceParameter(request,sig);
if (logger.isDebugEnabled())
logger.debug("found conf #" + String.valueOf(conf.getConfID()));
} // end try
catch (DataException de)
{ // error looking up the conference
page_title = "Database Error";
content = new ErrorBox(page_title,"Database error finding conference: " + de.getMessage(),"top");
} // end catch
} // end if
if (content==null)
{ // we got the conference parameter OK
try
{ // now we need a message parameter
msg = getMessageParameter(request,conf);
if (logger.isDebugEnabled())
logger.debug("found post #" + String.valueOf(msg.getPostID()));
} // end try
catch (DataException de)
{ // error looking up the conference
page_title = "Database Error";
content = new ErrorBox(page_title,"Database error finding message: " + de.getMessage(),"top");
} // end catch
} // end if
} // end try
catch (ValidationException ve)
{ // these all get handled in pretty much the same way
page_title = "Error";
content = new ErrorBox(null,ve.getMessage(),"top");
} // end catch
catch (AccessError ae)
{ // these all get handled in pretty much the same way
page_title = "Access Error";
content = new ErrorBox(page_title,ae.getMessage(),"top");
} // end catch
if (content==null)
{ // extract the attachment data from the message and send it out
try
{ // extract the attachment data and send it out!
rdat.sendBinaryData(msg.getAttachmentType(),msg.getAttachmentFilename(),
msg.getAttachmentLength(),msg.getAttachmentData());
return; // now we're all done!
} // end try
catch (AccessError ae)
{ // these all get handled in pretty much the same way
page_title = "Access Error";
content = new ErrorBox(page_title,ae.getMessage(),"top");
} // end catch
catch (DataException de)
{ // error looking up the conference
page_title = "Database Error";
content = new ErrorBox(page_title,"Database error retrieving attachment: " + de.getMessage(),"top");
} // end catch
} // end if
// we only get here if there were an error
BaseJSPData basedat = new BaseJSPData(page_title,"top",content);
basedat.transfer(getServletContext(),rdat);
} // end doGet
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException
{
UserContext user = getUserContext(request);
RenderData rdat = createRenderData(request,response);
ServletMultipartHandler mphandler = null;
String target = "top";
String page_title = null;
Object content = null;
SIGContext sig = null; // SIG context
ConferenceContext conf = null; // conference context
TopicMessageContext msg = null; // message context
try
{ // this outer try is to catch ValidationException
mphandler = new ServletMultipartHandler(request);
if (mphandler.isFileParam("target"))
throw new ValidationException("Internal Error: 'target' should be a normal param");
target = mphandler.getValue("target");
try
{ // all commands require a SIG parameter
sig = getSIGParameter(mphandler,user);
changeMenuSIG(request,sig);
if (logger.isDebugEnabled())
logger.debug("found SIG #" + String.valueOf(sig.getSIGID()));
} // end try
catch (DataException de)
{ // error looking up the SIG
page_title = "Database Error";
content = new ErrorBox(page_title,"Database error finding SIG: " + de.getMessage(),target);
} // end catch
if (content==null)
{ // we got the SIG parameter OK
try
{ // all commands require a conference parameter
conf = getConferenceParameter(mphandler,sig);
if (logger.isDebugEnabled())
logger.debug("found conf #" + String.valueOf(conf.getConfID()));
} // end try
catch (DataException de)
{ // error looking up the conference
page_title = "Database Error";
content = new ErrorBox(page_title,"Database error finding conference: " + de.getMessage(),target);
} // end catch
} // end if
if (content==null)
{ // we got the conference parameter OK
try
{ // now we need a message parameter
msg = getMessageParameter(mphandler,conf);
if (logger.isDebugEnabled())
logger.debug("found post #" + String.valueOf(msg.getPostID()));
} // end try
catch (DataException de)
{ // error looking up the conference
page_title = "Database Error";
content = new ErrorBox(page_title,"Database error finding message: " + de.getMessage(),target);
} // end catch
} // end if
// also check on file and target parameter status
if (!(mphandler.isFileParam("thefile")))
throw new ValidationException("Internal error: 'thefile' should be a file param");
} // end try
catch (ValidationException ve)
{ // these all get handled in pretty much the same way
page_title = "Error";
content = new ErrorBox(null,ve.getMessage(),target);
} // end catch
catch (AccessError ae)
{ // these all get handled in pretty much the same way
page_title = "Access Error";
content = new ErrorBox(page_title,ae.getMessage(),target);
} // end catch
catch (ServletMultipartException smpe)
{ // this is kind of a special case
page_title = "Error";
content = new ErrorBox(page_title,"Internal Error: " + smpe.getMessage(),target);
} // end catch
if (content==null)
{ // we're ready to get the data and attach it
try
{ // attach the data to the message!
msg.attachData(mphandler.getContentType("thefile"),mphandler.getValue("thefile"),
mphandler.getContentSize("thefile"),mphandler.getFileContentStream("thefile"));
// go back to where we should have gone before we uploaded the message
rdat.redirectTo(target);
return;
} // end try
catch (ServletMultipartException smpe)
{ // this is kind of a special case
page_title = "Error";
content = new ErrorBox(page_title,"Internal Error: " + smpe.getMessage(),target);
} // end catch
catch (AccessError ae)
{ // these all get handled in pretty much the same way
page_title = "Access Error";
content = new ErrorBox(page_title,ae.getMessage(),target);
} // end catch
catch (DataException de)
{ // error looking up the conference
page_title = "Database Error";
content = new ErrorBox(page_title,"Database error storing attachment: " + de.getMessage(),target);
} // end catch
} // end if (we got all the parameters OK)
// we only get here if there were an error
BaseJSPData basedat = new BaseJSPData(page_title,target,content);
basedat.transfer(getServletContext(),rdat);
} // end doPost
} // end class Attachment

View File

@ -453,9 +453,10 @@ public class ConfOperations extends VeniceServlet
final String yes = "Y"; final String yes = "Y";
if (yes.equals(request.getParameter("attach"))) if (yes.equals(request.getParameter("attach")))
{ // we need to upload an attachment for this post { // we need to upload an attachment for this post
// TODO: jump somewhere to upload an attachment TopicMessageContext msg = topic.getMessage(0); // load the "zero post"
rdat.redirectTo(on_error);
return; content = new AttachmentForm(sig,conf,msg,on_error);
page_title = "Upload Attachment";
} // end if } // end if
else else

View File

@ -276,12 +276,12 @@ public class PostMessage extends VeniceServlet
{ // no slippage - post the message!!! { // no slippage - post the message!!!
TopicMessageContext msg = topic.postNewMessage(0,request.getParameter("pseud"),raw_postdata); TopicMessageContext msg = topic.postNewMessage(0,request.getParameter("pseud"),raw_postdata);
if (yes.equals(request.getParameter("attach"))) if (yes.equals(request.getParameter("attach")))
{ // we have an attachment to upload... { // we have an attachment to upload...display the "Upload Attachment" form
// TODO: do something to upload the attachment String target = "confdisp?sig=" + String.valueOf(sig.getSIGID()) + "&conf="
rdat.redirectTo("confdisp?sig=" + String.valueOf(sig.getSIGID()) + "&conf=" + String.valueOf(conf.getConfID()) + "&top="
+ String.valueOf(conf.getConfID()) + "&top=" + String.valueOf(topic.getTopicNumber()) + "&rnm=1";
+ String.valueOf(topic.getTopicNumber()) + "&rnm=1"); content = new AttachmentForm(sig,conf,msg,target);
return; page_title = "Upload Attachment";
} // end if } // end if
else else

View File

@ -0,0 +1,118 @@
/*
* The contents of this file are subject to the Mozilla Public License Version 1.1
* (the "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at <http://www.mozilla.org/MPL/>.
*
* Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
* WARRANTY OF ANY KIND, either express or implied. See the License for the specific
* language governing rights and limitations under the License.
*
* The Original Code is the Venice Web Communities System.
*
* The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
* for Silverwrist Design Studios. Portions created by Eric J. Bowersox are
* Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved.
*
* Contributor(s):
*/
package com.silverwrist.venice.servlets.format;
import java.util.*;
import javax.servlet.*;
import javax.servlet.http.*;
import com.silverwrist.util.StringUtil;
import com.silverwrist.venice.htmlcheck.*;
import com.silverwrist.venice.core.*;
public class AttachmentForm implements JSPRender
{
/*--------------------------------------------------------------------------------
* Static data members
*--------------------------------------------------------------------------------
*/
// Attribute name for request attribute
protected static final String ATTR_NAME = "com.silverwrist.venice.content.AttachmentForm";
/*--------------------------------------------------------------------------------
* Attributes
*--------------------------------------------------------------------------------
*/
private int sigid;
private int confid;
private long postid;
private String target;
/*--------------------------------------------------------------------------------
* Constructor
*--------------------------------------------------------------------------------
*/
public AttachmentForm(SIGContext sig, ConferenceContext conf, TopicMessageContext msg, String target)
{
this.sigid = sig.getSIGID();
this.confid = conf.getConfID();
this.postid = msg.getPostID();
this.target = target;
} // end constructor
/*--------------------------------------------------------------------------------
* External static functions
*--------------------------------------------------------------------------------
*/
public static AttachmentForm retrieve(ServletRequest request)
{
return (AttachmentForm)(request.getAttribute(ATTR_NAME));
} // end retrieve
/*--------------------------------------------------------------------------------
* Implementations from interface JSPRender
*--------------------------------------------------------------------------------
*/
public void store(ServletRequest request)
{
request.setAttribute(ATTR_NAME,this);
} // end store
public String getTargetJSPName()
{
return "attach_form.jsp";
} // end getTargetJSPName
/*--------------------------------------------------------------------------------
* External operations
*--------------------------------------------------------------------------------
*/
public int getSIGID()
{
return sigid;
} // end getSIGID
public int getConfID()
{
return confid;
} // end getConfID
public long getPostID()
{
return postid;
} // end getPostID
public String getTarget()
{
return target;
} // end getTarget
} // end class AttachmentForm

View File

@ -299,4 +299,25 @@ public class RenderData
} // end nullResponse } // end nullResponse
public void sendBinaryData(String type, String filename, int length, InputStream data) throws IOException
{
response.setContentType(type);
response.setContentLength(length);
if (filename!=null) // make sure we pass the filename along, too
response.setHeader("Content-Disposition","attachment; filename=\"" + filename + "\";");
// Copy the contents of the "data" stream to the output.
ServletOutputStream stm = response.getOutputStream();
byte[] buffer = new byte[4096];
int rd = data.read(buffer);
while (rd>=0)
{ // simple read-write loop to shove data out the door
if (rd>0)
stm.write(buffer,0,rd);
rd = data.read(buffer);
} // end while
} // end sendBinaryData
} // end class RenderData } // end class RenderData

View File

@ -0,0 +1,41 @@
<%--
The contents of this file are subject to the Mozilla Public License Version 1.1
(the "License"); you may not use this file except in compliance with the License.
You may obtain a copy of the License at <http://www.mozilla.org/MPL/>.
Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
WARRANTY OF ANY KIND, either express or implied. See the License for the specific
language governing rights and limitations under the License.
The Original Code is the Venice Web Communities System.
The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
for Silverwrist Design Studios. Portions created by Eric J. Bowersox are
Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved.
Contributor(s):
--%>
<%@ page import = "java.util.*" %>
<%@ page import = "com.silverwrist.util.StringUtil" %>
<%@ page import = "com.silverwrist.venice.core.*" %>
<%@ page import = "com.silverwrist.venice.servlets.Variables" %>
<%@ page import = "com.silverwrist.venice.servlets.format.*" %>
<%
AttachmentForm data = AttachmentForm.retrieve(request);
Variables.failIfNull(data);
RenderData rdat = RenderConfig.createRenderData(application,request,response);
%>
<% rdat.writeContentHeader(out,"Upload Your Attachment",null); %>
<%= rdat.getStdFontTag(null,2) %>
Your attachment may be no more than <B>1 megabyte</B> in size.<P>
<FORM METHOD="POST" ENCTYPE="multipart/form-data" ACTION="<%= rdat.getEncodedServletPath("attachment") %>">
<INPUT TYPE=HIDDEN NAME="sig" VALUE="<%= data.getSIGID() %>">
<INPUT TYPE=HIDDEN NAME="conf" VALUE="<%= data.getConfID() %>">
<INPUT TYPE=HIDDEN NAME="msg" VALUE="<%= data.getPostID() %>">
<INPUT TYPE=HIDDEN NAME="target" VALUE="<%= data.getTarget() %>">
File to attach: <INPUT TYPE="FILE" NAME="thefile"><BR>
<INPUT TYPE=IMAGE SRC="<%= rdat.getFullImagePath("bn_upload.gif") %>" NAME="upload" ALT="Upload"
WIDTH=80 HEIGHT=24 BORDER=0>
</FORM><P>
</FONT>
<% rdat.writeFooter(out); %>

View File

@ -171,7 +171,13 @@
<A HREF="<%= rdat.getEncodedServletPath("user/" + poster) %>" TARGET="_blank"><%= poster %></A>, <A HREF="<%= rdat.getEncodedServletPath("user/" + poster) %>" TARGET="_blank"><%= poster %></A>,
<%= rdat.formatDateForDisplay(msg.getPostDate()) %> <%= rdat.formatDateForDisplay(msg.getPostDate()) %>
</EM>) </EM>)
<%-- TODO: paperclip goes here if we have an attachment --%> <% if (msg.hasAttachment()) { %>
<A HREF="<%= rdat.getEncodedServletPath("attachment?" + data.getConfLocator() + "&msg="
+ String.valueOf(msg.getPostID())) %>"><IMG
SRC="<%= rdat.getFullImagePath("attachment.gif") %>"
ALT="(Attachment <%= msg.getAttachmentFilename() %> - <%= msg.getAttachmentLength() %> bytes)"
WIDTH=16 HEIGHT=16 BORDER=0></A>
<% } // end if %>
</FONT><P> </FONT><P>
<% if (msg.isScribbled()) { %> <% if (msg.isScribbled()) { %>
<TT><EM><B> <TT><EM><B>

BIN
web/images/attachment.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 B

BIN
web/images/bn_upload.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 942 B