001/* 002 * The MIT License 003 * Copyright (c) 2012 Microsoft Corporation 004 * 005 * Permission is hereby granted, free of charge, to any person obtaining a copy 006 * of this software and associated documentation files (the "Software"), to deal 007 * in the Software without restriction, including without limitation the rights 008 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 009 * copies of the Software, and to permit persons to whom the Software is 010 * furnished to do so, subject to the following conditions: 011 * 012 * The above copyright notice and this permission notice shall be included in 013 * all copies or substantial portions of the Software. 014 * 015 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 016 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 017 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 018 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 019 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 020 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 021 * THE SOFTWARE. 022 */ 023 024package microsoft.exchange.webservices.data.property.complex; 025 026import microsoft.exchange.webservices.data.core.EwsServiceXmlReader; 027import microsoft.exchange.webservices.data.core.EwsServiceXmlWriter; 028import microsoft.exchange.webservices.data.core.EwsUtilities; 029import microsoft.exchange.webservices.data.core.XmlElementNames; 030import microsoft.exchange.webservices.data.core.service.item.Item; 031import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion; 032import microsoft.exchange.webservices.data.core.enumeration.misc.XmlNamespace; 033import microsoft.exchange.webservices.data.core.exception.service.local.ServiceValidationException; 034import microsoft.exchange.webservices.data.core.exception.service.local.ServiceVersionException; 035 036import org.apache.commons.io.IOUtils; 037 038import java.io.File; 039import java.io.FileInputStream; 040import java.io.FileOutputStream; 041import java.io.InputStream; 042import java.io.OutputStream; 043 044/** 045 * Represents a file attachment. 046 */ 047public final class FileAttachment extends Attachment { 048 049 /** 050 * The file name. 051 */ 052 private String fileName; 053 054 /** 055 * The content stream. 056 */ 057 private InputStream contentStream; 058 059 /** 060 * The content. 061 */ 062 private byte[] content; 063 064 /** 065 * The load to stream. 066 */ 067 private OutputStream loadToStream; 068 069 /** 070 * The is contact photo. 071 */ 072 private boolean isContactPhoto; 073 074 /** 075 * Initializes a new instance. 076 * 077 * @param owner the owner 078 */ 079 public FileAttachment(Item owner) { 080 super(owner); 081 } 082 083 /** 084 * Gets the name of the XML element. 085 * 086 * @return XML element name 087 */ 088 public String getXmlElementName() { 089 return XmlElementNames.FileAttachment; 090 } 091 092 /** 093 * {@inheritDoc} 094 */ 095 @Override 096 protected void validate(int attachmentIndex) throws ServiceValidationException { 097 if ((this.fileName == null || this.fileName.isEmpty()) 098 && this.content == null && this.contentStream == null) { 099 throw new ServiceValidationException(String.format( 100 "The content of the file attachment at index %d must be set.", 101 attachmentIndex)); 102 } 103 } 104 105 /** 106 * Tries to read element from XML. 107 * 108 * @param reader the reader 109 * @return True if element was read. 110 * @throws Exception the exception 111 */ 112 @Override 113 public boolean tryReadElementFromXml(EwsServiceXmlReader reader) 114 throws Exception { 115 boolean result = super.tryReadElementFromXml(reader); 116 117 if (!result) { 118 if (reader.getLocalName().equals(XmlElementNames.IsContactPhoto)) { 119 this.isContactPhoto = reader.readElementValue(Boolean.class); 120 } else if (reader.getLocalName().equals(XmlElementNames.Content)) { 121 if (this.loadToStream != null) { 122 reader.readBase64ElementValue(this.loadToStream); 123 } else { 124 // If there's a file attachment content handler, use it. 125 // Otherwise 126 // load the content into a byte array. 127 // TODO: Should we mark the attachment to indicate that 128 // content is stored elsewhere? 129 if (reader.getService().getFileAttachmentContentHandler() != null) { 130 OutputStream outputStream = reader.getService() 131 .getFileAttachmentContentHandler() 132 .getOutputStream(getId()); 133 if (outputStream != null) { 134 reader.readBase64ElementValue(outputStream); 135 } else { 136 this.content = reader.readBase64ElementValue(); 137 } 138 } else { 139 this.content = reader.readBase64ElementValue(); 140 } 141 } 142 143 result = true; 144 } 145 } 146 147 return result; 148 } 149 150 151 /** 152 * For FileAttachment, the only thing need to patch is the AttachmentId. 153 * 154 * @param reader The reader. 155 * @return true if element was read 156 */ 157 @Override 158 public boolean tryReadElementFromXmlToPatch(EwsServiceXmlReader reader) throws Exception { 159 return super.tryReadElementFromXml(reader); 160 } 161 162 163 /** 164 * Writes elements and content to XML. 165 * 166 * @param writer the writer 167 * @throws Exception the exception 168 */ 169 @Override 170 public void writeElementsToXml(EwsServiceXmlWriter writer) 171 throws Exception { 172 super.writeElementsToXml(writer); 173 // ExchangeVersion ev=writer.getService().getRequestedServerVersion(); 174 if (writer.getService().getRequestedServerVersion().ordinal() > 175 ExchangeVersion.Exchange2007_SP1 176 .ordinal()) { 177 writer.writeElementValue(XmlNamespace.Types, 178 XmlElementNames.IsContactPhoto, this.isContactPhoto); 179 } 180 181 writer.writeStartElement(XmlNamespace.Types, XmlElementNames.Content); 182 183 if (!(this.fileName == null || this.fileName.isEmpty())) { 184 File fileStream = new File(this.fileName); 185 FileInputStream fis = null; 186 try { 187 fis = new FileInputStream(fileStream); 188 writer.writeBase64ElementValue(fis); 189 } finally { 190 if (fis != null) { 191 fis.close(); 192 } 193 } 194 195 } else if (this.contentStream != null) { 196 writer.writeBase64ElementValue(this.contentStream); 197 } else if (this.content != null) { 198 writer.writeBase64ElementValue(this.content); 199 } else { 200 EwsUtilities 201 .ewsAssert(false, "FileAttachment.WriteElementsToXml", "The attachment's content is not set."); 202 } 203 204 writer.writeEndElement(); 205 } 206 207 /** 208 * Loads the content of the file attachment into the specified stream. 209 * Calling this method results in a call to EWS. 210 * 211 * @param stream the stream 212 * @throws Exception the exception 213 */ 214 public void load(OutputStream stream) throws Exception { 215 this.loadToStream = stream; 216 217 try { 218 this.load(); 219 } finally { 220 this.loadToStream = null; 221 } 222 } 223 224 /** 225 * Loads the content of the file attachment into the specified file. 226 * Calling this method results in a call to EWS. 227 * 228 * @param fileName the file name 229 * @throws Exception the exception 230 */ 231 public void load(String fileName) throws Exception { 232 File fileStream = new File(fileName); 233 234 try { 235 this.loadToStream = new FileOutputStream(fileStream); 236 this.load(); 237 this.loadToStream.flush(); 238 } finally { 239 IOUtils.closeQuietly(this.loadToStream); 240 this.loadToStream = null; 241 } 242 243 this.fileName = fileName; 244 this.content = null; 245 this.contentStream = null; 246 } 247 248 /** 249 * Gets the name of the file the attachment is linked to. 250 * 251 * @return the file name 252 */ 253 public String getFileName() { 254 return this.fileName; 255 } 256 257 /** 258 * Sets the file name. 259 * 260 * @param fileName the new file name 261 */ 262 protected void setFileName(String fileName) { 263 this.throwIfThisIsNotNew(); 264 265 this.fileName = fileName; 266 this.content = null; 267 this.contentStream = null; 268 } 269 270 /** 271 * Gets the content stream.Gets the name of the file the attachment 272 * is linked to. 273 * 274 * @return The content stream 275 */ 276 protected InputStream getContentStream() { 277 return this.contentStream; 278 } 279 280 /** 281 * Sets the content stream. 282 * 283 * @param contentStream the new content stream 284 */ 285 protected void setContentStream(InputStream contentStream) { 286 this.throwIfThisIsNotNew(); 287 288 this.contentStream = contentStream; 289 this.content = null; 290 this.fileName = null; 291 } 292 293 /** 294 * Gets the content of the attachment into memory. Content is set only 295 * when Load() is called. 296 * 297 * @return the content 298 */ 299 public byte[] getContent() { 300 return this.content; 301 } 302 303 /** 304 * Sets the content. 305 * 306 * @param content the new content 307 */ 308 protected void setContent(byte[] content) { 309 this.throwIfThisIsNotNew(); 310 311 this.content = content; 312 this.fileName = null; 313 this.contentStream = null; 314 } 315 316 /** 317 * Gets a value indicating whether this attachment is a contact 318 * photo. 319 * 320 * @return true, if is contact photo 321 * @throws ServiceVersionException the service version exception 322 */ 323 public boolean isContactPhoto() throws ServiceVersionException { 324 EwsUtilities.validatePropertyVersion(this.getOwner().getService(), 325 ExchangeVersion.Exchange2010, "IsContactPhoto"); 326 return this.isContactPhoto; 327 } 328 329 /** 330 * Sets the checks if is contact photo. 331 * 332 * @param isContactPhoto the new checks if is contact photo 333 * @throws ServiceVersionException the service version exception 334 */ 335 public void setIsContactPhoto(boolean isContactPhoto) 336 throws ServiceVersionException { 337 EwsUtilities.validatePropertyVersion(this.getOwner().getService(), 338 ExchangeVersion.Exchange2010, "IsContactPhoto"); 339 this.throwIfThisIsNotNew(); 340 this.isContactPhoto = isContactPhoto; 341 } 342 343}