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 if( action == null )
279 action = "";
280
281 int ix = header.indexOf( "boundary" );
282 return "multipart/related; type=\"application/xop+xml\"; start=\"" + ROOTPART_SOAPUI_ORG + "\"; " +
283 "startinfo=\"" + soapVersion.getContentType() + "; action=//\"" + action + "//\"\"; " + header.substring( ix );
284 }
285
286 public static boolean isSwaRefType(SchemaType schemaType)
287 {
288 return schemaType.getName() != null && schemaType.getName().equals( SWAREF_QNAME);
289 }
290
291 public static String getXmlMimeContentType(XmlCursor cursor)
292 {
293 String attributeText = cursor.getAttributeText( XMLMIME_CONTENTTYPE_200411);
294 if( attributeText == null )
295 attributeText = cursor.getAttributeText( XMLMIME_CONTENTTYPE_200505);
296 return attributeText;
297 }
298
299 public static boolean isXopInclude( SchemaType schemaType )
300 {
301 return XOP_INCLUDE_QNAME.equals( schemaType.getName() );
302 }
303
304 public static List<WsdlAttachmentPart> extractAttachmentParts( WsdlOperation operation, String requestContent, boolean addAnonymous )
305 {
306 List<WsdlAttachmentPart> result = new ArrayList<WsdlAttachmentPart>();
307
308 PartsConfig requestParts = operation.getConfig().getRequestParts();
309 if( requestParts != null )
310 {
311 for( Part part : requestParts.getPartList() )
312 {
313 WsdlAttachmentPart attachmentPart = new WsdlAttachmentPart( part.getName(), part.getContentTypeList() );
314 attachmentPart.setType( Attachment.AttachmentType.MIME );
315 result.add( attachmentPart );
316 }
317 }
318
319 if( requestContent.length() > 0 )
320 {
321 WsdlContext wsdlContext = ((WsdlInterface)operation.getInterface()).getWsdlContext();
322 WsdlValidator validator = new WsdlValidator( wsdlContext );
323 try
324 {
325 XmlObject[] requestDocuments = validator.getInputParts( requestContent, operation.getName() );
326
327 for( XmlObject partDoc : requestDocuments )
328 {
329 XmlCursor cursor = partDoc.newCursor();
330 while( !cursor.isEnddoc() )
331 {
332 if( cursor.isContainer() )
333 {
334 SchemaType schemaType = cursor.getObject().schemaType();
335 if( schemaType != null )
336 {
337 String attributeText = AttachmentUtils.getXmlMimeContentType(cursor);
338
339
340 if( SchemaUtils.isBinaryType( schemaType ))
341 {
342 String contentId = cursor.getTextValue();
343 if( contentId.startsWith( "cid:" ))
344 {
345 WsdlAttachmentPart attachmentPart = new WsdlAttachmentPart( contentId.substring( 4 ), attributeText);
346 attachmentPart.setType( attributeText == null ? Attachment.AttachmentType.CONTENT : Attachment.AttachmentType.XOP );
347 result.add( attachmentPart );
348 }
349 }
350 else if( AttachmentUtils.isXopInclude( schemaType ))
351 {
352 String contentId = cursor.getAttributeText( new QName("href"));
353 if( contentId != null && contentId.length() > 0 )
354 {
355 WsdlAttachmentPart attachmentPart = new WsdlAttachmentPart( contentId, attributeText);
356 attachmentPart.setType( Attachment.AttachmentType.XOP );
357 result.add( attachmentPart );
358 }
359 }
360
361 else if( AttachmentUtils.isSwaRefType(schemaType) )
362 {
363 String contentId = cursor.getTextValue();
364 if( contentId.startsWith( "cid:" ))
365 {
366 WsdlAttachmentPart attachmentPart = new WsdlAttachmentPart( contentId.substring( 4 ), attributeText);
367 attachmentPart.setType( Attachment.AttachmentType.SWAREF );
368 result.add( attachmentPart );
369 }
370 }
371 }
372 }
373
374 cursor.toNextToken();
375 }
376 }
377 }
378 catch( Exception e )
379 {
380 e.printStackTrace();
381 log.warn( e.toString() );
382 }
383 }
384
385 if( addAnonymous )
386 result.add( new WsdlAttachmentPart() );
387
388 return result;
389 }
390
391 /***
392 * Adds defined attachments as mimeparts
393 */
394
395 public static void addMimeParts(AttachmentContainer container, MimeMultipart mp, StringToStringMap contentIds) throws MessagingException
396 {
397
398 if( !container.isMultipartEnabled())
399 {
400 for( int c = 0; c < container.getAttachmentCount(); c++ )
401 {
402 Attachment att = container.getAttachmentAt( c );
403 addSingleAttachment(mp, contentIds, att);
404 }
405 }
406 else
407 {
408
409 Map<String,List<Attachment> > attachmentsMap = new HashMap<String, List<Attachment> >();
410 for( int c = 0; c < container.getAttachmentCount(); c++ )
411 {
412 Attachment att = container.getAttachmentAt( c );
413 String partName = att.getPart();
414
415 if( !attachmentsMap.containsKey( partName ))
416 {
417 attachmentsMap.put( partName, new ArrayList<Attachment>() );
418 }
419
420 attachmentsMap.get( partName ).add( att );
421 }
422
423
424 for( Iterator<String> i = attachmentsMap.keySet().iterator(); i.hasNext(); )
425 {
426 List<Attachment> attachments = attachmentsMap.get( i.next() );
427 if( attachments.size() == 1 )
428 {
429 Attachment att = attachments.get( 0 );
430 addSingleAttachment(mp, contentIds, att);
431 }
432
433 else if( attachments.size() > 1 )
434 {
435 addMultipartAttachment(mp, contentIds, attachments);
436 }
437 }
438 }
439 }
440
441 /***
442 * Adds a mulitpart MimeBodyPart from an array of attachments
443 */
444
445 public static void addMultipartAttachment(MimeMultipart mp, StringToStringMap contentIds, List<Attachment> attachments) throws MessagingException
446 {
447 MimeMultipart multipart = new MimeMultipart("mixed");
448 for( int c = 0; c < attachments.size(); c++ )
449 {
450 Attachment att = attachments.get( c );
451 String contentType = att.getContentType();
452
453 MimeBodyPart part = contentType.startsWith( "text/" ) ? new MimeBodyPart() : new PreencodedMimeBodyPart( "binary" );
454
455 part.setDataHandler( new DataHandler( new AttachmentDataSource( att ) ));
456 initPartContentId( contentIds, part, att, false );
457 multipart.addBodyPart( part );
458 }
459
460 MimeBodyPart part = new PreencodedMimeBodyPart( "binary" );
461 part.setDataHandler( new DataHandler( new MultipartAttachmentDataSource( multipart ) ));
462
463 Attachment attachment = attachments.get( 0 );
464 initPartContentId( contentIds, part, attachment, true );
465
466 mp.addBodyPart( part );
467 }
468
469 public static void initPartContentId( StringToStringMap contentIds, MimeBodyPart part, Attachment attachment, boolean isMultipart ) throws MessagingException
470 {
471 String partName = attachment.getPart();
472
473 String contentID = attachment.getContentID();
474 if( contentID != null )
475 {
476 int ix = contentID.indexOf( ' ' );
477 if( ix != -1 )
478 part.setContentID( "<" + (isMultipart ? contentID.substring( ix+1 ) : contentID.substring( 0, ix )) + ">" );
479 else
480 part.setContentID( contentID );
481
482 }
483 else if( partName != null && !partName.equals( WsdlAttachmentPart.ANONYMOUS_NAME ))
484 {
485 if( contentIds.containsKey( partName ))
486 {
487 part.setContentID( "<" + contentIds.get( partName )+ ">" );
488 }
489 else
490 {
491 part.setContentID( "<" + partName + "=" + System.nanoTime() + "@soapui.org>" );
492 }
493 }
494 }
495
496 /***
497 * Adds a simple MimeBodyPart from an attachment
498 */
499
500 public static void addSingleAttachment(MimeMultipart mp, StringToStringMap contentIds, Attachment att) throws MessagingException
501 {
502 String contentType = att.getContentType();
503 MimeBodyPart part = contentType.startsWith( "text/" ) ? new MimeBodyPart() : new PreencodedMimeBodyPart( "binary" );
504
505 part.setDataHandler( new DataHandler( new AttachmentDataSource( att ) ));
506 initPartContentId( contentIds, part, att, false );
507
508 mp.addBodyPart( part );
509 }
510
511 public static final Session JAVAMAIL_SESSION = Session.getDefaultInstance( new Properties() );
512 }