// Copyright 1997 The Open Group Research Institute.  All rights reserved.

package krb4.lib;

import java.io.*;
import java.util.*;

public class Krb4TicketFile implements Krb4CredsCache {
	private String cacheType;
	private String cacheName;
	private String cname;
	private String cinst;
	private String crealm;
	private Vector credsList;
	private boolean _hostLittleEndian;
	private RandomAccessFile cacheFile;
	private FileLock cacheLock;

	public Krb4TicketFile(String cache_name,
		String name, String inst, String realm,
		boolean hostLittleEndian)
		throws Krb4Exception, IOException, LockException {
		_hostLittleEndian = hostLittleEndian;
		init("FILE", cache_name, name, inst, realm);
	}

	public synchronized void init(String cache_type, String cache_name,
		String name, String inst, String realm)
		throws Krb4Exception, IOException, LockException {
		//try to read, but open for read/write
		//if does not exist, try to write
		//close
		cacheType = cache_type;
		cacheName = cache_name;
		cname = name;
		cinst = inst;
		crealm = realm;
		credsList = new Vector();
		File f = new File(cacheName);
		cacheLock = new FileLock(f.getParent(), f.getName());
		try {
			cacheLock.lock();
			try {
				read();
			} catch (Exception e) {
				write();
			}
		} finally {
			cacheLock.unlock();
		}
	}

	public synchronized Krb4Creds read(String sname, String sinst,
		String srealm)
		throws Krb4Exception, IOException, LockException {
		//try to read; open for read only
		//close
		try {
			cacheLock.lock();
			try {
				read();
			} catch (Exception e) {}
		} finally {
			cacheLock.unlock();
		}
		if (credsList != null) {
			Enumeration e = credsList.elements();
			while (e.hasMoreElements()) {
				Krb4Creds creds = (Krb4Creds)e.nextElement();
				long now = new java.util.Date().getTime();
				//XXX comment out for testing
				if (now < creds.timestamp) {
					if (creds.timestamp - now >
						(long)Krb4.CLOCK_SKEW * 1000L)
						continue;
				}
				else
					if (now - creds.timestamp > creds.lifetime)
						continue;
				if (sname.equals(creds.sname) &&
					sinst.equals(creds.sinst) &&
					srealm.equals(creds.srealm))
					//XXX need deep copy here
					return creds;
			}
		}
		return null;
	}

	public synchronized void write(Krb4Creds creds)
		throws Krb4Exception, IOException, LockException {
		//try to read, but open for read/write
		//if exists, try to write
		//close
		try {
			cacheLock.lock();
			try {
				read();
			} catch (Exception e) {}
			credsList.addElement(creds);
			write();
		} finally {
			cacheLock.unlock();
		}
	}

	public synchronized void print()
		throws Krb4Exception, IOException, LockException {
		try {
			cacheLock.lock();
			try {
				read();
			} catch (Exception e) {}
		} finally {
			cacheLock.unlock();
		}
		System.out.println("Ticket file:    " + cacheName);
		System.out.print("Principal:      " + cname);
		if (cinst != null && cinst.length() > 0)
			System.out.print("." + cinst);
		System.out.println("@" + crealm);
		System.out.println();
		System.out.println("  Issued           Expires          Principal");
		Enumeration e = credsList.elements();
		while (e.hasMoreElements())
			((Krb4Creds)e.nextElement()).print();
	}

	public synchronized Enumeration elements()
		throws Krb4Exception, IOException, LockException {
		//XXX needs a deep copy
		return null;
	}

	public synchronized void destroy()
		throws Krb4Exception, IOException, LockException {
		File f = new File(cacheName);
		try {
			cacheLock.lock();
			cacheFile = new RandomAccessFile(f, "rw");
			FileOutputStream o = new FileOutputStream(cacheFile.getFD());
			long len = cacheFile.length();
			long i = 0;
			while (i < len) {
				o.write(0);
				i++;
			}
		} finally {
			try {
				cacheFile.close();
			} catch (Exception e2) {}
			try {
				f.delete();
			} catch (Exception e2) {}
			cacheLock.unlock();
		}
	}

	protected synchronized void read() throws Krb4Exception, IOException {
		credsList = new Vector();
		byte[] data = null;
		try {
			cacheFile = new RandomAccessFile(cacheName, "r");
			FileInputStream i = new FileInputStream(cacheFile.getFD());
			data = new byte[i.available()];
			i.read(data);
		} finally {
			try {
				cacheFile.close();
			} catch (Exception e2) {}
		}
		Krb4Encode ref = new Krb4Encode(data);
		ref.setByteOrder(_hostLittleEndian);
		String temp_cname = ref.getString();
		String temp_cinst = ref.getString();
		while (ref.offset < data.length)
			credsList.addElement(Krb4Creds.fromFile(ref));
		cname = temp_cname;
		cinst = temp_cinst;
	}

	protected synchronized byte[] toBytes()
		throws Krb4Exception, IOException {
		ByteArrayOutputStream o = new ByteArrayOutputStream();
		o.write(Krb4Encode.toBytes(cname));
		o.write(Krb4Encode.toBytes(cinst));
		if (credsList != null) {
			Enumeration e = credsList.elements();
			while (e.hasMoreElements())
				o.write(((Krb4Creds)e.nextElement()).toBytes(
					_hostLittleEndian));
		}
		return o.toByteArray();
	}

	protected synchronized void write()
		throws Krb4Exception, IOException {
		try {
			cacheFile = new RandomAccessFile(cacheName, "rw");
			FileOutputStream o = new FileOutputStream(cacheFile.getFD());
			o.write(toBytes());
		} finally {
			try {
				cacheFile.close();
			} catch (Exception e2) {}
		}
	}

}
