viewpoint 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. data/COPYING.txt +674 -0
  2. data/README +60 -0
  3. data/Rakefile +39 -0
  4. data/examples/cal2ical.rb +29 -0
  5. data/examples/ews_fusefs.rb +364 -0
  6. data/examples/webclient/.gems +2 -0
  7. data/examples/webclient/README +1 -0
  8. data/examples/webclient/config.ru +26 -0
  9. data/examples/webclient/ews_access_handler.rb +42 -0
  10. data/examples/webclient/public/css/mail.css +33 -0
  11. data/examples/webclient/public/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  12. data/examples/webclient/public/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  13. data/examples/webclient/public/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  14. data/examples/webclient/public/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  15. data/examples/webclient/public/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  16. data/examples/webclient/public/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  17. data/examples/webclient/public/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  18. data/examples/webclient/public/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  19. data/examples/webclient/public/css/smoothness/images/ui-icons_222222_256x240.png +0 -0
  20. data/examples/webclient/public/css/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  21. data/examples/webclient/public/css/smoothness/images/ui-icons_454545_256x240.png +0 -0
  22. data/examples/webclient/public/css/smoothness/images/ui-icons_888888_256x240.png +0 -0
  23. data/examples/webclient/public/css/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  24. data/examples/webclient/public/css/smoothness/jquery-ui-1.7.2.custom.css +406 -0
  25. data/examples/webclient/public/css/ui-lightness/images/ui-bg_diagonals-thick_18_b81900_40x40.png +0 -0
  26. data/examples/webclient/public/css/ui-lightness/images/ui-bg_diagonals-thick_20_666666_40x40.png +0 -0
  27. data/examples/webclient/public/css/ui-lightness/images/ui-bg_flat_10_000000_40x100.png +0 -0
  28. data/examples/webclient/public/css/ui-lightness/images/ui-bg_glass_100_f6f6f6_1x400.png +0 -0
  29. data/examples/webclient/public/css/ui-lightness/images/ui-bg_glass_100_fdf5ce_1x400.png +0 -0
  30. data/examples/webclient/public/css/ui-lightness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  31. data/examples/webclient/public/css/ui-lightness/images/ui-bg_gloss-wave_35_f6a828_500x100.png +0 -0
  32. data/examples/webclient/public/css/ui-lightness/images/ui-bg_highlight-soft_100_eeeeee_1x100.png +0 -0
  33. data/examples/webclient/public/css/ui-lightness/images/ui-bg_highlight-soft_75_ffe45c_1x100.png +0 -0
  34. data/examples/webclient/public/css/ui-lightness/images/ui-icons_222222_256x240.png +0 -0
  35. data/examples/webclient/public/css/ui-lightness/images/ui-icons_228ef1_256x240.png +0 -0
  36. data/examples/webclient/public/css/ui-lightness/images/ui-icons_ef8c08_256x240.png +0 -0
  37. data/examples/webclient/public/css/ui-lightness/images/ui-icons_ffd27a_256x240.png +0 -0
  38. data/examples/webclient/public/css/ui-lightness/images/ui-icons_ffffff_256x240.png +0 -0
  39. data/examples/webclient/public/css/ui-lightness/jquery-ui-1.7.2.custom.css +406 -0
  40. data/examples/webclient/public/js/jquery-1.3.2.min.js +19 -0
  41. data/examples/webclient/public/js/jquery-ui-1.7.2.custom.min.js +145 -0
  42. data/examples/webclient/viewpoint_web.rb +295 -0
  43. data/examples/webclient/views/_footer.haml +1 -0
  44. data/examples/webclient/views/_header.haml +11 -0
  45. data/examples/webclient/views/_tagline.haml +1 -0
  46. data/examples/webclient/views/index.haml +20 -0
  47. data/examples/webclient/views/layout.haml +42 -0
  48. data/examples/webclient/views/mail.haml +45 -0
  49. data/examples/webclient/views/welcome.haml +0 -0
  50. data/lib/calendar.rb +121 -0
  51. data/lib/calendar_item.rb +110 -0
  52. data/lib/event.rb +0 -0
  53. data/lib/exchange_headers.rb +50 -0
  54. data/lib/exchwebserv.rb +228 -0
  55. data/lib/folder.rb +269 -0
  56. data/lib/item.rb +56 -0
  57. data/lib/mail.rb +118 -0
  58. data/lib/message.rb +134 -0
  59. data/lib/soap/viewpoint.conf +3 -0
  60. data/lib/task.rb +0 -0
  61. data/lib/tasks.rb +0 -0
  62. data/lib/viewpoint.rb +2 -0
  63. data/lib/wsdl/defaultMappingRegistry.rb +10680 -0
  64. data/lib/wsdl/exchangeServiceBinding.rb +349 -0
  65. data/lib/wsdl/exchangeServiceTypes.rb +11013 -0
  66. data/preamble +19 -0
  67. data/test/spec/authtest_spec.rb +12 -0
  68. data/test/spec/findfolders_spec.rb +14 -0
  69. data/test/test_client.rb +12 -0
  70. data/test/testrestrict.rb +75 -0
  71. metadata +130 -0
data/README ADDED
@@ -0,0 +1,60 @@
1
+ --------------------------------------------------------------------------
2
+ Viewpoint for Exchange Web Services
3
+ --------------------------------------------------------------------------
4
+ This program attempts to create a client access library for Exchange Web
5
+ Services (EWS) in Ruby.
6
+ --------------------------------------------------------------------------
7
+ REQUIRED GEMS:
8
+
9
+ # Current SOAP4R ( http://dev.ctor.org/soap4r )
10
+ gem install -r soap4r
11
+
12
+ # iCalendar
13
+ gem install -r icalendar
14
+
15
+ CONFIGURATION:
16
+ Configure your user/pass and EWS endpoint in lib/soap/viewpoint.conf
17
+ or $HOME/.viewpointrc. If the file $HOME/.viewpointrc exists it takes
18
+ presidence. See the sample in lib/soap/viewpoint.conf for how this
19
+ file should be configured.
20
+
21
+
22
+ Running Examples:
23
+ To run the FuseFS example you need the 'fusefs' gem
24
+
25
+
26
+ ******************** EXAMPLES *********************
27
+ # In the following examples the following are used:
28
+ # "item" is a type of Viewpoint::Item
29
+ # "folder" is a type of Viewpoint::Folder
30
+ # "new_folder" is a type of Viewpoint::Folder
31
+
32
+ # === Convert your Exchange Calendar to a Icalendar::Calendar object ===
33
+ require 'lib/exchwebserv'
34
+ vp = Viewpoint::ExchWebServ.instance
35
+ vp.authenticate
36
+ vp.find_folders
37
+ cal = vp.get_folder("Calendar")
38
+ ical = cal.to_ical
39
+ puts ical.to_ical
40
+
41
+ # === Get a listing of todays messages ===
42
+ # See test/test_client.rb
43
+
44
+ # === Moving an item ===
45
+ item.move_to!(new_folder)
46
+
47
+ # === Deleting an item ===
48
+ item.delete!
49
+ item.recycle! # Move to "Deleted Items"
50
+
51
+
52
+ --------------------------------------------------------------------------
53
+ DISCLAIMER: I am by no means a Ruby or EWS expert. There may be better
54
+ means of doing what I am attempting to do. If you see something that
55
+ could be done better or would like to help out in the development of this
56
+ code please feel free to clone the 'git' repository:
57
+ git clone git://github.com/zenchild/Viewpoint-for-MS-Exchange.git
58
+ or add an issue on GitHub:
59
+ http://github.com/zenchild/Viewpoint-for-MS-Exchange/issues
60
+ --------------------------------------------------------------------------
@@ -0,0 +1,39 @@
1
+ require 'rubygems'
2
+ require 'rake/clean'
3
+ require 'rake/gempackagetask'
4
+
5
+ CLEAN.include("pkg")
6
+ CLEAN.include("doc")
7
+
8
+ GEMSPEC = Gem::Specification.new do |gem|
9
+ gem.name = "viewpoint"
10
+ gem.version = File.open('VERSION').readline.chomp
11
+ gem.date = Date.today.to_s
12
+ gem.platform = Gem::Platform::RUBY
13
+ gem.rubyforge_project = nil
14
+
15
+ gem.author = "Dan Wanek"
16
+ gem.email = "dan.wanek@gmail.com"
17
+ gem.homepage = "http://github.com/zenchild/Viewpoint"
18
+
19
+ gem.summary = "A Ruby client access library for Microsoft Exchange Web Services (EWS)"
20
+ gem.description = <<-EOF
21
+ A Ruby client access library for Microsoft Exchange Web Services (EWS). It is a work in progress. Methods are still being added from the EWS API docs. Please see them for further information: http://msdn.microsoft.com/en-us/library/bb204119.aspx
22
+ EOF
23
+
24
+ gem.files = `git ls-files`.split(/\n/)
25
+ gem.require_path = "lib"
26
+ gem.rdoc_options = %w(-x wsdl/ -x test/ -x examples/)
27
+ gem.extra_rdoc_files = %w(README COPYING.txt)
28
+
29
+ gem.required_ruby_version = '>= 1.8.7'
30
+ gem.requirements = []
31
+ #gem.add_runtime_dependency ''
32
+ gem.post_install_message = "Don't forget to create .viewpointrc. See README"
33
+ end
34
+
35
+ Rake::GemPackageTask.new(GEMSPEC) do |pkg|
36
+ pkg.need_tar = true
37
+ end
38
+
39
+ task :default => [:clean, :repackage]
@@ -0,0 +1,29 @@
1
+ #############################################################################
2
+ # Copyright © 2009 Dan Wanek <dan.wanek@gmail.com>
3
+ #
4
+ #
5
+ # This file is part of Viewpoint.
6
+ #
7
+ # Viewpoint is free software: you can redistribute it and/or
8
+ # modify it under the terms of the GNU General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or (at
10
+ # your option) any later version.
11
+ #
12
+ # Viewpoint is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15
+ # Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License along
18
+ # with Viewpoint. If not, see <http://www.gnu.org/licenses/>.
19
+ #############################################################################
20
+ require File.dirname(__FILE__) + '/../lib/exchwebserv'
21
+ include Viewpoint
22
+
23
+ vp = ExchWebServ.instance
24
+ vp.authenticate
25
+ vp.find_folders
26
+ cal = vp.get_folder("Calendar")
27
+ ical = cal.to_ical
28
+ puts ical.to_ical
29
+
@@ -0,0 +1,364 @@
1
+ #############################################################################
2
+ # Copyright © 2009 Dan Wanek <dan.wanek@gmail.com>
3
+ #
4
+ #
5
+ # This file is part of Viewpoint.
6
+ #
7
+ # Viewpoint is free software: you can redistribute it and/or
8
+ # modify it under the terms of the GNU General Public License as published
9
+ # by the Free Software Foundation, either version 3 of the License, or (at
10
+ # your option) any later version.
11
+ #
12
+ # Viewpoint is distributed in the hope that it will be useful,
13
+ # but WITHOUT ANY WARRANTY; without even the implied warranty of
14
+ # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General
15
+ # Public License for more details.
16
+ #
17
+ # You should have received a copy of the GNU General Public License along
18
+ # with Viewpoint. If not, see <http://www.gnu.org/licenses/>.
19
+ #############################################################################
20
+ require 'fusefs'
21
+ require 'time'
22
+ include FuseFS
23
+ require File.dirname(__FILE__) + '/../lib/exchwebserv'
24
+ require File.dirname(__FILE__) + '/../lib/folder'
25
+ include Viewpoint
26
+
27
+
28
+ class EWSFuse < FuseDir
29
+
30
+ @@maildir_dirs = ['cur','new', 'tmp']
31
+ @@ewsfuse_dir = "#{ENV['HOME']}/.ewsfuse"
32
+
33
+ def initialize(mountpoint)
34
+ @vp = ExchWebServ.instance
35
+ @vp.authenticate
36
+ @vp.find_folders
37
+ @mf = (@vp.get_mail_folders)
38
+ puts "MF: #{@mf.class.to_s}"
39
+ @messages = {} # Message objects
40
+ @open_files = {} # Files open for RAW IO
41
+ @mountpoint = mountpoint
42
+
43
+ #File::umask(0077)
44
+ if( !File.directory?(@@ewsfuse_dir) )
45
+ Dir.mkdir(@@ewsfuse_dir)
46
+ end
47
+ end
48
+
49
+ # ----- FuserFS Methods ----- #
50
+ def contents(path)
51
+ puts "--- IN: contents ---"
52
+ if (local_path?(path))
53
+ puts "--- IN: contents: if ---"
54
+ Dir.entries(local_path(path))
55
+ else
56
+ puts "--- IN: contents: else ---"
57
+ parts = scan_path(path)
58
+ case parts.pop
59
+ when nil
60
+ puts "--- IN: contents: else --- while 'nil'"
61
+ output = @mf.keys do |folder_name|
62
+ folder_name
63
+ end
64
+ output + Dir.entries(local_path(path))
65
+ when 'cur'
66
+ np = parts.pop
67
+ puts "folder: #{np}"
68
+ mdir = @mf[np]
69
+ if( mdir == nil )
70
+ []
71
+ else
72
+ mdir = @vp.get_folder(mdir.display_name)
73
+ puts "Fetching todays messages for #{mdir.display_name} of type #{mdir.class.to_s}"
74
+ mdir.get_todays_messages.map do |msg|
75
+ puts "Got message of type: #{msg.class.to_s}"
76
+ msg_hash = msg.item_id.hash.abs
77
+ @messages[msg_hash.to_s] = msg
78
+ "#{msg.date_time_recieved.strftime('%s')}_#{msg_hash}"
79
+ end
80
+ end
81
+ when 'new','tmp'
82
+ []
83
+ else
84
+ ['cur','new','tmp']
85
+ end
86
+ end
87
+ end
88
+
89
+ def directory?(path)
90
+ if( local_path?(path) )
91
+ puts "Checking for Local_dir?"
92
+ File.directory?( local_path(path) )
93
+ else
94
+ parts = scan_path(path)
95
+ part = parts.pop
96
+ case part
97
+ when /^[0-9]{10,}_/
98
+ puts "NOT DIR: #{path}"
99
+ return false
100
+ when nil, 'cur','new','tmp'
101
+ puts "IS DIR: #{path}"
102
+ return true
103
+ else
104
+ if( (@mf[part] ) != nil )
105
+ puts "IS DIR: #{path}"
106
+ return true
107
+ else
108
+ puts "NOT DIR: #{path}"
109
+ return false
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ def file?(path)
116
+ if( local_path?(path) )
117
+ if(File.exists?(local_path(path)))
118
+ ans = !directory?(path)
119
+ else
120
+ ans = false
121
+ end
122
+ else
123
+ ans = !directory?(path)
124
+ end
125
+ puts "=> file?(#{path}) ... " + (ans ? "yes" : "no")
126
+ return ans
127
+ end
128
+
129
+ def executable?(path)
130
+ if( local_path?(path) )
131
+ return File.executable?(local_path(path))
132
+ else
133
+ return directory?(path)
134
+ end
135
+ end
136
+
137
+ def read_file(path)
138
+ if( local_path?(path) )
139
+ IO.read(local_path(path))
140
+ else
141
+ parts = scan_path(path)
142
+ msg_hash = parts.last.split('_').last
143
+ @messages[msg_hash.to_s].to_rfc822
144
+ end
145
+ end
146
+
147
+ def can_write?(path)
148
+ return false if path =~ /folders.db/
149
+ parts = scan_path(path)
150
+
151
+ ans = local_path?(path) || path == '/' #||
152
+ #parts.last =~ /^[0-9]{10,}_/
153
+
154
+ puts "=> can_write?(#{path}) ... " + (ans ? "yes" : "no")
155
+ return ans
156
+ end
157
+
158
+ def write_to(path, str)
159
+ puts "=> write_to(#{path}, str...)"
160
+ io = File.open(local_path(path),'w+')
161
+ io.write(str)
162
+ io.close
163
+ end
164
+
165
+ def can_delete?(path)
166
+ parts = scan_path(path)
167
+
168
+ ans = local_path?(path) ||
169
+ parts.last =~ /^[0-9]{10,}_/
170
+
171
+ puts "=> can_delete?(#{path}) ... " + (ans ? "yes" : "no")
172
+ ans
173
+ end
174
+
175
+ def delete(path)
176
+ puts "=> delete(#{path})"
177
+ if( local_path?(path) )
178
+ puts "In LOCAL DELETE"
179
+ lpath = local_path(path)
180
+ if( File.directory?(lpath) )
181
+ puts "Removing local directory #{lpath}"
182
+ else
183
+ puts "Removing local file #{lpath}"
184
+ File.unlink(lpath)
185
+ end
186
+ else
187
+ puts "In NON LOCAL DELETE"
188
+ parts = scan_path(path)
189
+ msg_hash = parts.last.split('_').last
190
+ puts "In NON LOCAL DELETE 2: #{msg_hash}"
191
+ puts "In NON LOCAL DELETE 3: #{parts[-3]}"
192
+ #mdir = @vp.get_folder(parts[-3])
193
+ if ( (msg = @messages[msg_hash.to_s]) == nil)
194
+ npath = parts
195
+ npath.pop
196
+ contents(npath.join('/'))
197
+ msg = @messages[msg_hash.to_s]
198
+ end
199
+ puts "In NON LOCAL DELETE 4: #{msg.class.to_s}"
200
+ puts "MESG: #{msg.class.to_s}, ID: #{msg.item_id}, PARENT: #{msg.parent_folder.class.to_s}"
201
+ #puts "MESG: #{mdir.class.to_s}"
202
+ msg.parent_folder.recycle_item(msg.item_id)
203
+ true
204
+ end
205
+ end
206
+
207
+ def can_mkdir?(path)
208
+ ans = local_path?(path)
209
+ puts "=> can_mkdir?(#{path}) ... " + (ans ? "yes" : "no")
210
+ ans
211
+ end
212
+
213
+ def mkdir(path)
214
+ puts "=> mkdir(#{path})"
215
+ lpath = local_path(path)
216
+ Dir.mkdir lpath
217
+ end
218
+
219
+ def can_rmdir?(path)
220
+ puts "=> can_rmdir?(#{path})"
221
+ local_path?(path)
222
+ end
223
+
224
+ def rmdir(path)
225
+ lpath = local_path(path)
226
+ puts "=> rmdir(#{lpath})"
227
+ Dir.rmdir(lpath)
228
+ end
229
+
230
+ def size(path)
231
+ if(local_path?(path))
232
+ File.size(local_path(path))
233
+ end
234
+ end
235
+
236
+ # --- RAW FuserFS Methods --- #
237
+ def raw_open(path,mode) # mode is "r" "w" or "rw", with "a" if the file
238
+ puts "** => raw_open(#{path}, '#{mode}')"
239
+
240
+ if( !local_path?(path) )
241
+ return false
242
+ end
243
+
244
+ lpath = local_path(path)
245
+
246
+ case mode
247
+ when /^r$/
248
+ mode = 'r'
249
+ when /^w$/
250
+ mode = 'a'
251
+ when /^[wr]{2}$/, /a/
252
+ if(File.exists?(lpath))
253
+ mode = 'r+'
254
+ else
255
+ mode = 'w+'
256
+ end
257
+ else
258
+ raise "Unknown mode error #{mode}"
259
+ end
260
+
261
+ puts "Setting mode to '#{mode}'"
262
+
263
+ return true if @open_files.has_key?(path)
264
+ begin
265
+ puts "Raw opening.... File.open(#{lpath}, #{mode})"
266
+ @open_files[path] = File.open(lpath, mode)
267
+ return true
268
+ rescue => error
269
+ puts "Error opening raw file: #{error}"
270
+ return false
271
+ end
272
+ end
273
+
274
+ def raw_read(path,offset,size)
275
+ begin
276
+ puts "** => raw_read(#{path}, #{offset}, #{size})"
277
+ file = @open_files[path]
278
+ return unless file
279
+ #file.sysseek(offset,IO::SEEK_SET)
280
+ #file.sysread(size)
281
+ file.seek(offset, File::SEEK_SET)
282
+ file.read(size)
283
+ rescue => error
284
+ puts "Error reading raw file: #{error}"
285
+ nil
286
+ end
287
+ end
288
+
289
+ def raw_write(path,offset,size,buf)
290
+ begin
291
+ puts "** => raw_write(#{path}, #{offset}, #{size}, ... buf)"
292
+ file = @open_files[path]
293
+ return unless file
294
+ file.seek(offset, File::SEEK_CUR)
295
+ file.write(buf[0, size])
296
+ #file.sysseek(offset,IO::SEEK_CUR)
297
+ #file.syswrite(buf[0, size])
298
+ rescue => error
299
+ puts "Error writing to raw file: #{error}"
300
+ end
301
+ end
302
+
303
+ def raw_close(path)
304
+ begin
305
+ puts "** => raw_close(#{path})"
306
+ file = @open_files[path]
307
+ return unless file
308
+ file.close
309
+ @open_files.delete path
310
+ rescue => error
311
+ puts "Error closing raw file: #{error}"
312
+ end
313
+ end
314
+
315
+
316
+ # --- End FuserFS Methods --- #
317
+
318
+ # Is this a virtual dir/file or a local file
319
+ def local_path?(path)
320
+ parts = scan_path(path)
321
+
322
+ if parts.empty?
323
+ ans = false
324
+ else
325
+ ans = ((!@@maildir_dirs.include? parts.last) &&
326
+ parts.last !~ /^[0-9]{10,}_/ &&
327
+ ((@mf[parts.last]) == nil)
328
+ )
329
+ end
330
+
331
+ puts "==> local_path?(#{path}) ... " + (ans ? "yes" : "no")
332
+ return ans
333
+ end
334
+
335
+ # Return local path as a string
336
+ def local_path(path)
337
+ puts "==> local_path(#{path})"
338
+ #lpath = path.gsub(/#{@mountpoint}\/?/,'')
339
+ lpath = path.gsub(/^\/?/,'')
340
+ return "#{@@ewsfuse_dir}/#{lpath}"
341
+ end
342
+ end
343
+
344
+ # FuserFS set-up
345
+ if (File.basename($0) == File.basename(__FILE__))
346
+ if (ARGV.size != 1)
347
+ puts "Usage: #{$0} <directory>"
348
+ exit
349
+ end
350
+
351
+ dirname = ARGV.shift
352
+
353
+ unless File.directory?(dirname)
354
+ puts "Usage: #{dirname} is not a directory."
355
+ exit
356
+ end
357
+
358
+ root = EWSFuse.new(dirname)
359
+
360
+ # Set the root FuseFS
361
+ FuseFS.set_root(root)
362
+ FuseFS.mount_under(dirname)
363
+ FuseFS.run # This doesn't return until we're unmounted.
364
+ end