Package org.kapott.hbci.GV

Source Code of org.kapott.hbci.GV.HBCIJobImpl

/*  $Id: HBCIJobImpl.java,v 1.5 2011/06/06 10:30:31 willuhn Exp $

    This file is part of HBCI4Java
    Copyright (C) 2001-2008  Stefan Palme

    HBCI4Java is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    HBCI4Java is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

package org.kapott.hbci.GV;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Properties;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import org.kapott.hbci.GV_Result.HBCIJobResult;
import org.kapott.hbci.GV_Result.HBCIJobResultImpl;
import org.kapott.hbci.callback.HBCICallback;
import org.kapott.hbci.exceptions.HBCI_Exception;
import org.kapott.hbci.exceptions.InvalidArgumentException;
import org.kapott.hbci.exceptions.InvalidUserDataException;
import org.kapott.hbci.exceptions.JobNotSupportedException;
import org.kapott.hbci.manager.HBCIHandler;
import org.kapott.hbci.manager.HBCIUtils;
import org.kapott.hbci.manager.HBCIUtilsInternal;
import org.kapott.hbci.manager.LogFilter;
import org.kapott.hbci.manager.MsgGen;
import org.kapott.hbci.passport.HBCIPassport;
import org.kapott.hbci.passport.HBCIPassportInternal;
import org.kapott.hbci.passport.HBCIPassportList;
import org.kapott.hbci.protocol.SEG;
import org.kapott.hbci.protocol.SyntaxElement;
import org.kapott.hbci.protocol.factory.SEGFactory;
import org.kapott.hbci.status.HBCIMsgStatus;
import org.kapott.hbci.status.HBCIRetVal;
import org.kapott.hbci.structures.Konto;
import org.kapott.hbci.structures.Value;

public abstract class HBCIJobImpl
    implements HBCIJob
{
    private String name;              /* Job-Name mit Versionsnummer */
    private String jobName;           /* Job-Name ohne Versionsnummer */
    public String getJobName() {
    return jobName;
  }

  private String segVersion;        /* Segment-Version */
    private Properties llParams;       /* Eingabeparameter f�r diesen GV (Saldo.KTV.number) */
    private HBCIPassportList passports;
    protected HBCIJobResultImpl jobResult;         /* Objekt mit R�ckgabedaten f�r diesen GV */
    private HBCIHandler parentHandler;
    private int idx;                  /* idx gibt an, der wievielte task innerhalb der aktuellen message
                                         dieser GV ist */
    private boolean executed;
    private int contentCounter;       /* Z�hler, wie viele R�ckgabedaten bereits in outStore eingetragen wurden
                                           (entspricht der anzahl der antwort-segmente!)*/
    private Hashtable<String, String[][]> constraints;    /* Festlegungen, welche Parameter eine Anwendung setzen muss, wie diese im
                                         HBCI-Kernel umgesetzt werden und welche default-Werte vorgesehen sind;
                                         die Hashtable hat als Schl�ssel einen String, der angibt, wie ein Wert aus einer
                                         Anwendung heraus zu setzen ist. Der dazugeh�rige Value ist ein Array. Jedes Element
                                         dieses Arrays ist ein String[2], wobei das erste Element angibt, wie der Pfadname heisst,
                                         unter dem der anwendungs-definierte Wert abzulegen ist, das zweite Element gibt den
                                         default-Wert an, falls f�r diesen Namen *kein* Wert angebeben wurde. Ist der default-
                                         Wert="", so kann das Syntaxelement weggelassen werden. Ist der default-Wert=null,
                                         so *muss* die Anwendung einen Wert spezifizieren */
    private Hashtable<String, Integer> logFilterLevels; /* hier wird f�r jeden hl-param-name gespeichert, ob der dazugeh�rige wert
                                          �ber den logfilter-Mechanimus gesch�tzt werden soll */
    private HashSet<String> indexedConstraints;
   
    protected HBCIJobImpl(HBCIHandler parentHandler,String jobnameLL,HBCIJobResultImpl jobResult)
    {
        findSpecNameForGV(jobnameLL,parentHandler);
        this.llParams=new Properties();
       
        this.passports=new HBCIPassportList();
        this.passports.addPassport((HBCIPassportInternal)parentHandler.getPassport(),HBCIPassport.ROLE_ISS);
       
        this.jobResult=jobResult;
        this.jobResult.setParentJob(this);
       
        this.contentCounter=0;
        this.constraints=new Hashtable<String, String[][]>();
        this.logFilterLevels=new Hashtable<String, Integer>();
        this.indexedConstraints=new HashSet<String>();
        this.executed=false;
       
        this.parentHandler=parentHandler;

        /* offensichtlich soll ein GV mit dem Namen name in die nachricht
           aufgenommen werden. da GV durch segmente definiert sind, und einige
           dieser segmente ein request-tag benoetigen (siehe klasse
           SyntaxElement), wird hier auf jeden fall das request-tag gesetzt.
           wenn es *nicht* benoetigt wird, schadet es auch nichts. und es ist
           auf keinen fall "zu viel" gesetzt, da dieser code nur ausgefuehrt wird,
           wenn das jeweilige segment tatsaechlich erzeugt werden soll */
        llParams.setProperty(this.name,"requested");
    }
   
    /* gibt den segmentcode f�r diesen job zur�ck */
    public String getHBCICode()
    {
        StringBuffer ret=null;
       
        // Macht aus z.Bsp. "KUmsZeit5" -> "KUmsZeitPar5.SegHead.code"
        StringBuffer searchString=new StringBuffer(name);
        for (int i=searchString.length()-1;i>=0;i--) {
            if (!(searchString.charAt(i)>='0' && searchString.charAt(i)<='9')) {
                searchString.insert(i+1,"Par");
                searchString.append(".SegHead.code");
                break;
            }
        }
       
        HBCIPassportInternal passport=getMainPassport();
        StringBuffer         tempkey=new StringBuffer();
       
        // durchsuchen aller param-segmente nach einem job mit dem jobnamen des
        // aktuellen jobs
        for (Enumeration i=passport.getBPD().propertyNames();i.hasMoreElements();) {
            String key=(String)i.nextElement();
           
            if (key.indexOf("Params")==0) {
                tempkey.setLength(0);
                tempkey.append(key);
                tempkey.delete(0,tempkey.indexOf(".")+1);
               
                if (tempkey.toString().equals(searchString.toString())) {
                    ret=new StringBuffer(passport.getBPD().getProperty(key));
                    ret.replace(1,2,"K");
                    ret.deleteCharAt(ret.length()-1);
                    break;
                }
            }
        }
       
        return ret.toString();
    }
   
    /* gibt zu einem gegebenen jobnamen des namen dieses jobs in der syntax-spez.
     * zur�ck (also mit angeh�ngter versionsnummer)
     */
    private void findSpecNameForGV(String jobnameLL,HBCIHandler handler)
    {
        int          maxVersion=0;
        StringBuffer key=new StringBuffer();
       
        // alle param-segmente durchlaufen
        Properties bpd = handler.getPassport().getBPD();
        for (Enumeration i=bpd.propertyNames();i.hasMoreElements();) {
            String path = (String)i.nextElement();
            key.setLength(0);
            key.append(path);
           
            if (key.indexOf("Params")==0) {
                key.delete(0,key.indexOf(".")+1);
                // wenn segment mit namen des aktuellen jobs gefunden wurde
               
                if (key.indexOf(jobnameLL+"Par")==0 &&
                    key.toString().endsWith(".SegHead.code"))
                {
                  // willuhn 2011-06-06 Maximal zulaessige Segment-Version ermitteln
                  // Hintergrund: Es gibt Szenarien, in denen nicht die hoechste verfuegbare
                  // Versionsnummer verwendet werden kann, weil die Voraussetzungen impliziert,
                  // die beim User nicht gegeben sind. Mit diesem Parameter kann die maximale
                  // Version nach oben begrenzt werden. In AbstractPinTanPassport#setBPD() ist
                  // ein konkretes Beispiel enthalten (Bank macht HITANS5 und damit HHD 1.4, der
                  // User hat aber nur ein HHD-1.3-tauglichen TAN-Generator)
                  int maxAllowedVersion = Integer.parseInt(HBCIUtils.getParam("kernel.gv." + bpd.getProperty(path,"default") + ".segversion.max","0"));
                 
                  key.delete(0,jobnameLL.length()+("Par").length());
                   
                    // extrahieren der versionsnummer aus dem spez-namen
                    String st=key.substring(0,key.indexOf("."));
                    int    version=0;
                   
                    try {
                        version=Integer.parseInt(st);
                    } catch (Exception e) {
                        HBCIUtils.log("found invalid job version: key="+key+", jobnameLL="+jobnameLL+" (this is a known, but harmless bug)", HBCIUtils.LOG_WARN);
                    }
                   
                    // willuhn 2011-06-06 Segment-Versionen ueberspringen, die groesser als die max. zulaessige sind
                    if (maxAllowedVersion > 0 && version > maxAllowedVersion)
                    {
                      HBCIUtils.log("skipping segment version " + version + " for task " + jobnameLL + ", larger than allowed version " + maxAllowedVersion, HBCIUtils.LOG_INFO);
                      continue;
                    }
                    // merken der gr��ten jemals aufgetretenen versionsnummer
                    if (version!=0) {
                        HBCIUtils.log("task "+jobnameLL+" is supported with segment version "+st,HBCIUtils.LOG_DEBUG2);
                        if (version>maxVersion) {
                            maxVersion=version;
                        }
                    }
                }
            }
        }
       
        if (maxVersion==0)
        {
            String msg=HBCIUtilsInternal.getLocMsg("EXCMSG_GVNOTSUPP",jobnameLL);
            if (!HBCIUtilsInternal.ignoreError(handler.getPassport(),"client.errors.ignoreJobNotSupported",msg))
                throw new JobNotSupportedException(jobnameLL);
           
            maxVersion = 1;
            HBCIUtils.log("Using segment version " + maxVersion + " for job " + jobnameLL + ", although not found in BPD. This may fail", HBCIUtils.LOG_WARN);
        }
       
        // namen+versionsnummer speichern
        this.jobName    = jobnameLL;
        this.segVersion = Integer.toString(maxVersion);
        this.name       = jobnameLL + this.segVersion;
    }
   
    /**
     * Legt die Versionsnummer des Segments manuell fest.
     * Ist u.a. noetig, um HKTAN-Segmente in genau der Version zu senden, in der
     * auch die HITANS empfangen wurden. Andernfalls koennte es passieren, dass
     * wir ein HKTAN mit einem TAN-Verfahren senden, welches in dieser HKTAN-Version
     * gar nicht von der Bank unterstuetzt wird. Das ist ein Dirty-Hack, ich weiss ;)
     * Falls das noch IRGENDWO anders verwendet wird, muss man hoellisch aufpassen,
     * dass alle Stellen, wo "this.name" bzw. "this.segVersion" direkt oder indirekt
     * verwendet wurde, ebenfalls beruecksichtigt werden.
     * @param version die neue Versionsnummer.
     */
    public synchronized void setSegVersion(String version)
    {
      if (version == null || version.length() == 0)
      {
        HBCIUtils.log("tried to change segment version for task " + this.jobName + " explicit, but no version given",HBCIUtils.LOG_WARN);
        return;
      }
     
      // Wenn sich die Versionsnummer nicht geaendert hat, muessen wir die
      // Huehner ja nicht verrueckt machen ;)
      if (version.equals(this.segVersion))
        return;
     
      HBCIUtils.log("changing segment version for task " + this.jobName + " explicit from " + this.segVersion + " to " + version,HBCIUtils.LOG_INFO);

      // Der alte Name
      String oldName = this.name;
     
      // Neuer Name und neue Versionsnummer
      this.segVersion = version;
      this.name       = this.jobName + version;

      // Bereits gesetzte llParams fixen
      String[] names = this.llParams.keySet().toArray(new String[this.llParams.size()]);
      for (String s:names)
      {
        if (!s.startsWith(oldName))
          continue; // nicht betroffen

        // Alten Schluessel entfernen und neuen einfuegen
        String value = this.llParams.getProperty(s);
        String newName = s.replaceFirst(oldName,this.name);
        this.llParams.remove(s);
        this.llParams.setProperty(newName,value);
      }

      // Destination-Namen in den LowLevel-Parameter auf den neuen Namen umbiegen
      Enumeration<String> e = constraints.keys();
      while (e.hasMoreElements())
      {
        String frontendName = e.nextElement();
        String[][] values = constraints.get(frontendName);
        for (int i=0;i<values.length;++i)
        {
          String[] value = values[i];
          // value[0] ist das Target
          if (!value[0].startsWith(oldName))
            continue;

          // Hier ersetzen wir z.Bsp. "TAN2Step5.process" gegen "TAN2Step3.process"
          value[0] = value[0].replaceFirst(oldName,this.name);
        }
      }
    }
   
    public int getMaxNumberPerMsg()
    {
        int ret=1;
       
        StringBuffer searchString=new StringBuffer(name);
        for (int i=searchString.length()-1;i>=0;i--) {
            if (!(searchString.charAt(i)>='0' && searchString.charAt(i)<='9')) {
                searchString.insert(i+1,"Par");
                searchString.append(".maxnum");
                break;
            }
        }
       
        HBCIPassportInternal passport=getMainPassport();
        StringBuffer         tempkey=new StringBuffer();
       
        for (Enumeration i=passport.getBPD().propertyNames();i.hasMoreElements();) {
            String key=(String)i.nextElement();
           
            if (key.indexOf("Params")==0) {
                tempkey.setLength(0);
                tempkey.append(key);
                tempkey.delete(0,tempkey.indexOf(".")+1);
               
                if (tempkey.toString().equals(searchString.toString())) {
                    ret=Integer.parseInt(passport.getBPD().getProperty(key));
                    break;
                }
            }
        }
       
        return ret;
    }

    public int getMinSigs()
    {
        int ret=0;
       
        StringBuffer searchString=new StringBuffer(name);
        for (int i=searchString.length()-1;i>=0;i--) {
            if (!(searchString.charAt(i)>='0' && searchString.charAt(i)<='9')) {
                searchString.insert(i+1,"Par");
                searchString.append(".minsigs");
                break;
            }
        }
       
        HBCIPassportInternal passport=getMainPassport();
        StringBuffer         tempkey=new StringBuffer();
       
        for (Enumeration i=passport.getBPD().propertyNames();i.hasMoreElements();) {
            String key=(String)i.nextElement();
           
            if (key.indexOf("Params")==0) {
                tempkey.setLength(0);
                tempkey.append(key);
                tempkey.delete(0,tempkey.indexOf(".")+1);
               
                if (tempkey.toString().equals(searchString.toString())) {
                    ret=Integer.parseInt(passport.getBPD().getProperty(key));
                    break;
                }
            }
        }
       
        return ret;
    }
   
    public int getSecurityClass()
    {
        int ret=1;
       
        StringBuffer searchString=new StringBuffer(name);
        for (int i=searchString.length()-1;i>=0;i--) {
            if (!(searchString.charAt(i)>='0' && searchString.charAt(i)<='9')) {
                searchString.insert(i+1,"Par");
                searchString.append(".secclass");
                break;
            }
        }
       
        HBCIPassportInternal passport=getMainPassport();
        StringBuffer         tempkey=new StringBuffer();
       
        for (Enumeration i=passport.getBPD().propertyNames();i.hasMoreElements();) {
            String key=(String)i.nextElement();
           
            if (key.indexOf("Params")==0) {
                tempkey.setLength(0);
                tempkey.append(key);
                tempkey.delete(0,tempkey.indexOf(".")+1);
               
                if (tempkey.toString().equals(searchString.toString())) {
                    ret=Integer.parseInt(passport.getBPD().getProperty(key));
                    break;
                }
            }
        }
       
        return ret;
    }

    protected void addConstraint(String frontendName,String destinationName,String defValue,int logFilterLevel)
    {
        addConstraint(frontendName, destinationName, defValue, logFilterLevel, false);
    }

    protected void addConstraint(String frontendName,String destinationName,String defValue,int logFilterLevel,boolean indexed)
    {
        // value ist array:(lowlevelparamname, defaultvalue)
        String[] value=new String[2];
        value[0]=getName()+"."+destinationName;
        value[1]=defValue;

        // alle schon gespeicherten "ziel-lowlevelparameternamen" f�r den gew�nschten
        // frontend-namen suchen
        String[][] values=(constraints.get(frontendName));

        if (values==null) {
            // wenn es noch keine gibt, ein neues frontend-ding anlegen //FIXME: was ist ein "frontend-ding"?
            values=new String[1][];
            values[0]=value;
        } else {
            ArrayList<String[]> a=new ArrayList<String[]>(Arrays.asList(values));
            a.add(value);
            values=(a.toArray(values));
        }

        constraints.put(frontendName,values);
       
        if (indexed) {
            indexedConstraints.add(frontendName);
        }
       
        if (logFilterLevel>0) {
          logFilterLevels.put(frontendName,new Integer(logFilterLevel));
        }
    }
   
    public void verifyConstraints()
    {
        HBCIPassportInternal passport=getMainPassport();
       
        // durch alle gespeicherten constraints durchlaufen
        for (Iterator<String> i=constraints.keySet().iterator();i.hasNext();) {
            // den frontendnamen f�r das constraint ermitteln
            String     frontendName=(i.next());
           
            // dazu alle ziel-lowlevelparameter mit default-wert extrahieren
            String[][] values=(constraints.get(frontendName));

            // durch alle ziel-lowlevel-parameternamen durchlaufen, die gesetzt werden m�ssen
            for (int j=0;j<values.length;j++) {
              //Array mit Pfadname und default Wert
                String[] value=values[j];
                // lowlevel-name (Pfadname) des parameters (z.B. wird Frontendname src.bic zum Pfad My.bic
                String   destination=value[0];
                // default-wert des parameters, wenn keiner angegeben wurde
                String   defValue=value[1];

                String   givenContent=getLowlevelParam(destination);
                if (givenContent==null && indexedConstraints.contains(frontendName)) {
                    givenContent = getLowlevelParam(insertIndex(destination, 0));
                }

                String   content=null;

                content=defValue;
                if (givenContent!=null && givenContent.length()!=0)
                    content=givenContent;

                if (content==null) {
                    String msg=HBCIUtilsInternal.getLocMsg("EXC_MISSING_HL_PROPERTY",frontendName);
                    if (!HBCIUtilsInternal.ignoreError(passport,"client.errors.ignoreWrongJobDataErrors",msg))
                        throw new InvalidUserDataException(msg);
                    content="";
                }

                // evtl. default-wert als aktuellen wert setzen (naemlich dann,
                // wenn kein content angegeben wurde (givenContent==null), aber
                // ein default-Content definiert wurde (content.length()!=0)
                if (content.length()!=0 && givenContent==null)
                    setLowlevelParam(destination,content);
            }
        }
       
        // verify if segment can be created
        SEG seg=null;
        try {
            seg=createJobSegment();
            seg.validate();
        } catch (Exception ex) {
            throw new HBCI_Exception("*** the job segment for this task can not be created",ex);
        } finally {
            if (seg!=null) {
                SEGFactory.getInstance().unuseObject(seg);
            }
        }
    }
   
    public SEG createJobSegment()
    {
        return createJobSegment(0);
    }
   
    public SEG createJobSegment(int segnum)
    {
        SEG seg=null;
        try {
            MsgGen gen=getParentHandler().getMsgGen();
            seg=SEGFactory.getInstance().createSEG(getName(),getName(),null,0,gen.getSyntax());
            for (Enumeration e=getLowlevelParams().propertyNames();e.hasMoreElements();) {
                String key=(String)e.nextElement();
                String value=getLowlevelParams().getProperty(key);
                seg.propagateValue(key,value,
                                   SyntaxElement.TRY_TO_CREATE,
                                   SyntaxElement.DONT_ALLOW_OVERWRITE);
            }
            seg.propagateValue(getName()+".SegHead.seq",Integer.toString(segnum),
                               SyntaxElement.DONT_TRY_TO_CREATE,
                               SyntaxElement.ALLOW_OVERWRITE);
        } catch (Exception ex) {
            throw new HBCI_Exception("*** the job segment for this task can not be created",ex);
        }
       
        return seg;
    }
   
    public List<String> getJobParameterNames()
    {
        MsgGen gen=getParentHandler().getMsgGen();
        return gen.getGVParameterNames(name);
    }
   
    public List<String> getJobResultNames()
    {
        MsgGen gen=getParentHandler().getMsgGen();
        return gen.getGVResultNames(name);
    }
   
    public Properties getJobRestrictions()
    {
        return passports.getMainPassport().getJobRestrictions(name);
    }
   
    /** Setzen eines komplexen Job-Parameters (Kontodaten). Einige Jobs ben�tigten Kontodaten
        als Parameter. Diese m�ssten auf "normalem" Wege durch drei Aufrufe von
        {@link #setParam(String,String)} erzeugt werden (je einer f�r
        die L�nderkennung, die Bankleitzahl und die Kontonummer). Durch Verwendung dieser
        Methode wird dieser Weg abgek�rzt. Es wird ein Kontoobjekt �bergeben, f�r welches
        die entsprechenden drei <code>setParam(String,String)</code>-Aufrufe automatisch
        erzeugt werden.
        @param paramname die Basis der Parameter f�r die Kontodaten (f�r "<code>my.country</code>",
        "<code>my.blz</code>", "<code>my.number</code>" w�re das also "<code>my</code>")
        @param acc ein Konto-Objekt, aus welchem die zu setzenden Parameterdaten entnommen werden */
    public void setParam(String paramname,Konto acc)
    {
        setParam(paramname, null, acc);
    }
   
    /**
     * @see org.kapott.hbci.GV.HBCIJob#setParam(java.lang.String, java.lang.Integer, org.kapott.hbci.structures.Konto)
     */
    public void setParam(String paramname,Integer index,Konto acc)
    {
        if (acceptsParam(paramname+".country") && acc.country!=null && acc.country.length()!=0)
            setParam(paramname+".country",index,acc.country);
       
        if (acceptsParam(paramname+".blz") && acc.blz!=null && acc.blz.length()!=0)
            setParam(paramname+".blz",index,acc.blz);
       
        if (acceptsParam(paramname+".number") && acc.number!=null && acc.number.length()!=0)
            setParam(paramname+".number",index,acc.number);
       
        if (acceptsParam(paramname+".subnumber") && acc.subnumber!=null && acc.subnumber.length()!=0)
            setParam(paramname+".subnumber",index,acc.subnumber);
       
        if (acceptsParam(paramname+".name") && acc.name!=null && acc.name.length()!=0)
            setParam(paramname+".name",index,acc.name);
       
        if (acceptsParam(paramname+".curr") && acc.curr!=null && acc.curr.length()!=0)
          setParam(paramname+".curr",index,acc.curr);

        if (acceptsParam(paramname+".bic") && acc.bic!=null && acc.bic.length()!=0)
            setParam(paramname+".bic",index,acc.bic);
       
        if (acceptsParam(paramname+".iban") && acc.iban!=null && acc.iban.length()!=0)
            setParam(paramname+".iban",index,acc.iban);
       
    }

    /** Setzen eines komplexen Job-Parameters (Geldbetrag). Einige Jobs ben�tigten Geldbetr�ge
        als Parameter. Diese m�ssten auf "normalem" Wege durch zwei Aufrufe von
        {@link #setParam(String,String)} erzeugt werden (je einer f�r
        den Wert und die W�hrung). Durch Verwendung dieser
        Methode wird dieser Weg abgek�rzt. Es wird ein Value-Objekt �bergeben, f�r welches
        die entsprechenden zwei <code>setParam(String,String)</code>-Aufrufe automatisch
        erzeugt werden.
        @param paramname die Basis der Parameter f�r die Geldbetragsdaten (f�r "<code>btg.value</code>" und
        "<code>btg.curr</code>" w�re das also "<code>btg</code>")
        @param v ein Value-Objekt, aus welchem die zu setzenden Parameterdaten entnommen werden */
    public void setParam(String paramname, Value v)
    {
        setParam(paramname, null, v);
    }

    public void setParam(String paramname, Integer index, Value v)
    {
        if (acceptsParam(paramname+".value"))
            setParam(paramname+".value",index,HBCIUtils.bigDecimal2String(v.getBigDecimalValue()));

        String curr=v.getCurr();
        if (acceptsParam(paramname+".curr") && curr!=null && curr.length()!=0)
            setParam(paramname+".curr",index,curr);
    }

    /** Setzen eines Job-Parameters, bei dem ein Datums als Wert erwartet wird. Diese Methode
        dient als Wrapper f�r {@link #setParam(String,String)}, um das Datum in einen korrekt
        formatierten String umzuwandeln. Das "richtige" Datumsformat ist dabei abh�ngig vom
        aktuellen Locale.
        @param paramName Name des zu setzenden Job-Parameters
        @param date Datum, welches als Wert f�r den Job-Parameter benutzt werden soll */
    public void setParam(String paramName, Date date)
    {
        setParam(paramName, null, date);
    }

    public void setParam(String paramName, Integer index, Date date)
    {
        setParam(paramName, index, HBCIUtils.date2StringISO(date));
    }

    /** Setzen eines Job-Parameters, bei dem ein Integer-Wert Da als Wert erwartet wird. Diese Methode
        dient nur als Wrapper f�r {@link #setParam(String,String)}.
        @param paramName Name des zu setzenden Job-Parameters
        @param i Integer-Wert, der als Wert gesetzt werden soll */    
    public void setParam(String paramName,int i)
    {
        setParam(paramName,Integer.toString(i));
    }
   
    protected boolean acceptsParam(String hlParamName)
    {
      return constraints.get(hlParamName)!=null;
    }

    /** <p>Setzen eines Job-Parameters. F�r alle Highlevel-Jobs ist in der Package-Beschreibung zum
        Package {@link org.kapott.hbci.GV} eine Auflistung aller Jobs und deren Parameter zu finden.
        F�r alle Lowlevel-Jobs kann eine Liste aller Parameter entweder mit dem Tool
        {@link org.kapott.hbci.tools.ShowLowlevelGVs} oder zur Laufzeit durch Aufruf
        der Methode {@link org.kapott.hbci.manager.HBCIHandler#getLowlevelJobParameterNames(String)}
        ermittelt werden.</p>
        <p>Bei Verwendung dieser oder einer der anderen <code>setParam()</code>-Methoden werden zus�tzlich
        einige der Job-Restriktionen (siehe {@link #getJobRestrictions()}) analysiert. Beim Verletzen einer
        der �berpr�ften Einschr�nkungen wird eine Exception mit einer entsprechenden Meldung erzeugt.
        Diese �berpr�fung findet allerdings nur bei Highlevel-Jobs statt.</p>
        @param paramName der Name des zu setzenden Parameters.
        @param value Wert, auf den der Parameter gesetzt werden soll */
    @Override
    public void setParam(String paramName,String value)
    {
        setParam(paramName,null,value);
    }

    /** <p>Setzen eines Job-Parameters. F�r alle Highlevel-Jobs ist in der Package-Beschreibung zum
        Package {@link org.kapott.hbci.GV} eine Auflistung aller Jobs und deren Parameter zu finden.
        F�r alle Lowlevel-Jobs kann eine Liste aller Parameter entweder mit dem Tool
        {@link org.kapott.hbci.tools.ShowLowlevelGVs} oder zur Laufzeit durch Aufruf
        der Methode {@link org.kapott.hbci.manager.HBCIHandler#getLowlevelJobParameterNames(String)}
        ermittelt werden.</p>
        <p>Bei Verwendung dieser oder einer der anderen <code>setParam()</code>-Methoden werden zus�tzlich
        einige der Job-Restriktionen (siehe {@link #getJobRestrictions()}) analysiert. Beim Verletzen einer
        der �berpr�ften Einschr�nkungen wird eine Exception mit einer entsprechenden Meldung erzeugt.
        Diese �berpr�fung findet allerdings nur bei Highlevel-Jobs statt.</p>
        @param paramName der Name des zu setzenden Parameters.
        @param index Der index oder <code>null</code>, wenn kein Index gew�nscht ist
        @param value Wert, auf den der Parameter gesetzt werden soll */
    @Override
    public void setParam(String paramName,Integer index,String value)
    {
      // wenn der Parameter einen LogFilter-Level gesetzt hat, dann den
      // betreffenden Wert zum Logfilter hinzuf�gen
      Integer logFilterLevel=logFilterLevels.get(paramName);
      if (logFilterLevel!=null && logFilterLevel.intValue()!=0) {
        LogFilter.getInstance().addSecretData(value,"X",logFilterLevel.intValue());
      }

        String[][]           destinations=constraints.get(paramName);
        HBCIPassportInternal passport=getMainPassport();
       
        if (destinations==null) {
            String msg=HBCIUtilsInternal.getLocMsg("EXCMSG_PARAM_NOTNEEDED",new String[] {paramName,getName()});
            if (!HBCIUtilsInternal.ignoreError(passport,"client.errors.ignoreWrongJobDataErrors",msg))
                throw new InvalidUserDataException(msg);
            destinations=new String[0][];
        }
       
        if (value==null || value.length()==0) {
            String msg=HBCIUtilsInternal.getLocMsg("EXCMSG_PARAM_EMPTY",new String[] {paramName,getName()});
            if (!HBCIUtilsInternal.ignoreError(passport,"client.errors.ignoreWrongJobDataErrors",msg))
                throw new InvalidUserDataException(msg);
            value="";
        }
       
        if (index!=null && !indexedConstraints.contains(paramName)) {
            String msg=HBCIUtilsInternal.getLocMsg("EXCMSG_PARAM_NOTINDEXED",new String[] {paramName,getName()});
            if (!HBCIUtilsInternal.ignoreError(passport,"client.errors.ignoreWrongJobDataErrors",msg))
                throw new InvalidUserDataException(msg);
        }

        for (int i=0;i<destinations.length;i++) {
            String[] valuePair=destinations[i];
            String   lowlevelname=valuePair[0];

            if (index != null && indexedConstraints.contains(paramName)) {
                lowlevelname = insertIndex(lowlevelname, index);
            }
           
            setLowlevelParam(lowlevelname,value);
        }
    }
   
    public void setContinueOffset(int loop)
    {
        String offset=getContinueOffset(loop);
        setLowlevelParam(getName()+".offset",(offset!=null)?offset:"");
    }
   
    protected void setLowlevelParam(String key,String value)
    {
        HBCIUtils.log("setting lowlevel parameter "+key+" = "+value,HBCIUtils.LOG_DEBUG);
        llParams.setProperty(key,value);
    }

    public Properties getLowlevelParams()
    {
        return llParams;
    }
   
    public String getLowlevelParam(String key)
    {
        return getLowlevelParams().getProperty(key);
    }

    public void setIdx(int idx)
    {
        this.idx=idx;
    }

    public String getName()
    {
        return name;
    }
   
    public String getSegVersion()
    {
        return this.segVersion;
    }

    /* stellt fest, ob f�r diesen Task ein neues Auftragssegment generiert werden muss.
       Das ist in zwei F�llen der Fall: der Task wurde noch nie ausgef�hrt; oder der Task
       wurde bereits ausgef�hrt, hat aber eine "offset"-Meldung zur�ckgegeben */
    public boolean needsContinue(int loop)
    {
        boolean needs=false;

        if (executed) {
            HBCIRetVal retval=null;
            int        num=jobResult.getRetNumber();

            for (int i=0;i<num;i++) {
                retval=jobResult.getRetVal(i);
               
                if (retval.code.equals("3040") && retval.params.length!=0 && (--loop)==0) {
                    needs=true;
                    break;
                }
            }
        }
        else needs=true;
           
        return needs;
    }

    /* gibt (sofern vorhanden) den offset-Wert des letzten HBCI-R�ckgabecodes
       zur�ck */
    private String getContinueOffset(int loop)
    {
        String ret=null;
        int    num=jobResult.getRetNumber();
       
        for (int i=0;i<num;i++) {
            HBCIRetVal retval=jobResult.getRetVal(i);

            if (retval.code.equals("3040") && retval.params.length!=0 && (--loop)==0) {
                ret=retval.params[0];
                break;
            }
        }

        return ret;
    }

    /* f�llt das Objekt mit den R�ckgabedaten. Dazu wird zuerst eine Liste aller
       Segmente erstellt, die R�ckgabedaten f�r diesen Task enthalten. Anschlie�end
       werden die HBCI-R�ckgabewerte (RetSegs) im outStore gespeichert. Danach werden
       die GV-spezifischen Daten im outStore abgelegt */
    public void fillJobResult(HBCIMsgStatus status,int offset)
    {
        try {
            executed=true;
            Properties result=status.getData();

            // nachsehen, welche antwortsegmente ueberhaupt
            // zu diesem task gehoeren
           
            // res-num --> segmentheader (wird f�r sortierung der
            // antwort-segmente ben�tigt)
            Hashtable<Integer,String> keyHeaders=new Hashtable<Integer, String>();
            for (Enumeration i=result.keys();i.hasMoreElements();) {
                String key=(String)(i.nextElement());
                if (key.startsWith("GVRes")&&
                    key.endsWith(".SegHead.ref")) {
                   
                    String segref=result.getProperty(key);
                    if ((Integer.parseInt(segref))-offset==idx) {
                        // nummer des antwortsegments ermitteln
                        int resnum=0;
                        if (key.startsWith("GVRes_")) {
                            resnum=Integer.parseInt(key.substring(key.indexOf('_')+1,key.indexOf('.')));
                        }
                       
                        keyHeaders.put(
                            new Integer(resnum),
                            key.substring(0,key.length()-(".SegHead.ref").length()));
                    }
                }
            }
           
            saveBasicValues(result,idx+offset);
            saveReturnValues(status,idx+offset);
           
            // segment-header-namen der antwortsegmente in der reihenfolge des
            // eintreffens sortieren
            Object[] resnums=keyHeaders.keySet().toArray(new Object[0]);
            Arrays.sort(resnums);

            // alle antwortsegmente durchlaufen
            for (int i=0;i<resnums.length;i++) {
                // dabei reihenfolge des eintreffens beachten
                String header=keyHeaders.get(resnums[i]);
               
                extractPlaintextResults(status,header,contentCounter);
                extractResults(status,header,contentCounter++);
                // der contentCounter wird fuer jedes antwortsegment um 1 erhoeht
            }
        } catch (Exception e) {
            String msg=HBCIUtilsInternal.getLocMsg("EXCMSG_CANTSTORERES",getName());
            if (!HBCIUtilsInternal.ignoreError(getMainPassport(),
                                       "client.errors.ignoreJobResultStoreErrors",
                                       msg+": "+HBCIUtils.exception2String(e))) {
                throw new HBCI_Exception(msg,e);
            }
        }
    }
   
    /* wenn wenigstens ein HBCI-R�ckgabewert f�r den aktuellen GV gefunden wurde,
       so werden im outStore zus�tzlich die entsprechenden Dialog-Parameter
       gespeichert (Property @c basic.*) */
    private void saveBasicValues(Properties result,int ref)
    {
        // wenn noch keine basic-daten gespeichert sind
        if (jobResult.getDialogId()==null) {
            // Pfad des originalen MsgHead-Segmentes holen und um "orig_" ergaenzen,
            // um den Key fuer die entsprechenden Daten in das result-Property zu erhalten
            String msgheadName="orig_"+result.getProperty("1");
           
            jobResult.storeResult("basic.dialogid",result.getProperty(msgheadName+".dialogid"));
            jobResult.storeResult("basic.msgnum",result.getProperty(msgheadName+".msgnum"));
            jobResult.storeResult("basic.segnum",Integer.toString(ref));

            HBCIUtils.log("basic values for " + getName() + " set to "
                    + jobResult.getDialogId() + "/"
                    + jobResult.getMsgNum()
                    + "/" + jobResult.getSegNum(),
                    HBCIUtils.LOG_DEBUG);
        }
    }

    /*
     * speichert die HBCI-R�ckgabewerte f�r diesen GV im outStore ab. Dazu
     * werden alle RetSegs durchgesehen; diejenigen, die den aktuellen GV
     * betreffen, werden im @c data Property unter dem namen @c ret_i.*
     * gespeichert. @i entspricht dabei dem @c retValCounter.
     */
    protected void saveReturnValues(HBCIMsgStatus status,int sref)
    {
        HBCIRetVal[] retVals=status.segStatus.getRetVals();
        String       segref=Integer.toString(sref);
       
        for (int i=0;i<retVals.length;i++) {
            HBCIRetVal rv=retVals[i];
           
            if (rv.segref!=null && rv.segref.equals(segref)) {
                jobResult.jobStatus.addRetVal(rv);
            }
        }
       
        /* bei Jobs, die mehrere Nachrichten ben�tigt haben, bewirkt das, dass nur
         * der globStatus der *letzten* ausgef�hrten Nachricht gespeichert wird.
         * Das ist aber auch ok, weil nach einem Fehler keine weiteren Nachrichten
         * ausgef�hrt werden, so dass im Fehlerfall der fehlerhafte globStatus zur
         * Verf�gung steht. Im OK-Fall werden h�chstens die OK-Meldungen der vorherigen
         * Nachrichten �berschrieben. */
        jobResult.globStatus=status.globStatus;
    }

    /* diese Methode wird i.d.R. durch abgeleitete GV-Klassen �berschrieben, um die
       R�ckgabedaten in einem passenden Format abzuspeichern. Diese default-Implementation
       tut nichts */
    protected void extractResults(HBCIMsgStatus msgstatus,String header,int idx)
    {
    }

    private void extractPlaintextResults(HBCIMsgStatus status,String header,int idx)
    {
        Properties result=status.getData();
        for (Enumeration e=result.keys();e.hasMoreElements();) {
            String key=(String)(e.nextElement());
            if (key.startsWith(header+".")) {
                jobResult.storeResult(HBCIUtilsInternal.withCounter("content",idx)+
                                      "."+
                                      key.substring(header.length()+1),result.getProperty(key));
            }
        }
    }

    public HBCIJobResult getJobResult()
    {
        return jobResult;
    }
   
    public HBCIPassportInternal getMainPassport()
    {
        return passports.getMainPassport();
    }
   
    private void _checkAccountCRC(String frontendname,
                    String blz,String number)
    {
        // pruefsummenberechnung nur wenn blz/kontonummer angegeben sind
        if (blz==null || number==null) {
          return;
        }
        if (blz.length()==0 || number.length()==0) {
          return;
        }
       
        // daten merken, die im urspruenglich verwendet wurden (um spaeter
        // zu wissen, ob sie korrigiert wurden)
        String orig_blz=blz;
        String orig_number=number;

        while (true) {
          // daten validieren
            boolean crcok=HBCIUtils.checkAccountCRC(blz,number);

            // aktuelle daten merken
            String old_blz=blz;
            String old_number=number;

            if (!crcok) {
              // wenn beim validieren ein fehler auftrat, nach neuen daten fragen
                StringBuffer sb=new StringBuffer(blz).append("|").append(number);
                HBCIUtilsInternal.getCallback().callback(getMainPassport(),
                                                 HBCICallback.HAVE_CRC_ERROR,
                                                 HBCIUtilsInternal.getLocMsg("CALLB_HAVE_CRC_ERROR"),
                                                 HBCICallback.TYPE_TEXT,
                                                 sb);

                int idx=sb.indexOf("|");
                blz=sb.substring(0,idx);
                number=sb.substring(idx+1);
            }
           
            if (blz.equals(old_blz) && number.equals(old_number)) {
              // blz und kontonummer auch nach rueckfrage unveraendert,
              // also tatsaechlich mit diesen daten weiterarbeiten
                break;
            }
        }
           
        if (!blz.equals(orig_blz)) {
            setParam(frontendname+".KIK.blz",blz);
        }
        if (!number.equals(orig_number)) {
            setParam(frontendname+".number",number);
        }
    }
   
    private void _checkIBANCRC(String frontendname,String iban)
    {
      // pruefsummenberechnung nur wenn iban vorhanden ist
      if (iban==null || iban.length()==0) {
        return;
      }

        // daten merken, die im urspruenglich verwendet wurden (um spaeter
        // zu wissen, ob sie korrigiert wurden)
      String orig_iban=iban;
      while (true) {
        boolean crcok=HBCIUtils.checkIBANCRC(iban);

        String old_iban=iban;

        if (!crcok) {
          StringBuffer sb=new StringBuffer(iban);
          HBCIUtilsInternal.getCallback().callback(getMainPassport(),
              HBCICallback.HAVE_IBAN_ERROR,
              HBCIUtilsInternal.getLocMsg("CALLB_HAVE_IBAN_ERROR"),
              HBCICallback.TYPE_TEXT,
              sb);

          iban=sb.toString();
        }

        if (iban.equals(old_iban)) {
          // iban unveraendert
          break;
        }
      }

      if (!iban.equals(orig_iban)) {
        setParam(frontendname+".iban",iban);
      }
    }

    protected void checkAccountCRC(String frontendname)
    {
        String[][] data=constraints.get(frontendname+".blz");
        if (data!=null && data.length!=0) {
          // wenn es tatsaechlich einen frontendparamter der form acc.blz gibt,
          // brauchen wir zunaechst den "basis-namen" ("acc")
            String paramname=data[0][0];
            String lowlevelHeader=paramname.substring(0,paramname.lastIndexOf(".KIK.blz"));

            // basierend auf dem basis-namen blz/number holen
            String blz=llParams.getProperty(lowlevelHeader+".KIK.blz");
            String number=llParams.getProperty(lowlevelHeader+".number");
            // blz/number ueberpruefen
            _checkAccountCRC(frontendname, blz,number);
        }
       
        // analoges fuer die IBAN
        String[][] data2=constraints.get(frontendname+".iban");
        if (data2!=null && data2.length!=0) {
            String paramname=data2[0][0];
            String lowlevelHeader=paramname.substring(0,paramname.lastIndexOf(".iban"));

            String iban=llParams.getProperty(lowlevelHeader+".iban");
            _checkIBANCRC(frontendname, iban);
        }
    }
   
    public void addSignaturePassport(HBCIPassport passport,String role)
    {
        HBCIUtils.log("adding additional passport to job "+getName(),
                HBCIUtils.LOG_DEBUG);
        passports.addPassport((HBCIPassportInternal)passport,role);
    }
   
    public HBCIPassportList getSignaturePassports()
    {
        return passports;
    }
   
    // die default-implementierung holt einfach aus den job-parametern
    // den genannten wert. eine bestimmte GV-klasse kann das �berschreiben,
    // um "besondere Werte" (z.B. sumValues) irgendwie anders zu errechnen
    public String getChallengeParam(String path)
    {
        String result;
        if (path.equals("SegHead.code")) {
            /* this special value is required for HHD1.3, where the segcode
             * can be part of the challenge parameters, but the segcode is
             * not a "lowlevel param", so have to handle this manually */
            result=getHBCICode();  
        } else {
            // normal lowlevel param
            String valuePath=this.getName()+"."+path;
            result=this.getLowlevelParam(valuePath);
        }
        return result;
    }
   
    /**
     * Liefert das Auftraggeber-Konto, wie es ab HKTAN5 erforderlich ist.
     * @return das Auftraggeber-Konto oder NULL, wenn keines angegeben ist.
     */
    public Konto getOrderAccount()
    {
      // Checken, ob wir das Konto unter "My.[number/iban]" haben
      String prefix = this.getName() + ".My.";
      String number = this.getLowlevelParam(prefix + "number");
      String iban   = this.getLowlevelParam(prefix + "iban");
      if ((number == null || number.length() == 0) && (iban == null || iban.length() == 0))
      {
        // OK, vielleicht unter "KTV.[number/iban]"?
        prefix = this.getName() + ".KTV.";
        number = this.getLowlevelParam(prefix + "number");
        iban   = this.getLowlevelParam(prefix + "iban");

        if ((number == null || number.length() == 0) && (iban == null || iban.length() == 0))
          return null; // definitiv kein Konto vorhanden
      }
      Konto k = new Konto();
      k.number    = number;
      k.iban      = iban;
      k.bic       = this.getLowlevelParam(prefix + "bic");
      k.subnumber = this.getLowlevelParam(prefix + "subnumber");
      k.blz       = this.getLowlevelParam(prefix + "KIK.blz");
      k.country   = this.getLowlevelParam(prefix + "KIK.country");
      return k;
    }
   
    public HBCIHandler getParentHandler()
    {
        return this.parentHandler;
    }
   
    public void addToQueue(String customerId)
    {
      getParentHandler().addJobToDialog(customerId,this);
    }
   
    public void addToQueue()
    {
      addToQueue(null);
    }
   
    protected boolean twoDigitValueInList(String value, String list)
    {
        boolean found=false;
        int     len=list.length();
       
        if ((len&1)!=0) {
            throw new InvalidArgumentException("list must have 2*n digits");
        }
        if (value.length()!=2) {
            throw new InvalidArgumentException("value must have 2 digits");
        }
       
        for (int i=0; i<len; i+=2) {
            String x=list.substring(i,i+2);
            if (value.equals(x)) {
                found=true;
                break;
            }
        }
       
        return found;
    }
   
    private static final Pattern INDEX_PATTERN = Pattern.compile("(\\w+\\.\\w+\\.\\w+)(\\.\\w+)?");
    private String insertIndex(String key, Integer index)
    {
        if (index != null) {
            Matcher m = INDEX_PATTERN.matcher(key);
            if (m.matches()) {
                return m.group(1) + '[' + index + ']' + (m.group(2) != null ? m.group(2) : "");
            }
        }
        return key;
    }
}
TOP

Related Classes of org.kapott.hbci.GV.HBCIJobImpl

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.