Merge lp:~igors/do-plugins/Thunderbird-fix into lp:do-plugins

Proposed by Igor Slepchin
Status: Merged
Merged at revision: 757
Proposed branch: lp:~igors/do-plugins/Thunderbird-fix
Merge into: lp:do-plugins
Diff against target: 367 lines (+213/-44)
3 files modified
Thunderbird/Makefile.am (+2/-1)
Thunderbird/Thunderbird.mdp (+1/-0)
Thunderbird/src/ThunderbirdContactItemSource.cs (+210/-43)
To merge this branch: bzr merge lp:~igors/do-plugins/Thunderbird-fix
Reviewer Review Type Date Requested Status
Chris Halse Rogers Needs Information
Review via email: mp+17102@code.launchpad.net
To post a comment you must log in.
Revision history for this message
Igor Slepchin (igors) wrote :

This fixes Thunderbird plugin. The original problem was described in https://bugs.launchpad.net/do-plugins/+bug/305778 and was fixed by the first commit on the branch.

The rest of the changes add the following:

 * Support for multiple Thunderbird emails on a single ContactItem (ContactDetailItems are introduced)
 * Importing emails from Thunderbird history in addition to Thunderbird address book
 * Using the most used email for main ContactItem (as determined by Thunderbird's PopularityIndex)

I know the plugin is marked as "Community" but people still seem to distribute it (e.g., Fedora does).

Revision history for this message
Chris Halse Rogers (raof) wrote :

Bah. Sometimes, I hate bzr.

This branch looks like it's got some valuable improvements, but I can't actually pull it because of a repository version mismatch! If you could hit the “upgrade” button on your branch, it should convert it to a 2a repository, which I'll be able to pull.

It looks good, with the exception of the EmailList class. I'm not sure why you don't just pass a Dictionary<string, uint> around directly? The only value of EmailList appears to be .Add, which is only called once, and looks like it could replace with TryGetValue?

review: Needs Information
Revision history for this message
Igor Slepchin (igors) wrote :

Hey Chris - I clicked "upgrade" half a dozen times now but I'm not sure it's changed anything (upgrade button is still available and the repo is still marked as RepositoryFormatKnitPack6RichRoot (bzr 1.9)). Please let me know if this still does not work for you and I'll try to do the merge to the trunk locally or just re-publish it as a new branch (I still have the branch sources that I seem to be able to work with).

As for EmailList class, the primary motivation for it was overwriting Add method so that I didn't have to check if the key was already present in the Dictionary whenever I needed to update "popularity" value.

Revision history for this message
Chris Halse Rogers (raof) wrote :

Bah. No, it hasn't upgraded, so I'm still unable to pull from it.

Running “bzr upgrade --2a lp:~igors/do-plugins/Thunderbird-fix” might upgrade it properly, or you could (hopefully) push a new branch.

Ah. Somehow I didn't notice all the .Adds when doing my cursory review. I still find the class a bit of a heavyweight solution, so I might play around with some other options. I don't expect you to need to do any work there, though.

Revision history for this message
Igor Slepchin (igors) wrote :

bzr upgrade apparently worked, thanks!

I also merged this to the current trunk and re-pushed to https://code.launchpad.net/~igors/do-plugins/Thunderbird-fix-redux just in case so please let me know if either of these works for you.

Revision history for this message
Chris Halse Rogers (raof) wrote :

Excellent. That works.

I'll perform a more thorough review and merge.

lp:~igors/do-plugins/Thunderbird-fix updated
687. By Igor

Strip leading/trailing spaces and quotes from display names so that
multiple Thunderbird contacts get merged better.

Revision history for this message
Igor Slepchin (igors) wrote :

While I have your attention: do you think you could commit the fix for bug 698078 while at it? Thunderbird plugin is fairly useless if the email action itself does not work :p

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file 'Thunderbird/Makefile.am'
2--- Thunderbird/Makefile.am 2009-01-15 06:27:03 +0000
3+++ Thunderbird/Makefile.am 2011-02-08 03:44:13 +0000
4@@ -11,4 +11,5 @@
5
6 REFERENCES = \
7 System \
8- $(DO_UNIVERSE_LIBS)
9+ $(DO_UNIVERSE_LIBS) \
10+ $(DO_PLATFORM_LIBS)
11
12=== modified file 'Thunderbird/Thunderbird.mdp'
13--- Thunderbird/Thunderbird.mdp 2009-06-23 00:52:54 +0000
14+++ Thunderbird/Thunderbird.mdp 2011-02-08 03:44:13 +0000
15@@ -22,5 +22,6 @@
16 <References>
17 <ProjectReference type="Gac" localcopy="True" refto="System, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089" />
18 <ProjectReference type="Gac" localcopy="True" refto="Do.Universe, Version=0.9.0.0, Culture=neutral" />
19+ <ProjectReference type="Gac" localcopy="True" refto="Do.Platform, Version=0.9.0.0, Culture=neutral" />
20 </References>
21 </Project>
22\ No newline at end of file
23
24=== modified file 'Thunderbird/src/ThunderbirdContactItemSource.cs'
25--- Thunderbird/src/ThunderbirdContactItemSource.cs 2009-05-29 09:39:41 +0000
26+++ Thunderbird/src/ThunderbirdContactItemSource.cs 2011-02-08 03:44:13 +0000
27@@ -17,14 +17,13 @@
28 // You should have received a copy of the GNU General Public License
29 // along with this program. If not, see <http://www.gnu.org/licenses/>.
30
31-//
32-
33 using System;
34 using System.IO;
35 using System.Collections;
36 using System.Collections.Generic;
37
38 using Do.Universe;
39+using Do.Platform;
40
41 using Beagle.Util;
42
43@@ -33,16 +32,107 @@
44
45 public class ThunderbirdContactItemSource : ItemSource
46 {
47-
48- const string BeginProfileName = "Path=";
49+
50+ class EmailContactDetail: Item, IContactDetailItem
51+ {
52+ readonly string detail, description;
53+ readonly ContactItem owner;
54+
55+ public EmailContactDetail (ContactItem owner, string detail)
56+ {
57+ this.owner = owner;
58+ this.detail = detail;
59+ description = string.IsNullOrEmpty (owner["name"])
60+ ? owner[detail]
61+ : owner["name"];
62+ }
63+
64+ public override string Name
65+ {
66+ get { return owner[detail]; }
67+ }
68+
69+ public override string Description
70+ {
71+ get { return description; }
72+ }
73+
74+ public override string Icon
75+ {
76+ get { return "thunderbird"; }
77+ }
78+
79+ public string Key
80+ {
81+ get { return detail; }
82+ }
83+
84+ public string Value
85+ {
86+ get { return owner[detail]; }
87+ }
88+ }
89+
90+ class EmailList
91+ {
92+ private Dictionary<string, uint> set;
93+
94+ public EmailList () { set = new Dictionary<string, uint> (); }
95+
96+ public void Add (string email, uint popularity)
97+ {
98+ if (!set.ContainsKey (email))
99+ {
100+ set.Add (email, popularity);
101+ }
102+ else
103+ {
104+ set[email] += popularity;
105+ }
106+ }
107+
108+ public bool Contains (string email)
109+ {
110+ return set.ContainsKey (email);
111+ }
112+
113+ public uint this [string email]
114+ {
115+ get { return set[email]; }
116+ }
117+
118+ public int Count {
119+ get { return set.Count; }
120+ }
121+
122+ public ICollection<string> Keys {
123+ get { return set.Keys; }
124+ }
125+ }
126+
127+ class ThunderbirdEmail
128+ {
129+ public readonly string email;
130+ public readonly uint popularity;
131+
132+ public ThunderbirdEmail (string email, uint popularity)
133+ {
134+ this.email = email;
135+ this.popularity = popularity;
136+ }
137+ }
138+
139+
140+ const string BeginProfileName = "Path=";
141 const string BeginDefaultProfile = "Name=default";
142-
143- List<Item> contacts;
144+ const string THUNDERBIRD_EMAIL = "email.thunderbird";
145+ static readonly char[] nameDelimiters = { ' ', '\'', '"' };
146+
147+ Dictionary<string, Item> contacts; // name => ContactItem
148
149 public ThunderbirdContactItemSource ()
150 {
151- contacts = new List<Item> ();
152- UpdateItems ();
153+ contacts = new Dictionary<string, Item> ();
154 }
155
156 public override IEnumerable<Type> SupportedItemTypes {
157@@ -50,7 +140,7 @@
158 return new Type[] {
159 typeof (ContactItem),
160 };
161- }
162+ }
163 }
164
165 public override string Name { get { return "Thunderbird Contacts"; } }
166@@ -62,73 +152,135 @@
167 try {
168 _UpdateItems ();
169 } catch (Exception e) {
170- Console.Error.WriteLine ("Cannot index Thunderbird contacts because a {0} was thrown: {1}", e.GetType (), e.Message);
171+ Log<ThunderbirdContactItemSource>.Error ("Cannot index Thunderbird contacts because a {0} was thrown: {1}", e.GetType (), e.Message);
172 return;
173 }
174 }
175
176 public override IEnumerable<Item> Items {
177- get { return contacts; }
178+ get { return contacts.Values; }
179 }
180
181 public override IEnumerable<Item> ChildrenOfItem (Item item)
182 {
183+ ContactItem contact = item as ContactItem;
184+
185+ foreach (string detail in contact.Details)
186+ {
187+ if (detail.StartsWith (THUNDERBIRD_EMAIL))
188+ {
189+ yield return new EmailContactDetail (contact, detail);
190+ }
191+ }
192 yield break;
193 }
194
195 void _UpdateItems ()
196 {
197- MorkDatabase database;
198-
199+ MorkDatabase abook, history;
200+ Dictionary<string, EmailList> emails = new Dictionary<string, EmailList> ();
201+
202+ abook = new MorkDatabase (GetThunderbirdAddressBookFilePath ());
203+ abook.Read ();
204+ abook.EnumNamespace = "ns:addrbk:db:row:scope:card:all";
205+
206+ history = new MorkDatabase (GetThunderbirdHistoryFilePath ());
207+ history.Read ();
208+ history.EnumNamespace = "ns:addrbk:db:row:scope:card:all";
209+
210+ addEmails (emails, history);
211+ addEmails (emails, abook);
212+
213 contacts.Clear ();
214- database = new MorkDatabase (GetThunderbirdAddressBookFilePath ());
215- database.Read ();
216- database.EnumNamespace = "ns:addrbk:db:row:scope:card:all";
217+ foreach (string name in emails.Keys)
218+ {
219+ CreateThunderbirdContactItem (name, emails[name]);
220+ }
221+ }
222
223- foreach (string id in database) {
224- Hashtable contact_row;
225- ContactItem contact;
226-
227- contact_row = database.Compile (id, database.EnumNamespace);
228- contact = CreateThunderbirdContactItem (contact_row);
229- if (contact != null)
230- contacts.Add (contact);
231+ void addEmails (Dictionary<string, EmailList> emails, MorkDatabase database)
232+ {
233+ foreach (string id in database)
234+ {
235+ Hashtable contact_row = database.Compile (id, database.EnumNamespace);
236+ AddThunderbirdEmail (emails, contact_row);
237 }
238 }
239
240- ContactItem CreateThunderbirdContactItem (Hashtable row) {
241- ContactItem contact;
242+ void AddThunderbirdEmail (Dictionary<string, EmailList> emails, Hashtable row)
243+ {
244 string name, email;
245-
246-// foreach (object o in row.Keys)
247-// Console.WriteLine ("\t{0} --> {1}", o, row[o]);
248+ uint popularity;
249
250 // I think this will detect deleted contacts... Hmm...
251 if (row["table"] == null || row["table"] as string == "C6")
252- return null;
253+ return;
254
255 // Name
256 name = row["DisplayName"] as string;
257+ if (name != null) {
258+ name = name.Trim (nameDelimiters);
259+ }
260 if (name == null || name == string.Empty)
261 name = string.Format ("{0} {1}", row["FirstName"], row["LastName"]);
262- contact = ContactItem.Create (name);
263
264 // Email
265- email = row["PrimaryEmail"] as string;
266- if (email != null && email != string.Empty)
267- contact["email"] = email;
268+ email = row["PrimaryEmail"] as string;
269+ string p = row["PopularityIndex"] as string;
270+ try {
271+ popularity = UInt32.Parse (p, System.Globalization.NumberStyles.HexNumber);
272+ }
273+ catch (Exception) {
274+ popularity = 0;
275+ }
276
277- return contact;
278- }
279-
280- string GetThunderbirdAddressBookFilePath ()
281+ if (name == null || name.Trim () == string.Empty)
282+ name = email;
283+
284+ if (string.IsNullOrEmpty (email))
285+ return;
286+
287+ if (!emails.ContainsKey (name))
288+ {
289+ emails[name] = new EmailList ();
290+ }
291+ emails[name].Add (email, popularity);
292+ }
293+
294+ void CreateThunderbirdContactItem (string name, EmailList emails)
295+ {
296+ int emailCount = emails.Count;
297+ ThunderbirdEmail[] sortedEmails = new ThunderbirdEmail[emailCount];
298+
299+ int i = 0;
300+ foreach (string key in emails.Keys)
301+ {
302+ sortedEmails[i] = new ThunderbirdEmail (key, emails[key]);
303+ i++;
304+ }
305+ Array.Sort (sortedEmails, (x, y) => (int) (y.popularity - x.popularity));
306+
307+ ContactItem contact = ContactItem.Create (name);
308+ for (i = 0; i < emailCount; i++)
309+ {
310+ string detail = THUNDERBIRD_EMAIL + "." + i;
311+ contact[detail] = sortedEmails[i].email;
312+ }
313+
314+ if (!contacts.ContainsKey (name.ToLower ()))
315+ {
316+ contacts.Add (name.ToLower (), contact);
317+ }
318+ }
319+
320+ string GetThunderbirdDefaultProfilePath ()
321 {
322 string home, path, profile;
323 StreamReader reader;
324
325 profile = null;
326 home = System.Environment.GetFolderPath (System.Environment.SpecialFolder.Personal);
327- path = System.IO.Path.Combine (home, ".mozilla-thunderbird/profiles.ini");
328+ path = System.IO.Path.Combine (home, ".thunderbird/profiles.ini");
329 try {
330 reader = System.IO.File.OpenText (path);
331 } catch {
332@@ -148,16 +300,31 @@
333 }
334 }
335 reader.Close ();
336-
337+ return profile;
338+ }
339+
340+ string GetThunderbirdFilePath (string filename)
341+ {
342+ string path, home, profile;
343+ home = System.Environment.GetFolderPath (System.Environment.SpecialFolder.Personal);
344+ profile = GetThunderbirdDefaultProfilePath ();
345 if (profile == null) {
346 return null;
347 }
348- path = System.IO.Path.Combine (home, ".mozilla-thunderbird");
349+ path = System.IO.Path.Combine (home, ".thunderbird");
350 path = System.IO.Path.Combine (path, profile);
351- path = System.IO.Path.Combine (path, "abook.mab");
352+ path = System.IO.Path.Combine (path, filename);
353 return path;
354-
355 }
356
357+ string GetThunderbirdHistoryFilePath ()
358+ {
359+ return GetThunderbirdFilePath ("history.mab");
360+ }
361+
362+ string GetThunderbirdAddressBookFilePath ()
363+ {
364+ return GetThunderbirdFilePath ("abook.mab");
365+ }
366 }
367 }

Subscribers

People subscribed via source and target branches