Mercurial Hosting > nabble
view src/nabble/model/FileUpload.java @ 47:72765b66e2c3
remove mailing list code
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Fri, 18 Jun 2021 17:44:24 -0600 |
parents | c4ed473452d4 |
children |
line wrap: on
line source
package nabble.model; import fschmidt.db.DbDatabase; import fschmidt.db.Listener; import fschmidt.html.HtmlTag; import fschmidt.util.java.ImageUtils; import nabble.view.web.forum.FileDownload; import org.apache.commons.fileupload.FileItem; import org.apache.commons.fileupload.FileItemHeaders; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import javax.imageio.ImageIO; import java.awt.image.BufferedImage; import java.awt.image.ImagingOpException; import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.io.FilterInputStream; import java.io.IOException; import java.io.InputStream; import java.net.URL; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; import java.util.ArrayList; import java.util.Date; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.Set; public final class FileUpload { private static final Logger logger = LoggerFactory.getLogger(FileUpload.class); public static final int MAX_IMAGE_SIZE = 1048576; public static final int MAX_FILE_SIZE = 5242880; public static class FileDetails { private String name; private Date date; public FileDetails(String name, Date date) { this.name = name; this.date = date; } public String getName() { return name; } public Date getDate() { return date; } } private static void addToSql(Message.Source src,StringBuilder sql) { Message.SourceType type = src.getMessageSourceType(); sql.append( " from file_" ).append( type.getName() ); String idField = type.getIdField(); if( idField == null ) { sql.append( " where true" ); } else { sql.append( " where " ).append( idField ).append( " = ?" ); } } private static int setParams(Message.Source src,PreparedStatement pstmt) throws SQLException { int i = 0; if( src.getMessageSourceType().getIdField() != null ) { pstmt.setLong(++i,src.getSourceId()); } return i; } public static InputStream getFileContent(Message.Source src,String name) { if( src==null ) return null; Message.SourceType type = src.getMessageSourceType(); try { final Connection con = src.getSite().getDb().getConnection(); StringBuilder sql = new StringBuilder(); sql.append( "select content" ); addToSql(src,sql); sql.append( " and name = ?" ); final PreparedStatement pstmt = con.prepareStatement(sql.toString()); boolean isDone = false; try { int i = setParams(src,pstmt); pstmt.setString(++i,name); ResultSet rs = pstmt.executeQuery(); if( !rs.next() ) return null; InputStream rtn = new FilterInputStream(rs.getBinaryStream("content")) { public void close() throws IOException { super.close(); try { pstmt.close(); con.close(); } catch(SQLException e) { throw new RuntimeException(e); } } }; isDone = true; return rtn; } finally { if( !isDone ) { pstmt.close(); con.close(); } } } catch(SQLException e) { throw new RuntimeException(e); } } static boolean hasFile(Message.Source src,String name) { Message.SourceType type = src.getMessageSourceType(); try { final Connection con = src.getSite().getDb().getConnection(); StringBuilder sql = new StringBuilder(); sql.append( "select 'whatever'" ); addToSql(src,sql); sql.append( " and name = ?" ); final PreparedStatement pstmt = con.prepareStatement(sql.toString()); try { int i = setParams(src,pstmt); pstmt.setString(++i,name); return pstmt.executeQuery().next(); } finally { pstmt.close(); con.close(); } } catch(SQLException e) { throw new RuntimeException(e); } } public static FileDetails[] getFileDetails(Message.Source src,String prefix) { Message.SourceType type = src.getMessageSourceType(); try { final Connection con = src.getSite().getDb().getConnection(); StringBuilder sql = new StringBuilder(); sql.append( "select name, date_" ); addToSql(src,sql); if( prefix != null ) sql.append( " and name like '" ).append( prefix ).append( "%'" ); sql.append( " order by name" ); final PreparedStatement pstmt = con.prepareStatement(sql.toString()); try { setParams(src,pstmt); List<FileDetails> names = new ArrayList<FileDetails>(); ResultSet set = pstmt.executeQuery(); while (set.next()) { names.add(new FileDetails(set.getString("name"), set.getDate("date_"))); } return names.toArray(new FileDetails[0]); } finally { pstmt.close(); con.close(); } } catch(SQLException e) { throw new RuntimeException(e); } } public static String getName(FileItem fi) { String[] a = fi.getName().split("[\\\\/:]"); String[] b = a[a.length-1].split("\\."); String ext2 = b[b.length-1]; int n = (int)(Math.random()*1000); if(b[0].equals("image")) b[0] = b[0]+n; return b[0]+"."+ext2; } public static String uploadImage(final FileItem fi,Message.Source src, int resize) throws ModelException { try { return uploadImage1(fi,src,resize); } catch(IOException e) { throw ModelException.newInstance("upload_image_failed",e); } } private static String uploadImage1(final FileItem fi,Message.Source src, int resize) throws ModelException, IOException { InputStream in = fi.getInputStream(); BufferedImage bi; try { bi = ImageIO.read(in); } catch(IllegalArgumentException e) { throw ModelException.newInstance("upload_image_failed",e); } catch (RuntimeException e) { throw ModelException.newInstance("unknown_image_error",e); } in.close(); if (bi == null) throw ModelException.newInstance("unsupported_image_type","Unsupported image type - please use PNG, JPG or GIF."); String filename = getName(fi); filename = filename.replaceAll("[ ']+","_"); // Replace spaces and single quotes with underscores int iDot = filename.lastIndexOf('.'); if( iDot == -1 ) throw ModelException.newInstance("file_has_no_ending","File must have ending"); String ending = filename.substring(iDot+1).toLowerCase(); InputStream bais; long size; if (resize > 0) { try { bi = ImageUtils.getThumbnail(bi, resize, resize); final byte[] contents = ImageUtils.asOutputStream(bi, ending).toByteArray(); bi = null; size = contents.length; bais = new ByteArrayInputStream(contents); } catch (ImagingOpException e) { throw ModelException.newInstance("unable_to_resize_image","Unable to resize image", e); } } else { bi = null; bais = fi.getInputStream(); size = fi.getSize(); } final InputStream inputStream = bais; if (size <= MAX_IMAGE_SIZE) { filename = filename.substring(0,iDot+1) + ending; return uploadFile2(size,filename,src, true, new InputStreamFactory() { public InputStream in() throws IOException { return inputStream; } } ); } else { throw ModelException.newInstance("image_too_large","Image is too large: use the resize option to make it smaller. Maximum size 1MB."); } } /* static boolean isDifferent(final FileItem fi,Message.Source src) throws ModelException { try { InputStream inDb = getFileContent(src,fi.getName()); if( inDb==null ) return true; InputStream inFi = null; try { inFi = fi.getInputStream(); return IoUtils.compare(inDb,inFi) != 0; } finally { if (inFi != null) inFi.close(); inDb.close(); } } catch(IOException e) { throw ModelException.newInstance("file_io_exception",e); } } */ public static String uploadFile(final FileItem fi,Message.Source src) throws ModelException { try { return uploadFile1(fi,src); } catch(IOException e) { throw ModelException.newInstance("file_io_exception",e); } } private static String uploadFile1(final FileItem fi,Message.Source src) throws ModelException, IOException { String filename = getName(fi); filename = filename.replaceAll("[ ']+", "_"); // Replace spaces and single quotes with underscores long size = fi.getSize(); return uploadFile2(size,filename,src, true, new InputStreamFactory() { public InputStream in() throws IOException { return fi.getInputStream(); } } ); } private static interface InputStreamFactory { public InputStream in() throws IOException; } private static String uploadFile2(long size, String filename, final Message.Source src, boolean checkSize, InputStreamFactory inf) throws ModelException, IOException { if( "".equals(filename.trim()) ) throw ModelException.newInstance("empty_filename","empty filename"); if( size==0 ) throw ModelException.newInstance("empty_file","empty file"); if( size > MAX_FILE_SIZE && checkSize ) throw ModelException.newInstance("file_is_too_large","file is too large - maximum size 5mb"); synchronized(src) { try { Connection con = src.getSite().getDb().getConnection(); try { { StringBuilder sql = new StringBuilder(); sql.append( "delete" ); addToSql(src,sql); sql.append( " and name = ?" ); PreparedStatement pstmt = con.prepareStatement(sql.toString()); int i = setParams(src,pstmt); pstmt.setString(++i,filename); pstmt.executeUpdate(); pstmt.close(); } { StringBuilder sql = new StringBuilder(); Message.SourceType type = src.getMessageSourceType(); sql.append( "insert into file_" ).append( type.getName() ); sql.append( " ( name, content" ); String idField = type.getIdField(); if( idField != null ) sql.append( ", " ).append( idField ); sql.append( " ) values (?,?" ); if( idField != null ) sql.append( ",?" ); sql.append( ")" ); PreparedStatement pstmt = con.prepareStatement(sql.toString()); pstmt.setString(1,filename); InputStream in = inf.in(); pstmt.setBinaryStream(2,in,(int)size); if( idField != null ) pstmt.setLong(3,src.getSourceId()); pstmt.executeUpdate(); in.close(); pstmt.close(); } return filename; } finally { con.close(); src.getSite().getDb().runAfterCommit(new Runnable(){public void run(){ fireFileUpdateListeners(src); }}); } } catch(SQLException e) { throw new RuntimeException(e); } } } private static List<Listener<Message.Source>> fileUpdateListeners = new ArrayList<Listener<Message.Source>>(); public static void addFileUpdateListener(Listener<Message.Source> listener) { fileUpdateListeners.add(listener); } static void fireFileUpdateListeners(Message.Source src) { for( Listener<Message.Source> listener : fileUpdateListeners ) { listener.event(src); } } static { NodeImpl.addPostInsertListener(new Listener<NodeImpl>(){ public void event(NodeImpl node) { Message.Format fmt = node.getMessage().getFormat(); if( !(fmt instanceof MailMessageFormat) && node.getOwner() instanceof User) fixFileTags(node.getMessage(),(User)node.getOwner()); } }); NodeImpl.addPostUpdateListener(new Listener<NodeImpl>(){ public void event(NodeImpl node) { if( ModelHome.insideImportProcedure.get() ) return; Message.Format fmt = node.getMessage().getFormat(); if( !(fmt instanceof MailMessageFormat) ) deleteUnusedFiles(node.getMessage()); } }); } private static void deleteUnusedFiles(Message message) { Message.Source src = message.getSource(); if (src == null) return; DbDatabase db = src.getSite().getDb(); Message.SourceType type = src.getMessageSourceType(); Set<String> names = getFileInfo(message.parse(),src).keySet(); StringBuilder sql = new StringBuilder(); sql.append( "delete" ); addToSql(src,sql); if( !names.isEmpty() ) { sql.append( " and name not in (" ); Iterator<String> iter = names.iterator(); sql.append( db.arcana().quote(iter.next()) ); while( iter.hasNext() ) { sql.append( ',' ); sql.append( db.arcana().quote(iter.next()) ); } sql.append( ")" ); } try { Connection con = db.getConnection(); PreparedStatement pstmt = con.prepareStatement(sql.toString()); setParams(src,pstmt); pstmt.executeUpdate(); pstmt.close(); con.close(); } catch(SQLException e) { throw new RuntimeException(e); } } public static void deleteFile(String fileName, Message.Source src) { Message.SourceType type = src.getMessageSourceType(); try { Connection con = src.getSite().getDb().getConnection(); try { StringBuilder sql = new StringBuilder(); sql.append( "delete" ); addToSql(src,sql); sql.append( " and name = ?" ); PreparedStatement pstmt = con.prepareStatement(sql.toString()); int i = setParams(src,pstmt); pstmt.setString(++i,fileName); pstmt.executeUpdate(); pstmt.close(); } finally { con.close(); } } catch(SQLException e) { throw new RuntimeException(e); } } private static void fixFileTags(Message message,User user) { for( Object obj : message.parse() ) { if( !(obj instanceof HtmlTag) ) continue; HtmlTag tag = (HtmlTag)obj; String tagName = tag.getName().toLowerCase(); if( tagName.equals("nabble_a") ) { fix(tag,"href",message.getSource(),user); } else if( tagName.equals("nabble_img") ) { fix(tag,"src",message.getSource(),user); } } } public static void checkFileTags(Message message,Person visitor) throws ModelException { Set<String> fileNames = new HashSet<String>(); if( visitor instanceof User ) { User user = (User)visitor; Message.Source srcTemp = Message.SourceType.getType('t').getSource(user.getSite(),user.getId()); FileDetails[] fileDetails = getFileDetails(srcTemp, null); for (FileDetails d : fileDetails) { fileNames.add(d.name); } } for( Object obj : message.parse() ) { if( !(obj instanceof HtmlTag) ) continue; HtmlTag tag = (HtmlTag)obj; String tagName = tag.getName().toLowerCase(); if( tagName.equals("nabble_a") ) { String href = HtmlTag.unquote(tag.getAttributeValue("href")); if (!fileNames.contains(href)) throw new ModelException.InvalidFile(href); } else if( tagName.equals("nabble_img") ) { String src = HtmlTag.unquote(tag.getAttributeValue("src")); if (!fileNames.contains(src)) throw new ModelException.InvalidFile(src); } } } private static void fix(HtmlTag tag,String fileAttr,Message.Source src,User user) { String filename = HtmlTag.unquote(tag.getAttributeValue(fileAttr)); if( filename==null ) return; Message.SourceType type = src.getMessageSourceType(); try { Connection con = src.getSite().getDb().getConnection(); try { { PreparedStatement pstmt = con.prepareStatement( "select 'x' from file_temp" +" where user_id = ?" +" and name = ?" ); pstmt.setLong(1,user.getId()); pstmt.setString(2,filename); ResultSet rs = pstmt.executeQuery(); try { if( !rs.next() ) return; } finally { rs.close(); pstmt.close(); } } { StringBuilder sql = new StringBuilder(); sql.append( "delete" ); addToSql(src,sql); sql.append( " and name = ?" ); PreparedStatement pstmt = con.prepareStatement(sql.toString()); int i = setParams(src,pstmt); pstmt.setString(++i,filename); pstmt.executeUpdate(); pstmt.close(); } PreparedStatement pstmt = con.prepareStatement( "insert into file_" + type.getName() +" (" + type.getIdField() + ", name, content)" +" select ?, name, content" +" from file_temp" +" where user_id = ?" +" and name=?" ); pstmt.setLong(1,src.getSourceId()); pstmt.setLong(2,user.getId()); pstmt.setString(3,filename); pstmt.executeUpdate(); pstmt.close(); { Statement stmt = con.createStatement(); stmt.executeUpdate( "delete from file_temp" +" where date_ < " + Db.arcana.dateSub("now()",1,"day") ); stmt.close(); } } finally { con.close(); } } catch(SQLException e) { throw new RuntimeException(e); } } public static void processFileTags(List<Object> list,Message.Source src) { if( src == null ) return; for( Object obj : list ) { if( !(obj instanceof HtmlTag) ) continue; HtmlTag tag = (HtmlTag)obj; String tagName = tag.getName().toLowerCase(); if( tagName.equals("nabble_a") ) { String filename = HtmlTag.unquote(tag.getAttributeValue("href")); if( filename==null ) continue; String url = getUrl(filename,src); tag.setAttribute("href",HtmlTag.quote(url)); tag.setName("a"); if( tag.getAttributeValue("target") == null ) { tag.setAttribute("target","\"_top\""); } } else if( tagName.equals("/nabble_a") ) { tag.setName("/a"); } else if( tagName.equals("nabble_img") ) { String filename = HtmlTag.unquote(tag.getAttributeValue("src")); if( filename==null ) continue; String url = getUrl(filename,src); tag.setAttribute("src",HtmlTag.quote(url)); tag.setName("img"); } } } static Map<String,String> getFileInfo(List<Object> list,Message.Source src) { Map<String,String> info = new HashMap<String,String>(); for( Object obj : list ) { if( !(obj instanceof HtmlTag) ) continue; HtmlTag tag = (HtmlTag)obj; String tagName = tag.getName().toLowerCase(); if( tagName.equals("nabble_a") ) { String filename = HtmlTag.unquote(tag.getAttributeValue("href")); if( filename==null ) continue; String url = getUrl(filename,src); info.put(filename,url); } else if( tagName.equals("nabble_img") ) { String filename = HtmlTag.unquote(tag.getAttributeValue("src")); if( filename==null ) continue; String url = getUrl(filename,src); info.put(filename,url); } } return info; } static String getUrl(String filename,Message.Source src) { return FileDownload.url(filename,src); } public static void saveImage(BufferedImage image, String fileName, Message.Source src) throws ModelException { try { // First convert to byte array ByteArrayOutputStream baos = new ByteArrayOutputStream(); ImageIO.write(image, "png", baos); final byte[] bytes = baos.toByteArray(); InputStreamFactory factory = new InputStreamFactory() { public InputStream in() throws IOException { return new ByteArrayInputStream(bytes); } }; uploadFile2(bytes.length, fileName, src, true, factory); } catch(IOException e) { throw ModelException.newInstance("file_io_exception",e); } } public static void saveFile(final byte[] contents, String fileName, Message.Source src) throws ModelException { saveFile(contents, fileName, src, true); } public static void saveFile(final byte[] contents, String fileName, Message.Source src, boolean checkSize) throws ModelException { try { InputStreamFactory factory = new InputStreamFactory() { public InputStream in() throws IOException { return new ByteArrayInputStream(contents); } }; uploadFile2(contents.length, fileName, src, checkSize, factory); } catch(IOException e) { throw ModelException.newInstance("file_io_exception",e); } } public static FileDetails[] getFiles(Message.Source src) { return getFileDetails(src, null); } public static final class UrlFileItem implements FileItem { private final URL url; public UrlFileItem(URL url) { this.url = url; } public InputStream getInputStream() throws IOException { try { return url.openConnection().getInputStream(); } catch(IllegalArgumentException e) { logger.warn("",e); throw new IOException(e.getMessage()); } catch(RuntimeException e) { logger.error("url = "+url,e); throw e; } } public String getContentType() { throw new UnsupportedOperationException(); } public String getName() { String s = url.getPath(); int i = s.lastIndexOf('/'); if( i != -1 ) s = s.substring(i+1); return s; } public boolean isInMemory() { return false; } public long getSize() { try { long len = url.openConnection().getContentLength(); if( len == -1 ) { len = 0; InputStream in = getInputStream(); while(true) { long n = in.skip(1000000L); len += n; if( n==0 ) { if( in.read() == -1 ) break; len++; } } in.close(); } return len; } catch(IOException e) { throw new RuntimeException(e); } } public byte[] get() { throw new UnsupportedOperationException(); } public java.lang.String getString(java.lang.String encoding) throws java.io.UnsupportedEncodingException { throw new UnsupportedOperationException(); } public java.lang.String getString() { throw new UnsupportedOperationException(); } public void write(java.io.File file) throws java.lang.Exception { throw new UnsupportedOperationException(); } public void delete() { throw new UnsupportedOperationException(); } public java.lang.String getFieldName() { throw new UnsupportedOperationException(); } public void setFieldName(java.lang.String name) { throw new UnsupportedOperationException(); } public boolean isFormField() { return false; } public void setFormField(boolean state) { throw new UnsupportedOperationException(); } public java.io.OutputStream getOutputStream() throws java.io.IOException { throw new UnsupportedOperationException(); } public FileItemHeaders getHeaders() { throw new UnsupportedOperationException(); } public void setHeaders(FileItemHeaders fileItemHeaders) { throw new UnsupportedOperationException(); } public String toString() { return "UrlFileItem-"+url; } } static void nop() {} private FileUpload() {} // never }