4 // Copyright (C) 2005 Novell, Inc.
8 // Permission is hereby granted, free of charge, to any person obtaining a
9 // copy of this software and associated documentation files (the "Software"),
10 // to deal in the Software without restriction, including without limitation
11 // the rights to use, copy, modify, merge, publish, distribute, sublicense,
12 // and/or sell copies of the Software, and to permit persons to whom the
13 // Software is furnished to do so, subject to the following conditions:
15 // The above copyright notice and this permission notice shall be included in
16 // all copies or substantial portions of the Software.
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
20 // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
21 // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
22 // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
23 // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
24 // DEALINGS IN THE SOFTWARE.
28 using System
.Globalization
;
35 namespace Beagle
.Daemon
.EvolutionDataServerQueryable
{
37 public class BookContainer
: Container
{
40 private BookView book_view
;
41 private bool ignore_first_batch
= false;
42 private Scheduler
.Priority priority
= Scheduler
.Priority
.Delayed
;
44 public BookContainer (Evolution
.Source source
, EvolutionDataServerQueryable queryable
, string fingerprint
) : base (source
, queryable
, fingerprint
) { }
46 public override bool OpenClient ()
48 if (!this.source
.IsLocal ()) {
49 Logger
.Log
.Debug ("Skipping remote addressbook {0}", this.source
.Uri
);
54 this.book
= new Book (this.source
);
55 this.book
.Open (true);
56 } catch (Exception e
) {
57 Logger
.Log
.Warn (e
, "Unable to open addressbook {0}:", this.source
.Uri
);
64 public override void OpenView ()
66 this.book_view
= this.book
.GetBookView (BookQuery
.AnyFieldContains (""),
70 this.book_view
.ContactsAdded
+= OnContactsAdded
;
71 this.book_view
.ContactsRemoved
+= OnContactsRemoved
;
72 this.book_view
.ContactsChanged
+= OnContactsChanged
;
73 this.book_view
.SequenceComplete
+= OnSequenceComplete
;
75 this.book_view
.Start ();
78 public override void IndexAll ()
80 // We don't need to do anything here. All of the
81 // contacts will be added as a result of
82 // OnContactsAdded being called for every item
83 // after setting up the BookView.
86 public override void IndexChanges ()
88 // Ignore the torrent of add events that will come in
89 // after the BookView is set up.
90 ignore_first_batch
= true;
92 Contact
[] added
, changed
;
95 Logger
.Log
.Debug ("Getting addressbook changes for {0}", this.source
.Uri
);
98 this.book
.GetChanges ("beagle-" + this.fingerprint
, out added
, out changed
, out removed
);
99 } catch (Exception e
) {
100 Logger
.Log
.Warn (e
, "Unable to get changes for {0}:", this.source
.Uri
);
104 Logger
.Log
.Debug ("Addressbook {0}: {1} added, {2} changed, {3} removed",
105 this.book
.Uri
, added
.Length
, changed
.Length
, removed
.Length
);
107 foreach (Contact contact
in added
)
108 AddContact (contact
);
110 foreach (Contact contact
in changed
)
111 AddContact (contact
);
113 foreach (string id
in removed
)
117 public override void Remove ()
119 Logger
.Log
.Debug ("Removing addressbook source {0}", this.source
.Uid
);
121 Property prop
= Property
.NewUnsearched ("fixme:source_uid", this.source
.Uid
);
122 this.queryable
.RemovePropertyIndexable (prop
);
124 this.book_view
.Stop ();
125 this.book_view
.Dispose ();
127 this.book
.Dispose ();
130 private void OnContactsAdded (object o
, Evolution
.ContactsAddedArgs args
)
132 if (ignore_first_batch
)
135 foreach (Evolution
.Contact contact
in args
.Contacts
)
136 AddContact (contact
);
139 private void OnContactsChanged (object o
, Evolution
.ContactsChangedArgs args
)
141 if (ignore_first_batch
)
144 foreach (Evolution
.Contact contact
in args
.Contacts
)
145 AddContact (contact
);
148 private void OnContactsRemoved (object o
, Evolution
.ContactsRemovedArgs args
)
150 if (ignore_first_batch
)
153 // FIXME: This is a temporary workaround for the
154 // fact that the evolution bindings return a
155 // GLib.List with an object type, but there are
156 // really strings in there.
158 GLib
.List id_list
= new GLib
.List (args
.Ids
.Handle
,
161 foreach (string id
in id_list
)
165 private void OnSequenceComplete (object o
, Evolution
.SequenceCompleteArgs args
)
167 ignore_first_batch
= false;
169 // Now that we're done synching with the original
170 // state of the addressbook, switch all new changes to
172 priority
= Scheduler
.Priority
.Immediate
;
174 Logger
.Log
.Debug ("Sequence complete!");
177 /////////////////////////////////////
180 // contacts:///?source-uid=<value>&contact-uid=<value>
182 // The Uri class sucks SO MUCH ASS. It shits itself
183 // on foo:///?bar so we have to insert something in
184 // before "?bar". This is filed as Ximian bug #76146.
185 // Hopefully it is just a bug in Mono and not a
186 // fundamental problem of the Uri class. Fortunately
187 // Evolution can handle the horribly mangled URIs
188 // that come out of it.
190 private Uri
GetContactUri (Evolution
.Contact contact
) {
191 return GetContactUri (contact
.Id
);
194 private Uri
GetContactUri (string id
) {
195 return new Uri (String
.Format ("contacts://uri-class-sucks/?source-uid={0}&contact-uid={1}",
196 this.source
.Uid
, id
));
199 /////////////////////////////////////
201 private void AddContact (Evolution
.Contact contact
)
203 Logger
.Log
.Debug ("We'll index {0}!", contact
.FullName
);
204 Indexable indexable
= ContactToIndexable (contact
);
206 this.queryable
.AddIndexable (indexable
, this.priority
);
209 private void RemoveContact (string id
)
211 Logger
.Log
.Debug ("We'll remove {0}", id
);
212 this.queryable
.RemoveIndexable (GetContactUri (id
));
215 /////////////////////////////////////
217 private string GetPhotoFilename (string id
)
219 return Path
.Combine (this.queryable
.PhotoDir
, id
);
222 private static DateTime
RevStringToDateTime (string date_str
)
224 if (date_str
== null)
225 return DateTime
.MinValue
;
228 "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'",
229 "yyyyMMdd'T'HHmmss'Z'"
233 return DateTime
.ParseExact (date_str
, formats
,
234 CultureInfo
.InvariantCulture
,
235 DateTimeStyles
.None
);
236 } catch (FormatException
) {
237 Logger
.Log
.Warn ("Unable to parse last revision string: {0}", date_str
);
238 return DateTime
.MinValue
;
242 private Indexable
ContactToIndexable (Evolution
.Contact contact
)
244 DateTime rev
= RevStringToDateTime (contact
.Rev
);
246 Indexable indexable
= new Indexable (GetContactUri (contact
));
247 indexable
.Timestamp
= rev
;
248 indexable
.HitType
= "Contact";
250 indexable
.AddProperty (Property
.NewUnsearched ("fixme:source_uid", this.source
.Uid
));
251 indexable
.AddProperty (Property
.NewUnsearched ("fixme:uid", contact
.Id
));
253 indexable
.AddProperty (Property
.New ("fixme:FileAs", contact
.FileAs
));
254 indexable
.AddProperty (Property
.New ("fixme:FullName", contact
.FullName
));
255 indexable
.AddProperty (Property
.New ("fixme:GivenName", contact
.GivenName
));
256 indexable
.AddProperty (Property
.New ("fixme:FamilyName", contact
.FamilyName
));
257 indexable
.AddProperty (Property
.New ("fixme:Nickname", contact
.Nickname
));
258 indexable
.AddProperty (Property
.New ("fixme:AddressLabelHome", contact
.AddressLabelHome
));
259 indexable
.AddProperty (Property
.New ("fixme:AddressLabelWork", contact
.AddressLabelWork
));
260 indexable
.AddProperty (Property
.New ("fixme:AddressLabelOther", contact
.AddressLabelOther
));
261 indexable
.AddProperty (Property
.New ("fixme:AssistantPhone", contact
.AssistantPhone
));
262 indexable
.AddProperty (Property
.New ("fixme:BusinessPhone", contact
.BusinessPhone
));
263 indexable
.AddProperty (Property
.New ("fixme:BusinessPhone2", contact
.BusinessPhone2
));
264 indexable
.AddProperty (Property
.New ("fixme:BusinessFax", contact
.BusinessFax
));
265 indexable
.AddProperty (Property
.New ("fixme:CallbackPhone", contact
.CallbackPhone
));
266 indexable
.AddProperty (Property
.New ("fixme:CarPhone", contact
.CarPhone
));
267 indexable
.AddProperty (Property
.New ("fixme:CompanyPhone", contact
.CompanyPhone
));
268 indexable
.AddProperty (Property
.New ("fixme:HomePhone", contact
.HomePhone
));
269 indexable
.AddProperty (Property
.New ("fixme:HomePhone2", contact
.HomePhone2
));
270 indexable
.AddProperty (Property
.New ("fixme:HomeFax", contact
.HomeFax
));
271 indexable
.AddProperty (Property
.New ("fixme:IsdnPhone", contact
.IsdnPhone
));
272 indexable
.AddProperty (Property
.New ("fixme:MobilePhone", contact
.MobilePhone
));
273 indexable
.AddProperty (Property
.New ("fixme:OtherPhone", contact
.OtherPhone
));
274 indexable
.AddProperty (Property
.New ("fixme:OtherFax", contact
.OtherFax
));
275 indexable
.AddProperty (Property
.New ("fixme:Pager", contact
.Pager
));
276 indexable
.AddProperty (Property
.New ("fixme:PrimaryPhone", contact
.PrimaryPhone
));
277 indexable
.AddProperty (Property
.New ("fixme:Radio", contact
.Radio
));
278 indexable
.AddProperty (Property
.New ("fixme:Telex", contact
.Telex
));
279 indexable
.AddProperty (Property
.NewUnsearched ("fixme:Tty", contact
.Tty
));
280 indexable
.AddProperty (Property
.NewKeyword ("fixme:Email1", contact
.Email1
));
281 indexable
.AddProperty (Property
.NewKeyword ("fixme:Email2", contact
.Email2
));
282 indexable
.AddProperty (Property
.NewKeyword ("fixme:Email3", contact
.Email3
));
283 indexable
.AddProperty (Property
.NewUnsearched ("fixme:Mailer", contact
.Mailer
));
284 indexable
.AddProperty (Property
.New ("fixme:Org", contact
.Org
));
285 indexable
.AddProperty (Property
.New ("fixme:OrgUnit", contact
.OrgUnit
));
286 indexable
.AddProperty (Property
.New ("fixme:Office", contact
.Office
));
287 indexable
.AddProperty (Property
.New ("fixme:Title", contact
.Title
));
288 indexable
.AddProperty (Property
.New ("fixme:Role", contact
.Role
));
289 indexable
.AddProperty (Property
.New ("fixme:Manager", contact
.Manager
));
290 indexable
.AddProperty (Property
.New ("fixme:Assistant", contact
.Assistant
));
291 indexable
.AddProperty (Property
.NewKeyword ("fixme:HomepageUrl", contact
.HomepageUrl
));
292 indexable
.AddProperty (Property
.NewKeyword ("fixme:BlogUrl", contact
.BlogUrl
));
293 indexable
.AddProperty (Property
.NewUnsearched ("fixme:Categories", contact
.Categories
));
294 indexable
.AddProperty (Property
.NewUnsearched ("fixme:Caluri", contact
.Caluri
));
295 indexable
.AddProperty (Property
.NewUnsearched ("fixme:Icscalendar", contact
.Icscalendar
));
296 indexable
.AddProperty (Property
.New ("fixme:Spouse", contact
.Spouse
));
297 indexable
.AddProperty (Property
.New ("fixme:Note", contact
.Note
));
299 Evolution
.ContactPhoto photo
= contact
.Photo
;
301 if (photo
.Data
!= null && photo
.Data
.Length
> 0) {
302 string photo_filename
= GetPhotoFilename (contact
.Id
);
303 Stream s
= new FileStream (photo_filename
, FileMode
.Create
, FileAccess
.Write
, FileShare
.ReadWrite
);
304 BinaryWriter w
= new BinaryWriter (s
);
305 w
.Write (photo
.Data
);
309 indexable
.AddProperty (Property
.NewUnsearched ("beagle:Photo", photo_filename
));
312 foreach (string im
in contact
.ImAim
)
313 indexable
.AddProperty (Property
.NewUnsearched ("fixme:ImAim", im
));
314 foreach (string im
in contact
.ImIcq
)
315 indexable
.AddProperty (Property
.NewUnsearched ("fixme:ImIcq", im
));
316 foreach (string im
in contact
.ImJabber
)
317 indexable
.AddProperty (Property
.NewUnsearched ("fixme:ImJabber", im
));
318 foreach (string im
in contact
.ImMsn
)
319 indexable
.AddProperty (Property
.NewUnsearched ("fixme:ImMsn", im
));
320 foreach (string im
in contact
.ImYahoo
)
321 indexable
.AddProperty (Property
.NewUnsearched ("fixme:ImYahoo", im
));
322 foreach (string im
in contact
.ImGroupwise
)
323 indexable
.AddProperty (Property
.NewUnsearched ("fixme:ImGroupwise", im
));
326 if (contact
.GivenName
!= null && contact
.GivenName
!= "")
327 name
= contact
.GivenName
;
328 if (contact
.FamilyName
!= null && contact
.FamilyName
!= "")
329 name
+= " " + contact
.FamilyName
;
331 indexable
.AddProperty (Property
.New ("fixme:Name", name
));
333 if (contact
.Email1
!= null)
334 indexable
.AddProperty (Property
.NewKeyword ("fixme:Email",