/**
* Copyright (c) 2007, Markus Jevring <markus@jevring.net>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. The names of the contributors may not be used to endorse or promote
* products derived from this software without specific prior written
* permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
*/
package cu.ftpd.modules.requests;
import cu.ftpd.logging.Logging;
import cu.ftpd.user.User;
import cu.ftpd.modules.requests.Request;
import java.io.*;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.text.MessageFormat;
/**
* @author Markus Jevring <markus@jevring.net>
* @since 2007-okt-10 : 12:21:05
* @version $Id: RequestLog.java 292 2009-03-04 19:44:36Z jevring $
*/
public class RequestLog {
private final File log;
private final boolean autoApprove;
private final List<Request> requests = Collections.synchronizedList(new LinkedList<Request>());
// request, req_user, req_group, req_tagline,
protected static final MessageFormat request = new MessageFormat("REQUEST: \"{0}\" \"{1}\" \"{2}\" \"{3}\"");
// request, fill_user, fill_group, fill_tagline, requester
protected static final MessageFormat reqfilled = new MessageFormat("REQFILLED: \"{0}\" \"{1}\" \"{2}\" \"{3}\" \"{4}\"");
// request, approve_user, approve_group, approve_tagline
protected static final MessageFormat approveRequest = new MessageFormat("REQAPPROVE: \"{0}\" \"{1}\" \"{2}\" \"{3}\"");
public RequestLog(String logfile, boolean autoApprove) throws IOException {
if (logfile == null || "".equals(logfile)) {
throw new IllegalArgumentException("'logfile' parameter to RequestLog() cannot be null or \"\"");
}
log = new File(logfile);
this.autoApprove = autoApprove;
load();
}
public void request(User user, String request) {
Request r = new Request(System.currentTimeMillis(), user.getUsername(), request);
requests.add(r);
if (autoApprove) {
r.setApproved(true);
}
save();
// _todo: make a reqdel command
// _todo: allow setting for dirs to be automatically created (for this we need a pattern (user setable? heaven forbid) as well as a setting in the settings)(as well as WHERE these dirs should be created)
// if we do this, do we auto-rename directories that are filled?
// do we also auto-del directories that are reqdelled (requests, yes, but what about filled requests?)
// no, if people want that kind of advanced request system, they can build it as a module
logRequestToEventLog(user, r);
}
public boolean fillRequest(User filler, int requestNumber) {
// even though doing .get(int) isn't particularly effective on a LinkedList, this operation happens rarely, and we don't need to shift the entire array down, which would be even more costly
synchronized (requests) { // added this lock here to keep .remove() from throwing an IndexOutOfBoundsException.
if (requestNumber <= requests.size() - 1 && requestNumber >= 0) {
Request r = requests.remove(requestNumber);
save();
logRequestFilledToEventLog(filler, r);
return true;
} else {
return false;
}
}
}
public boolean approve(User approver, int requestNumber, boolean approved) {
synchronized (requests) {
if (requestNumber <= requests.size() - 1 && requestNumber >= 0) {
Request r = requests.get(requestNumber);
r.setApproved(approved);
save();
logRequestApprovedToEventLog(approver, r);
return true;
} else {
return false;
}
}
}
public List<Request> getRequests() {
return requests;
}
public void logRequestToEventLog(User user, Request r) {
Logging.getEventLog().log(request.format(new String[]{r.getDescription(), user.getUsername(), user.getPrimaryGroup(), user.getTagline()}));
}
public void logRequestFilledToEventLog(User filler, Request request) {
Logging.getEventLog().log(reqfilled.format(new String[]{request.getDescription(), filler.getUsername(), filler.getPrimaryGroup(), filler.getTagline(), request.getRequester()}));
}
public void logRequestApprovedToEventLog(User user, Request request) {
Logging.getEventLog().log(approveRequest.format(new String[]{request.getDescription(), user.getUsername(), user.getPrimaryGroup(), user.getTagline()}));
}
private void load() throws IOException {
if (!requests.isEmpty()) {
throw new IllegalStateException("Cannot load the requestlog more than once!");
}
BufferedReader in = null;
if (!log.exists()) {
System.out.println("Request log not found, creating...");
if (!log.createNewFile()) {
throw new IOException("Could not create new request log!");
}
return; // we might as well return here since we don't want to read an empty file anyway
}
try {
in = new BufferedReader(new InputStreamReader(new FileInputStream(log)));
String line;
while ((line = in.readLine()) != null) {
String[] request = line.split(";");
// time + ";" + requester + ";" + approved + ";" + description;
Request r = new Request(Long.parseLong(request[0]), request[1], request[3]);
r.setApproved(Boolean.parseBoolean(request[2]));
requests.add(r);
}
} catch (IOException e) {
// should we even report this?
Logging.getErrorLog().reportCritical("Error while loading request log: " + e.getMessage());
} finally {
if (in != null) {
try {
in.close();
} catch (IOException e) {
Logging.getErrorLog().reportException("Failed to close input stream", e);
//e.printStackTrace();
}
}
}
}
public void save() {
synchronized(requests) {
PrintWriter out = null;
try {
out = new PrintWriter(new BufferedWriter(new OutputStreamWriter(new FileOutputStream(log))));
// create the log
for (Request r : requests) {
// write it to the log
out.println(r);
}
out.flush();
} catch (IOException e) {
Logging.getErrorLog().reportCritical("Could not write request log to file: " + e.getMessage());
} finally {
if (out != null) {
out.close();
}
}
}
}
}