/* $Id$ */
package org.noreply.fancydress.directory;
import org.bouncycastle.util.encoders.Base64;
import org.bouncycastle.crypto.InvalidCipherTextException;
import org.noreply.fancydress.directory.parser.*;
import org.noreply.fancydress.misc.Util;
import org.noreply.fancydress.crypto.*;
import org.noreply.fancydress.status.*;
import org.noreply.fancydress.type3.*;
import java.util.*;
import java.text.ParseException;
/**
* A single Server Descriptor of a type III node.
*
* This class holds all information of a single server descriptor. It may also
* hold subsections like Incoming/MMTP or Delivery/MBOX if such sections exist
* in this server descriptor.
*
* An instance of Server pools ServerDescriptors of a single node.
*
* @see Server
*/
public class ServerDescriptor {
/*
ปญญญญญญญ * descriptor version we understand.
ปญญญญญญญ */
private static final String DESCRIPTOR_VERSION = "0.2";
/* Required */
private String descriptorVersion;
private String nickname;
private byte[] identity;
private RSAPublicKey identityKey;
private byte[] identityDigest;
private byte[] digest;
private byte[] signature;
private Date published;
private Date validAfter;
private Date validUntil;
private RSAPublicKey packetKey;
private String[] packetVersions;
/* Optional */
private String contact;
private String contactFingerprint;
private String comments;
private String software;
private Boolean secureConfiguration;
private String whyInsecure;
private IncomingMMTPSection incomingMMTPSection;
private OutgoingMMTPSection outgoingMMTPSection;
private DeliveryMBOXSection deliveryMBOXSection;
private DeliverySMTPSection deliverySMTPSection;
private Object deliveryFragmentedSection;
/**
* Construct a ServerDescriptor instance from the message in m.
*
* This constructor builds up a ServerDescriptor from the message
* m
which contains a single server descriptor.
*
* The DirectoryMessage needs to have a 'Server' section.
*
* @param server a DirectoryMessage consisting of one of more sections.
* @throws Mix3BadServerFormatException if a section is syntactially invalid and the error cannot be ignored.
* @throws Mix3BadServerSignatureException if the server descriptor's signature is invalid.
*/
public ServerDescriptor(DirectoryMessage m)
throws Mix3BadServerFormatException, Mix3BadServerSignatureException
{
DirectorySection serverSection = m.getSection("Server");
if (serverSection == null)
throw new Mix3BadServerFormatException("No Server section found.");
parseServerSection(serverSection);
identityKey = new RSAPublicKey(identity);
identityDigest = CryptoPrimitives.hash(identity);
checkSignature(m);
DirectorySection section;
section = m.getSection("Incoming/MMTP");
try {
incomingMMTPSection = section == null ? null : new IncomingMMTPSection(section);
} catch (Mix3BadServerFormatException e) { System.err.println(e); };
section = m.getSection("Outgoing/MMTP");
try {
outgoingMMTPSection = section == null ? null : new OutgoingMMTPSection(section);
} catch (Mix3BadServerFormatException e) { System.err.println(e); };
section = m.getSection("Delivery/MBOX");
try {
deliveryMBOXSection = section == null ? null : new DeliveryMBOXSection(section);
} catch (Mix3BadServerFormatException e) { System.err.println(e); };
section = m.getSection("Delivery/SMTP");
try {
deliverySMTPSection = section == null ? null : new DeliverySMTPSection(section);
} catch (Mix3BadServerFormatException e) { System.err.println(e); };
}
/**
* Check if s
is a valid nickname.
*
* @throws Mix3BadServerFormatException if s
is not a valid nickame
*/
private static void checkSyntaxNickname(String s) throws Mix3BadServerFormatException {
for (int i=0; i= 'A' && c <= 'Z') ||
(c >= 'a' && c <= 'z') ||
(c >= '0' && c <= '9') ||
c == '_' || c == '@' || c == '-'))
throw new Mix3BadServerFormatException("Illegal characters in nickname: "+c);
}
}
/**
* Tokenize a Packet-Versions string into single Packet-Versions.
*
* The Packet-Versions is a comma separated list of Packet-Versions.
* This method parses a Packet-Versions value and returns an array of
* Strings, holding one Packet-Version each.
*
* Whitespace is trimmed off each Packet-Version.
*
* @param s the comma separated list of Packet-Versions
* @return array of Packet-Versions.
*/
public static String[] parsePacketVersions(String s) {
ArrayList versions = new ArrayList();
int indexFrom = 0;
int indexOf;
String[] tokens = Util.tokenize(s, ',');
for (int i=0; i 128)
throw new Mix3BadServerFormatException("Contact is too long for "+nickname);
contactFingerprint = entryContactFingerprint == null ? null : entryContactFingerprint.getValue();
if (contactFingerprint != null && contactFingerprint.length() > 128)
throw new Mix3BadServerFormatException("Contact-Fingerprint is too long for "+nickname);
comments = entryComments == null ? null : entryComments.getValue();
if (comments != null && comments.length() >= 1024)
throw new Mix3BadServerFormatException("Comments is too long for "+nickname);
software = entrySoftware == null ? null : entrySoftware.getValue();
try {
secureConfiguration = entrySecureConfiguration == null ? null :
new Boolean(Util.parseBoolean(entrySecureConfiguration.getValue()));
} catch (ParseException e) {
throw new Mix3BadServerFormatException("Cannot parse SecureConfiguration for "+nickname, e);
};
whyInsecure = entryWhyInsecure == null ? null : entryWhyInsecure.getValue();
}
/**
* Check the signature of the server descriptor in m
.
*
* @param m the ServerDescriptor to check
* @throws Mix3BadServerSignatureException if the signature is invalid.
*/
private void checkSignature(DirectoryMessage m) throws Mix3BadServerSignatureException {
DirectoryMessage cleanedDescriptor = new DirectoryMessage(m);
cleanedDescriptor.blindValue("Server","Digest");
cleanedDescriptor.blindValue("Server","Signature");
boolean verifies;
try {
verifies = identityKey.verify(signature, Util.toOctets(cleanedDescriptor.toString()));
} catch (InvalidCipherTextException e) {
throw new Mix3BadServerSignatureException("Server signature has invalid format.", e);
};
if (!verifies)
throw new Mix3BadServerSignatureException("Server signature does not verify.");
}
/**
* Get this server descriptor's nickname.
*
* @return nickname of the server
*/
public String getNickname() {
return nickname;
}
/**
* Get this server descriptor's identity key.
*
* @return asn1 encoded identity key of the server
*/
public byte[] getIdentity() {
return identity;
}
/**
* Get this server descriptor's identity key.
*
* @return identity key of the server
*/
public RSAPublicKey getIdentityKey() {
return identityKey;
}
/**
* Get this server descriptor's packet key.
*
* @return packet key of this server descriptor
*/
public RSAPublicKey getPacketKey() {
return packetKey;
}
/**
* Get this server descriptor's supported packet versions.
*
* @return supported packet versions
*/
public String[] getPacketVersions() {
return packetVersions;
}
/**
* Get the date after which this server descriptor is valid.
*
* @return date after which this server descriptor is valid
*/
public Date getValidAfter() {
return validAfter;
}
/**
* Get the date until which this server descriptor is valid.
*
* @return date until which this server descriptor is valid
*/
public Date getValidUntil() {
return validUntil;
}
/**
* Get the date when this server descriptor was published.
*
* @return date when this server descriptor was published
*/
public Date getPublished() {
return published;
}
/**
* Get the Incoming/MMTP section.
*
* @return this server descriptor's Incoming/MMTP section
*/
public IncomingMMTPSection getIncomingMMTPSection() {
return incomingMMTPSection;
}
/**
* Get the Outgoing/MMTP section.
*
* @return this server descriptor's Outgoing/MMTP section
*/
public OutgoingMMTPSection getOutgoingMMTPSection() {
return outgoingMMTPSection;
}
/**
* Get the Delivery/MBOX section.
*
* @return this server descriptor's Delivery/MBOX section
*/
public DeliveryMBOXSection getDeliveryMBOXSection() {
return deliveryMBOXSection;
}
/**
* Get the Delivery/SMTP section.
*
* @return this server descriptor's Delivery/SMTP section
*/
public DeliverySMTPSection getDeliverySMTPSection() {
return deliverySMTPSection;
}
}