package com.github.lindenb.jvarkit.tools.cgi; import java.io.BufferedReader; import java.io.ByteArrayInputStream; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStream; import java.io.PrintStream; import java.io.Reader; import java.io.StringReader; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.prefs.InvalidPreferencesFormatException; import java.util.prefs.Preferences; import javax.xml.stream.XMLStreamException; import javax.xml.stream.XMLStreamWriter; import htsjdk.samtools.util.CloserUtil; import com.github.lindenb.jvarkit.util.jcommander.Launcher; public abstract class AbstractCGI extends Launcher { //private static final Logger LOG=Logger.build(AbstractCGI.class).make(); private static final String PROPERTY_PREFFILE="prefs.file.xml"; protected StringBuilder logStream=new StringBuilder(); private List<Parameter> parameters=new ArrayList<Parameter>(); private int contentMaxLength=2048; private Preferences prefs=null; private boolean mimeHeaderPrinted=false; /** Interface for a Parameter */ static public interface Parameter { public String getKey(); public boolean isFile(); } static public class DefaultParameter implements Parameter { private String key; private String value; public DefaultParameter(String key,String value) { this.key=key; this.value=value; } @Override public String getKey() { return key; } public String getValue() { return value;} @Override public final boolean isFile() { return false; } } protected AbstractCGI() { super(); final PrintStream originalStderr=System.err; OutputStream redirect=new OutputStream() { @Override public void write(int c) throws IOException { if(c==-1) return; originalStderr.write(c); if(logStream.length() < 2048) { logStream.append((char)c); } } }; System.setErr(new PrintStream(redirect)); // } protected List<File> getPreferenceFiles() { String s = System.getProperty(PROPERTY_PREFFILE); if(s==null) return Collections.emptyList(); return Arrays.asList(new File(s)); } protected InputStream openPreferences() throws IOException { for(File f:getPreferenceFiles()) { if(f.exists() && f.isFile() && f.canRead()) { return new FileInputStream(f); } } StringBuilder xml=new StringBuilder("<!DOCTYPE preferences SYSTEM 'http://java.sun.com/dtd/preferences.dtd'>"); xml.append("<preferences EXTERNAL_XML_VERSION=\"1.0\">"); xml.append("<root type='user'>"); xml.append("<map/>"); xml.append("</root>"); xml.append("</preferences>"); return new ByteArrayInputStream(xml.toString().getBytes()); } protected Preferences getPreferences() throws IOException { if(this.prefs==null) { InputStream is=null; try { is=openPreferences(); Preferences.importPreferences(is); } catch(InvalidPreferencesFormatException er) { throw new IOException(er); } finally { CloserUtil.close(is); } this.prefs=Preferences.userRoot(); } return this.prefs; } public void setContentMaxLength(int contentMaxLength) { this.contentMaxLength = contentMaxLength; } public int getContentMaxLength() { return contentMaxLength; } public String getenv(String key) { return System.getenv(key); } public Set<String> getParameterNames() { Set<String> keys= new HashSet<String>(getParameters().size()); for(Parameter p:getParameters()) { keys.add(p.getKey()); } return keys; } public List<Parameter> getParameters() { return this.parameters; } public List<Parameter> getParameters(String key) { List<Parameter> L= new ArrayList<Parameter>(); for(Parameter p:getParameters()) { if(p.getKey().equals(key)) L.add(p); } return L; } /** find simple parameter and return its value as a String */ public String getString(String key) { Parameter p= getParameter(key); if(p==null) return null; if(p instanceof DefaultParameter) { return DefaultParameter.class.cast(p).getValue(); } return null; } public Parameter getParameter(String key) { for(Parameter p:getParameters()) { if(p.getKey().equals(key))return p; } return null; } public boolean hasParameter(String key) { return getParameter(key)!=null; } /** par the HTTP request */ public void parse() throws IOException { String requestMethod=null; if((requestMethod=getenv("REQUEST_METHOD"))==null) throw new IOException("Cannot find REQUEST_METHOD"); if(requestMethod.equals("POST")) { parsePOST(); } else if(requestMethod.equals("GET")) { parseGET(); } else { throw new IOException("Unknown REQUEST_METHOD \""+requestMethod+"\""); } } private void parseGET() throws IOException { String queryString = getenv("QUERY_STRING"); if(queryString==null) throw new IOException("Cannot find QUERY_STRING"); if(queryString.length()>getContentMaxLength()) throw new IOException("Content too large"); parse(new StringReader(queryString),queryString.length()); } private boolean isMultipart() { String contentType= getenv("CONTENT_TYPE"); if(contentType==null) return false; return contentType.indexOf("multipart/form-data")!=-1; } private void parsePOST() throws IOException { String contentLengthStr= getenv("CONTENT_LENGTH"); if(contentLengthStr==null) throw new IOException("CONTENT_LENGTH missing"); int contentLength=0; try { contentLength= Integer.parseInt(contentLengthStr); } catch (Exception e) { throw new IOException("Bad content Length "+contentLength); } if(contentLength>getContentMaxLength()) throw new IOException("Content too large"); if(!isMultipart()) { parse(new BufferedReader( new InputStreamReader(System.in)), contentLength); } else { throw new IOException("Cannot parse multipart actions"); } } private void parse(Reader in,int maxCharRead) throws IOException { int c; StringBuilder key=new StringBuilder(); StringBuilder value=null; int count=0; while((c=in.read())!=-1 && count < maxCharRead) { ++count; if(c=='+') c=' '; if(c=='&') { if(key.length()!=0) { addParameter(key.toString(),value); } key=new StringBuilder(); value=null; } else if(c=='=' && key.length()>0 && value==null) { value = new StringBuilder(); } else { if(c=='%' && count+2<=maxCharRead) { int c2= in.read(); if(c2==-1) throw new IOException("Bad Input"); int c3= in.read(); if(c3==-1) throw new IOException("Bad Input"); c=x2c(c2,c3); count+=2; } if(value!=null) { value.append((char)c); } else { key.append((char)c); } } } if(key.length()!=0) { addParameter(key.toString(),value); } } private static int x2c(int c1,int c2) throws IOException { try { return Integer.parseInt(String.valueOf((char)c1)+(char)c2, 16); } catch (NumberFormatException e) { throw new IOException("cannot cast hexa int("+(c1)+")int("+c2+")",e); } } private void addParameter(String key,CharSequence value) { if(key==null || key.isEmpty()) return; this.parameters.add(new DefaultParameter(key.toString(),value==null?"":value.toString())); } protected boolean isMimeHeaderPrinted() { return mimeHeaderPrinted; } protected void setMimeHeaderPrinted(boolean mimeHeaderPrinted) { this.mimeHeaderPrinted = mimeHeaderPrinted; } protected void writeHTMLException(XMLStreamWriter w,Throwable err) throws XMLStreamException { if(err==null) return; w.writeStartElement("pre"); w.writeCharacters(String.valueOf(err.getMessage()));w.writeEmptyElement("br"); for(StackTraceElement e:err.getStackTrace()) { w.writeCharacters(" "); w.writeCharacters(String.valueOf(e.getClassName())); w.writeCharacters(" "); w.writeCharacters(String.valueOf(e.getMethodName())); w.writeCharacters(" "); w.writeCharacters(String.valueOf(e.getLineNumber())); w.writeEmptyElement("br"); } w.writeEndElement(); } protected void writeHtmlFooter(XMLStreamWriter w) throws XMLStreamException { w.writeEmptyElement("hr"); w.writeStartElement("div"); w.writeCharacters("Author : "); w.writeStartElement("a"); w.writeAttribute("href","#"); w.writeAttribute("title","my mail"); w.writeCharacters("Pierre Lindenbaum"); w.writeEndElement(); /* String s=getOnlineDocUrl(); if(s!=null && !s.isEmpty()) { w.writeCharacters(" Documentation : "); w.writeStartElement("a"); w.writeAttribute("href", s); w.writeAttribute("title", s); w.writeCharacters(s); w.writeEndElement(); }*/ w.writeCharacters(" Version: "); w.writeStartElement("i"); w.writeCharacters(String.valueOf(getVersion())); w.writeEndElement(); w.writeCharacters(" Compilation: "); w.writeStartElement("i"); w.writeCharacters(String.valueOf(getCompileDate())); w.writeEndElement(); w.writeEndElement(); } abstract protected void doCGI(); @Override final public int doWork(List<String> args) { try { parse(); doCGI(); } catch (Exception e) { if(!isMimeHeaderPrinted()) { setMimeHeaderPrinted(true); System.out.print("Content-type: text/plain\n"); System.out.println(); } e.printStackTrace(System.out); } finally { System.out.flush(); System.out.close(); } return 0; } protected void writeHTMLFooter(XMLStreamException out) throws XMLStreamException { } }