5 // Copyright (C) 2004-2005 Novell, Inc.
9 // Permission is hereby granted, free of charge, to any person obtaining a
10 // copy of this software and associated documentation files (the "Software"),
11 // to deal in the Software without restriction, including without limitation
12 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
13 // and/or sell copies of the Software, and to permit persons to whom the
14 // Software is furnished to do so, subject to the following conditions:
16 // The above copyright notice and this permission notice shall be included in
17 // all copies or substantial portions of the Software.
19 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
20 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
21 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
22 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
23 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
24 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
25 // DEALINGS IN THE SOFTWARE.
29 using System
.Collections
;
39 namespace Beagle
.Filters
{
41 public class FilterMail
: Beagle
.Daemon
.Filter
, IDisposable
{
43 private static bool gmime_initialized
= false;
45 private GMime
.Stream stream
;
46 private GMime
.Parser parser
;
47 private GMime
.Message message
;
48 private PartHandler handler
;
52 AddSupportedFlavor (FilterFlavor
.NewFromMimeType ("message/rfc822"));
55 protected override void DoOpen (FileInfo info
)
57 if (!gmime_initialized
) {
60 gmime_initialized
= true;
67 int mail_fd
= Syscall
.open (info
.FullName
, OpenFlags
.O_RDONLY
);
70 throw new IOException (String
.Format ("Unable to read {0} for parsing mail", info
.FullName
));
72 this.stream
= new GMime
.StreamFs (mail_fd
);
73 this.parser
= new GMime
.Parser (stream
);
76 protected override void DoPullProperties ()
78 this.message
= this.parser
.ConstructMessage ();
80 string subject
= GMime
.Utils
.HeaderDecodePhrase (this.message
.Subject
);
81 AddProperty (Property
.New ("dc:title", subject
));
83 AddProperty (Property
.NewDate ("fixme:date", message
.Date
));
85 GMime
.InternetAddressList addrs
;
86 addrs
= this.message
.GetRecipients (GMime
.Message
.RecipientType
.To
);
87 foreach (GMime
.InternetAddress ia
in addrs
) {
88 AddProperty (Property
.NewKeyword ("fixme:to", ia
.ToString (false)));
89 AddProperty (Property
.NewKeyword ("fixme:to_address", ia
.Addr
));
90 AddProperty (Property
.New ("fixme:to_name", ia
.Name
));
94 addrs
= this.message
.GetRecipients (GMime
.Message
.RecipientType
.Cc
);
95 foreach (GMime
.InternetAddress ia
in addrs
) {
96 AddProperty (Property
.NewKeyword ("fixme:cc", ia
.ToString (false)));
97 AddProperty (Property
.NewKeyword ("fixme:cc_address", ia
.Addr
));
98 AddProperty (Property
.New ("fixme:cc_name", ia
.Name
));
102 addrs
= GMime
.InternetAddressList
.ParseString (GMime
.Utils
.HeaderDecodePhrase (this.message
.Sender
));
103 foreach (GMime
.InternetAddress ia
in addrs
) {
104 AddProperty (Property
.NewKeyword ("fixme:from", ia
.ToString (false)));
105 AddProperty (Property
.NewKeyword ("fixme:from_address", ia
.Addr
));
106 AddProperty (Property
.New ("fixme:from_name", ia
.Name
));
110 if (this.message
.MimePart
is GMime
.Multipart
|| this.message
.MimePart
is GMime
.MessagePart
)
111 AddProperty (Property
.NewFlag ("fixme:hasAttachments"));
113 string list_id
= this.message
.GetHeader ("List-Id");
115 if (list_id
!= null) {
116 // FIXME: Might need some additional parsing.
117 AddProperty (Property
.NewKeyword ("fixme:mlist", GMime
.Utils
.HeaderDecodePhrase (list_id
)));
121 protected override void DoPullSetup ()
123 this.handler
= new PartHandler (this);
124 using (GMime
.Object mime_part
= this.message
.MimePart
)
125 this.handler
.OnEachPart (mime_part
);
127 AddChildIndexables (this.handler
.ChildIndexables
);
130 protected override void DoPull ()
132 if (handler
.Reader
== null) {
137 string l
= handler
.Reader
.ReadLine ();
145 protected override void DoClose ()
150 public void Dispose ()
152 if (this.message
!= null)
153 this.message
.Dispose ();
155 if (this.stream
!= null) {
156 this.stream
.Close ();
157 this.stream
.Dispose ();
160 if (this.parser
!= null)
161 this.parser
.Dispose ();
164 private class PartHandler
{
165 private Beagle
.Daemon
.Filter filter
;
166 private int count
= 0; // parts handled so far
167 private int depth
= 0; // part recursion depth
168 private ArrayList child_indexables
= new ArrayList ();
169 private TextReader reader
;
171 public PartHandler (Beagle
.Daemon
.Filter filter
)
173 this.filter
= filter
;
176 public void OnEachPart (GMime
.Object part
)
178 //for (int i = 0; i < depth; i++)
179 // Console.Write (" ");
180 //Console.WriteLine ("Content-Type: {0}", part.ContentType);
184 if (part
is GMime
.MessagePart
) {
185 GMime
.MessagePart msg_part
= (GMime
.MessagePart
) part
;
187 using (GMime
.Message message
= msg_part
.Message
) {
188 using (GMime
.Object subpart
= message
.MimePart
)
189 this.OnEachPart (subpart
);
191 } else if (part
is GMime
.Multipart
) {
192 GMime
.Multipart multipart
= (GMime
.Multipart
) part
;
194 int num_parts
= multipart
.Number
;
195 for (int i
= 0; i
< num_parts
; i
++) {
196 using (GMime
.Object subpart
= multipart
.GetPart (i
))
197 this.OnEachPart (subpart
);
199 } else if (part
is GMime
.Part
) {
200 MemoryStream stream
= null;
202 using (GMime
.DataWrapper content_obj
= ((GMime
.Part
) part
).ContentObject
) {
203 stream
= new MemoryStream ();
204 content_obj
.WriteToStream (stream
);
205 stream
.Seek (0, SeekOrigin
.Begin
);
208 // If this is the only part and it's plain text, we
209 // want to just attach it to our filter instead of
210 // creating a child indexable for it.
211 bool no_child_needed
= false;
213 if (this.depth
== 1 && this.count
== 0) {
214 if (part
.ContentType
.ToString ().ToLower () == "text/plain") {
215 no_child_needed
= true;
217 this.reader
= new StreamReader (stream
);
221 if (!no_child_needed
) {
222 string sub_uri
= this.filter
.Uri
.ToString () + "#" + this.count
;
223 Indexable child
= new Indexable (new Uri (sub_uri
));
225 child
.Type
= "MailMessage";
226 child
.MimeType
= part
.ContentType
.ToString ();
227 child
.CacheContent
= false;
229 child
.AddProperty (Property
.NewKeyword ("fixme:attachment_title", ((GMime
.Part
)part
).Filename
));
231 if (part
.ContentType
.Type
.ToLower () == "text")
232 child
.SetTextReader (new StreamReader (stream
));
234 child
.SetBinaryStream (stream
);
236 this.child_indexables
.Add (child
);
241 throw new Exception (String
.Format ("Unknown part type: {0}", part
.GetType ()));
247 public ICollection ChildIndexables
{
248 get { return this.child_indexables; }
251 public TextReader Reader
{
252 get { return this.reader; }