TunnelSoapUtils.java

/*
 * GovWay - A customizable API Gateway 
 * https://govway.org
 * 
 * Copyright (c) 2005-2024 Link.it srl (https://link.it). 
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License version 3, as published by
 * the Free Software Foundation.
 *
 * This program 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, see <http://www.gnu.org/licenses/>.
 *
 */



package org.openspcoop2.message.soap;


import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;

import javax.activation.DataHandler;
import javax.mail.util.ByteArrayDataSource;
import javax.xml.namespace.QName;
import javax.xml.soap.AttachmentPart;
import javax.xml.soap.SOAPBody;
import javax.xml.soap.SOAPElement;
import javax.xml.soap.SOAPEnvelope;
import javax.xml.soap.SOAPFault;
import javax.xml.transform.Source;
import javax.xml.transform.dom.DOMSource;

import org.apache.commons.io.output.CountingOutputStream;
import org.openspcoop2.message.OpenSPCoop2DataContentHandler;
import org.openspcoop2.message.OpenSPCoop2DataContentHandlerInputStream;
import org.openspcoop2.message.OpenSPCoop2Message;
import org.openspcoop2.message.OpenSPCoop2MessageFactory;
import org.openspcoop2.message.OpenSPCoop2SoapMessage;
import org.openspcoop2.message.constants.Costanti;
import org.openspcoop2.message.constants.MessageRole;
import org.openspcoop2.message.constants.MessageType;
import org.openspcoop2.message.constants.ServiceBinding;
import org.openspcoop2.message.exception.MessageException;
import org.openspcoop2.message.exception.MessageNotSupportedException;
import org.openspcoop2.message.xml.MessageXMLUtils;
import org.openspcoop2.utils.CopyStream;
import org.openspcoop2.utils.UtilsException;
import org.openspcoop2.utils.transport.http.HttpConstants;
import org.w3c.dom.Element;
import org.w3c.dom.Node;


/**
 * Libreria contenente metodi utili per la gestione della busta Soap.
 *
 *
 * @author Poli Andrea (apoli@link.it)
 * @author $Author$
 * @version $Rev$, $Date$
 */

public class TunnelSoapUtils {
	
	
	
	
	/* ********  ALLEGA BODY  ******** */ 
	
	public static void allegaBody(OpenSPCoop2Message message, String ns) throws MessageException{
		
		if(ServiceBinding.SOAP.equals(message.getServiceBinding())==false){
			throw new MessageException("Funzionalita 'ScartaBody' valida solamente per Service Binding SOAP");
		}
		OpenSPCoop2SoapMessage soapMessage = message.castAsSoap();				
		
		try{
		
			// E' permesso SOLO per messaggi senza attachment
			if (soapMessage.countAttachments() > 0) {
				throw new MessageException("La funzionalita' non e' permessa per messaggi SOAP With Attachments");
			}
			
			SOAPBody body = soapMessage.getSOAPPart().getEnvelope().getBody();
			if(body==null){
				throw new Exception("Body non presente");
			}
			if(body.hasFault()){
				throw new Exception("Body contenente un SOAPFault");
			}
			List<Node> listNode = SoapUtils.getNotEmptyChildNodes(message.getFactory(), body, false);
			boolean bodyWithMultiRootElement = false;
			if(listNode!=null && listNode.size()>1){
				//System.out.println("MULTI ELEMENT: "+listNode.size());
				bodyWithMultiRootElement = true;
			}
			byte[] bodySbustato = TunnelSoapUtils.sbustamentoSOAPEnvelope(message.getFactory(), soapMessage.getSOAPPart().getEnvelope());
			AttachmentPart ap = null;
			if(bodyWithMultiRootElement){
				//System.out.println("OCTECT");
				org.openspcoop2.utils.dch.InputStreamDataSource isSource = new org.openspcoop2.utils.dch.InputStreamDataSource("MultiRootElement", 
						HttpConstants.CONTENT_TYPE_APPLICATION_OCTET_STREAM, bodySbustato);
				ap = soapMessage.createAttachmentPart(new DataHandler(isSource));
			}else{
				//System.out.println("XML");
				Element e = null;
				try{
					e = MessageXMLUtils.getInstance(message.getFactory()).newElement(bodySbustato);
					Source streamSource = new DOMSource(e);
					ap = soapMessage.createAttachmentPart();
					ap.setContent(streamSource, HttpConstants.CONTENT_TYPE_TEXT_XML);
				}catch(Exception eParse){
					org.openspcoop2.utils.dch.InputStreamDataSource isSource = new org.openspcoop2.utils.dch.InputStreamDataSource("BodyNotParsable", 
							HttpConstants.CONTENT_TYPE_APPLICATION_OCTET_STREAM, bodySbustato);
					ap = soapMessage.createAttachmentPart(new DataHandler(isSource));
				}
			}
			ap.setContentId(soapMessage.createContentID(ns));
			soapMessage.addAttachmentPart(ap);
			
			// Aggiungo contentID all'attachmet contenente la SOAPEnvelope
			// Necessario per essere compatibile con alcune implementazioni, es axis14, 
			// altrimenti essendo il ContentType senza Start element, Axis14 utilizza come xml per costruire la SOAPEnvelope 
			// il primo attachment nel messaggio MIME che contiene il ContentID.
			soapMessage.getSOAPPart().addMimeHeader(HttpConstants.CONTENT_ID, soapMessage.createContentID(ns));
			
			// Rimuovo contenuti del body
			soapMessage.getSOAPBody().removeContents();
			
		}catch(Exception e){
			throw new MessageException(e.getMessage(),e);
		}
	}
	
	
	
	
	
	
	
	/* ********  S B U S T A M E N T O    M E S S A G G I  ******** */ 

	public static boolean isTunnelOpenSPCoopSoap(OpenSPCoop2SoapMessage message) throws MessageException, MessageNotSupportedException{
		return isTunnelOpenSPCoopSoap(message.getFactory(), message.getSOAPBody());
	}
	public static boolean isTunnelOpenSPCoopSoap(OpenSPCoop2MessageFactory messageFactory, SOAPBody body){
		List<Node> bodyChildren = SoapUtils.getNotEmptyChildNodes(messageFactory, body);
		if(body!=null && 
				bodyChildren.size() > 0 && 
				bodyChildren.get(0)!=null &&
				"SOAPTunnel".equals(bodyChildren.get(0).getLocalName()) &&
				"http://www.govway.org/out/xml2soap".equals(bodyChildren.get(0).getNamespaceURI()) &&
				org.openspcoop2.utils.Costanti.OPENSPCOOP2.equals(bodyChildren.get(0).getPrefix())){
			return true;
		}else{
			return false;
		}
	}
	
	public static String getContentTypeTunnelOpenSPCoopSoap(SOAPBody body) throws MessageException {
		if(body!=null && body.hasChildNodes()){
			return ((SOAPElement)body.getChildElements().next()).getValue();
		}else{
			throw new MessageException("Body non presente");
		}
	}
	

	public static void sbustamentoMessaggio(OpenSPCoop2Message msgParam,OutputStream streamParam) throws MessageException, MessageNotSupportedException{

		if(!MessageType.SOAP_11.equals(msgParam.getMessageType()) && !MessageType.SOAP_12.equals(msgParam.getMessageType())){
			throw MessageNotSupportedException.newMessageNotSupportedException(msgParam.getMessageType());
		}
		OpenSPCoop2SoapMessage msg = msgParam.castAsSoap();
		
		CountingOutputStream cout = null;
		try{
						
			// Nota: non viene usato DocumentToStream, poiche' altrimenti viene prodotto l'<?xml ... /> 
			cout = new CountingOutputStream(streamParam);
			
			//	Sbustamento Senza Attachments
			if(msg.countAttachments() == 0){
				SOAPBody bd = msg.getSOAPBody();
				if(bd.hasFault()){
					SOAPFault fault = bd.getFault();
					cout.write(msg.getAsByte(fault, true));
				}else{
					java.util.Iterator<?> it = bd.getChildElements();
					while(it.hasNext()){
						Object bodyObject = it.next();
						if(!(bodyObject instanceof SOAPElement)) continue;
						SOAPElement bodyElement = (SOAPElement) bodyObject;
						cout.write(msg.getAsByte(bodyElement, true));
					}
				}
			}
			// Sbustamento Con Attachmnets
			else{
				SOAPBody body = msg.getSOAPBody();
				if(TunnelSoapUtils.isTunnelOpenSPCoopSoap(msgParam.getFactory(), body)){
					// Sbustamento OpenSPCoop
					AttachmentPart ap  = (AttachmentPart) msg.getAttachments().next();
					Object o = ap.getContent();
					//DataHandler dh = ap.getDataHandler();
					//InputStream inputDH = (InputStream) dh.getContent();
					InputStream inputDH = null;
					if(o instanceof OpenSPCoop2DataContentHandlerInputStream){
						inputDH = (OpenSPCoop2DataContentHandlerInputStream) o;
					}
					else if(o instanceof InputStream){
						inputDH = (InputStream) OpenSPCoop2DataContentHandler.getContent((InputStream)o);
					}
					else if(o instanceof byte[]){
						inputDH = (InputStream) OpenSPCoop2DataContentHandler.getContent(new ByteArrayInputStream((byte[]) o));
					} else {
						throw new Exception("Tipo non gestito: "+o.getClass().getName());
					}
					java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream();
			    	byte [] readB = new byte[8192];
					int readByte = 0;
					while((readByte = inputDH.read(readB))!= -1)
						bout.write(readB,0,readByte);
					inputDH.close();
					bout.close();
					cout.write(bout.toByteArray());
				}else{
				
					ByteArrayOutputStream sbustamentoAttachments = new ByteArrayOutputStream();
					msg.writeTo(sbustamentoAttachments, true);
					String msgString = sbustamentoAttachments.toString();
					byte [] msgByte =  sbustamentoAttachments.toByteArray();
					String soapEnvelopeStart = "<" + msg.getSOAPPart().getEnvelope().getPrefix() + ":" + msg.getSOAPPart().getEnvelope().getLocalName();
					String xmlTagDecode = "<?xml";
					String soapEnvelopeStop =  "</" + msg.getSOAPPart().getEnvelope().getPrefix() + ":" + msg.getSOAPPart().getEnvelope().getLocalName()+">";
					//System.out.println("SoapStart: "+soapEnvelopeStart);
					//System.out.println("SoapStartIndexOf: "+msgString.indexOf(soapEnvelopeStart));
					//System.out.println("SoapStop: "+soapEnvelopeStop);
					//System.out.println("SoapStopIndexOf: "+msgString.indexOf(soapEnvelopeStop));
					// Prima parte del Multipart
					if(msgString.indexOf(xmlTagDecode)!=-1){
						cout.write(msgByte,0,msgString.indexOf(xmlTagDecode));
					}else{
						cout.write(msgByte,0,msgString.indexOf(soapEnvelopeStart));
					}
					// Body
					SOAPBody bd = msg.getSOAPBody();
					if(bd.hasFault()){
						SOAPFault fault = bd.getFault();
						cout.write(msg.getAsByte(fault, true));
					}else{
						cout.write(msg.getAsByte(msg.getFirstChildElement(bd), true));
					}
					// Resto degli attachments
					int indexOf = msgString.indexOf(soapEnvelopeStop)+soapEnvelopeStop.length();
					cout.write(msgByte,indexOf,msgByte.length - indexOf);
				}
			}
			
			// Aggiorno le lunghezze del messaggio (normalmente le aggiorna la writeTo)
			msg.updateIncomingMessageContentLength();
			// Come dimensione di uscita utilizzo i bytes che produco
			cout.flush();
			msg.updateOutgoingMessageContentLength(cout.getByteCount());
			
		}
		catch (MessageException e){
			throw e;
		}
		catch (MessageNotSupportedException e){
			throw e;
		}
		catch (Exception e){
			throw new MessageException("Sbustamento msg_inputStream non riuscito: "+e.getMessage(),e);
		}finally{
			try{
				if(cout!=null)
					cout.close();
			}catch(Exception e){
				// close
			}
		}
		
		
	}
	
	/**
	 * Ritorna i bytes del contenuto del messaggio Soap passato come parametro
	 *
	 * @param msg Messaggio Soap da sbustare
	 * @return byte del contenuto (sbustati dalla SoapEnvelope).
	 * 
	 */
	public static byte[] sbustamentoMessaggio(OpenSPCoop2Message msg) throws MessageException, MessageNotSupportedException{
		ByteArrayOutputStream bodySbustato = new ByteArrayOutputStream();
		try{
			TunnelSoapUtils.sbustamentoMessaggio(msg,bodySbustato);
			return bodySbustato.toByteArray();	
		}
		catch (MessageException e){
			throw e;
		}
		catch (MessageNotSupportedException e){
			throw e;
		}
		catch(Exception e){
			try{
				if(bodySbustato!=null)
					bodySbustato.close();
			}catch(Exception eis){
				// close
			}	
			throw new MessageException("Sbustamento msg non riuscito: "+e.getMessage(),e);
		}
	}

	/**
	 * Ritorna i bytes del contenuto del messaggio Soap passato come parametro
	 *
	 * @param env SoapEnvelope da sbustare
	 * 
	 */
	public static byte[] sbustamentoSOAPEnvelope(OpenSPCoop2MessageFactory messageFactory, SOAPEnvelope env) throws MessageException, MessageNotSupportedException{
		return sbustamentoSOAPEnvelope(messageFactory, env, true);
	}
	public static byte[] sbustamentoSOAPEnvelope(OpenSPCoop2MessageFactory messageFactory, SOAPEnvelope env, boolean consume) throws MessageException, MessageNotSupportedException{
		ByteArrayOutputStream bout = null;
		
		try{
			SOAPBody bd = env.getBody();
			byte[] body = null;
			if(bd.hasFault()){
				SOAPFault fault = bd.getFault();
				body = OpenSPCoop2MessageFactory.getAsByte(messageFactory, fault, consume);
			}else{
				bout = new ByteArrayOutputStream();
				java.util.Iterator<?> it = bd.getChildElements();
				while(it.hasNext()){
					Object bodyElementObj = it.next();
					if(!(bodyElementObj instanceof SOAPElement)){
						continue;
					}
					SOAPElement bodyElement = (SOAPElement) bodyElementObj;
					bout.write(OpenSPCoop2MessageFactory.getAsByte(messageFactory, bodyElement, consume));
				}
				bout.flush();
				bout.close();
				body = bout.toByteArray();
				bout = null;
			}

			return body;

		}
		catch (Exception e){
			throw new MessageException("Sbustamento SoapEnvelope non riuscito: "+e.getMessage(),e);
		}
		finally{
			try{
				if(bout!=null)
					bout.close();
			}catch(Exception eis){
				// close
			}
		}
	}
	
	
	
	
	
	
	

	
	
	
	/* ********  I M B U S T A M E N T O    M E S S A G G I  ******** */ 

	/**
	 * Ritorna un messaggio che contiene i bytes in un attachment
	 *
	 * @param inputBody contenuto.
	 * @return msg Messaggio Soap imbustato
	 * 
	 */
	public static OpenSPCoop2Message imbustamentoMessaggioConAttachment(OpenSPCoop2MessageFactory messageFactory, MessageType messageType, MessageRole messageRole, 
			InputStream inputBody,String tipoAttachment,boolean buildAsDataHandler,String contentTypeMessaggioOriginale, String ns) throws MessageException, MessageNotSupportedException{
		
		if(!MessageType.SOAP_11.equals(messageType) && !MessageType.SOAP_12.equals(messageType)){
			throw MessageNotSupportedException.newMessageNotSupportedException(messageType);
		}
		try{
			// Metto inputBody in un byte[] proprio perche' il ByteArrayInputStream non deve essere chiuso.
			java.io.ByteArrayOutputStream byteBuffer = new java.io.ByteArrayOutputStream();
//			byte [] readB = new byte[Utilities.DIMENSIONE_BUFFER];
//			int readByte = 0;
//			while((readByte = inputBody.read(readB))!= -1){
//				byteBuffer.write(readB,0,readByte);
//			}
			CopyStream.copy(inputBody, byteBuffer);
			inputBody.close();
			if(byteBuffer.size()==0){
				throw new MessageException("Contenuto da imbustare non presente");
			}
			
			return TunnelSoapUtils.imbustamentoMessaggioConAttachment(messageFactory, messageType, messageRole, byteBuffer.toByteArray(), tipoAttachment, buildAsDataHandler, contentTypeMessaggioOriginale, ns);
			
		}
		catch (MessageException e){
			throw e;
		}
		catch (MessageNotSupportedException e){
			throw e;
		}
		catch (Exception e){
			throw new MessageException("Imbustamento msgConAttachment_inputStream non riuscito: "+e.getMessage(),e);
		}
	}
	
	public static OpenSPCoop2Message imbustamentoMessaggioConAttachment(OpenSPCoop2MessageFactory messageFactory, MessageType messageType, MessageRole messageRole, 
			byte [] inputBody,String tipoAttachment,boolean buildAsDataHandler,String contentTypeMessaggioOriginale, String ns) throws MessageException, MessageNotSupportedException{
		
		if(!MessageType.SOAP_11.equals(messageType) && !MessageType.SOAP_12.equals(messageType)){
			throw MessageNotSupportedException.newMessageNotSupportedException(messageType);
		}
		OpenSPCoop2Message msg = null;
		try{
			msg = messageFactory.createEmptyMessage(messageType,messageRole);
			return imbustamentoMessaggioConAttachment(msg, inputBody, tipoAttachment, buildAsDataHandler, contentTypeMessaggioOriginale, ns);
		}
		catch (MessageException e){
			throw e;
		}
		catch (MessageNotSupportedException e){
			throw e;
		}
		catch (Exception e){
			throw new MessageException("Imbustamento msgConAttachment_inputStream non riuscito: "+e.getMessage(),e);
		}
	}
	public static OpenSPCoop2Message imbustamentoMessaggioConAttachment(OpenSPCoop2Message msgParam, 
			byte [] inputBody,String tipoAttachment,boolean buildAsDataHandler,String contentTypeMessaggioOriginale, String ns) throws MessageException, MessageNotSupportedException{
		
		if(!MessageType.SOAP_11.equals(msgParam.getMessageType()) && !MessageType.SOAP_12.equals(msgParam.getMessageType())){
			throw MessageNotSupportedException.newMessageNotSupportedException(msgParam.getMessageType());
		}
		OpenSPCoop2SoapMessage msg = msgParam.castAsSoap();
		try{
			
			if(inputBody==null || inputBody.length<=0){
				throw new UtilsException("Contenuto da imbustare non presente");
			}
						
			SOAPBody soapBody = msg.getSOAPBody();
			QName name = null;
			if(HttpConstants.CONTENT_TYPE_OPENSPCOOP2_TUNNEL_SOAP.equals(tipoAttachment)){
				name = new QName(Costanti.SOAP_TUNNEL_NAMESPACE,
						Costanti.SOAP_TUNNEL_ATTACHMENT_ELEMENT_OPENSPCOOP2_TYPE,org.openspcoop2.utils.Costanti.OPENSPCOOP2);	    
			}else{
				name = new QName(Costanti.SOAP_TUNNEL_NAMESPACE,
						Costanti.SOAP_TUNNEL_ATTACHMENT_ELEMENT,org.openspcoop2.utils.Costanti.OPENSPCOOP2);	 
			}
			SOAPElement bodyElement = soapBody.addChildElement(name);
			
			if(HttpConstants.CONTENT_TYPE_OPENSPCOOP2_TUNNEL_SOAP.equals(tipoAttachment)){
				if(contentTypeMessaggioOriginale==null){
					throw new Exception("ContentType messaggio per cui applicare il tunnel non definito?");
				}else{
					bodyElement.setValue(contentTypeMessaggioOriginale);
				}
			}
			AttachmentPart ap = msg.createAttachmentPart();			
			
			if(buildAsDataHandler){
				ap.setDataHandler(new DataHandler(new ByteArrayDataSource(inputBody,tipoAttachment)));
				TunnelSoapUtils.saveAttachmentOpenSPCoop(ap);		
			}else{
				ap.setRawContentBytes(inputBody,0,inputBody.length,tipoAttachment);
				//ap.setContent(new ByteArrayInputStream(inputBody),tipoAttachment);
			}
			ap.setContentId(msg.createContentID(ns));
			msg.addAttachmentPart(ap);
			//System.out.println("debug - start");
			//msg.writeTo(System.out);
			//System.out.println(msg.getAsString(msg.getSOAPPart().getEnvelope()));
			//System.out.println("debug - stop");
			return msg;

		}
		catch (MessageException e){
			throw e;
		}
		catch (MessageNotSupportedException e){
			throw e;
		}
		catch (Exception e){
			throw new MessageException("Imbustamento msgConAttachment_inputStream non riuscito: "+e.getMessage(),e);
		}
	}
	private static void saveAttachmentOpenSPCoop(AttachmentPart ap) throws MessageException{
		try{
			// FIX: se non c'e' il dump abilitato serve il codice seguente per forzare il salvataggio, in caso si passi da DataHandler.
			javax.activation.DataHandler dh= ap.getDataHandler();  
	    	java.io.InputStream inputDH = dh.getInputStream();
			java.io.ByteArrayOutputStream bout = new java.io.ByteArrayOutputStream();
	    	byte [] readB = new byte[8192];
			int readByte = 0;
			while((readByte = inputDH.read(readB))!= -1)
				bout.write(readB,0,readByte);
			inputDH.close();
			bout.flush();
			bout.close();
			ap.setDataHandler(new DataHandler(bout.toByteArray(),ap.getContentType()));
		}catch(Exception e){
			throw new MessageException(e.getMessage(),e);
		}
	}
	
	/**
	 * Ritorna un messaggio  che contiene i byte in un attachment
	 *
	 * @param body byte del contenuto.
	 * @return msg Messaggio Soap imbustato
	 * 
	 */
	public static OpenSPCoop2Message imbustamentoMessaggioConAttachment(OpenSPCoop2MessageFactory messageFactory, MessageType messageType, MessageRole messageRole, byte [] body, String contentTypeMessaggioOriginale, String ns) throws MessageException, MessageNotSupportedException{
		
		if(!MessageType.SOAP_11.equals(messageType) && !MessageType.SOAP_12.equals(messageType)){
			throw MessageNotSupportedException.newMessageNotSupportedException(messageType);
		}
		
		OpenSPCoop2Message risposta = null;
		try{	    
			risposta = TunnelSoapUtils.imbustamentoMessaggioConAttachment(messageFactory, messageType, messageRole, body,HttpConstants.CONTENT_TYPE_PLAIN,false, contentTypeMessaggioOriginale, ns);
			return risposta;
		}
		catch (MessageException e){
			throw e;
		}
		catch (MessageNotSupportedException e){
			throw e;
		}
		catch (Exception e){
			throw new MessageException("Imbustamento msgConAttachment non riuscito: "+e.getMessage(),e);
		}
	}

	
	
}