exchange-offline-address-book 0.0.1 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 55f6145921765406c06e60039b1e9a8ca0a22649
4
- data.tar.gz: 7c33e19b2ac23809b6790a053d38f7e981e797c1
3
+ metadata.gz: 36482b6cf832cab6b56979b4f9632c010b3fa645
4
+ data.tar.gz: ca6a463ea3983aa043505fbbc842ebf0f2b9174b
5
5
  SHA512:
6
- metadata.gz: c129660484fa8e0ec44b1d1d1099661e9c9ad10119f696040969c4b163e7b7c8005f299964fadfb5816466b52ce79bb029aa6e1e90906fbce439609ea74496aa
7
- data.tar.gz: 1f96026d119a43e392f9332dd3aeb31dcf00a35d0683505585507502647a95a7ffb65aa2c6f93983584b06eb5cfcdba1150c1d136a3fb453b330056b446b4ad5
6
+ metadata.gz: 3950eaaeaabcb130cfc2c632d242937279c52e42b735cd493e4d4df4e8cf0db8370e368f3ac2fb8541439f4d64ad5353f471735439f62c3d01f86aebfbad1f7f
7
+ data.tar.gz: 4a47c4aec5d94119ae97364103d0d515fd6c382145448c57c6d5b817c92568de7a9bc0f6800cc1302c045d2456bbe5caa5fc89fa767f8fdad3e97d139a20e9f9
data/.gitignore ADDED
@@ -0,0 +1,2 @@
1
+ .env
2
+ *.gem
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ #ruby=2.3
2
+ #ruby-gemset=oab
3
+
4
+ source 'https://rubygems.org'
5
+
6
+ # Specify your gem's dependencies in gemspec
7
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,54 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ exchange-offline-address-book (0.0.5)
5
+ autodiscover (~> 1)
6
+ curb (~> 0.9)
7
+ libmspack (~> 0.1)
8
+ nokogiri (~> 1.8)
9
+
10
+ GEM
11
+ remote: https://rubygems.org/
12
+ specs:
13
+ autodiscover (1.0.2)
14
+ httpclient
15
+ logging
16
+ nokogiri
17
+ nori
18
+ bump (0.5.3)
19
+ curb (0.9.3)
20
+ dotenv (2.2.1)
21
+ ffi (1.9.18)
22
+ ffi-compiler2 (2.0.0)
23
+ ffi (>= 1.0.0)
24
+ rake
25
+ gem-release (1.0.0)
26
+ httpclient (2.8.3)
27
+ libmspack (0.1.0)
28
+ ffi
29
+ ffi-compiler2 (>= 2.0.0)
30
+ little-plugger (1.1.4)
31
+ logging (2.2.2)
32
+ little-plugger (~> 1.1)
33
+ multi_json (~> 1.10)
34
+ mini_portile2 (2.2.0)
35
+ minitest (5.10.2)
36
+ multi_json (1.12.1)
37
+ nokogiri (1.8.0)
38
+ mini_portile2 (~> 2.2.0)
39
+ nori (2.6.0)
40
+ rake (12.0.0)
41
+
42
+ PLATFORMS
43
+ ruby
44
+
45
+ DEPENDENCIES
46
+ bump (~> 0.5)
47
+ bundler (~> 1.3)
48
+ dotenv (~> 2)
49
+ exchange-offline-address-book!
50
+ gem-release (~> 1)
51
+ minitest (~> 5)
52
+
53
+ BUNDLED WITH
54
+ 1.15.1
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rake/testtask'
4
+ require 'bump/tasks'
5
+ #Bump.tag_by_default = true
6
+
7
+ Rake::TestTask.new do |t|
8
+ t.libs << 'test'
9
+ end
10
+
11
+ desc 'Run tests'
12
+ task :default => :test
13
+
@@ -0,0 +1,32 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "exchange-offline-address-book/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "exchange-offline-address-book"
8
+ spec.version = OfflineAddressBook::VERSION
9
+ spec.authors = ["Emiliano Heyns"]
10
+ spec.email = ["Emiliano.Heyns@iris-advies.com"]
11
+ spec.description = %q{Get Exchange Offline Address Book}
12
+ spec.summary = %q{Get Exchange Offline Address Book}
13
+ spec.homepage = "http://github.io"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+ spec.required_ruby_version = '>= 2.3'
21
+
22
+ spec.add_development_dependency "bundler", "~> 1.3"
23
+ spec.add_development_dependency "dotenv", '~> 2'
24
+ spec.add_development_dependency "minitest", '~> 5'
25
+ spec.add_development_dependency "gem-release", '~> 1'
26
+ spec.add_development_dependency "bump", '~> 0.5'
27
+
28
+ spec.add_runtime_dependency 'nokogiri', '~> 1.8'
29
+ spec.add_runtime_dependency 'autodiscover', '~> 1'
30
+ spec.add_runtime_dependency 'libmspack', '~> 0.1'
31
+ spec.add_runtime_dependency 'curb', '~> 0.9'
32
+ end
@@ -0,0 +1,114 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'dotenv/load'
4
+
5
+ require 'autodiscover'
6
+ #require "autodiscover/debug"
7
+
8
+ require 'yaml'
9
+ require 'nokogiri'
10
+ require 'tempfile'
11
+ require 'json'
12
+ require 'ostruct'
13
+
14
+ require 'curb'
15
+ require_relative 'exchange-offline-address-book/parser'
16
+ require_relative 'exchange-offline-address-book/mspack'
17
+
18
+ class OfflineAddressBook
19
+ def initialize(email: nil, password: nil, username: nil, cachedir: nil, update: true)
20
+ @email = email
21
+ @username = username || email
22
+ @password = password
23
+ @cachedir = cachedir
24
+ @update = update
25
+ end
26
+
27
+ def download(file, target = nil)
28
+ client = Curl::Easy.new(baseurl + file)
29
+ client.http_auth_types = :ntlm
30
+ client.username = @username
31
+ client.password = @password
32
+ client.perform
33
+
34
+ return client.body_str if target.nil?
35
+
36
+ open(target, 'wb'){|f| f.write(client.body_str) }
37
+ end
38
+
39
+ def baseurl
40
+ @baseurl ||= begin
41
+ client = Autodiscover::Client.new(email: @email, password: @password, username: @username)
42
+ data = client.autodiscover(ignore_ssl_errors: true)
43
+ raise "No data" unless data
44
+ data.response['Account']['Protocol'].detect{|p| p['OABUrl'] && p['Type'] == 'EXPR'}['OABUrl']
45
+ end
46
+ end
47
+
48
+ def addressbook
49
+ @addressbook ||= begin
50
+ lzx = nil
51
+ if !@update & @cachedir
52
+ oab = Dir[File.join(@cachedir, '*.oab')][0]
53
+ lzx = File.basename(oab, File.extname(oab)) + '.lzx' if oab
54
+ end
55
+
56
+ lzx ||= Nokogiri::XML(download('oab.xml')).at('//Full').inner_text
57
+ oab = File.basename(lzx, File.extname(lzx)) + '.oab'
58
+
59
+ if @cachedir && File.file?(File.join(@cachedir, oab))
60
+ oab = File.join(@cachedir, oab)
61
+ else
62
+ if @cachedir
63
+ %w{*.oab *.lzx *.yml *.json}.each{|ext|
64
+ Dir[File.join(@cachedir, ext)].each{|f| File.unlink(f) }
65
+ }
66
+ download(lzx, File.join(@cachedir, lzx))
67
+ lzx = File.join(@cachedir, lzx)
68
+ @oab = File.join(@cachedir, oab)
69
+ else
70
+ _lzx = Tempfile.new('lzx')
71
+ download(lzx, _lzx.path)
72
+ lzx = _lzx.path
73
+ oab = Tempfile.new('oab').path
74
+ end
75
+
76
+ puts 'decompressing'
77
+ LibMsPack.oab_decompress(lzx, oab)
78
+ puts 'done'
79
+ end
80
+
81
+ oab
82
+ end
83
+ end
84
+
85
+ def cache
86
+ @cache ||= File.join(File.dirname(addressbook), File.basename(addressbook, File.extname(addressbook)) + '.json')
87
+ end
88
+
89
+ def header
90
+ @parsed ||= Parser.new(addressbook)
91
+ @parsed.header
92
+ end
93
+
94
+ def records
95
+ @records ||= begin
96
+ if File.file?(cache)
97
+ parsed = JSON.parse(open(cache).read).collect{|record| OpenStruct.new(record) }
98
+ else
99
+ @parsed ||= Parser.new(addressbook)
100
+ parsed = @parsed.records.collect{|record|
101
+ record.to_h.each_pair{|k, v|
102
+ record[k] = v[0] if v.length == 1
103
+ }
104
+ # no idea what's going on here
105
+ record.AddressBookObjectGuid = record.AddressBookObjectGuid.inspect if record.AddressBookObjectGuid
106
+ record
107
+ }
108
+ open(cache, 'w'){|f| f.write(JSON.pretty_generate(parsed.collect{|r| r.to_h}))} if @cachedir
109
+ end
110
+
111
+ parsed
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,18 @@
1
+ require 'libmspack'
2
+
3
+ module LibMsPack
4
+ attach_function :mspack_create_oab_decompressor, [ :pointer ], :pointer
5
+ attach_function :mspack_destroy_oab_decompressor, [ :pointer ], :void
6
+
7
+ class OABDecompressor < FFI::Struct
8
+ layout :decompress, callback([:pointer, :string, :string], :int),
9
+ :decompress_incremental, callback([:pointer, :string, :string, :string], :int)
10
+ end
11
+
12
+ def LibMsPack.oab_decompress(source, target)
13
+ dec = LibMsPack.mspack_create_oab_decompressor(nil)
14
+ msoab = LibMsPack::OABDecompressor.new(dec)
15
+ msoab[:decompress].call(dec, source, target)
16
+ LibMsPack.mspack_destroy_oab_decompressor(dec)
17
+ end
18
+ end
@@ -0,0 +1,741 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'ostruct'
4
+
5
+ MapiPropertyDataType = OpenStruct.new({
6
+ Unspecified: 0,
7
+ Null: 1,
8
+ Short: 2,
9
+ Long: 3,
10
+ Float: 4,
11
+ Double: 5,
12
+ Currency: 6,
13
+ ApplicationTime: 7,
14
+ Error: 10,
15
+ Boolean: 11,
16
+ Object: 13,
17
+ LongLong: 20,
18
+ AnsiString: 30,
19
+ UnicodeString: 31,
20
+ SystemTime: 64,
21
+ Clsid: 72,
22
+ Binary: 258,
23
+ Mv: 0x1000 # Multi-value (aka array) flag
24
+ })
25
+ MapiPropertyDataType.MvShort = MapiPropertyDataType.Short | MapiPropertyDataType.Mv
26
+ MapiPropertyDataType.MvLong = MapiPropertyDataType.Long | MapiPropertyDataType.Mv
27
+ MapiPropertyDataType.MvFloat = MapiPropertyDataType.Float | MapiPropertyDataType.Mv
28
+ MapiPropertyDataType.MvDouble = MapiPropertyDataType.Double | MapiPropertyDataType.Mv
29
+ MapiPropertyDataType.MvCurrency = MapiPropertyDataType.Currency | MapiPropertyDataType.Mv
30
+ MapiPropertyDataType.MvApplicationTime = MapiPropertyDataType.ApplicationTime | MapiPropertyDataType.Mv
31
+ MapiPropertyDataType.MvLongLong = MapiPropertyDataType.LongLong | MapiPropertyDataType.Mv
32
+ MapiPropertyDataType.MvAnsiString = MapiPropertyDataType.AnsiString | MapiPropertyDataType.Mv
33
+ MapiPropertyDataType.MvUnicodeString = MapiPropertyDataType.UnicodeString | MapiPropertyDataType.Mv
34
+ MapiPropertyDataType.MvSystemTime = MapiPropertyDataType.SystemTime | MapiPropertyDataType.Mv
35
+ MapiPropertyDataType.MvClsid = MapiPropertyDataType.Clsid | MapiPropertyDataType.Mv
36
+ MapiPropertyDataType.MvBinary = MapiPropertyDataType.Binary | MapiPropertyDataType.Mv
37
+ MapiPropertyDataType.to_h.each_pair{|k, v| MapiPropertyDataType[v.to_s.to_sym] = k }
38
+
39
+ MapiPropertyName = {
40
+ '0001' => :TemplateData,
41
+ '0002' => :AlternateRecipientAllowed,
42
+ '0004' => :AutoForwardComment,
43
+ # '0004' => :ScriptData,
44
+ '0005' => :AutoForwarded,
45
+ '000F' => :DeferredDeliveryTime,
46
+ '0015' => :ExpiryTime,
47
+ '0017' => :Importance,
48
+ '001A' => :MessageClass,
49
+ '0023' => :OriginatorDeliveryReportRequested,
50
+ '0025' => :ParentKey,
51
+ '0026' => :Priority,
52
+ '0029' => :ReadReceiptRequested,
53
+ '002B' => :RecipientReassignmentProhibited,
54
+ '002E' => :OriginalSensitivity,
55
+ '0030' => :ReplyTime,
56
+ '0031' => :ReportTag,
57
+ '0032' => :ReportTime,
58
+ '0036' => :Sensitivity,
59
+ '0037' => :Subject,
60
+ '0039' => :ClientSubmitTime,
61
+ '003A' => :ReportName,
62
+ '003B' => :SentRepresentingSearchKey,
63
+ '003D' => :SubjectPrefix,
64
+ '003F' => :ReceivedByEntryId,
65
+ '0040' => :ReceivedByName,
66
+ '0041' => :SentRepresentingEntryId,
67
+ '0042' => :SentRepresentingName,
68
+ '0043' => :ReceivedRepresentingEntryId,
69
+ '0044' => :ReceivedRepresentingName,
70
+ '0045' => :ReportEntryId,
71
+ '0046' => :ReadReceiptEntryId,
72
+ '0047' => :MessageSubmissionId,
73
+ '0048' => :ProviderSubmitTime,
74
+ '0049' => :OriginalSubject,
75
+ '004B' => :OriginalMessageClass,
76
+ '004C' => :OriginalAuthorEntryId,
77
+ '004D' => :OriginalAuthorName,
78
+ '004E' => :OriginalSubmitTime,
79
+ '004F' => :ReplyRecipientEntries,
80
+ '0050' => :ReplyRecipientNames,
81
+ '0051' => :ReceivedBySearchKey,
82
+ '0052' => :ReceivedRepresentingSearchKey,
83
+ '0053' => :ReadReceiptSearchKey,
84
+ '0054' => :ReportSearchKey,
85
+ '0055' => :OriginalDeliveryTime,
86
+ '0057' => :MessageToMe,
87
+ '0058' => :MessageCcMe,
88
+ '0059' => :MessageRecipientMe,
89
+ '005A' => :OriginalSenderName,
90
+ '005B' => :OriginalSenderEntryId,
91
+ '005C' => :OriginalSenderSearchKey,
92
+ '005D' => :OriginalSentRepresentingName,
93
+ '005E' => :OriginalSentRepresentingEntryId,
94
+ '005F' => :OriginalSentRepresentingSearchKey,
95
+ '0060' => :StartDate,
96
+ '0061' => :EndDate,
97
+ '0062' => :OwnerAppointmentId,
98
+ '0063' => :ResponseRequested,
99
+ '0064' => :SentRepresentingAddressType,
100
+ '0065' => :SentRepresentingEmailAddress,
101
+ '0066' => :OriginalSenderAddressType,
102
+ '0067' => :OriginalSenderEmailAddress,
103
+ '0068' => :OriginalSentRepresentingAddressType,
104
+ '0069' => :OriginalSentRepresentingEmailAddress,
105
+ '0070' => :ConversationTopic,
106
+ '0071' => :ConversationIndex,
107
+ '0072' => :OriginalDisplayBcc,
108
+ '0073' => :OriginalDisplayCc,
109
+ '0074' => :OriginalDisplayTo,
110
+ '0075' => :ReceivedByAddressType,
111
+ '0076' => :ReceivedByEmailAddress,
112
+ '0077' => :ReceivedRepresentingAddressType,
113
+ '0078' => :ReceivedRepresentingEmailAddress,
114
+ '007D' => :TransportMessageHeaders,
115
+ '007F' => :TnefCorrelationKey,
116
+ '0080' => :ReportDisposition,
117
+ '0081' => :ReportDispositionMode,
118
+ '0807' => :AddressBookRoomCapacity,
119
+ '0809' => :AddressBookRoomDescription,
120
+ '0C06' => :NonReceiptNotificationRequested,
121
+ '0C08' => :OriginatorNonDeliveryReportRequested,
122
+ '0C15' => :RecipientType,
123
+ '0C17' => :ReplyRequested,
124
+ '0C19' => :SenderEntryId,
125
+ '0C1A' => :SenderName,
126
+ '0C1D' => :SenderSearchKey,
127
+ '0C1E' => :SenderAddressType,
128
+ '0C1F' => :SenderEmailAddress,
129
+ '0E01' => :DeleteAfterSubmit,
130
+ '0E02' => :DisplayBcc,
131
+ '0E03' => :DisplayCc,
132
+ '0E04' => :DisplayTo,
133
+ '0E06' => :MessageDeliveryTime,
134
+ '0E07' => :MessageFlags,
135
+ '0E08' => :MessageSize,
136
+ # '0E08' => :MessageSizeExtended,
137
+ '0E09' => :ParentEntryId,
138
+ '0E0F' => :Responsibility,
139
+ '0E12' => :MessageRecipients,
140
+ '0E13' => :MessageAttachments,
141
+ '0E17' => :MessageStatus,
142
+ '0E1B' => :HasAttachments,
143
+ '0E1D' => :NormalizedSubject,
144
+ '0E1F' => :RtfInSync,
145
+ '0E20' => :AttachSize,
146
+ '0E21' => :AttachNumber,
147
+ '0E23' => :InternetArticleNumber,
148
+ '0E28' => :PrimarySendAccount,
149
+ '0E29' => :NextSendAcct,
150
+ '0E2B' => :ToDoItemFlags,
151
+ '0E2C' => :SwappedToDoStore,
152
+ '0E2D' => :SwappedToDoData,
153
+ '0E69' => :Read,
154
+ '0E6A' => :SecurityDescriptorAsXml,
155
+ '0E79' => :TrustSender,
156
+ '0E84' => :ExchangeNTSecurityDescriptor,
157
+ '0E99' => :ExtendedRuleMessageActions,
158
+ '0E9A' => :ExtendedRuleMessageCondition,
159
+ '0E9B' => :ExtendedRuleSizeLimit,
160
+ '0FF4' => :Access,
161
+ '0FF5' => :RowType,
162
+ '0FF6' => :InstanceKey,
163
+ '0FF7' => :AccessLevel,
164
+ '0FF8' => :MappingSignature,
165
+ '0FF9' => :RecordKey,
166
+ '0FFB' => :StoreEntryId,
167
+ '0FFE' => :ObjectType,
168
+ '0FFF' => :EntryId,
169
+ '1000' => :Body,
170
+ '1001' => :ReportText,
171
+ '1009' => :RtfCompressed,
172
+ '1013' => :BodyHtml,
173
+ # '1013' => :Html,
174
+ '1014' => :BodyContentLocation,
175
+ '1015' => :BodyContentId,
176
+ '1016' => :NativeBody,
177
+ '1035' => :InternetMessageId,
178
+ '1039' => :InternetReferences,
179
+ '1042' => :InReplyToId,
180
+ '1043' => :ListHelp,
181
+ '1044' => :ListSubscribe,
182
+ '1045' => :ListUnsubscribe,
183
+ '1046' => :OriginalMessageId,
184
+ '1080' => :IconIndex,
185
+ '1081' => :LastVerbExecuted,
186
+ '1082' => :LastVerbExecutionTime,
187
+ '1090' => :FlagStatus,
188
+ '1091' => :FlagCompleteTime,
189
+ '1095' => :FollowupIcon,
190
+ '1096' => :BlockStatus,
191
+ '10C3' => :ICalendarStartTime,
192
+ '10C4' => :ICalendarEndTime,
193
+ '10C5' => :CdoRecurrenceid,
194
+ '10CA' => :ICalendarReminderNextTime,
195
+ '10F3' => :UrlCompName,
196
+ '10F4' => :AttributeHidden,
197
+ '10F6' => :AttributeReadOnly,
198
+ '3000' => :Rowid,
199
+ '3001' => :DisplayName,
200
+ '3002' => :AddressType,
201
+ '3003' => :EmailAddress,
202
+ '3004' => :Comment,
203
+ '3005' => :Depth,
204
+ '3007' => :CreationTime,
205
+ '3008' => :LastModificationTime,
206
+ '300B' => :SearchKey,
207
+ '3010' => :TargetEntryId,
208
+ '3013' => :ConversationId,
209
+ '3016' => :ConversationIndexTracking,
210
+ '3018' => :ArchiveTag,
211
+ '3019' => :PolicyTag,
212
+ '301A' => :RetentionPeriod,
213
+ '301B' => :StartDateEtc,
214
+ '301C' => :RetentionDate,
215
+ '301D' => :RetentionFlags,
216
+ '301E' => :ArchivePeriod,
217
+ '301F' => :ArchiveDate,
218
+ '340D' => :StoreSupportMask,
219
+ '340E' => :StoreState,
220
+ '3600' => :ContainerFlags,
221
+ '3601' => :FolderType,
222
+ '3602' => :ContentCount,
223
+ '3603' => :ContentUnreadCount,
224
+ '3609' => :Selectable,
225
+ '360A' => :Subfolders,
226
+ '360C' => :Anr,
227
+ '360E' => :ContainerHierarchy,
228
+ '360F' => :ContainerContents,
229
+ '3610' => :FolderAssociatedContents,
230
+ '3613' => :ContainerClass,
231
+ '36D0' => :IpmAppointmentEntryId,
232
+ '36D1' => :IpmContactEntryId,
233
+ '36D2' => :IpmJournalEntryId,
234
+ '36D3' => :IpmNoteEntryId,
235
+ '36D4' => :IpmTaskEntryId,
236
+ '36D5' => :RemindersOnlineEntryId,
237
+ '36D7' => :IpmDraftsEntryId,
238
+ '36D8' => :AdditionalRenEntryIds,
239
+ '36D9' => :AdditionalRenEntryIdsEx,
240
+ '36DA' => :ExtendedFolderFlags,
241
+ '36E2' => :OrdinalMost,
242
+ '36E4' => :FreeBusyEntryIds,
243
+ '36E5' => :DefaultPostMessageClass,
244
+ '3701' => :AttachDataObject,
245
+ # '3701' => :AttachDataBinary,
246
+ '3702' => :AttachEncoding,
247
+ '3703' => :AttachExtension,
248
+ '3704' => :AttachFilename,
249
+ '3705' => :AttachMethod,
250
+ '3707' => :AttachLongFilename,
251
+ '3708' => :AttachPathname,
252
+ '3709' => :AttachRendering,
253
+ '370A' => :AttachTag,
254
+ '370B' => :RenderingPosition,
255
+ '370C' => :AttachTransportName,
256
+ '370D' => :AttachLongPathname,
257
+ '370E' => :AttachMimeTag,
258
+ '370F' => :AttachAdditionalInformation,
259
+ '3711' => :AttachContentBase,
260
+ '3712' => :CID,
261
+ '3713' => :AttachContentLocation,
262
+ '3714' => :AttachFlags,
263
+ '3719' => :AttachPayloadProviderGuidString,
264
+ '371A' => :AttachPayloadClass,
265
+ '371B' => :TextAttachmentCharset,
266
+ '3900' => :DisplayType,
267
+ '3902' => :Templateid,
268
+ '3905' => :DisplayTypeEx,
269
+ '39FE' => :SmtpAddress,
270
+ '39FF' => :AddressBookDisplayNamePrintable,
271
+ '3A00' => :Account,
272
+ '3A02' => :CallbackTelephoneNumber,
273
+ '3A05' => :Generation,
274
+ '3A06' => :GivenName,
275
+ '3A07' => :GovernmentIdNumber,
276
+ '3A08' => :BusinessTelephoneNumber,
277
+ '3A09' => :HomeTelephoneNumber,
278
+ '3A0A' => :Initials,
279
+ '3A0B' => :Keyword,
280
+ '3A0C' => :Language,
281
+ '3A0D' => :Location,
282
+ '3A0F' => :MessageHandlingSystemCommonName,
283
+ '3A10' => :OrganizationalIdNumber,
284
+ '3A11' => :Surname,
285
+ '3A12' => :OriginalEntryId,
286
+ '3A15' => :PostalAddress,
287
+ '3A16' => :CompanyName,
288
+ '3A17' => :Title,
289
+ '3A18' => :DepartmentName,
290
+ '3A19' => :OfficeLocation,
291
+ '3A1A' => :PrimaryTelephoneNumber,
292
+ '3A1B' => :Business2TelephoneNumber,
293
+ # '3A1B' => :Business2TelephoneNumbers,
294
+ '3A1C' => :MobileTelephoneNumber,
295
+ '3A1D' => :RadioTelephoneNumber,
296
+ '3A1E' => :CarTelephoneNumber,
297
+ '3A1F' => :OtherTelephoneNumber,
298
+ '3A20' => :TransmittableDisplayName,
299
+ '3A21' => :PagerTelephoneNumber,
300
+ '3A22' => :UserCertificate,
301
+ '3A23' => :PrimaryFaxNumber,
302
+ '3A24' => :BusinessFaxNumber,
303
+ '3A25' => :HomeFaxNumber,
304
+ '3A26' => :Country,
305
+ '3A27' => :Locality,
306
+ '3A28' => :StateOrProvince,
307
+ '3A29' => :StreetAddress,
308
+ '3A2A' => :PostalCode,
309
+ '3A2B' => :PostOfficeBox,
310
+ '3A2C' => :TelexNumber,
311
+ '3A2D' => :IsdnNumber,
312
+ '3A2E' => :AssistantTelephoneNumber,
313
+ '3A2F' => :Home2TelephoneNumber,
314
+ # '3A2F' => :Home2TelephoneNumbers,
315
+ '3A30' => :Assistant,
316
+ '3A40' => :SendRichInfo,
317
+ '3A41' => :WeddingAnniversary,
318
+ '3A42' => :Birthday,
319
+ '3A43' => :Hobbies,
320
+ '3A44' => :MiddleName,
321
+ '3A45' => :DisplayNamePrefix,
322
+ '3A46' => :Profession,
323
+ '3A47' => :ReferredByName,
324
+ '3A48' => :SpouseName,
325
+ '3A49' => :ComputerNetworkName,
326
+ '3A4A' => :CustomerId,
327
+ '3A4B' => :TelecommunicationsDeviceForDeafTelephoneNumber,
328
+ '3A4C' => :FtpSite,
329
+ '3A4D' => :Gender,
330
+ '3A4E' => :ManagerName,
331
+ '3A4F' => :Nickname,
332
+ '3A50' => :PersonalHomePage,
333
+ '3A51' => :BusinessHomePage,
334
+ '3A57' => :CompanyMainTelephoneNumber,
335
+ '3A58' => :ChildrensNames,
336
+ '3A59' => :HomeAddressCity,
337
+ '3A5A' => :HomeAddressCountry,
338
+ '3A5B' => :HomeAddressPostalCode,
339
+ '3A5C' => :HomeAddressStateOrProvince,
340
+ '3A5D' => :HomeAddressStreet,
341
+ '3A5E' => :HomeAddressPostOfficeBox,
342
+ '3A5F' => :OtherAddressCity,
343
+ '3A60' => :OtherAddressCountry,
344
+ '3A61' => :OtherAddressPostalCode,
345
+ '3A62' => :OtherAddressStateOrProvince,
346
+ '3A63' => :OtherAddressStreet,
347
+ '3A64' => :OtherAddressPostOfficeBox,
348
+ '3A70' => :UserX509Certificate,
349
+ '3A71' => :SendInternetEncoding,
350
+ '3F08' => :InitialDetailsPane,
351
+ '3F20' => :TemporaryDefaultDocument,
352
+ '3FDE' => :InternetCodepage,
353
+ '3FDF' => :AutoResponseSuppress,
354
+ '3FE0' => :AccessControlListData,
355
+ '3FE3' => :DelegatedByRule,
356
+ '3FE7' => :ResolveMethod,
357
+ '3FEA' => :HasDeferredActionMessages,
358
+ '3FEB' => :DeferredSendNumber,
359
+ '3FEC' => :DeferredSendUnits,
360
+ '3FED' => :ExpiryNumber,
361
+ '3FEE' => :ExpiryUnits,
362
+ '3FEF' => :DeferredSendTime,
363
+ '3FF0' => :ConflictEntryId,
364
+ '3FF1' => :MessageLocaleId,
365
+ '3FF8' => :CreatorName,
366
+ '3FF9' => :CreatorEntryId,
367
+ '3FFA' => :LastModifierName,
368
+ '3FFB' => :LastModifierEntryId,
369
+ '3FFD' => :MessageCodepage,
370
+ '401A' => :SentRepresentingFlags,
371
+ '4029' => :ReadReceiptAddressType,
372
+ '402A' => :ReadReceiptEmailAddress,
373
+ '402B' => :ReadReceiptName,
374
+ '4076' => :ContentFilterSpamConfidenceLevel,
375
+ '4079' => :SenderIdStatus,
376
+ '4083' => :PurportedSenderDomain,
377
+ '5902' => :InternetMailOverrideFormat,
378
+ '5909' => :MessageEditorFormat,
379
+ '5D01' => :SenderSmtpAddress,
380
+ '5FDE' => :RecipientResourceState,
381
+ '5FDF' => :RecipientOrder,
382
+ '5FE1' => :RecipientProposed,
383
+ '5FE3' => :RecipientProposedStartTime,
384
+ '5FE4' => :RecipientProposedEndTime,
385
+ '5FF6' => :RecipientDisplayName,
386
+ '5FF7' => :RecipientEntryId,
387
+ '5FFB' => :RecipientTrackStatusTime,
388
+ '5FFD' => :RecipientFlags,
389
+ '5FFF' => :RecipientTrackStatus,
390
+ '6100' => :JunkIncludeContacts,
391
+ '6101' => :JunkThreshold,
392
+ '6102' => :JunkPermanentlyDelete,
393
+ '6103' => :JunkAddRecipientsToSafeSendersList,
394
+ '6107' => :JunkPhishingEnableLinks,
395
+ '64F0' => :MimeSkeleton,
396
+ '65C2' => :ReplyTemplateId,
397
+ '65E0' => :SourceKey,
398
+ '65E1' => :ParentSourceKey,
399
+ '65E2' => :ChangeKey,
400
+ '65E3' => :PredecessorChangeList,
401
+ '65E9' => :RuleMessageState,
402
+ '65EA' => :RuleMessageUserFlags,
403
+ '65EB' => :RuleMessageProvider,
404
+ '65EC' => :RuleMessageName,
405
+ '65ED' => :RuleMessageLevel,
406
+ '65EE' => :RuleMessageProviderData,
407
+ '65F3' => :RuleMessageSequence,
408
+ '6619' => :UserEntryId,
409
+ '661B' => :MailboxOwnerEntryId,
410
+ '661C' => :MailboxOwnerName,
411
+ '661D' => :OutOfOfficeState,
412
+ '6622' => :ScheduleFreeBusy,
413
+ '6639' => :Rights,
414
+ '663A' => :HasRules,
415
+ '663B' => :AddressBookEntryId,
416
+ '663E' => :HierarchyChangeNumber,
417
+ '6645' => :ClientActions,
418
+ '6646' => :DamOriginalEntryId,
419
+ '6647' => :DamBackPatched,
420
+ '6648' => :RuleError,
421
+ '6649' => :RuleActionType,
422
+ '664A' => :HasNamedProperties,
423
+ '6650' => :RuleActionNumber,
424
+ '6651' => :RuleFolderEntryId,
425
+ '666A' => :ProhibitReceiveQuota,
426
+ '666C' => :InConflict,
427
+ '666D' => :MaximumSubmitMessageSize,
428
+ '666E' => :ProhibitSendQuota,
429
+ '6671' => :MemberId,
430
+ '6672' => :MemberName,
431
+ '6673' => :MemberRights,
432
+ '6674' => :RuleId,
433
+ '6675' => :RuleIds,
434
+ '6676' => :RuleSequence,
435
+ '6677' => :RuleState,
436
+ '6678' => :RuleUserFlags,
437
+ '6679' => :RuleCondition,
438
+ '6680' => :RuleActions,
439
+ '6681' => :RuleProvider,
440
+ '6682' => :RuleName,
441
+ '6683' => :RuleLevel,
442
+ '6684' => :RuleProviderData,
443
+ '668F' => :DeletedOn,
444
+ '66A1' => :LocaleId,
445
+ '66B3' => :NormalMessageSize,
446
+ '66C3' => :CodePageId,
447
+ '6704' => :AddressBookManageDistributionList,
448
+ '6705' => :SortLocaleId,
449
+ '6707' => :UrlName,
450
+ '6708' => :Subfolder,
451
+ '6709' => :LocalCommitTime,
452
+ '670A' => :LocalCommitTimeMax,
453
+ '670B' => :DeletedCountTotal,
454
+ '670E' => :FlatUrlName,
455
+ '671C' => :PublicFolderAdministrativeDescription,
456
+ '671D' => :PublicFolderProxy,
457
+ '6740' => :SentMailSvrEID,
458
+ '6741' => :DeferredActionMessageOriginalEntryId,
459
+ '6748' => :FolderId,
460
+ '6749' => :ParentFolderId,
461
+ '674A' => :Mid,
462
+ '674D' => :InstID,
463
+ '674E' => :InstanceNum,
464
+ '674F' => :AddressBookMessageId,
465
+ '67A4' => :ChangeNumber,
466
+ '67AA' => :Associated,
467
+ '6800' => :OfflineAddressBookName,
468
+ '6801' => :OfflineAddressBookSequence,
469
+ # '6801' => :VoiceMessageDuration,
470
+ '6802' => :OfflineAddressBookContainerGuid,
471
+ # '6802' => :SenderTelephoneNumber,
472
+ # '6802' => :RwRulesStream,
473
+ '6803' => :OfflineAddressBookMessageClass,
474
+ # '6803' => :VoiceMessageSenderName,
475
+ '6804' => :FaxNumberOfPages,
476
+ # '6804' => :OfflineAddressBookDistinguishedName,
477
+ '6805' => :VoiceMessageAttachmentOrder,
478
+ # '6805' => :OfflineAddressBookTruncatedProperties,
479
+ '6806' => :CallId,
480
+ '6834' => :SearchFolderLastUsed,
481
+ '683A' => :SearchFolderExpiration,
482
+ '6841' => :ScheduleInfoResourceType,
483
+ # '6841' => :SearchFolderTemplateId,
484
+ '6842' => :ScheduleInfoDelegatorWantsCopy,
485
+ # '6842' => :SearchFolderId,
486
+ # '6842' => :WlinkGroupHeaderID,
487
+ '6843' => :ScheduleInfoDontMailDelegates,
488
+ '6844' => :SearchFolderRecreateInfo,
489
+ # '6844' => :ScheduleInfoDelegateNames,
490
+ '6845' => :SearchFolderDefinition,
491
+ # '6845' => :ScheduleInfoDelegateEntryIds,
492
+ '6846' => :SearchFolderStorageType,
493
+ # '6846' => :GatewayNeedsToRefresh,
494
+ '6847' => :FreeBusyPublishStart,
495
+ # '6847' => :SearchFolderTag,
496
+ # '6847' => :WlinkSaveStamp,
497
+ '6848' => :FreeBusyPublishEnd,
498
+ # '6848' => :SearchFolderEfpFlags,
499
+ '6849' => :WlinkType,
500
+ # '6849' => :FreeBusyMessageEmailAddress,
501
+ '684A' => :WlinkFlags,
502
+ # '684A' => :ScheduleInfoDelegateNamesW,
503
+ '684B' => :ScheduleInfoDelegatorWantsInfo,
504
+ # '684B' => :WlinkOrdinal,
505
+ '684C' => :WlinkEntryId,
506
+ '684D' => :WlinkRecordKey,
507
+ '684E' => :WlinkStoreEntryId,
508
+ '684F' => :WlinkFolderType,
509
+ # '684F' => :ScheduleInfoMonthsMerged,
510
+ '6850' => :WlinkGroupClsid,
511
+ # '6850' => :ScheduleInfoFreeBusyMerged,
512
+ '6851' => :WlinkGroupName,
513
+ # '6851' => :ScheduleInfoMonthsTentative,
514
+ '6852' => :WlinkSection,
515
+ # '6852' => :ScheduleInfoFreeBusyTentative,
516
+ '6853' => :WlinkCalendarColor,
517
+ # '6853' => :ScheduleInfoMonthsBusy,
518
+ '6854' => :WlinkAddressBookEID,
519
+ # '6854' => :ScheduleInfoFreeBusyBusy,
520
+ '6855' => :ScheduleInfoMonthsAway,
521
+ '6856' => :ScheduleInfoFreeBusyAway,
522
+ '6868' => :FreeBusyRangeTimestamp,
523
+ '6869' => :FreeBusyCountMonths,
524
+ '686A' => :ScheduleInfoAppointmentTombstone,
525
+ '686B' => :DelegateFlags,
526
+ '686C' => :ScheduleInfoFreeBusy,
527
+ '686D' => :ScheduleInfoAutoAcceptAppointments,
528
+ '686F' => :ScheduleInfoDisallowOverlappingAppts,
529
+ '6890' => :WlinkClientID,
530
+ '6891' => :WlinkAddressBookStoreEID,
531
+ '6892' => :WlinkROGroupType,
532
+ '7001' => :ViewDescriptorBinary,
533
+ '7002' => :ViewDescriptorStrings,
534
+ '7006' => :ViewDescriptorName,
535
+ '7007' => :ViewDescriptorVersion,
536
+ '7C06' => :RoamingDatatypes,
537
+ '7C07' => :RoamingDictionary,
538
+ '7C08' => :RoamingXmlStream,
539
+ '7C24' => :OscSyncEnabled,
540
+ '7D01' => :Processed,
541
+ '7FF9' => :ExceptionReplaceTime,
542
+ '7FFA' => :AttachmentLinkId,
543
+ '7FFB' => :ExceptionStartTime,
544
+ '7FFC' => :ExceptionEndTime,
545
+ '7FFD' => :AttachmentFlags,
546
+ '7FFE' => :AttachmentHidden,
547
+ '7FFF' => :AttachmentContactPhoto,
548
+ '8004' => :AddressBookFolderPathname,
549
+ '8005' => :AddressBookManager,
550
+ # '8005' => :AddressBookManagerDistinguishedName,
551
+ '8006' => :AddressBookHomeMessageDatabase,
552
+ '8008' => :AddressBookIsMemberOfDistributionList,
553
+ '8009' => :AddressBookMember,
554
+ '800C' => :AddressBookOwner,
555
+ '800E' => :AddressBookReports,
556
+ '800F' => :AddressBookProxyAddresses,
557
+ '8011' => :AddressBookTargetAddress,
558
+ '8015' => :AddressBookPublicDelegates,
559
+ '8024' => :AddressBookOwnerBackLink,
560
+ '802D' => :AddressBookExtensionAttribute1,
561
+ '802E' => :AddressBookExtensionAttribute2,
562
+ '802F' => :AddressBookExtensionAttribute3,
563
+ '8030' => :AddressBookExtensionAttribute4,
564
+ '8031' => :AddressBookExtensionAttribute5,
565
+ '8032' => :AddressBookExtensionAttribute6,
566
+ '8033' => :AddressBookExtensionAttribute7,
567
+ '8034' => :AddressBookExtensionAttribute8,
568
+ '8035' => :AddressBookExtensionAttribute9,
569
+ '8036' => :AddressBookExtensionAttribute10,
570
+ '803C' => :AddressBookObjectDistinguishedName,
571
+ '806A' => :AddressBookDeliveryContentLength,
572
+ '8073' => :AddressBookDistributionListMemberSubmitAccepted,
573
+ '8170' => :AddressBookNetworkAddress,
574
+ '8C57' => :AddressBookExtensionAttribute11,
575
+ '8C58' => :AddressBookExtensionAttribute12,
576
+ '8C59' => :AddressBookExtensionAttribute13,
577
+ '8C60' => :AddressBookExtensionAttribute14,
578
+ '8C61' => :AddressBookExtensionAttribute15,
579
+ '8C6A' => :AddressBookX509Certificate,
580
+ '8C6D' => :AddressBookObjectGuid,
581
+ '8C8E' => :AddressBookPhoneticGivenName,
582
+ '8C8F' => :AddressBookPhoneticSurname,
583
+ '8C90' => :AddressBookPhoneticDepartmentName,
584
+ '8C91' => :AddressBookPhoneticCompanyName,
585
+ '8C92' => :AddressBookPhoneticDisplayName,
586
+ '8C93' => :AddressBookDisplayTypeExtended,
587
+ '8C94' => :AddressBookHierarchicalShowInDepartments,
588
+ '8C96' => :AddressBookRoomContainers,
589
+ '8C97' => :AddressBookHierarchicalDepartmentMembers,
590
+ '8C98' => :AddressBookHierarchicalRootDepartment,
591
+ '8C99' => :AddressBookHierarchicalParentDepartment,
592
+ '8C9A' => :AddressBookHierarchicalChildDepartments,
593
+ '8C9E' => :ThumbnailPhoto,
594
+ '8CA0' => :AddressBookSeniorityIndex,
595
+ '8CA8' => :AddressBookOrganizationalUnitRootDistinguishedName,
596
+ '8CAC' => :AddressBookSenderHintTranslations,
597
+ '8CB5' => :AddressBookModerationEnabled,
598
+ '8CC2' => :SpokenName,
599
+ '8CD8' => :AddressBookAuthorizedSenders,
600
+ '8CD9' => :AddressBookUnauthorizedSenders,
601
+ '8CDA' => :AddressBookDistributionListMemberSubmitRejected,
602
+ '8CDB' => :AddressBookDistributionListRejectMessagesFromDLMembers,
603
+ '8CDD' => :AddressBookHierarchicalIsHierarchicalGroup,
604
+ '8CE2' => :AddressBookDistributionListMemberCount,
605
+ '8CE3' => :AddressBookDistributionListExternalMemberCount,
606
+ 'FFFB' => :AddressBookIsMaster,
607
+ 'FFFC' => :AddressBookParentEntryId,
608
+ 'FFFD' => :AddressBookContainerId,
609
+ }
610
+
611
+ class OfflineAddressBook
612
+ class Parser
613
+ def initialize(oab)
614
+ @oab = open(oab)
615
+ @header = _header
616
+ end
617
+
618
+ def pos
619
+ return sprintf('%08o', @oab.pos)
620
+ end
621
+ def typeof(type)
622
+ return MapiPropertyDataType[type.to_s.to_sym]
623
+ end
624
+
625
+ attr_reader :serialNumber, :totalRecords, :header
626
+
627
+ def _uint32
628
+ return @oab.read(4).unpack('V*')[0]
629
+ end
630
+ def _ubyte
631
+ return @oab.read(1).unpack('C*')[0]
632
+ end
633
+
634
+ def _integer
635
+ firstByte = _ubyte
636
+
637
+ return firstByte if firstByte < 0x80
638
+
639
+ case firstByte
640
+ when 0x81 then return _ubyte
641
+ when 0x82 then return _ubyte + (_ubyte << 8)
642
+ when 0x83 then return _ubyte + (_ubyte << 8) + (_ubyte << 16)
643
+ when 0x84 then return _ubyte + (_ubyte << 8) + (_ubyte << 16) + (_ubyte << 24)
644
+ end
645
+ raise "Unexpected first byte #{sprintf('%x', firstByte)} of integer"
646
+ end
647
+
648
+ def _propertyTypes
649
+ n = _uint32
650
+ return 1.upto(n).collect{|i|
651
+ prop = OpenStruct.new
652
+ prop.pos = pos
653
+
654
+ id = _uint32
655
+ prop._id = id.to_s(16)
656
+ prop.id = sprintf('%04x', id >> 16).upcase
657
+ prop.name = MapiPropertyName[prop.id]
658
+ throw prop.id unless prop.name && prop.name != ''
659
+ type = id & 0xffff
660
+ prop.type = typeof(type & ~MapiPropertyDataType.Mv)
661
+ prop._type = sprintf('%04x', (type & ~MapiPropertyDataType.Mv))
662
+ prop.array = ((type & MapiPropertyDataType.Mv) != 0)
663
+ prop.flags = _uint32
664
+ prop
665
+ }
666
+ end
667
+
668
+ def _header
669
+ # Read OAB_HDR
670
+ version = _uint32
671
+ raise "Version not found, got #{version.inspect}" unless version == 0x20 # version
672
+ @serialNumber = _uint32
673
+ @totalRecords = _uint32
674
+ # Read OAB_META_DATA
675
+ metadataSize = _uint32
676
+
677
+ @headerProperties = _propertyTypes
678
+ @oabProperties = _propertyTypes
679
+ return _record(true)
680
+ end
681
+
682
+ def _property(prop)
683
+ if prop.array
684
+ valueCount = _integer
685
+ value = []
686
+ valueCount.times{
687
+ value << _scalar(prop.type)
688
+ }
689
+ else
690
+ value = _scalar(prop.type)
691
+ end
692
+ p = OpenStruct.new(type: prop.type, id: prop.id, name: prop.name, value: value)
693
+ return p
694
+ end
695
+
696
+ def _scalar(type)
697
+ case type
698
+ when :Long then return _integer
699
+ when :Boolean then return (_ubyte > 0)
700
+ when :Binary then return @oab.read(_integer)
701
+ when :AnsiString, :UnicodeString
702
+ string = ''
703
+ while (byte = @oab.read(1)) != "\x00"
704
+ string << byte
705
+ end
706
+ # TODO: string.force_encoding(MapiPropertyDataType.UnicodeString ? 'UTF-8' : 'ASCII')
707
+ string.force_encoding(type == :UnicodeString ? 'UTF-8' : 'ASCII')
708
+ return string
709
+ end
710
+ raise "Unknown scalar type #{type}"
711
+ end
712
+
713
+ def _record(headerRecord = false)
714
+ initialPosition = @oab.pos
715
+ recordSize = 0
716
+ record = OpenStruct.new
717
+ begin
718
+ properties = headerRecord ? @headerProperties : @oabProperties
719
+ recordSize = _uint32
720
+ recordPresence = @oab.read((properties.length + 7) / 8).unpack("b*").join.split('').collect{|bit| bit.to_i != 0 }
721
+ properties.each_with_index{|prop, i|
722
+ next unless recordPresence[i + 7 - 2 * (i % 8)]
723
+ p = _property(prop)
724
+ pn = p.name.to_s
725
+ record[pn] ||= []
726
+ record[pn] << p.value
727
+ }
728
+ ensure
729
+ @oab.pos = initialPosition + recordSize
730
+ end
731
+ return record
732
+ end
733
+
734
+ def records
735
+ @records = Enumerator.new(@totalRecords) do |y|
736
+ @totalRecords.times { y.yield _record }
737
+ end
738
+ return @records
739
+ end
740
+ end
741
+ end
@@ -0,0 +1,3 @@
1
+ class OfflineAddressBook
2
+ VERSION = '0.0.5'
3
+ end
@@ -0,0 +1,11 @@
1
+ require 'dotenv/load'
2
+
3
+ require 'minitest/autorun'
4
+ require 'exchange-offline-address-book'
5
+
6
+ class EOABTest < Minitest::Test
7
+ def test_downlod
8
+ oab = OfflineAddressBook.new(username: ENV['USERNAME'], password: ENV['PASSWORD'], email: ENV['EMAIL'])
9
+ puts oab.addressbook
10
+ end
11
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: exchange-offline-address-book
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Emiliano Heyns
@@ -25,75 +25,117 @@ dependencies:
25
25
  - !ruby/object:Gem::Version
26
26
  version: '1.3'
27
27
  - !ruby/object:Gem::Dependency
28
- name: nokogiri
28
+ name: dotenv
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ">="
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: '0'
34
- type: :runtime
33
+ version: '2'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5'
48
+ type: :development
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
- - - ">="
52
+ - - "~>"
39
53
  - !ruby/object:Gem::Version
40
- version: '0'
54
+ version: '5'
41
55
  - !ruby/object:Gem::Dependency
42
- name: logging
56
+ name: gem-release
43
57
  requirement: !ruby/object:Gem::Requirement
44
58
  requirements:
45
- - - ">="
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
46
67
  - !ruby/object:Gem::Version
47
- version: '0'
68
+ version: '1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bump
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '0.5'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '0.5'
83
+ - !ruby/object:Gem::Dependency
84
+ name: nokogiri
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '1.8'
48
90
  type: :runtime
49
91
  prerelease: false
50
92
  version_requirements: !ruby/object:Gem::Requirement
51
93
  requirements:
52
- - - ">="
94
+ - - "~>"
53
95
  - !ruby/object:Gem::Version
54
- version: '0'
96
+ version: '1.8'
55
97
  - !ruby/object:Gem::Dependency
56
98
  name: autodiscover
57
99
  requirement: !ruby/object:Gem::Requirement
58
100
  requirements:
59
- - - ">="
101
+ - - "~>"
60
102
  - !ruby/object:Gem::Version
61
- version: '0'
103
+ version: '1'
62
104
  type: :runtime
63
105
  prerelease: false
64
106
  version_requirements: !ruby/object:Gem::Requirement
65
107
  requirements:
66
- - - ">="
108
+ - - "~>"
67
109
  - !ruby/object:Gem::Version
68
- version: '0'
110
+ version: '1'
69
111
  - !ruby/object:Gem::Dependency
70
112
  name: libmspack
71
113
  requirement: !ruby/object:Gem::Requirement
72
114
  requirements:
73
- - - ">="
115
+ - - "~>"
74
116
  - !ruby/object:Gem::Version
75
- version: '0'
117
+ version: '0.1'
76
118
  type: :runtime
77
119
  prerelease: false
78
120
  version_requirements: !ruby/object:Gem::Requirement
79
121
  requirements:
80
- - - ">="
122
+ - - "~>"
81
123
  - !ruby/object:Gem::Version
82
- version: '0'
124
+ version: '0.1'
83
125
  - !ruby/object:Gem::Dependency
84
126
  name: curb
85
127
  requirement: !ruby/object:Gem::Requirement
86
128
  requirements:
87
- - - ">="
129
+ - - "~>"
88
130
  - !ruby/object:Gem::Version
89
- version: '0'
131
+ version: '0.9'
90
132
  type: :runtime
91
133
  prerelease: false
92
134
  version_requirements: !ruby/object:Gem::Requirement
93
135
  requirements:
94
- - - ">="
136
+ - - "~>"
95
137
  - !ruby/object:Gem::Version
96
- version: '0'
138
+ version: '0.9'
97
139
  description: Get Exchange Offline Address Book
98
140
  email:
99
141
  - Emiliano.Heyns@iris-advies.com
@@ -101,7 +143,17 @@ executables: []
101
143
  extensions: []
102
144
  extra_rdoc_files: []
103
145
  files:
146
+ - ".gitignore"
147
+ - Gemfile
148
+ - Gemfile.lock
104
149
  - README.md
150
+ - Rakefile
151
+ - exchange-offline-address-book.gemspec
152
+ - lib/exchange-offline-address-book.rb
153
+ - lib/exchange-offline-address-book/mspack.rb
154
+ - lib/exchange-offline-address-book/parser.rb
155
+ - lib/exchange-offline-address-book/version.rb
156
+ - test/test_exchange-offline-address-book.rb
105
157
  homepage: http://github.io
106
158
  licenses:
107
159
  - MIT
@@ -126,4 +178,5 @@ rubygems_version: 2.6.12
126
178
  signing_key:
127
179
  specification_version: 4
128
180
  summary: Get Exchange Offline Address Book
129
- test_files: []
181
+ test_files:
182
+ - test/test_exchange-offline-address-book.rb