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 java.io.File;
037import java.io.FileInputStream;
038import java.io.FileOutputStream;
039import java.io.InputStream;
040import java.io.OutputStream;
041
042/**
043 * Represents a file attachment.
044 */
045public final class FileAttachment extends Attachment {
046
047  /**
048   * The file name.
049   */
050  private String fileName;
051
052  /**
053   * The content stream.
054   */
055  private InputStream contentStream;
056
057  /**
058   * The content.
059   */
060  private byte[] content;
061
062  /**
063   * The load to stream.
064   */
065  private OutputStream loadToStream;
066
067  /**
068   * The is contact photo.
069   */
070  private boolean isContactPhoto;
071
072  /**
073   * Initializes a new instance.
074   *
075   * @param owner the owner
076   */
077  protected FileAttachment(Item owner) {
078    super(owner);
079  }
080
081  /**
082   * Gets the name of the XML element.
083   *
084   * @return XML element name
085   */
086  public String getXmlElementName() {
087    return XmlElementNames.FileAttachment;
088  }
089
090  /**
091   * {@inheritDoc}
092   */
093  @Override
094  protected void validate(int attachmentIndex) throws ServiceValidationException {
095    if ((this.fileName == null || this.fileName.isEmpty())
096        && this.content == null && this.contentStream == null) {
097      throw new ServiceValidationException(String.format(
098          "The content of the file attachment at index %d must be set.",
099          attachmentIndex));
100    }
101  }
102
103  /**
104   * Tries to read element from XML.
105   *
106   * @param reader the reader
107   * @return True if element was read.
108   * @throws Exception the exception
109   */
110  @Override
111  public boolean tryReadElementFromXml(EwsServiceXmlReader reader)
112      throws Exception {
113    boolean result = super.tryReadElementFromXml(reader);
114
115    if (!result) {
116      if (reader.getLocalName().equals(XmlElementNames.IsContactPhoto)) {
117        this.isContactPhoto = reader.readElementValue(Boolean.class);
118      } else if (reader.getLocalName().equals(XmlElementNames.Content)) {
119        if (this.loadToStream != null) {
120          reader.readBase64ElementValue(this.loadToStream);
121        } else {
122          // If there's a file attachment content handler, use it.
123          // Otherwise
124          // load the content into a byte array.
125          // TODO: Should we mark the attachment to indicate that
126          // content is stored elsewhere?
127          if (reader.getService().getFileAttachmentContentHandler() != null) {
128            OutputStream outputStream = reader.getService()
129                .getFileAttachmentContentHandler()
130                .getOutputStream(getId());
131            if (outputStream != null) {
132              reader.readBase64ElementValue(outputStream);
133            } else {
134              this.content = reader.readBase64ElementValue();
135            }
136          } else {
137            this.content = reader.readBase64ElementValue();
138          }
139        }
140
141        result = true;
142      }
143    }
144
145    return result;
146  }
147
148
149  /**
150   * For FileAttachment, the only thing need to patch is the AttachmentId.
151   *
152   * @param reader The reader.
153   * @return true if element was read
154   */
155  @Override
156  public boolean tryReadElementFromXmlToPatch(EwsServiceXmlReader reader) throws Exception {
157    return super.tryReadElementFromXml(reader);
158  }
159
160
161  /**
162   * Writes elements and content to XML.
163   *
164   * @param writer the writer
165   * @throws Exception the exception
166   */
167  @Override
168  public void writeElementsToXml(EwsServiceXmlWriter writer)
169      throws Exception {
170    super.writeElementsToXml(writer);
171    // ExchangeVersion ev=writer.getService().getRequestedServerVersion();
172    if (writer.getService().getRequestedServerVersion().ordinal() >
173        ExchangeVersion.Exchange2007_SP1
174            .ordinal()) {
175      writer.writeElementValue(XmlNamespace.Types,
176          XmlElementNames.IsContactPhoto, this.isContactPhoto);
177    }
178
179    writer.writeStartElement(XmlNamespace.Types, XmlElementNames.Content);
180
181    if (!(this.fileName == null || this.fileName.isEmpty())) {
182      File fileStream = new File(this.fileName);
183      FileInputStream fis = null;
184      try {
185        fis = new FileInputStream(fileStream);
186        writer.writeBase64ElementValue(fis);
187      } finally {
188        if (fis != null) {
189          fis.close();
190        }
191      }
192
193    } else if (this.contentStream != null) {
194      writer.writeBase64ElementValue(this.contentStream);
195    } else if (this.content != null) {
196      writer.writeBase64ElementValue(this.content);
197    } else {
198      EwsUtilities
199          .ewsAssert(false, "FileAttachment.WriteElementsToXml", "The attachment's content is not set.");
200    }
201
202    writer.writeEndElement();
203  }
204
205  /**
206   * Loads the content of the file attachment into the specified stream.
207   * Calling this method results in a call to EWS.
208   *
209   * @param stream the stream
210   * @throws Exception the exception
211   */
212  public void load(OutputStream stream) throws Exception {
213    this.loadToStream = stream;
214
215    try {
216      this.load();
217    } finally {
218      this.loadToStream = null;
219    }
220  }
221
222  /**
223   * Loads the content of the file attachment into the specified file.
224   * Calling this method results in a call to EWS.
225   *
226   * @param fileName the file name
227   * @throws Exception the exception
228   */
229  public void load(String fileName) throws Exception {
230    File fileStream = new File(fileName);
231
232    try {
233      this.loadToStream = new FileOutputStream(fileStream);
234      this.load();
235      this.loadToStream.flush();
236    } finally {
237      try {
238        this.loadToStream.close();
239      } catch(Exception e) {
240        //ignore exception on close
241      }
242      this.loadToStream = null;
243    }
244
245    this.fileName = fileName;
246    this.content = null;
247    this.contentStream = null;
248  }
249
250  /**
251   * Gets the name of the file the attachment is linked to.
252   *
253   * @return the file name
254   */
255  public String getFileName() {
256    return this.fileName;
257  }
258
259  /**
260   * Sets the file name.
261   *
262   * @param fileName the new file name
263   */
264  protected void setFileName(String fileName) {
265    this.throwIfThisIsNotNew();
266
267    this.fileName = fileName;
268    this.content = null;
269    this.contentStream = null;
270  }
271
272  /**
273   * Gets  the content stream.Gets the name of the file the attachment
274   * is linked to.
275   *
276   * @return The content stream
277   */
278  protected InputStream getContentStream() {
279    return this.contentStream;
280  }
281
282  /**
283   * Sets the content stream.
284   *
285   * @param contentStream the new content stream
286   */
287  protected void setContentStream(InputStream contentStream) {
288    this.throwIfThisIsNotNew();
289
290    this.contentStream = contentStream;
291    this.content = null;
292    this.fileName = null;
293  }
294
295  /**
296   * Gets the content of the attachment into memory. Content is set only
297   * when Load() is called.
298   *
299   * @return the content
300   */
301  public byte[] getContent() {
302    return this.content;
303  }
304
305  /**
306   * Sets the content.
307   *
308   * @param content the new content
309   */
310  protected void setContent(byte[] content) {
311    this.throwIfThisIsNotNew();
312
313    this.content = content;
314    this.fileName = null;
315    this.contentStream = null;
316  }
317
318  /**
319   * Gets  a value indicating whether this attachment is a contact
320   * photo.
321   *
322   * @return true, if is contact photo
323   * @throws ServiceVersionException the service version exception
324   */
325  public boolean isContactPhoto() throws ServiceVersionException {
326    EwsUtilities.validatePropertyVersion(this.getOwner().getService(),
327        ExchangeVersion.Exchange2010, "IsContactPhoto");
328    return this.isContactPhoto;
329  }
330
331  /**
332   * Sets the checks if is contact photo.
333   *
334   * @param isContactPhoto the new checks if is contact photo
335   * @throws ServiceVersionException the service version exception
336   */
337  public void setIsContactPhoto(boolean isContactPhoto)
338      throws ServiceVersionException {
339    EwsUtilities.validatePropertyVersion(this.getOwner().getService(),
340        ExchangeVersion.Exchange2010, "IsContactPhoto");
341    this.throwIfThisIsNotNew();
342    this.isContactPhoto = isContactPhoto;
343  }
344
345}