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 ("Unable to open addressbook {0}: {1}", this.source
.Uri
, e
.Message
);
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
);
96 this.book
.GetChanges ("beagle-" + this.fingerprint
, out added
, out changed
, out removed
);
97 Logger
.Log
.Debug ("Addressbook {0}: {1} added, {2} changed, {3} removed",
98 this.book
.Uri
, added
.Length
, changed
.Length
, removed
.Length
);
100 foreach (Contact contact
in added
)
101 AddContact (contact
);
103 foreach (Contact contact
in changed
)
104 AddContact (contact
);
106 foreach (string id
in removed
)
110 public override void Remove ()
112 Logger
.Log
.Debug ("Removing addressbook source {0}", this.source
.Uid
);
114 Property prop
= Property
.NewUnsearched ("fixme:source_uid", this.source
.Uid
);
115 this.queryable
.RemovePropertyIndexable (prop
);
117 this.book_view
.Stop ();
118 this.book_view
.Dispose ();
120 this.book
.Dispose ();
123 private void OnContactsAdded (object o
, Evolution
.ContactsAddedArgs args
)
125 if (ignore_first_batch
)
128 foreach (Evolution
.Contact contact
in args
.Contacts
)
129 AddContact (contact
);
132 private void OnContactsChanged (object o
, Evolution
.ContactsChangedArgs args
)
134 if (ignore_first_batch
)
137 foreach (Evolution
.Contact contact
in args
.Contacts
)
138 AddContact (contact
);
141 private void OnContactsRemoved (object o
, Evolution
.ContactsRemovedArgs args
)
143 if (ignore_first_batch
)
146 // FIXME: This is a temporary workaround for the
147 // fact that the evolution bindings return a
148 // GLib.List with an object type, but there are
149 // really strings in there.
151 GLib
.List id_list
= new GLib
.List (args
.Ids
.Handle
,
154 foreach (string id
in id_list
)
158 private void OnSequenceComplete (object o
, Evolution
.SequenceCompleteArgs args
)
160 ignore_first_batch
= false;
162 // Now that we're done synching with the original
163 // state of the addressbook, switch all new changes to
165 priority
= Scheduler
.Priority
.Immediate
;
167 Logger
.Log
.Debug ("Sequence complete!");
170 /////////////////////////////////////
173 // contacts:///?source-uid=<value>&contact-uid=<value>
175 // The Uri class sucks SO MUCH ASS. It shits itself
176 // on foo:///?bar so we have to insert something in
177 // before "?bar". This is filed as Ximian bug #76146.
178 // Hopefully it is just a bug in Mono and not a
179 // fundamental problem of the Uri class. Fortunately
180 // Evolution can handle the horribly mangled URIs
181 // that come out of it.
183 private Uri
GetContactUri (Evolution
.Contact contact
) {
184 return GetContactUri (contact
.Id
);
187 private Uri
GetContactUri (string id
) {
188 return new Uri (String
.Format ("contacts://uri-class-sucks/?source-uid={0}&contact-uid={1}",
189 this.source
.Uid
, id
));
192 /////////////////////////////////////
194 private void AddContact (Evolution
.Contact contact
)
196 Logger
.Log
.Debug ("We'll index {0}!", contact
.FullName
);
197 Indexable indexable
= ContactToIndexable (contact
);
199 this.queryable
.AddIndexable (indexable
, this.priority
);
202 private void RemoveContact (string id
)
204 Logger
.Log
.Debug ("We'll remove {0}", id
);
205 this.queryable
.RemoveIndexable (GetContactUri (id
));
208 /////////////////////////////////////
210 private string GetPhotoFilename (string id
)
212 return Path
.Combine (this.queryable
.PhotoDir
, id
);
215 private static DateTime
RevStringToDateTime (string date_str
)
217 if (date_str
== null)
218 return DateTime
.MinValue
;
221 "yyyy'-'MM'-'dd'T'HH':'mm':'ss'Z'",
222 "yyyyMMdd'T'HHmmss'Z'"
226 return DateTime
.ParseExact (date_str
, formats
,
227 CultureInfo
.InvariantCulture
,
228 DateTimeStyles
.None
);
229 } catch (FormatException
) {
230 Logger
.Log
.Warn ("Unable to parse last revision string: {0}", date_str
);
231 return DateTime
.MinValue
;
235 private Indexable
ContactToIndexable (Evolution
.Contact contact
)
237 DateTime rev
= RevStringToDateTime (contact
.Rev
);
239 Indexable indexable
= new Indexable (GetContactUri (contact
));
240 indexable
.Timestamp
= rev
;
241 indexable
.HitType
= "Contact";
243 indexable
.AddProperty (Property
.NewUnsearched ("fixme:source_uid", this.source
.Uid
));
244 indexable
.AddProperty (Property
.NewUnsearched ("fixme:uid", contact
.Id
));
246 indexable
.AddProperty (Property
.New ("fixme:FileAs", contact
.FileAs
));
247 indexable
.AddProperty (Property
.New ("fixme:FullName", contact
.FullName
));
248 indexable
.AddProperty (Property
.New ("fixme:GivenName", contact
.GivenName
));
249 indexable
.AddProperty (Property
.New ("fixme:FamilyName", contact
.FamilyName
));
250 indexable
.AddProperty (Property
.New ("fixme:Nickname", contact
.Nickname
));
251 indexable
.AddProperty (Property
.New ("fixme:AddressLabelHome", contact
.AddressLabelHome
));
252 indexable
.AddProperty (Property
.New ("fixme:AddressLabelWork", contact
.AddressLabelWork
));
253 indexable
.AddProperty (Property
.New ("fixme:AddressLabelOther", contact
.AddressLabelOther
));
254 indexable
.AddProperty (Property
.New ("fixme:AssistantPhone", contact
.AssistantPhone
));
255 indexable
.AddProperty (Property
.New ("fixme:BusinessPhone", contact
.BusinessPhone
));
256 indexable
.AddProperty (Property
.New ("fixme:BusinessPhone2", contact
.BusinessPhone2
));
257 indexable
.AddProperty (Property
.New ("fixme:BusinessFax", contact
.BusinessFax
));
258 indexable
.AddProperty (Property
.New ("fixme:CallbackPhone", contact
.CallbackPhone
));
259 indexable
.AddProperty (Property
.New ("fixme:CarPhone", contact
.CarPhone
));
260 indexable
.AddProperty (Property
.New ("fixme:CompanyPhone", contact
.CompanyPhone
));
261 indexable
.AddProperty (Property
.New ("fixme:HomePhone", contact
.HomePhone
));
262 indexable
.AddProperty (Property
.New ("fixme:HomePhone2", contact
.HomePhone2
));
263 indexable
.AddProperty (Property
.New ("fixme:HomeFax", contact
.HomeFax
));
264 indexable
.AddProperty (Property
.New ("fixme:IsdnPhone", contact
.IsdnPhone
));
265 indexable
.AddProperty (Property
.New ("fixme:MobilePhone", contact
.MobilePhone
));
266 indexable
.AddProperty (Property
.New ("fixme:OtherPhone", contact
.OtherPhone
));
267 indexable
.AddProperty (Property
.New ("fixme:OtherFax", contact
.OtherFax
));
268 indexable
.AddProperty (Property
.New ("fixme:Pager", contact
.Pager
));
269 indexable
.AddProperty (Property
.New ("fixme:PrimaryPhone", contact
.PrimaryPhone
));
270 indexable
.AddProperty (Property
.New ("fixme:Radio", contact
.Radio
));
271 indexable
.AddProperty (Property
.New ("fixme:Telex", contact
.Telex
));
272 indexable
.AddProperty (Property
.NewUnsearched ("fixme:Tty", contact
.Tty
));
273 indexable
.AddProperty (Property
.NewKeyword ("fixme:Email1", contact
.Email1
));
274 indexable
.AddProperty (Property
.NewKeyword ("fixme:Email2", contact
.Email2
));
275 indexable
.AddProperty (Property
.NewKeyword ("fixme:Email3", contact
.Email3
));
276 indexable
.AddProperty (Property
.NewUnsearched ("fixme:Mailer", contact
.Mailer
));
277 indexable
.AddProperty (Property
.New ("fixme:Org", contact
.Org
));
278 indexable
.AddProperty (Property
.New ("fixme:OrgUnit", contact
.OrgUnit
));
279 indexable
.AddProperty (Property
.New ("fixme:Office", contact
.Office
));
280 indexable
.AddProperty (Property
.New ("fixme:Title", contact
.Title
));
281 indexable
.AddProperty (Property
.New ("fixme:Role", contact
.Role
));
282 indexable
.AddProperty (Property
.New ("fixme:Manager", contact
.Manager
));
283 indexable
.AddProperty (Property
.New ("fixme:Assistant", contact
.Assistant
));
284 indexable
.AddProperty (Property
.NewKeyword ("fixme:HomepageUrl", contact
.HomepageUrl
));
285 indexable
.AddProperty (Property
.NewKeyword ("fixme:BlogUrl", contact
.BlogUrl
));
286 indexable
.AddProperty (Property
.NewUnsearched ("fixme:Categories", contact
.Categories
));
287 indexable
.AddProperty (Property
.NewUnsearched ("fixme:Caluri", contact
.Caluri
));
288 indexable
.AddProperty (Property
.NewUnsearched ("fixme:Icscalendar", contact
.Icscalendar
));
289 indexable
.AddProperty (Property
.New ("fixme:Spouse", contact
.Spouse
));
290 indexable
.AddProperty (Property
.New ("fixme:Note", contact
.Note
));
292 Evolution
.ContactPhoto photo
= contact
.Photo
;
294 if (photo
.Data
!= null && photo
.Data
.Length
> 0) {
295 string photo_filename
= GetPhotoFilename (contact
.Id
);
296 Stream s
= new FileStream (photo_filename
, FileMode
.Create
, FileAccess
.Write
, FileShare
.ReadWrite
);
297 BinaryWriter w
= new BinaryWriter (s
);
298 w
.Write (photo
.Data
);
302 indexable
.AddProperty (Property
.NewUnsearched ("beagle:Photo", photo_filename
));
305 foreach (string im
in contact
.ImAim
)
306 indexable
.AddProperty (Property
.NewUnsearched ("fixme:ImAim", im
));
307 foreach (string im
in contact
.ImIcq
)
308 indexable
.AddProperty (Property
.NewUnsearched ("fixme:ImIcq", im
));
309 foreach (string im
in contact
.ImJabber
)
310 indexable
.AddProperty (Property
.NewUnsearched ("fixme:ImJabber", im
));
311 foreach (string im
in contact
.ImMsn
)
312 indexable
.AddProperty (Property
.NewUnsearched ("fixme:ImMsn", im
));
313 foreach (string im
in contact
.ImYahoo
)
314 indexable
.AddProperty (Property
.NewUnsearched ("fixme:ImYahoo", im
));
315 foreach (string im
in contact
.ImGroupwise
)
316 indexable
.AddProperty (Property
.NewUnsearched ("fixme:ImGroupwise", im
));
319 if (contact
.GivenName
!= null && contact
.GivenName
!= "")
320 name
= contact
.GivenName
;
321 if (contact
.FamilyName
!= null && contact
.FamilyName
!= "")
322 name
+= " " + contact
.FamilyName
;
324 indexable
.AddProperty (Property
.New ("fixme:Name", name
));
326 if (contact
.Email1
!= null)
327 indexable
.AddProperty (Property
.NewKeyword ("fixme:Email",