source: contrib/MailArchiver/sources/src/serpro/mailarchiver/domain/metaarchive/Message.java @ 6785

Revision 6785, 21.0 KB checked in by rafaelraymundo, 12 years ago (diff)

Ticket #2946 - Liberado codigo do MailArchiver?. Documentação na subpasta DOCS.

Line 
1/**
2 * MailArchiver is an application that provides services for storing and managing e-mail messages through a Web Services SOAP interface.
3 * Copyright (C) 2012  Marcio Andre Scholl Levien and Fernando Alberto Reuter Wendt and Jose Ronaldo Nogueira Fonseca Junior
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU Affero General Public License as
7 * published by the Free Software Foundation, either version 3 of the
8 * License, or (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU Affero General Public License for more details.
14 *
15 * You should have received a copy of the GNU Affero General Public License
16 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19/******************************************************************************\
20*
21*  This product was developed by
22*
23*        SERVIÇO FEDERAL DE PROCESSAMENTO DE DADOS (SERPRO),
24*
25*  a government company established under Brazilian law (5.615/70),
26*  at Department of Development of Porto Alegre.
27*
28\******************************************************************************/
29
30package serpro.mailarchiver.domain.metaarchive;
31
32import java.io.IOException;
33import java.io.InputStream;
34import java.io.StringWriter;
35import java.nio.file.Files;
36import java.nio.file.Path;
37import java.nio.file.Paths;
38import java.util.Collections;
39import java.util.LinkedHashSet;
40import java.util.Set;
41
42import javax.jdo.JDOHelper;
43import javax.jdo.annotations.NotPersistent;
44import javax.jdo.annotations.PersistenceCapable;
45import javax.xml.stream.XMLStreamException;
46
47import org.apache.commons.lang3.mutable.MutableObject;
48import org.apache.james.mime4j.io.LineNumberInputStream;
49
50import org.springframework.beans.factory.annotation.Autowired;
51
52import org.codehaus.jettison.AbstractXMLStreamWriter;
53import org.codehaus.jettison.badgerfish.BadgerFishXMLStreamWriter;
54import org.codehaus.jettison.mapped.MappedNamespaceConvention;
55import org.codehaus.jettison.mapped.MappedXMLStreamWriter;
56
57import org.codehaus.staxmate.SMOutputFactory;
58import org.codehaus.staxmate.out.SMOutputDocument;
59import org.codehaus.staxmate.out.SMOutputElement;
60
61import de.schlichtherle.truezip.file.TFile;
62import de.schlichtherle.truezip.nio.file.TPath;
63
64import serpro.mailarchiver.domain.metaarchive.UnstructuredField.ContentTransferEncodingField;
65import serpro.mailarchiver.util.BodyVisitor;
66import serpro.mailarchiver.util.JSONMappingConvention;
67import serpro.mailarchiver.util.Logger;
68import serpro.mailarchiver.util.UserAppConfig;
69
70@PersistenceCapable
71public class Message
72    extends Entity
73{
74    @NotPersistent
75    private static final Logger log = Logger.getLocalLogger();
76
77    @NotPersistent private static final String TAG_SEEN = "seen";
78    @NotPersistent private static final String TAG_UNSEEN = "unseen";
79
80    @NotPersistent private static final String TAG_ANSWERED = "answered";
81    @NotPersistent private static final String TAG_UNANSWERED = "unanswered";
82
83    @NotPersistent private static final String TAG_FLAGGED = "flagged";
84    @NotPersistent private static final String TAG_UNFLAGGED = "unflagged";
85
86    @NotPersistent private static final String TAG_IMPORTANCE_HIGH = "importance_high";
87    @NotPersistent private static final String TAG_IMPORTANCE_NORMAL = "importance_normal";
88    @NotPersistent private static final String TAG_IMPORTANCE_LOW = "importance_low";
89
90    @NotPersistent private static final String TAG_FORWARDED = "forwarded";
91    @NotPersistent private static final String TAG_DELETED = "deleted";
92    @NotPersistent private static final String TAG_DRAFT = "draft";
93    @NotPersistent private static final String TAG_SPAM = "spam";
94
95    //**** P E R S I S T E N T ****
96    private Folder folder;
97    private Integer folderIdx;
98    private LinkedHashSet<String> tags = new LinkedHashSet<String>();
99    private Long queryCandidatesSet;
100    //*****************************
101
102    public Message(LineNumberInputStream.Entity lnisEntity) {
103        super(lnisEntity);
104    }
105
106    public Message() {}
107
108    @Override
109    public void jdoPreDelete() {
110        super.jdoPreDelete();
111
112        setFolder(null);
113    }
114
115    @Override
116    public Message getRootMessage() {
117        return this;
118    }
119
120    public final Folder getFolder() {
121        return folder;
122    }
123
124    public final void setFolder(Folder folder) {
125        if(this.folder != null) {
126            this.folder.internal_removeMessage(this);
127        }
128        this.folder = folder;
129        if(this.folder != null) {
130            this.folder.internal_addMessage(this);
131        }
132    }
133
134    public final int getFolderIdx() {
135        return (folderIdx != null) ? folderIdx
136                : (getFolder() != null) ? getFolder().indexOf(this)
137                : -1;
138    }
139
140    public final void setQueryCandidatesSet(Long queryCandidatesSet) {
141        this.queryCandidatesSet = queryCandidatesSet;
142    }
143
144    //--------------------------------------------------------------------------
145    public final Set<String> getTags() {
146        return Collections.unmodifiableSet(tags);
147    }
148
149    public final boolean addTag(String value) {
150        if(value != null) {
151            String tag = value.trim().toLowerCase();
152            if(tag.equals(TAG_SEEN)) {
153                tags.remove(TAG_UNSEEN);
154            }
155            else if(tag.equals(TAG_UNSEEN)) {
156                tags.remove(TAG_SEEN);
157            }
158            else if(tag.equals(TAG_ANSWERED)) {
159                tags.remove(TAG_UNANSWERED);
160            }
161            else if(tag.equals(TAG_UNANSWERED)) {
162                tags.remove(TAG_ANSWERED);
163            }
164            else if(tag.equals(TAG_FLAGGED)) {
165                tags.remove(TAG_UNFLAGGED);
166            }
167            else if(tag.equals(TAG_UNFLAGGED)) {
168                tags.remove(TAG_FLAGGED);
169            }
170            else if(tag.equals(TAG_IMPORTANCE_HIGH)) {
171                tags.remove(TAG_IMPORTANCE_NORMAL);
172                tags.remove(TAG_IMPORTANCE_LOW);
173            }
174            else if(tag.equals(TAG_IMPORTANCE_NORMAL)) {
175                tags.remove(TAG_IMPORTANCE_HIGH);
176                tags.remove(TAG_IMPORTANCE_LOW);
177            }
178            else if(tag.equals(TAG_IMPORTANCE_LOW)) {
179                tags.remove(TAG_IMPORTANCE_HIGH);
180                tags.remove(TAG_IMPORTANCE_NORMAL);
181            }
182            return tags.add(tag);
183        }
184        return false;
185    }
186
187    public final boolean removeTag(String value) {
188        if(value != null) {
189            String tag = value.trim().toLowerCase();
190            if(tag.equals(TAG_SEEN)) {
191                tags.add(TAG_UNSEEN);
192            }
193            else if(tag.equals(TAG_UNSEEN)) {
194                tags.add(TAG_SEEN);
195            }
196            else if(tag.equals(TAG_ANSWERED)) {
197                tags.add(TAG_UNANSWERED);
198            }
199            else if(tag.equals(TAG_UNANSWERED)) {
200                tags.add(TAG_ANSWERED);
201            }
202            else if(tag.equals(TAG_FLAGGED)) {
203                tags.add(TAG_UNFLAGGED);
204            }
205            else if(tag.equals(TAG_UNFLAGGED)) {
206                tags.add(TAG_FLAGGED);
207            }
208            else if(tag.equals(TAG_IMPORTANCE_HIGH)) {
209                if(!(tags.contains(TAG_IMPORTANCE_NORMAL) || tags.contains(TAG_IMPORTANCE_LOW))) {
210                    tags.add(TAG_IMPORTANCE_NORMAL);
211                }
212            }
213            else if(tag.equals(TAG_IMPORTANCE_NORMAL)) {
214                if(!(tags.contains(TAG_IMPORTANCE_HIGH) || tags.contains(TAG_IMPORTANCE_LOW))) {
215                    tags.add(TAG_IMPORTANCE_HIGH);
216                }
217            }
218            else if(tag.equals(TAG_IMPORTANCE_LOW)) {
219                if(!(tags.contains(TAG_IMPORTANCE_HIGH) || tags.contains(TAG_IMPORTANCE_NORMAL))) {
220                    tags.add(TAG_IMPORTANCE_NORMAL);
221                }
222            }
223            return tags.remove(tag);
224        }
225        return false;
226    }
227
228    //--------------------------------------------------------------------------
229    @Override
230    String toString(String pad) {
231        return String.format(
232                "Message%n"
233              + "%1$sjdoState: %2$s%n"
234              + "%1$soid: %3$s%n"
235              + "%1$shash: %4$x%n"
236              + "%1$sstartLine: %5$d%n"
237              + "%1$sseparatorLine: %6$d%n"
238              + "%1$sendLine: %7$d%n"
239              + "%1$ssize: %8$d"
240              , pad
241              , JDOHelper.getObjectState(this)
242              , getOid()
243              , hashCode()
244              , getStartLine()
245              , getSeparatorLine()
246              , getEndLine()
247              , getSize());
248    }
249
250    @Override
251    int dumpPath(StringBuilder sb, boolean quit) {
252        if(quit) {
253            sb.append(toString("    "));
254        }
255        else {
256            sb.append(toString("|   ")).append("\n")
257              .append("|\n")
258              .append("+---");
259        }
260        return 1;
261    }
262
263    //--------------------------------------------------------------------------
264    public final Path getRelativePath() {
265        if(getFolder() == null) {
266            return Paths.get("");
267        }
268        else {
269            return getFolder().getRelativePath().resolve(getOid() + ".eml");
270        }
271    }
272
273    public final Path getQualifiedPath() {
274        if(getFolder() == null) {
275            return Paths.get(getOid() + ".eml");
276        }
277        else {
278            return getFolder().getQualifiedPath().resolve(getOid() + ".eml");
279        }
280    }
281
282    @Autowired
283    @NotPersistent
284    private UserAppConfig userAppConfig;
285
286    public final Path getAbsolutePath() {
287        return userAppConfig.SERVER.getArchiveDir()
288                .resolve("mail")
289                .resolve(getQualifiedPath());
290    }
291
292    //--------------------------------------------------------------------------
293    public void setSeen(boolean seen) {
294        if(seen) {
295            addTag(TAG_SEEN);
296        }
297        else {
298            removeTag(TAG_SEEN);
299        }
300    }
301
302    public boolean isSeen() {
303        return tags.contains(TAG_SEEN);
304    }
305
306    public void setUnseen(boolean unseen) {
307        if(unseen) {
308            addTag(TAG_UNSEEN);
309        }
310        else {
311            removeTag(TAG_UNSEEN);
312        }
313    }
314
315    public boolean isUnseen() {
316        return tags.contains(TAG_UNSEEN);
317    }
318
319    //--------------------------------------------------------------------------
320    public void setAnswered(boolean answered) {
321        if(answered) {
322            addTag(TAG_ANSWERED);
323        }
324        else {
325            removeTag(TAG_ANSWERED);
326        }
327    }
328
329    public boolean isAnswered() {
330        return tags.contains(TAG_ANSWERED);
331    }
332
333    public void setUnanswered(boolean unanswered) {
334        if(unanswered) {
335            addTag(TAG_UNANSWERED);
336        }
337        else {
338            removeTag(TAG_UNANSWERED);
339        }
340    }
341
342    public boolean isUnanswered() {
343        return tags.contains(TAG_UNANSWERED);
344    }
345
346    //--------------------------------------------------------------------------
347    public void setFlagged(boolean flagged) {
348        if(flagged) {
349            addTag(TAG_FLAGGED);
350        }
351        else {
352            removeTag(TAG_FLAGGED);
353        }
354    }
355
356    public boolean isFlagged() {
357        return tags.contains(TAG_FLAGGED);
358    }
359
360    public void setUnflagged(boolean unflagged) {
361        if(unflagged) {
362            addTag(TAG_UNFLAGGED);
363        }
364        else {
365            removeTag(TAG_UNFLAGGED);
366        }
367    }
368
369    public boolean isUnflagged() {
370        return tags.contains(TAG_UNFLAGGED);
371    }
372
373    //--------------------------------------------------------------------------
374    public void setImportanceHigh(boolean importanceHigh) {
375        if(importanceHigh) {
376            addTag(TAG_IMPORTANCE_HIGH);
377        }
378        else {
379            removeTag(TAG_IMPORTANCE_HIGH);
380        }
381    }
382
383    public boolean isImportanceHigh() {
384        return tags.contains(TAG_IMPORTANCE_HIGH);
385    }
386
387    public void setImportanceNormal(boolean importanceNormal) {
388        if(importanceNormal) {
389            addTag(TAG_IMPORTANCE_NORMAL);
390        }
391        else {
392            removeTag(TAG_IMPORTANCE_NORMAL);
393        }
394    }
395
396    public boolean isImportanceNormal() {
397        return tags.contains(TAG_IMPORTANCE_NORMAL);
398    }
399
400    public void setImportanceLow(boolean importanceLow) {
401        if(importanceLow) {
402            addTag(TAG_IMPORTANCE_LOW);
403        }
404        else {
405            removeTag(TAG_IMPORTANCE_LOW);
406        }
407    }
408
409    public boolean isImportanceLow() {
410        return tags.contains(TAG_IMPORTANCE_LOW);
411    }
412
413    //--------------------------------------------------------------------------
414    public void setForwarded(boolean forwarded) {
415        if(forwarded) {
416            addTag(TAG_FORWARDED);
417        }
418        else {
419            removeTag(TAG_FORWARDED);
420        }
421    }
422
423    public boolean isForwarded() {
424        return tags.contains(TAG_FORWARDED);
425    }
426
427    //--------------------------------------------------------------------------
428    public void setDeleted(boolean deleted) {
429        if(deleted) {
430            addTag(TAG_DELETED);
431        }
432        else {
433            removeTag(TAG_DELETED);
434        }
435    }
436
437    public boolean isDeleted() {
438        return tags.contains(TAG_DELETED);
439    }
440
441    //--------------------------------------------------------------------------
442    public void setDraft(boolean draft) {
443        if(draft) {
444            addTag(TAG_DRAFT);
445        }
446        else {
447            removeTag(TAG_DRAFT);
448        }
449    }
450
451    public boolean isDraft() {
452        return tags.contains(TAG_DRAFT);
453    }
454
455    //--------------------------------------------------------------------------
456    public void setSpam(boolean spam) {
457        if(spam) {
458            addTag(TAG_SPAM);
459        }
460        else {
461            removeTag(TAG_SPAM);
462        }
463    }
464
465    public boolean isSpam() {
466        return tags.contains(TAG_SPAM);
467    }
468
469    //--------------------------------------------------------------------------
470    public final String getTagsJSONString() {
471        return getTagsJSONString(JSONMappingConvention.Mapped_Attributes);
472    }
473
474    public final String getTagsJSONString(JSONMappingConvention convention) {
475        StringWriter strWriter = new StringWriter();
476
477        try {
478            AbstractXMLStreamWriter xmlWriter = null;
479            if(convention.useMapped()) {
480                MappedNamespaceConvention con = new MappedNamespaceConvention(JSONMappingConvention.mappedConfiguration);
481                xmlWriter = new MappedXMLStreamWriter(con, strWriter);
482            }
483            else if(convention.useBadgerFish()) {
484                xmlWriter = new BadgerFishXMLStreamWriter(strWriter);
485            }
486
487            SMOutputDocument doc = SMOutputFactory.createOutputDocument(xmlWriter);
488
489            for(String tag : getTags()) {
490                SMOutputElement tagElement = doc.addElement("tag");
491
492                if(convention.useAttributes()) {
493                    tagElement.addAttribute("value", tag);
494                }
495                else if(convention.useNestedElements()) {
496                    tagElement.addElement("value").addCharacters(tag);
497                }
498            }
499
500            doc.closeRootAndWriter();
501        }
502        catch(XMLStreamException ex) {
503            log.error(ex, ex.getMessage());
504            return null;
505        }
506
507        return strWriter.toString();
508    }
509
510    //--------------------------------------------------------------------------
511    public String getAttachmentsJSONString() {
512        return getAttachmentsJSONString(JSONMappingConvention.Mapped_Attributes);
513    }
514
515    public String getAttachmentsJSONString(final JSONMappingConvention convention) {
516        StringWriter strWriter = new StringWriter();
517
518        try {
519            AbstractXMLStreamWriter xmlWriter = null;
520            if(convention.useMapped()) {
521                MappedNamespaceConvention con = new MappedNamespaceConvention(JSONMappingConvention.mappedConfiguration);
522                xmlWriter = new MappedXMLStreamWriter(con, strWriter);
523            }
524            else if(convention.useBadgerFish()) {
525                xmlWriter = new BadgerFishXMLStreamWriter(strWriter);
526            }
527
528            final SMOutputDocument doc = SMOutputFactory.createOutputDocument(xmlWriter);
529
530            visitBodies(new BodyVisitor() {
531
532                @Override
533                public void visitAttachmentBody(SingleBody singleBody) {
534                    addAttachment(singleBody);
535                }
536
537                @Override
538                public void visitInlineBody(SingleBody singleBody) {
539                    addAttachment(singleBody);
540                }
541
542                private void addAttachment(SingleBody singleBody) {
543
544                    ContentTypeField contentTypeField = singleBody.getEntity().getContentTypeField();
545                    if(contentTypeField != null) {
546                        if(contentTypeField.isMessageDeliveryStatusMimeType()) {
547                            return;
548                        }
549                    }
550
551                    try {
552                        SMOutputElement attachmentElement = doc.addElement("attachment");
553
554                        String id = singleBody.getOid();
555
556                        String name = singleBody.getFileName();
557
558                        long size = singleBody.getSize();
559
560                        String mediaType = "";
561                        String subType = "";
562                        if(contentTypeField != null) {
563                            mediaType = contentTypeField.getMediaType();
564                            subType = contentTypeField.getSubType();
565                        }
566
567                        String encoding = "";
568                        ContentTransferEncodingField contentTransferEncodingField = singleBody.getEntity().getContentTransferEncoding();
569                        if(contentTransferEncodingField != null) {
570                            encoding = contentTransferEncodingField.getEncoding();
571                        }
572
573                        if(convention.useAttributes()) {
574                            attachmentElement.addAttribute("id", id);
575                            attachmentElement.addAttribute("name", name);
576                            attachmentElement.addAttribute(null, "size", size);
577                            attachmentElement.addAttribute("mediaType", mediaType);
578                            attachmentElement.addAttribute("subType", subType);
579                            attachmentElement.addAttribute("encoding", encoding);
580                        }
581                        else if(convention.useNestedElements()) {
582                            attachmentElement.addElement("id").addCharacters(id);
583                            attachmentElement.addElement("name").addCharacters(name);
584                            attachmentElement.addElement("size").addValue(size);
585                            attachmentElement.addElement("mediaType").addCharacters(mediaType);
586                            attachmentElement.addElement("subType").addCharacters(subType);
587                            attachmentElement.addElement("encoding").addCharacters(encoding);
588                        }
589                    }
590                    catch (XMLStreamException ex) {
591                        log.error(ex);
592                    }
593                }
594            });
595
596            doc.closeRootAndWriter();
597        }
598        catch(Exception ex) {
599            log.error(ex, ex.getMessage());
600            return null;
601        }
602
603        return strWriter.toString();
604    }
605
606    //--------------------------------------------------------------------------
607    public String getPartsZipFileName(String extension) {
608        return "parts_" + getOid() + "." + extension;
609    }
610
611    public Path getPartsZipPath(String extension) {
612        return userAppConfig.SERVER.getArchiveDir().resolve("temp").resolve(getPartsZipFileName(extension));
613    }
614
615    public void publishPartsZip(String extension) throws IOException {
616
617        if(Files.exists(getPartsZipPath(extension))) {
618            //already published
619            return;
620        }
621
622        log.info("Publishing: %s", getPartsZipFileName(extension));
623
624        final TPath zip = new TPath(getPartsZipPath(extension));
625        final MutableObject<IOException> ioex = new MutableObject<IOException>();
626
627        visitBodies(new BodyVisitor() {
628
629            @Override
630            public void visitAttachmentBody(SingleBody singleBody) {
631                addAttachment(singleBody);
632            }
633
634            @Override
635            public void visitInlineBody(SingleBody singleBody) {
636                addAttachment(singleBody);
637            }
638
639            private void addAttachment(SingleBody singleBody) {
640                try {
641                    InputStream is = singleBody.getDecoderInputStream();
642                    TFile.cp(is, zip.resolve(singleBody.getFileName()).toFile());
643                    is.close();
644                }
645                catch(IOException ex) {
646                    ioex.setValue(ex);
647                    quit();
648                }
649            }
650        });
651
652        TFile.umount(zip.toFile(), true);
653
654        if(ioex.getValue() != null) {
655            throw ioex.getValue();
656        }
657    }
658}
Note: See TracBrowser for help on using the repository browser.