viewpoint 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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