1
2
3
4
5
6
7
8
9
10
11
12
13 package com.eviware.soapui.impl.wsdl.submit.transports.http;
14
15 import java.io.File;
16 import java.io.FileInputStream;
17 import java.io.IOException;
18 import java.io.InputStream;
19 import java.net.URI;
20 import java.util.ArrayList;
21 import java.util.HashMap;
22 import java.util.Iterator;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.Properties;
26
27 import javax.activation.DataHandler;
28 import javax.mail.MessagingException;
29 import javax.mail.Session;
30 import javax.mail.internet.MimeBodyPart;
31 import javax.mail.internet.MimeMultipart;
32 import javax.mail.internet.PreencodedMimeBodyPart;
33 import javax.xml.namespace.QName;
34
35 import org.apache.commons.codec.binary.Base64;
36 import org.apache.commons.codec.binary.Hex;
37 import org.apache.log4j.Logger;
38 import org.apache.xmlbeans.SchemaType;
39 import org.apache.xmlbeans.XmlBase64Binary;
40 import org.apache.xmlbeans.XmlCursor;
41 import org.apache.xmlbeans.XmlHexBinary;
42 import org.apache.xmlbeans.XmlObject;
43
44 import com.eviware.soapui.config.PartsConfig;
45 import com.eviware.soapui.config.PartsConfig.Part;
46 import com.eviware.soapui.impl.wsdl.AttachmentContainer;
47 import com.eviware.soapui.impl.wsdl.WsdlAttachmentPart;
48 import com.eviware.soapui.impl.wsdl.WsdlInterface;
49 import com.eviware.soapui.impl.wsdl.WsdlOperation;
50 import com.eviware.soapui.impl.wsdl.support.MessageXmlPart;
51 import com.eviware.soapui.impl.wsdl.support.soap.SoapVersion;
52 import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlContext;
53 import com.eviware.soapui.impl.wsdl.support.wsdl.WsdlValidator;
54 import com.eviware.soapui.impl.wsdl.support.xsd.SchemaUtils;
55 import com.eviware.soapui.model.iface.Attachment;
56 import com.eviware.soapui.support.Tools;
57 import com.eviware.soapui.support.types.StringToStringMap;
58
59 public class AttachmentUtils
60 {
61 private final static Logger log = Logger.getLogger( AttachmentUtils.class );
62 private static final QName XMLMIME_CONTENTTYPE_200505 = new QName( "http://www.w3.org/2005/05/xmlmime", "contentType");
63 private static final QName XMLMIME_CONTENTTYPE_200411 = new QName( "http://www.w3.org/2004/11/xmlmime", "contentType");
64 private static final QName SWAREF_QNAME = new QName("http://ws-i.org/profiles/basic/1.1/xsd","swaRef");
65 public static final QName XOP_HREF_QNAME = new QName( "href" );
66 private static final QName XOP_INCLUDE_QNAME = new QName("http://www.w3.org/2004/08/xop/include", "Include" );
67 public static final String ROOTPART_SOAPUI_ORG = "<rootpart@soapui.org>";
68
69 public static boolean prepareMessagePart(AttachmentContainer container, MimeMultipart mp, MessageXmlPart messagePart, StringToStringMap contentIds) throws Exception, MessagingException
70 {
71 boolean isXop = false;
72
73 XmlCursor cursor = messagePart.newCursor();
74
75 try
76 {
77 while( !cursor.isEnddoc() )
78 {
79 if( cursor.isContainer() )
80 {
81
82
83 if( messagePart.isAttachmentPart() )
84 {
85 String href = cursor.getAttributeText( XOP_HREF_QNAME);
86 if( href != null && href.length() > 0 )
87 {
88 contentIds.put( messagePart.getPart().getName(), href );
89 }
90
91 break;
92 }
93
94 SchemaType schemaType = cursor.getObject().schemaType();
95
96 if( AttachmentUtils.isSwaRefType( schemaType ))
97 {
98 String textContent = cursor.getTextValue();
99 if( textContent.startsWith( "cid:" ))
100 {
101 textContent = textContent.substring( 4 );
102
103 try
104 {
105
106 new URI( textContent );
107 contentIds.put( textContent, textContent );
108 }
109 catch( RuntimeException e )
110 {
111
112 String contentId = textContent + "@soapui.org";
113 cursor.setTextValue( "cid:" + contentId );
114 contentIds.put( textContent, contentId );
115 }
116 }
117 }
118 else if( AttachmentUtils.isXopInclude( schemaType ))
119 {
120 String contentId = cursor.getAttributeText( new QName("href"));
121 if( contentId != null && contentId.length() > 0 )
122 {
123 contentIds.put( contentId, contentId );
124 isXop = true;
125
126 Attachment[] attachments = container.getAttachmentsForPart( contentId );
127 if( attachments.length == 1 )
128 {
129 XmlCursor cur = cursor.newCursor();
130 if( cur.toParent())
131 {
132 String contentType = getXmlMimeContentType( cur );
133 if( contentType != null && contentType.length() > 0 )
134 attachments[0].setContentType( contentType );
135 }
136
137 cur.dispose();
138 }
139 }
140 }
141 else if( SchemaUtils.isBinaryType(schemaType) )
142 {
143 String xmimeContentType = getXmlMimeContentType( cursor );
144
145
146 String textContent = cursor.getTextValue();
147 Attachment attachment = null;
148 boolean isXopAttachment = false;
149
150
151 if( textContent.startsWith( "file:" ))
152 {
153 String filename = textContent.substring( 5 );
154 if( xmimeContentType == null )
155 {
156 inlineData(cursor, schemaType, new FileInputStream( filename));
157 }
158 else if( container.isMtomEnabled() )
159 {
160 MimeBodyPart part = new PreencodedMimeBodyPart( "binary" );
161
162 part.setDataHandler( new DataHandler( new XOPPartDataSource( new File(filename ), xmimeContentType, schemaType ) ));
163 part.setContentID( "<" + filename + ">" );
164 mp.addBodyPart( part );
165
166 isXopAttachment = true;
167 }
168 }
169
170 else if( textContent.startsWith( "cid:" ))
171 {
172 textContent = textContent.substring( 4 );
173
174 Attachment[] attachments = container.getAttachmentsForPart( textContent );
175 if( attachments.length == 1 )
176 {
177 attachment = attachments[0];
178 }
179 else if( attachments.length > 1 )
180 {
181 attachment = buildMulitpartAttachment( attachments );
182 }
183
184 isXopAttachment = xmimeContentType != null;
185 contentIds.put( textContent, textContent );
186 }
187
188 else if( container.isMtomEnabled() && xmimeContentType != null )
189 {
190 MimeBodyPart part = new PreencodedMimeBodyPart( "binary" );
191
192 part.setDataHandler( new DataHandler( new XOPPartDataSource( textContent, xmimeContentType, schemaType ) ));
193
194 textContent = "http://www.soapui.org/" + System.nanoTime();
195
196 part.setContentID( "<" + textContent + ">" );
197 mp.addBodyPart( part );
198
199 isXopAttachment = true;
200 }
201
202
203 if( isXopAttachment && container.isMtomEnabled() )
204 {
205 buildXopInclude(cursor, textContent);
206 isXop = true;
207 }
208
209 else if( attachment != null )
210 {
211 inlineAttachment(cursor, schemaType, attachment);
212 }
213 }
214 }
215
216 cursor.toNextToken();
217 }
218 }
219 finally
220 {
221 cursor.dispose();
222 }
223
224 return isXop;
225 }
226
227 private static void inlineAttachment(XmlCursor cursor, SchemaType schemaType, Attachment attachment) throws IOException
228 {
229 inlineData( cursor, schemaType, attachment.getInputStream() );
230 }
231
232 private static void inlineData(XmlCursor cursor, SchemaType schemaType, InputStream in ) throws IOException
233 {
234 String content = null;
235 byte [] data = Tools.readAll( in, -1 ).toByteArray();
236
237 if (SchemaUtils.isInstanceOf( schemaType, XmlHexBinary.type ))
238 {
239 content = new String( Hex.encodeHex( data ));
240 }
241 else if (SchemaUtils.isInstanceOf( schemaType, XmlBase64Binary.type ))
242 {
243 content = new String( Base64.encodeBase64( data ));
244 }
245
246 XmlCursor c = cursor.newCursor();
247 c.setTextValue( content );
248 c.dispose();
249 }
250
251 private static void buildXopInclude(XmlCursor cursor, String contentId)
252 {
253
254 XmlCursor c = cursor.newCursor();
255 c.removeXmlContents();
256 c.toFirstContentToken();
257 c.beginElement( XOP_INCLUDE_QNAME );
258 c.insertAttributeWithValue( XOP_HREF_QNAME, "cid:" + contentId );
259 c.toNextSibling();
260 c.removeXml();
261 c.dispose();
262 }
263
264 private static Attachment buildMulitpartAttachment(Attachment[] attachments)
265 {
266 System.out.println( "buildMulitpartAttachment(Attachment[] attachments) not implemented!" );
267 return null;
268 }
269
270 public static String buildRootPartContentType( String action, SoapVersion soapVersion )
271 {
272 return "application/xop+xml; charset=UTF-8; type=\"" + soapVersion.getContentType() + "; action=//\"" +
273 action + "//\"\"";
274 }
275
276 public static String buildMTOMContentType(String header, String action, SoapVersion soapVersion )
277 {
278 int ix = header.indexOf( "boundary" );
279 return "multipart/related; type=\"application/xop+xml\"; start=\"" + ROOTPART_SOAPUI_ORG + "\"; " +
280 "startinfo=\"" + soapVersion.getContentType() + "; action=//\"" + action + "//\"\"; " + header.substring( ix );
281 }
282
283 public static boolean isSwaRefType(SchemaType schemaType)
284 {
285 return schemaType.getName() != null && schemaType.getName().equals( SWAREF_QNAME);
286 }
287
288 public static String getXmlMimeContentType(XmlCursor cursor)
289 {
290 String attributeText = cursor.getAttributeText( XMLMIME_CONTENTTYPE_200411);
291 if( attributeText == null )
292 attributeText = cursor.getAttributeText( XMLMIME_CONTENTTYPE_200505);
293 return attributeText;
294 }
295
296 public static boolean isXopInclude( SchemaType schemaType )
297 {
298 return XOP_INCLUDE_QNAME.equals( schemaType.getName() );
299 }
300
301 public static List<WsdlAttachmentPart> extractAttachmentParts( WsdlOperation operation, String requestContent, boolean addAnonymous )
302 {
303 List<WsdlAttachmentPart> result = new ArrayList<WsdlAttachmentPart>();
304
305 PartsConfig requestParts = operation.getConfig().getRequestParts();
306 if( requestParts != null )
307 {
308 for( Part part : requestParts.getPartList() )
309 {
310 WsdlAttachmentPart attachmentPart = new WsdlAttachmentPart( part.getName(), part.getContentTypeList() );
311 attachmentPart.setType( Attachment.AttachmentType.MIME );
312 result.add( attachmentPart );
313 }
314 }
315
316 if( requestContent.length() > 0 )
317 {
318 WsdlContext wsdlContext = ((WsdlInterface)operation.getInterface()).getWsdlContext();
319 WsdlValidator validator = new WsdlValidator( wsdlContext );
320 try
321 {
322 XmlObject[] requestDocuments = validator.getInputParts( requestContent, operation.getName() );
323
324 for( XmlObject partDoc : requestDocuments )
325 {
326 XmlCursor cursor = partDoc.newCursor();
327 while( !cursor.isEnddoc() )
328 {
329 if( cursor.isContainer() )
330 {
331 SchemaType schemaType = cursor.getObject().schemaType();
332 if( schemaType != null )
333 {
334 String attributeText = AttachmentUtils.getXmlMimeContentType(cursor);
335
336
337 if( SchemaUtils.isBinaryType( schemaType ))
338 {
339 String contentId = cursor.getTextValue();
340 if( contentId.startsWith( "cid:" ))
341 {
342 WsdlAttachmentPart attachmentPart = new WsdlAttachmentPart( contentId.substring( 4 ), attributeText);
343 attachmentPart.setType( attributeText == null ? Attachment.AttachmentType.CONTENT : Attachment.AttachmentType.XOP );
344 result.add( attachmentPart );
345 }
346 }
347 else if( AttachmentUtils.isXopInclude( schemaType ))
348 {
349 String contentId = cursor.getAttributeText( new QName("href"));
350 if( contentId != null && contentId.length() > 0 )
351 {
352 WsdlAttachmentPart attachmentPart = new WsdlAttachmentPart( contentId, attributeText);
353 attachmentPart.setType( Attachment.AttachmentType.XOP );
354 result.add( attachmentPart );
355 }
356 }
357
358 else if( AttachmentUtils.isSwaRefType(schemaType) )
359 {
360 String contentId = cursor.getTextValue();
361 if( contentId.startsWith( "cid:" ))
362 {
363 WsdlAttachmentPart attachmentPart = new WsdlAttachmentPart( contentId.substring( 4 ), attributeText);
364 attachmentPart.setType( Attachment.AttachmentType.SWAREF );
365 result.add( attachmentPart );
366 }
367 }
368 }
369 }
370
371 cursor.toNextToken();
372 }
373 }
374 }
375 catch( Exception e )
376 {
377 log.warn( e.toString() );
378 }
379 }
380
381 if( addAnonymous )
382 result.add( new WsdlAttachmentPart() );
383
384 return result;
385 }
386
387 /***
388 * Adds defined attachments as mimeparts
389 */
390
391 public static void addMimeParts(AttachmentContainer container, MimeMultipart mp, StringToStringMap contentIds) throws MessagingException
392 {
393
394 if( !container.isMultipartEnabled())
395 {
396 for( int c = 0; c < container.getAttachmentCount(); c++ )
397 {
398 Attachment att = container.getAttachmentAt( c );
399 addSingleAttachment(mp, contentIds, att);
400 }
401 }
402 else
403 {
404
405 Map<String,List<Attachment> > attachmentsMap = new HashMap<String, List<Attachment> >();
406 for( int c = 0; c < container.getAttachmentCount(); c++ )
407 {
408 Attachment att = container.getAttachmentAt( c );
409 String partName = att.getPart();
410
411 if( !attachmentsMap.containsKey( partName ))
412 {
413 attachmentsMap.put( partName, new ArrayList<Attachment>() );
414 }
415
416 attachmentsMap.get( partName ).add( att );
417 }
418
419
420 for( Iterator<String> i = attachmentsMap.keySet().iterator(); i.hasNext(); )
421 {
422 List<Attachment> attachments = attachmentsMap.get( i.next() );
423 if( attachments.size() == 1 )
424 {
425 Attachment att = attachments.get( 0 );
426 addSingleAttachment(mp, contentIds, att);
427 }
428
429 else if( attachments.size() > 1 )
430 {
431 addMultipartAttachment(mp, contentIds, attachments);
432 }
433 }
434 }
435 }
436
437 /***
438 * Adds a mulitpart MimeBodyPart from an array of attachments
439 */
440
441 public static void addMultipartAttachment(MimeMultipart mp, StringToStringMap contentIds, List<Attachment> attachments) throws MessagingException
442 {
443 MimeMultipart multipart = new MimeMultipart("mixed");
444 for( int c = 0; c < attachments.size(); c++ )
445 {
446 Attachment att = attachments.get( c );
447 String contentType = att.getContentType();
448
449 MimeBodyPart part = contentType.startsWith( "text/" ) ? new MimeBodyPart() : new PreencodedMimeBodyPart( "binary" );
450
451 part.setDataHandler( new DataHandler( new AttachmentDataSource( att ) ));
452 initPartContentId( contentIds, part, att, false );
453 multipart.addBodyPart( part );
454 }
455
456 MimeBodyPart part = new PreencodedMimeBodyPart( "binary" );
457 part.setDataHandler( new DataHandler( new MultipartAttachmentDataSource( multipart ) ));
458
459 Attachment attachment = attachments.get( 0 );
460 initPartContentId( contentIds, part, attachment, true );
461
462 mp.addBodyPart( part );
463 }
464
465 public static void initPartContentId( StringToStringMap contentIds, MimeBodyPart part, Attachment attachment, boolean isMultipart ) throws MessagingException
466 {
467 String partName = attachment.getPart();
468
469 String contentID = attachment.getContentID();
470 if( contentID != null )
471 {
472 int ix = contentID.indexOf( ' ' );
473 if( ix != -1 )
474 part.setContentID( "<" + (isMultipart ? contentID.substring( ix+1 ) : contentID.substring( 0, ix )) + ">" );
475 else
476 part.setContentID( contentID );
477
478 }
479 else if( partName != null && !partName.equals( WsdlAttachmentPart.ANONYMOUS_NAME ))
480 {
481 if( contentIds.containsKey( partName ))
482 {
483 part.setContentID( "<" + contentIds.get( partName )+ ">" );
484 }
485 else
486 {
487 part.setContentID( "<" + partName + "=" + System.nanoTime() + "@soapui.org>" );
488 }
489 }
490 }
491
492 /***
493 * Adds a simple MimeBodyPart from an attachment
494 */
495
496 public static void addSingleAttachment(MimeMultipart mp, StringToStringMap contentIds, Attachment att) throws MessagingException
497 {
498 String contentType = att.getContentType();
499 MimeBodyPart part = contentType.startsWith( "text/" ) ? new MimeBodyPart() : new PreencodedMimeBodyPart( "binary" );
500
501 part.setDataHandler( new DataHandler( new AttachmentDataSource( att ) ));
502 initPartContentId( contentIds, part, att, false );
503
504 mp.addBodyPart( part );
505 }
506
507 public static final Session JAVAMAIL_SESSION = Session.getDefaultInstance( new Properties() );
508 }