Skip to content

Commit 1d08de0

Browse files
committed
Implement a named properties map, capturing names of outlook's model, needed for vcard converter.
git-svn-id: https://ruby-msg.googlecode.com/svn/trunk@28 c30d66de-b626-0410-988f-81f6512a6d81
1 parent 26fbd6c commit 1d08de0

File tree

3 files changed

+165
-13
lines changed

3 files changed

+165
-13
lines changed

TODO

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,12 @@ Draws on msgconvert.pl, and tries to be a cleaner and more complete approach.
44

55
Highest priority
66
------------------------
7+
* certain parsing still not correct, especially small properties directory,
8+
things like bools, multiavlue shorts etc.
9+
* still a lot of cleaning up to do. removal of dead debug code, moving stuff
10+
into the right classes.
11+
* unit tests, to lock down the functionality i have so far
12+
713
* get some sort of working From: header
814
there doesn't appear to be a way to get an smtp address for the sender in an
915
internal mail. you need to query the exchanger server, using the sender_entryid

data/named_map.yaml

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
# this file provides for the mapping of the keys of named properties
2+
# to symbolic names (as opposed to mapitags.yaml, which is currently
3+
# in a different format, has a different source, and is only fixed
4+
# code properties)
5+
#
6+
# essentially the symbols are slightly munged versions of the names
7+
# given to these properties by CDO, or Outlook's object model.
8+
# it was parsed out of cdo10.htm, and neatened up a bit.
9+
#
10+
# interestingly, despite having separate guids, the codes are picked not to
11+
# clash. further the names themselves have only 3 clashes in all the below.
12+
{
13+
[0x8005, PSETID_Address]: file_under,
14+
[0x8017, PSETID_Address]: last_name_and_first_name,
15+
[0x8018, PSETID_Address]: company_and_full_name,
16+
[0x8019, PSETID_Address]: full_name_and_company,
17+
[0x801a, PSETID_Address]: home_address,
18+
[0x801b, PSETID_Address]: business_address,
19+
[0x801c, PSETID_Address]: other_address,
20+
[0x8022, PSETID_Address]: selected_address,
21+
[0x802b, PSETID_Address]: web_page,
22+
[0x802c, PSETID_Address]: yomi_first_name,
23+
[0x802d, PSETID_Address]: yomi_last_name,
24+
[0x802e, PSETID_Address]: yomi_company_name,
25+
[0x8030, PSETID_Address]: last_first_no_space,
26+
[0x8031, PSETID_Address]: last_first_space_only,
27+
[0x8032, PSETID_Address]: company_last_first_no_space,
28+
[0x8033, PSETID_Address]: company_last_first_space_only,
29+
[0x8034, PSETID_Address]: last_first_no_space_company,
30+
[0x8035, PSETID_Address]: last_first_space_only_company,
31+
[0x8036, PSETID_Address]: last_first_and_suffix,
32+
[0x8045, PSETID_Address]: business_address_street,
33+
[0x8046, PSETID_Address]: business_address_city,
34+
[0x8047, PSETID_Address]: business_address_state,
35+
[0x8048, PSETID_Address]: business_address_postal_code,
36+
[0x8049, PSETID_Address]: business_address_country,
37+
[0x804a, PSETID_Address]: business_address_post_office_box,
38+
[0x804f, PSETID_Address]: user_field1,
39+
[0x8050, PSETID_Address]: user_field2,
40+
[0x8051, PSETID_Address]: user_field3,
41+
[0x8052, PSETID_Address]: user_field4,
42+
[0x8062, PSETID_Address]: imaddress,
43+
[0x8082, PSETID_Address]: email_addr_type,
44+
[0x8083, PSETID_Address]: email_email_address,
45+
[0x8084, PSETID_Address]: email_original_display_name,
46+
[0x8085, PSETID_Address]: email_original_entry_id,
47+
[0x8092, PSETID_Address]: email2_addr_type,
48+
[0x8093, PSETID_Address]: email2_email_address,
49+
[0x8094, PSETID_Address]: email2_original_display_name,
50+
[0x8095, PSETID_Address]: email2_original_entry_id,
51+
[0x80a2, PSETID_Address]: email3_addr_type,
52+
[0x80a3, PSETID_Address]: email3_email_address,
53+
[0x80a4, PSETID_Address]: email3_original_display_name,
54+
[0x80a5, PSETID_Address]: email3_original_entry_id,
55+
[0x80d8, PSETID_Address]: internet_free_busy_address,
56+
[0x8101, PSETID_Task]: status,
57+
[0x8102, PSETID_Task]: percent_complete,
58+
[0x8103, PSETID_Task]: team_task,
59+
[0x8104, PSETID_Task]: start_date,
60+
[0x8105, PSETID_Task]: due_date,
61+
[0x8106, PSETID_Task]: duration,
62+
[0x810f, PSETID_Task]: date_completed,
63+
[0x8110, PSETID_Task]: actual_work,
64+
[0x8111, PSETID_Task]: total_work,
65+
[0x811c, PSETID_Task]: complete,
66+
[0x811f, PSETID_Task]: owner,
67+
[0x8126, PSETID_Task]: is_recurring,
68+
[0x8205, PSETID_Appointment]: busy_status,
69+
[0x8208, PSETID_Appointment]: location,
70+
[0x820d, PSETID_Appointment]: start_date,
71+
[0x820e, PSETID_Appointment]: end_date,
72+
[0x8213, PSETID_Appointment]: duration,
73+
[0x8214, PSETID_Appointment]: colors,
74+
[0x8216, PSETID_Appointment]: recurrence_state,
75+
[0x8218, PSETID_Appointment]: response_status,
76+
[0x8222, PSETID_Appointment]: reply_time,
77+
[0x8223, PSETID_Appointment]: is_recurring,
78+
[0x822e, PSETID_Appointment]: organizer,
79+
[0x8231, PSETID_Appointment]: recurrence_type,
80+
[0x8232, PSETID_Appointment]: recurrence_pattern,
81+
# also had CdoPR_FLAG_DUE_BY, when applied to messages. i don't currently
82+
# use message class specific names
83+
[0x8502, PSETID_Common]: reminder_time,
84+
[0x8503, PSETID_Common]: reminder_set,
85+
[0x8516, PSETID_Common]: common_start,
86+
[0x8517, PSETID_Common]: common_end,
87+
[0x851c, PSETID_Common]: reminder_override,
88+
[0x851e, PSETID_Common]: reminder_sound,
89+
[0x851f, PSETID_Common]: reminder_file,
90+
# this one only listed as CdoPR_FLAG_TEXT. maybe should be
91+
# reminder_text
92+
[0x8530, PSETID_Common]: flag_text,
93+
[0x8534, PSETID_Common]: mileage,
94+
[0x8535, PSETID_Common]: billing_information,
95+
[0x8539, PSETID_Common]: companies,
96+
[0x853a, PSETID_Common]: contact_names,
97+
# had CdoPR_FLAG_DUE_BY_NEXT for this one also
98+
[0x8560, PSETID_Common]: reminder_next_time,
99+
[0x8700, PSETID_Log]: entry,
100+
[0x8704, PSETID_Log]: start_date,
101+
[0x8705, PSETID_Log]: start_time,
102+
[0x8706, PSETID_Log]: start,
103+
[0x8707, PSETID_Log]: duration,
104+
[0x8708, PSETID_Log]: end,
105+
[0x870e, PSETID_Log]: doc_printed,
106+
[0x870f, PSETID_Log]: doc_saved,
107+
[0x8710, PSETID_Log]: doc_routed,
108+
[0x8711, PSETID_Log]: doc_posted,
109+
[0x8712, PSETID_Log]: entry_type,
110+
[0x8b00, PSETID_Note]: color,
111+
[0x8b02, PSETID_Note]: width,
112+
[0x8b03, PSETID_Note]: height,
113+
["Keywords", PS_PUBLIC_STRINGS]: categories
114+
}

lib/msg.rb

Lines changed: 45 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -207,7 +207,18 @@ def inspect
207207

208208
# --------
209209
# beginnings of conversion stuff
210-
210+
211+
def convert
212+
#
213+
# for now, multiplex between returning a Mime object,
214+
# a Vpim::Vcard object,
215+
# a Vpim::Vcalendar object
216+
#
217+
# all of which should support a common serialization,
218+
# to save the result to a file.
219+
#
220+
end
221+
211222
def body_to_mime
212223
# to create the body
213224
# should have some options about serializing rtf. and possibly options to check the rtf
@@ -242,6 +253,9 @@ def body_to_mime
242253
end
243254

244255
def to_mime
256+
# intended to be used for IPM.note, which is the email type. can use it for others if desired,
257+
# YMMV
258+
Log.warn "to_mime used on a #{props.message_class}" unless props.message_class == 'IPM.Note'
245259
# we always have a body
246260
mime = body = body_to_mime
247261

@@ -272,6 +286,9 @@ def to_mime
272286
mime
273287
end
274288

289+
def to_vcard
290+
end
291+
275292
# after looking at other MAPI property stores, such as tnef / pst, i might want to separate
276293
# the parsing from other stuff here, but for now its ok.
277294
class Properties
@@ -283,23 +300,28 @@ class Properties
283300
0x0102 => IDENTITY_PROC, # binary?
284301
}
285302

286-
MAPITAGS = open('data/mapitags.yaml') { |file| YAML.load file }
287-
288303
# these won't be strings for much longer.
304+
# maybe later, the Key#inspect could automatically show symbolic guid names if they
305+
# are part of this builtin list.
289306
# FIXME
290307
PS_MAPI = '{not-really-sure-what-this-should-say}'
291308
PS_PUBLIC_STRINGS = '{00020329-0000-0000-c000-000000000046}'
292309
# string properties in this namespace automatically get added to the internet headers
293310
PS_INTERNET_HEADERS = '{00020386-0000-0000-c000-000000000046}'
294-
# const GUID PS_PUBLIC_STRINGS = {0x00020329, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
295-
# const GUID PS_INTERNET_HEADERS = {0x00020386, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
296-
# theres are bunch of outlook onoes i think
297-
# const GUID PSETID_Appointment = {0x00062002, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
298-
# const GUID PSETID_Task = {0x00062003, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
299-
# const GUID PSETID_Address = {0x00062004, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
300-
# const GUID PSETID_Common = {0x00062008, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
301-
# const GUID PSETID_Log = {0x0006200A, 0x0000, 0x0000, {0xC0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46}};
311+
# theres are bunch of outlook ones i think
302312
# http://blogs.msdn.com/stephen_griffin/archive/2006/05/10/outlook-2007-beta-documentation-notification-based-indexing-support.aspx
313+
# IPM.Appointment
314+
PSETID_Appointment = '{00062002-0000-0000-c000-000000000046}'
315+
# IPM.Task
316+
PSETID_Task = '{00062003-0000-0000-c000-000000000046}'
317+
# used for IPM.Contact
318+
PSETID_Address = '{00062004-0000-0000-c000-000000000046}'
319+
PSETID_Common = '{00062008-0000-0000-c000-000000000046}'
320+
# didn't find a source for this name. it is for IPM.StickyNote
321+
PSETID_Note = '{0006200e-0000-0000-c000-000000000046}'
322+
# for IPM.Activity. also called the journal?
323+
PSETID_Log = '{0006200a-0000-0000-c000-000000000046}'
324+
303325
# Enumerable removed. not really needed
304326

305327
# access the underlying raw properties by code. no implicit type conversion
@@ -462,7 +484,7 @@ def parse_properties obj
462484
add_property key, Ole::Storage::OleDir.parse_time(*data[8..-1].unpack('L*'))
463485
else
464486
Log.warn "ignoring data in __properties section, encoding: #{encoding}"
465-
Log << data.unpack('H*').inspect
487+
Log << data.unpack('H*').inspect + "\n"
466488
end
467489
end
468490
end
@@ -601,10 +623,12 @@ def to_sym
601623
else
602624
# handle other guids here, like mapping names to outlook properties, based on the
603625
# outlook object model.
604-
code
626+
NAMED_MAP[self].to_sym rescue code
605627
end
606628
when String
607629
# return something like
630+
# note that named properties don't go through the map at the moment. so #categories
631+
# doesn't work yet
608632
code.downcase.to_sym
609633
end
610634
end
@@ -644,6 +668,14 @@ def inspect
644668
end
645669
end
646670
end
671+
672+
# YUCK moved here because we need Key
673+
# data files that provide for the code to symbolic name mapping
674+
# guids in named_map are really constant references to the above
675+
MAPITAGS = open('data/mapitags.yaml') { |file| YAML.load file }
676+
NAMED_MAP = Hash[*open('data/named_map.yaml') { |file| YAML.load file }.map do |key, value|
677+
[Key.new(key[0], const_get(key[1])), value]
678+
end.flatten]
647679
end
648680

649681
class Attachment

0 commit comments

Comments
 (0)