Package net.easymodo.asagi

Source Code of net.easymodo.asagi.Local

package net.easymodo.asagi;

import com.google.common.io.ByteStreams;
import com.sun.jna.Native;
import com.sun.jna.Platform;
import net.easymodo.asagi.exception.ContentGetException;
import net.easymodo.asagi.exception.ContentStoreException;
import net.easymodo.asagi.exception.DBConnectionException;
import net.easymodo.asagi.model.*;
import net.easymodo.asagi.posix.Group;
import net.easymodo.asagi.posix.Posix;
import net.easymodo.asagi.settings.BoardSettings;
import org.apache.http.annotation.ThreadSafe;

import java.io.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@ThreadSafe
public class Local extends Board {
    private final String path;
    private final boolean useOldDirectoryStructure;
    private final int webGroupId;

    private final static int DIR_THUMB = 1;
    private final static int DIR_MEDIA = 2;
    private final static int FULL_FILE = 1;
    private final static int TEMP_FILE = 2;

    private final static Posix posix;
    private DB db;

    private final static Pattern oldDirectoryMatchingPattern = Pattern.compile("(\\d+?)(\\d{2})\\d{0,3}$");

    static {
        if(Platform.isWindows()) {
          posix = null;
        } else {
          posix = (Posix)Native.loadLibrary("c", Posix.class);
        }
    }

    public Local(String path, BoardSettings info, DB db) {
        this.path = path;
        this.useOldDirectoryStructure = info.getUseOldDirectoryStructure();
        this.db = db;

        // getgrnam is thread-safe on sensible OSes, but it's not thread safe
        // on most ones.
        // Performing the gid lookup in the constructor and calling chmod and
        // chown from the C library (which are reentrant functions) keeps this
        // class thread-safe.
        String webServerGroup = info.getWebserverGroup();
        if(webServerGroup != null && posix != null) {
            Group group = posix.getgrnam(webServerGroup);
            if(group == null)
                webGroupId = 0;
            else
                webGroupId = (int)group.getGid();
        } else {
            webGroupId = 0;
        }
    }

    @Override
    public Page getPage(int pageNum, String lastMod) {
        // Unimplemented
        throw new UnsupportedOperationException();
    }

    @Override
    public Topic getThread(int threadNum, String lastMod) {
        // Unimplemented
        throw new UnsupportedOperationException();
    }

    @Override
    public Page getAllThreads(String lastMod) throws ContentGetException {
        // Unimplemented
        throw new UnsupportedOperationException();
    }

    @Override
    public InputStream getMediaPreview(MediaPost h) {
        // Unimplemented
        throw new UnsupportedOperationException();
    }

    @Override
    public InputStream getMedia(MediaPost h) {
        // Unimplemented
        throw new UnsupportedOperationException();
    }

    public String[] getSubdirs(String filename) {
        String subdir = filename.substring(0, 4);
        String subdir2 = filename.substring(4, 6);

        return new String[] {subdir, subdir2};
    }

    public String[] getSubdirs(MediaPost h) {
        Matcher mat = oldDirectoryMatchingPattern.matcher(Integer.toString(h.getThreadNum()));
        mat.find();

        String subdir = String.format("%04d", Integer.parseInt(mat.group(1)));
        String subdir2 = String.format("%02d", Integer.parseInt(mat.group(2)));

        return new String[] {subdir, subdir2};
    }

    public String getDir(String[] subdirs, int dirType, int fileType) {
        if(fileType == TEMP_FILE) {
            return String.format("%s/tmp", this.path);
        } else if(fileType == FULL_FILE) {
            if(dirType == DIR_THUMB) {
                return String.format("%s/thumb/%s/%s", this.path, subdirs[0], subdirs[1]);
            } else if(dirType == DIR_MEDIA) {
                return String.format("%s/image/%s/%s", this.path, subdirs[0], subdirs[1]);
            } else {
                return null;
            }
        } else {
            return null;
        }
    }

    public String makeDir(String filename, int dirType, int fileType) throws ContentStoreException {
        String[] subdirs = this.getSubdirs(filename);
        return this.makeDir(subdirs, dirType, fileType);
    }

    public String makeDir(MediaPost h, int dirType, int fileType) throws ContentStoreException {
        String[] subdirs = this.getSubdirs(h);
        return this.makeDir(subdirs, dirType, fileType);
    }

    public String makeDir(String[] subdirs, int dirType, int fileType) throws ContentStoreException {
        String dir;
        if(dirType == DIR_THUMB) {
            dir = "thumb";
        } else if(dirType == DIR_MEDIA) {
            dir = "image";
        } else {
            return null;
        }

        String subDir = String.format("%s/%s/%s", this.path, dir, subdirs[0]);
        String subDir2 = String.format("%s/%s/%s/%s", this.path, dir, subdirs[0], subdirs[1]);
        File subDir2File = new File(subDir2);

        String tempDir = String.format("%s/%s", this.path, "tmp");
        File tempDirFile = new File(tempDir);

        synchronized(this) {
            if(!subDir2File.exists())
                if(!subDir2File.mkdirs())
                    throw new ContentStoreException("Could not create dirs at path " + subDir2);

            if(!tempDirFile.exists())
                if(!tempDirFile.mkdirs())
                    throw new ContentStoreException("Could not create temp dir at path " + tempDir);

            if(this.webGroupId != 0) {
                posix.chmod(subDir, 0775);
                posix.chmod(subDir2, 0775);
                posix.chown(subDir, -1, this.webGroupId);
                posix.chown(subDir2, -1, this.webGroupId);

                posix.chmod(tempDir, 0775);
                posix.chown(tempDir, -1, this.webGroupId);
            }
        }

        return this.getDir(subdirs, dirType, fileType);
    }

    public void insert(Topic topic) throws ContentStoreException, DBConnectionException {
        this.db.insert(topic);
    }

    public void markDeleted(DeletedPost post) throws ContentStoreException {
        try{
            this.db.markDeleted(post);
        } catch(DBConnectionException e) {
            throw new ContentStoreException("Lost connection to database, can't reconnect", e);
        }
    }

    public void insertMediaPreview(MediaPost h, Board source) throws ContentGetException, ContentStoreException {
        this.insertMedia(h, source, true);
    }

    public void insertMedia(MediaPost h, Board source) throws ContentGetException, ContentStoreException {
        this.insertMedia(h, source, false);
    }

    public void insertMedia(MediaPost h, Board source, boolean isPreview) throws ContentGetException, ContentStoreException {
        // Post has no media
        if((isPreview && h.getPreview() == null) || (!isPreview && h.getMedia() == null))
            return;

        Media mediaRow;
        try {
            mediaRow = db.getMedia(h);
        } catch(DBConnectionException e) {
            throw new ContentStoreException("Lost connection to database, can't reconnect", e);
        }

        // Media is banned from archiving
        if(mediaRow.getBanned() == 1) return;

        // Get the proper filename for the file type we're outputting
        String filename;
        if(this.useOldDirectoryStructure)
            filename = isPreview ? h.getPreview() : h.getMedia();
        else
            filename = isPreview ? (h.isOp() ?  mediaRow.getPreviewOp() : mediaRow.getPreviewReply()) :
                mediaRow.getMedia();

        if(filename == null) return;

        // Create the dir structure (if necessary) and return the path to where we're outputting our file
        // Filename is enough for us here, we just need the first part of the string
        String outputDir;
        if(this.useOldDirectoryStructure)
            outputDir = makeDir(h, isPreview ? DIR_THUMB : DIR_MEDIA, FULL_FILE);
        else
            outputDir = makeDir(filename, isPreview ? DIR_THUMB : DIR_MEDIA, FULL_FILE);

        // Construct the path and back down if the file already exists
        File outputFile = new File(outputDir + "/" + filename);
        if(outputFile.exists()) {
            if (!isPreview) outputFile.setLastModified(System.currentTimeMillis());
            return;
        }

        // Open a temp file for writing
        String tempFilePath;
        if(this.useOldDirectoryStructure)
            tempFilePath = makeDir(h, isPreview ? DIR_THUMB : DIR_MEDIA, TEMP_FILE);
        else
            tempFilePath = makeDir(filename, isPreview ? DIR_THUMB : DIR_MEDIA, TEMP_FILE);


        // Throws ContentGetException on failure
        InputStream inStream = isPreview ? source.getMediaPreview(h) : source.getMedia(h);

        OutputStream outFile = null;
        File tempFile = null;
        try {
             tempFile = File.createTempFile(filename + "_", null, new File(tempFilePath + "/"));

            outFile = new BufferedOutputStream(new FileOutputStream(tempFile));

            // Copy the network input stream to our local file
            // In case the connection is cut off or something similar happens, an IOException
            // will be thrown.
            ByteStreams.copy(inStream, outFile);
        } catch(FileNotFoundException e) {
            throw new ContentStoreException("The temp file we just created wasn't there!", e);
        } catch(IOException e) {
            if(tempFile != null && !tempFile.delete())
                System.err.println("Additionally, temporary file " + tempFilePath + "/" + filename + " could not be deleted.");
                e.printStackTrace();
            throw new ContentStoreException("IOException in file download", e);
        } finally {
            try {
                if(outFile != null) outFile.close();
                inStream.close();
            } catch(IOException e) {
                System.err.println("IOException trying to close streams after file download");
                e.printStackTrace();
            }
        }

        // Move the temporary file into place
        if(!tempFile.renameTo(outputFile))
            throw new ContentStoreException("Unable to move temporary file " + tempFilePath + "/" + filename + " into place");

        try {
            if(this.webGroupId != 0) {
                posix.chmod(outputFile.getCanonicalPath(), 0664);
                posix.chown(outputFile.getCanonicalPath(), -1, this.webGroupId);
            }
        } catch(IOException e) {
            throw new ContentStoreException("IOException trying to get filename for output file (nice broken filesystem you have there)", e);
        }
    }
}
TOP

Related Classes of net.easymodo.asagi.Local

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.