package mireka.pop.command;
import static mireka.pop.SessionState.*;
import java.io.IOException;
import java.util.List;
import javax.annotation.concurrent.GuardedBy;
import mireka.login.LoginDecision;
import mireka.login.LoginResult;
import mireka.login.LoginSpecification;
import mireka.pop.CommandParser;
import mireka.pop.CommandSyntaxException;
import mireka.pop.IllegalSessionStateException;
import mireka.pop.Pop3Exception;
import mireka.pop.Session;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class ApopCommand extends AbstractLoginCommand {
private final Logger logger = LoggerFactory.getLogger(ApopCommand.class);
private final LoginSpecification loginSpecification;
private String timestamp;
@GuardedBy("ApopCommand.class")
private static long uniqueClockValue;
public ApopCommand(Session session) {
super(session);
loginSpecification = session.getServer().getLoginSpecification();
}
public String generateTimeStamp() {
if (timestamp != null)
throw new IllegalStateException();
long clockValue = generateUniqueClockValue();
int random = (int) Math.round(Math.random() * 100000);
timestamp =
"<" + random + "." + clockValue + "@"
+ session.getServer().getHostName() + ">";
return timestamp;
}
private static long generateUniqueClockValue() {
long millis = System.currentTimeMillis();
synchronized (ApopCommand.class) {
if (millis > uniqueClockValue) {
uniqueClockValue = millis;
} else {
++uniqueClockValue;
}
return uniqueClockValue;
}
}
@Override
public void execute(CommandParser commandParser) throws IOException,
Pop3Exception {
if (session.getSessionState() != AUTHORIZATION)
throw new IllegalSessionStateException();
List<String> args = commandParser.parseArguments();
if (args.size() != 2)
throw new CommandSyntaxException("Two arguments are expected");
String user = args.get(0);
String digest = args.get(1);
byte[] digestBytes = valueOfHex(digest);
LoginResult result =
loginSpecification.evaluateApop(user, timestamp, digestBytes);
if (result.decision == LoginDecision.VALID) {
startTransaction(result.principal);
} else {
logger.debug("Unsuccessful login result: {}", result.decision);
session.getThread().sendResponse("-ERR [AUTH] permission denied");
session.setSessionState(AUTHORIZATION);
}
}
private static byte[] valueOfHex(String s) throws CommandSyntaxException {
if (s.length() != 32)
throw new CommandSyntaxException(
"Second argument must be a 16 bytes hexadecimal number");
byte[] result = new byte[16];
for (int i = 0; i < 16; i++) {
String byteString = s.substring(i * 2, i * 2 + 2);
try {
result[i] = (byte) Integer.parseInt(byteString, 16);
} catch (NumberFormatException e) {
throw new CommandSyntaxException(
"Second argument must be a 16 bytes hexadecimal number");
}
}
return result;
}
}