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}