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    
024    package microsoft.exchange.webservices.data.property.complex;
025    
026    import microsoft.exchange.webservices.data.core.EwsServiceXmlReader;
027    import microsoft.exchange.webservices.data.core.EwsServiceXmlWriter;
028    import microsoft.exchange.webservices.data.core.EwsUtilities;
029    import microsoft.exchange.webservices.data.core.XmlElementNames;
030    import microsoft.exchange.webservices.data.core.service.item.Item;
031    import microsoft.exchange.webservices.data.core.enumeration.misc.ExchangeVersion;
032    import microsoft.exchange.webservices.data.core.enumeration.misc.XmlNamespace;
033    import microsoft.exchange.webservices.data.core.exception.service.local.ServiceValidationException;
034    import microsoft.exchange.webservices.data.core.exception.service.local.ServiceVersionException;
035    
036    import java.io.File;
037    import java.io.FileInputStream;
038    import java.io.FileOutputStream;
039    import java.io.InputStream;
040    import java.io.OutputStream;
041    
042    /**
043     * Represents a file attachment.
044     */
045    public 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    }