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

Revision 6785, 20.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.StringReader;
33import java.util.ArrayList;
34import java.util.Collections;
35import java.util.Date;
36import java.util.List;
37import java.util.regex.Pattern;
38
39import javax.jdo.annotations.NotPersistent;
40import javax.jdo.annotations.PersistenceCapable;
41
42import org.apache.james.mime4j.dom.datetime.DateTime;
43import org.apache.james.mime4j.field.datetime.parser.DateTimeParser;
44import org.apache.james.mime4j.io.LineNumberInputStream;
45
46import com.google.common.collect.LinkedListMultimap;
47
48import serpro.mailarchiver.domain.BaseObject;
49import serpro.mailarchiver.domain.metaarchive.UnstructuredField.ContentEncodingField;
50import serpro.mailarchiver.domain.metaarchive.UnstructuredField.ContentLanguageField;
51import serpro.mailarchiver.domain.metaarchive.UnstructuredField.ContentLengthField;
52import serpro.mailarchiver.domain.metaarchive.UnstructuredField.ContentLocationField;
53import serpro.mailarchiver.domain.metaarchive.UnstructuredField.ContentTransferEncodingField;
54import serpro.mailarchiver.domain.metaarchive.UnstructuredField.MessageIdSequenceField;
55import serpro.mailarchiver.domain.metaarchive.UnstructuredField.MimeVersionField;
56import serpro.mailarchiver.util.BodyVisitor;
57import serpro.mailarchiver.util.Logger;
58
59@PersistenceCapable
60public abstract class Entity
61    extends BaseObject
62{
63    @NotPersistent
64    private static final Logger log = Logger.getLocalLogger();
65
66    //**** P E R S I S T E N T ****
67    private String oid;
68    private int startLine;
69    private int separatorLine;
70    private int endLine;
71    private int size;
72    private Body body;
73    private ArrayList<Field> fields = new ArrayList<Field>();
74    //*****************************
75
76    @NotPersistent
77    private final LineNumberInputStream.Entity lnisEntity;
78
79    public Entity(LineNumberInputStream.Entity lnisEntity) {
80        this.lnisEntity = lnisEntity;
81    }
82
83    public Entity() {
84        lnisEntity = null;
85    }
86
87    public abstract Message getRootMessage();
88
89    public void sync() {
90        startLine = lnisEntity.getStartLine();
91        separatorLine = lnisEntity.getSeparatorLine();
92        endLine = lnisEntity.getEndLine();
93
94        if(body instanceof SingleBody) {
95            SingleBody singleBody = (SingleBody)body;
96            singleBody.setOffset(lnisEntity.getBodyOffset());
97            singleBody.setLength(lnisEntity.getBodyLength());
98        }
99        else if(body instanceof MessageBody) {
100            MessageBody messageBody = (MessageBody)body;
101            EmbeddedMessage embeddedMessage = messageBody.getEmbeddedMessage();
102            embeddedMessage.sync();
103        }
104        else if(body instanceof Multipart) {
105            Multipart multipart = (Multipart)body;
106            for(BodyPart bodyPart : multipart.getBodyParts()) {
107                bodyPart.sync();
108            }
109        }
110    }
111
112    public final String getOid() {
113        return oid;
114    }
115
116    public final void setOid(String oid) {
117        this.oid = oid;
118    }
119
120    public final int getStartLine() {
121        return startLine;
122    }
123
124    public final void setStartLine(int startLine) {
125        this.startLine = startLine;
126    }
127
128    public final int getSeparatorLine() {
129        return separatorLine;
130    }
131
132    public final void setSeparatorLine(int separatorLine) {
133        this.separatorLine = separatorLine;
134    }
135
136    public final int getEndLine() {
137        return endLine;
138    }
139
140    public final void setEndLine(int endLine) {
141        this.endLine = endLine;
142    }
143
144    public final int getSize() {
145        return size;
146    }
147
148    public final void setSize(int size) {
149        this.size = size;
150    }
151
152    //--------------------------------------------------------------------------
153    public final Body getBody() {
154        return body;
155    }
156
157    public final void setBody(Body body) {
158        if(this.body != null) {
159            this.body.internal_setEntity(null);
160        }
161        this.body = body;
162        if(this.body != null) {
163            this.body.internal_setEntity(this);
164        }
165    }
166
167    final void internal_setBody(Body body) {
168        this.body = body;
169    }
170
171    //--------------------------------------------------------------------------
172    public final List<? extends Field> getFields() {
173        return Collections.unmodifiableList(fields);
174    }
175
176    public final int indexOf(Field field) {
177        return fields.indexOf(field);
178    }
179
180    public final void addField(Field field) {
181        if(field != null) {
182            field.setEntity(this);
183        }
184    }
185
186    final void internal_addField(Field field) {
187        fields.add(field);
188        dirtyCache = true;
189    }
190
191    public final void removeField(Field field) {
192        if((field != null) && (field.getEntity() == this)) {
193            field.setEntity(null);
194        }
195    }
196
197    final void internal_removeField(Field field) {
198        fields.remove(field);
199        dirtyCache = true;
200    }
201
202    //--------------------------------------------------------------------------
203    public final void incSize(int size) {
204        setSize(getSize() + size);
205        if(this instanceof BodyEntity) {
206            ((BodyEntity)this).getComposite().getEntity().incSize(size);
207        }
208    }
209
210    //--------------------------------------------------------------------------
211    public final void visitBodies(BodyVisitor visitor) {
212        visitBodies(visitor, "multipart/*", "message/*", "text/html", "text/plain", "text/*");
213    }
214
215    public final void visitBodies(BodyVisitor visitor, String... alternativeMimeTypesPriority) {
216        List<Pattern> patterns = new ArrayList<Pattern>();
217        for(String s : alternativeMimeTypesPriority) {
218            patterns.add(Pattern.compile("^" + s.trim().replaceAll("\\*", "(.+)") + "$", Pattern.CASE_INSENSITIVE));
219        }
220        visitBodies(visitor, patterns.toArray(new Pattern[patterns.size()]));
221    }
222
223    public final void visitBodies(BodyVisitor visitor, Pattern... alternativeMimeTypesPriority) {
224        if(visitor.isQuitted()) {
225            return;
226        }
227        if(body instanceof SingleBody) {
228            SingleBody singleBody = (SingleBody)body;
229            visitor.visitSingleBody(singleBody);
230        }
231        else if(body instanceof MessageBody) {
232            MessageBody messageBody = (MessageBody)body;
233            Entity embeddedMessage = messageBody.getEmbeddedMessage();
234            if(embeddedMessage != null) {
235                embeddedMessage.visitBodies(visitor, alternativeMimeTypesPriority);
236            }
237        }
238        else if(body instanceof Multipart) {
239            Multipart multipart = (Multipart)body;
240            ContentTypeField contentTypeField = getContentTypeField();
241            if((contentTypeField != null) && (contentTypeField.isMultipartAlternativeMimeType())) {
242
243                // multipart-alternative
244
245                boolean found = false;
246                priority:
247                for(Pattern mimeType : alternativeMimeTypesPriority) {
248                    for(Entity bodyPart : multipart.getBodyParts()) {
249                        ContentTypeField bodyPartContentTypeField = bodyPart.getContentTypeField();
250                        if((bodyPartContentTypeField != null) && (mimeType.matcher(bodyPartContentTypeField.getMimeType()).matches())) {
251                            found = true;
252                            bodyPart.visitBodies(visitor, alternativeMimeTypesPriority);
253                            break priority;
254                        }
255                    }
256                }
257                if(!found) {
258                    //choose the plainest (first) alternative. see RFC2046
259                    Entity bodyPart = multipart.getBodyParts().get(0);
260                    bodyPart.visitBodies(visitor, alternativeMimeTypesPriority);
261                }
262            }
263            else {
264                for(Entity bodyPart : multipart.getBodyParts()) {
265                    bodyPart.visitBodies(visitor, alternativeMimeTypesPriority);
266                    if(visitor.isQuitted()) {
267                        break;
268                    }
269                }
270            }
271        }
272    }
273
274    //--------------------------------------------------------------------------
275    public final String dumpPath() {
276        StringBuilder sb = new StringBuilder();
277        dumpPath(sb, true);
278        return sb.toString();
279    }
280
281    abstract int dumpPath(StringBuilder sb, boolean quit);
282
283    public final String dumpTree() {
284        StringBuilder sb = new StringBuilder();
285        dumpTree(sb, "");
286        return sb.toString();
287    }
288
289    final void dumpTree(StringBuilder sb, String pad) {
290        int c = fields.size();
291        sb.append(toString(pad + (((c > 0) || (body != null)) ? "|   " : "    ")));
292        for(int i = 0; i < c; i++) {
293            sb.append("\n")
294              .append(pad).append("|\n")
295              .append(pad).append("+---");
296            if((i < (c - 1)) || (body != null)) {
297                fields.get(i).dumpTree(sb, pad + "|   ");
298            }
299            else {
300                fields.get(i).dumpTree(sb, pad + "    ");
301            }
302        }
303        if(body != null) {
304            sb.append("\n")
305              .append(pad).append("|\n")
306              .append(pad).append("+---");
307            body.dumpTree(sb, pad + "    ");
308        }
309    }
310
311    @Override
312    public final String toString() {
313        return toString("    ");
314    }
315
316    abstract String toString(String pad);
317
318
319    //<editor-fold defaultstate="collapsed" desc=" convenience ">
320
321    @NotPersistent
322    private LinkedListMultimap<String, Field> readCache = LinkedListMultimap.create();
323
324    @NotPersistent
325    private boolean dirtyCache = true;
326
327    public synchronized final Field getField(String name) {
328        if(dirtyCache) {
329            updateCache();
330        }
331        List<Field> fs = readCache.get(name);
332        if(fs.isEmpty()) {
333            return null;
334        }
335        return fs.get(0);
336    }
337
338    public synchronized final List<? extends Field> getFields(String name) {
339        if(dirtyCache) {
340            updateCache();
341        }
342        return Collections.unmodifiableList(readCache.get(name));
343    }
344
345    private void updateCache() {
346        readCache.clear();
347        for(Field f : getFields()) {
348            readCache.put(f.getName().toLowerCase(), f);
349        }
350        dirtyCache = false;
351    }
352
353//----
354    public final ContentTypeField getContentTypeField() {
355        return (ContentTypeField) getField("content-type");
356    }
357
358    public final ContentDispositionField getContentDispositionField() {
359        return (ContentDispositionField) getField("content-disposition");
360    }
361
362//----
363    public final DateTimeField getDateField() {
364        return (DateTimeField) getField("date");
365    }
366
367    public final List<DateTimeField> getResentDateFields() {
368        return (List<DateTimeField>) getFields("resent-date");
369    }
370
371//----
372    public final MailboxField getSenderField() {
373        return (MailboxField) getField("sender");
374    }
375
376    public final List<MailboxField> getResentSenderFields() {
377        return (List<MailboxField>) getFields("resent-sender");
378    }
379
380//----
381    public final MailboxListField getFromField() {
382        return (MailboxListField) getField("from");
383    }
384
385    public final MailboxListField getDispositionNotificationToField() {
386        return (MailboxListField) getField("disposition-notification-to");
387    }
388
389    public final List<MailboxListField> getResentFromFields() {
390        return (List<MailboxListField>) getFields("resent-from");
391    }
392
393//----
394    public final AddressListField getToField() {
395        return (AddressListField) getField("to");
396    }
397
398    public final AddressListField getCcField() {
399        return (AddressListField) getField("cc");
400    }
401
402    public final AddressListField getBccField() {
403        return (AddressListField) getField("bcc");
404    }
405
406    public final AddressListField getReplyToField() {
407        return (AddressListField) getField("reply-to");
408    }
409
410    public final List<AddressListField> getResentToFields() {
411        return (List<AddressListField>) getFields("resent-to");
412    }
413
414    public final List<AddressListField> getResentCcFields() {
415        return (List<AddressListField>) getFields("resent-cc");
416    }
417
418    public final List<AddressListField> getResentBccFields() {
419        return (List<AddressListField>) getFields("resent-bcc");
420    }
421
422//----
423    public final UnstructuredField getSubjectField() {
424        return (UnstructuredField) getField("subject");
425    }
426
427    public final UnstructuredField getContentDescriptionField() {
428        return (UnstructuredField) getField("content-description");
429    }
430
431    public final UnstructuredField getMessageIdField() {
432        return (UnstructuredField) getField("message-id");
433    }
434
435    public final UnstructuredField getContentIdField() {
436        return (UnstructuredField) getField("content-id");
437    }
438
439    public final UnstructuredField getContentMD5Field() {
440        return (UnstructuredField) getField("content-md5");
441    }
442
443    public final List<UnstructuredField> getResentMsgIdFields() {
444        return (List<UnstructuredField>) getFields("resent-msg-id");
445    }
446
447    public final List<UnstructuredField> getCommentsFields() {
448        return (List<UnstructuredField>) getFields("comments");
449    }
450
451    public final List<UnstructuredField> getKeywordsFields() {
452        return (List<UnstructuredField>) getFields("keywords");
453    }
454
455//---
456    public final MessageIdSequenceField getReferencesField() {
457        UnstructuredField field = (UnstructuredField)getField("references");
458        if(field != null) {
459            return field.getDecorator();
460        }
461        return null;
462    }
463
464    public final MessageIdSequenceField getInReplyToField() {
465        UnstructuredField field = (UnstructuredField)getField("in-reply-to");
466        if(field != null) {
467            return field.getDecorator();
468        }
469        return null;
470    }
471
472    public final MimeVersionField getMimeVersionField() {
473        UnstructuredField field = (UnstructuredField)getField("mime-version");
474        if(field != null) {
475            return field.getDecorator();
476        }
477        return null;
478    }
479
480    public final ContentLengthField getContentLengthField() {
481        UnstructuredField field = (UnstructuredField)getField("content-length");
482        if(field != null) {
483            return field.getDecorator();
484        }
485        return null;
486    }
487
488    public final ContentLanguageField getContentLanguageField() {
489        UnstructuredField field = (UnstructuredField)getField("content-language");
490        if(field != null) {
491            return field.getDecorator();
492        }
493        return null;
494    }
495
496    public final ContentLocationField getContentLocationField() {
497        UnstructuredField field = (UnstructuredField)getField("content-location");
498        if(field != null) {
499            return field.getDecorator();
500        }
501        return null;
502    }
503
504    public final ContentEncodingField getContentEncodingField() {
505        UnstructuredField field = (UnstructuredField)getField("content-encoding");
506        if(field != null) {
507            return field.getDecorator();
508        }
509        return null;
510    }
511
512    public final ContentTransferEncodingField getContentTransferEncoding() {
513        UnstructuredField field = (UnstructuredField)getField("content-transfer-encoding");
514        if(field != null) {
515            return field.getDecorator();
516        }
517        return null;
518    }
519
520//---
521    public final Date getLastReceivedDate() {
522        List<UnstructuredField> receivedFields = (List<UnstructuredField>) getFields("received");
523        if(receivedFields.size() > 0) {
524            String receivedFieldText = receivedFields.get(0).getText();
525            int k = receivedFieldText.lastIndexOf(";");
526            if(k != -1) {
527                StringReader reader = new StringReader(receivedFieldText.substring(k + 1));
528                DateTimeParser dtp = new DateTimeParser(reader);
529                try {
530                    DateTime dt = dtp.parseAll();
531                    if(dt != null) {
532                        return dt.getDate();
533                    }
534                }
535                catch(Exception ex) {
536                    log.error(ex, "received date: %s", receivedFieldText);
537                }
538            }
539            else {
540                log.error("received date: %s", receivedFieldText);
541            }
542        }
543        return null;
544    }
545
546
547/* RFC 5322 - Internet Message Format
548   +----------------+--------+------------+----------------------------+
549   | Field          | Min    | Max number | Notes                      |
550   |                | number |            |                            |
551   +----------------+--------+------------+----------------------------+
552   | trace          | 0      | unlimited  | Block prepended - see      |
553   |                |        |            | 3.6.7                      |
554   | resent-date    | 0*     | unlimited* | One per block, required if |
555   |                |        |            | other resent fields are    |
556   |                |        |            | present - see 3.6.6        |
557   | resent-from    | 0      | unlimited* | One per block - see 3.6.6  |
558   | resent-sender  | 0*     | unlimited* | One per block, MUST occur  |
559   |                |        |            | with multi-address         |
560   |                |        |            | resent-from - see 3.6.6    |
561   | resent-to      | 0      | unlimited* | One per block - see 3.6.6  |
562   | resent-cc      | 0      | unlimited* | One per block - see 3.6.6  |
563   | resent-bcc     | 0      | unlimited* | One per block - see 3.6.6  |
564   | resent-msg-id  | 0      | unlimited* | One per block - see 3.6.6  |
565   | orig-date      | 1      | 1          |                            |
566   | from           | 1      | 1          | See sender and 3.6.2       |
567   | sender         | 0*     | 1          | MUST occur with            |
568   |                |        |            | multi-address from - see   |
569   |                |        |            | 3.6.2                      |
570   | reply-to       | 0      | 1          |                            |
571   | to             | 0      | 1          |                            |
572   | cc             | 0      | 1          |                            |
573   | bcc            | 0      | 1          |                            |
574   | message-id     | 0*     | 1          | SHOULD be present - see    |
575   |                |        |            | 3.6.4                      |
576   | in-reply-to    | 0*     | 1          | SHOULD occur in some       |
577   |                |        |            | replies - see 3.6.4        |
578   | references     | 0*     | 1          | SHOULD occur in some       |
579   |                |        |            | replies - see 3.6.4        |
580   | subject        | 0      | 1          |                            |
581   | comments       | 0      | unlimited  |                            |
582   | keywords       | 0      | unlimited  |                            |
583   | optional-field | 0      | unlimited  |                            |
584   +----------------+--------+------------+----------------------------+
585*/
586
587    //</editor-fold>
588
589
590}
Note: See TracBrowser for help on using the repository browser.