outlook2gcal 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,2 @@
1
+ === Version 0.0.1
2
+ * Initial version. Code based on ncal2gcal 0.1.7 (c) 2012 Elias Kugler
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2012 Elias Kugler
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,85 @@
1
+ =outlook2gcal
2
+
3
+ This app syncs your Outlook calendar with your (private) Google calendar. The synchronisation is only one-way: Outlook events are pushed to your Google Calendar. All types of events (including recurring events like anniversaries) are supported.
4
+ Note: no admin rights required
5
+
6
+ == Alternatives
7
+
8
+ GoogleCalendarSync worked fine for me until I had to switch from Windows XP to Windows 7. In Windows 7 GoogleCalendarSync requires admin rights to install it.
9
+
10
+ http://support.google.com/calendar/bin/answer.py?hl=en&answer=98563&topic=13948&ctx=topic
11
+
12
+ == Installation Instructions
13
+
14
+ gem install outlook2gcal
15
+
16
+
17
+ ==Usage
18
+
19
+ Usage: outlook2gcal <command> <opts>
20
+
21
+ Commands: sync
22
+
23
+ Specific options:
24
+ -U, --gmail-username USERNAME Google user name
25
+ -P, --gmail-password PASSWORD Google user password
26
+ -C, --gmail-calendar CALENDAR Google calendar (default: 'Outlook'). Note: calendar must exist
27
+ -D, --days DAYS Do not sync events older then DAYS days
28
+ --sync-desc Sync event description (default: no)
29
+ --sync-alarm Sync event alarm notification (default: no)
30
+ --sync-names Sync attendees (default: no)"
31
+
32
+
33
+ Example:
34
+ outlook2gcal sync -U username@gmail.com -P 123secret -C Outlook -D 14
35
+
36
+ Common options:
37
+ -?, --help Show this message
38
+
39
+ == Prerequisites
40
+
41
+ 1. Ruby 1.9.1 or later
42
+ 1.1 Sqlite3 installed.
43
+ A description how to install Sqlite3 on Windows: http://www.skorks.com/2009/08/installing-and-using-sqlite-with-ruby-on-windows/
44
+ 2. Outlook 2007
45
+ * might work with other versions too
46
+ 3. Google Account
47
+ * use your gmail-account or create a new one
48
+ * create an additional calendar for your LotusNotes Calender entries. Name it as you like
49
+ (i.e. Outlook) but do not use whitespaces or any other special characters.
50
+
51
+ == Supported Platforms
52
+
53
+ This library is supported on Windows 2000 or later.
54
+
55
+ ==Used libs:
56
+ * gcal4ruby (modified version)
57
+ * win32ole
58
+ * datamapper
59
+ * do_sqlite3
60
+ * log4r
61
+ ...
62
+
63
+
64
+ == Licence/Copyright
65
+
66
+ Copyright (c) 2012 Elias Kugler
67
+
68
+ Permission is hereby granted, free of charge, to any person obtaining
69
+ a copy of this software and associated documentation files (the
70
+ "Software"), to deal in the Software without restriction, including
71
+ without limitation the rights to use, copy, modify, merge, publish,
72
+ distribute, sublicense, and/or sell copies of the Software, and to
73
+ permit persons to whom the Software is furnished to do so, subject to
74
+ the following conditions:
75
+
76
+ The above copyright notice and this permission notice shall be
77
+ included in all copies or substantial portions of the Software.
78
+
79
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
80
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
81
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
82
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
83
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
84
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
85
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,6 @@
1
+ rem build ncal2gcal portable using the ocra gem
2
+ rem installing ocra: gem install ocra
3
+ cp outlook2gcal "%appdata%\outlook2gcal_portable"
4
+ cd /d %appdata%
5
+ ocra outlook2gcal_portable
6
+ pause
@@ -0,0 +1,73 @@
1
+ #$:.unshift '../test/mock/win32ole','../test/mock/gcal4ruby','../lib' #<< '../lib'
2
+ #$:.unshift '../test/mock/win32ole','../lib' #<< '../lib'
3
+ $:.unshift '../lib' #<< '../lib'
4
+
5
+ require 'rubygems'
6
+ require 'bundler/setup'
7
+ require 'optparse'
8
+ require 'outlook2gcal/sync'
9
+ require 'outlook2gcal/install'
10
+
11
+ conf = {}
12
+
13
+ opts = OptionParser.new do |opts|
14
+ opts.banner = "Usage: outlook2gcal <command> <opts> "
15
+ opts.separator ""
16
+ opts.separator "Commands: sync"
17
+ opts.separator ""
18
+ opts.separator "Specific options:"
19
+ opts.on("-U", "--gmail-username USERNAME", "Google mail user name") { |gmail_user| conf[:gmail_user]=gmail_user }
20
+ opts.on("-P", "--gmail-password PASSWORD", "Google mail password") { |gmail_password| conf[:gmail_password]= gmail_password}
21
+ opts.on("-C", "--gmail-calendar CALENDAR", "Google calendar (default: 'LotusNotes')") { |gmail_calendar|conf[:gmail_calendar]=gmail_calendar }
22
+ opts.on("-D", "--days DAYS", "Do not sync events older then DAYS days (default: no limit)") { |days| conf[:days]=days.to_i }
23
+ opts.on("--sync-desc", "Sync event description (default: no)") { |sync_desc| conf[:sync_desc]=sync_desc }
24
+ opts.on("--sync-alarm", "Sync alarm notification (default: no)") { |sync_alarm| conf[:sync_alarm]= sync_alarm }
25
+ opts.on("--sync-names", "Sync attendees (default: no)") { |sync_names| conf[:sync_names]=sync_names }
26
+
27
+ opts.separator ""
28
+ opts.separator "Example:"
29
+ opts.separator " outlook2gcal sync -U username@gmail.com -P top123secret -C Outlook -D 14"
30
+ opts.separator ""
31
+ opts.separator "Common options:"
32
+
33
+ # No argument, shows at tail. This will print an options summary.
34
+ opts.on_tail("-?", "--help", "Show this message") do
35
+ puts opts
36
+ exit
37
+ end
38
+
39
+ opts.on_tail("-v", "--version", "Show version") do
40
+ puts "outlook2gcal 0.0.1"
41
+ exit
42
+ end
43
+
44
+ opts.on_tail "\n(c) 2012 Elias Kugler"
45
+
46
+ end
47
+
48
+ opts.parse! ARGV
49
+
50
+ if defined?Ocra
51
+ exit
52
+ else
53
+ if ARGV.length != 1
54
+ puts opts
55
+ exit
56
+ end
57
+ conf[:command] = ARGV[0]
58
+ end
59
+
60
+ # --------------------------------------------------
61
+ #
62
+ # --------------------------------------------------
63
+ if conf[:command] == 'sync'
64
+ olsync = Outlook2GCal::OutlookGoogleSync.new(conf)
65
+ c = olsync.sync_events
66
+ c.show # show statistics
67
+ elsif conf[:command] == 'install'
68
+ Outlook2GCal::install(conf)
69
+ exit
70
+ else
71
+ puts opts
72
+ exit
73
+ end
@@ -0,0 +1,36 @@
1
+ module Outlook2GCal
2
+
3
+ class Counter
4
+ attr_accessor :updates, :inserts, :deletes, :selects, :ignored, :t_start, :t_end
5
+
6
+ def initialize
7
+ @updates = 0
8
+ @inserts = 0
9
+ @deletes = 0
10
+ @selects = 0
11
+ @ignored = 0
12
+ start
13
+ end
14
+ def runtime
15
+ @t_end - @t_start
16
+ end
17
+ def start
18
+ @t_start = Time.now
19
+ end
20
+ def end
21
+ @t_end = Time.now
22
+ end
23
+ def show
24
+ puts "\nStatistics:"
25
+ puts "-"*20
26
+ puts sprintf("Inserts : %05d", @inserts)
27
+ puts sprintf("Updates : %05d", @updates)
28
+ puts sprintf("Deletes : %05d", @deletes)
29
+ puts sprintf("Ingored : %05d", @ignored)
30
+ puts "-"*20
31
+ puts sprintf("Total : %05d", @selects)
32
+ puts "Runtime : #{runtime} sec"
33
+ end
34
+ end
35
+
36
+ end
@@ -0,0 +1,66 @@
1
+ #encoding: utf-8
2
+ require 'rubygems'
3
+ require 'gcal4ruby'
4
+
5
+ ##
6
+ # This is an extension/modification of the classes
7
+ # in the gcal4ruby-module
8
+ #
9
+ # at the moment it is only used to allaw the usage
10
+ # of the user-relevant end-date in all-day events
11
+ #
12
+ # This module requires a modified version of gcal4ruby (based on 0.5.5)
13
+ #
14
+ module GCal4Ruby
15
+ DAY_SECS = 86400
16
+ class Event
17
+
18
+ #alias :start_time= :start=
19
+ #alias :start_time :start
20
+ alias :end_time_orig= :end_time=
21
+ alias :end_time_orig :end_time
22
+
23
+ def all_day=(is_all_day)
24
+ if self.end_time_orig and !(is_all_day == @all_day)
25
+ # Not necessary in Outlook (was/is a Lotus Notes issue)!!
26
+ # is_all_day ? self.end_time_orig=(self.end_time_orig()+DAY_SECS) : self.end_time_orig=(self.end_time_orig-DAY_SECS)
27
+ end
28
+ @all_day = is_all_day
29
+ end
30
+ ##
31
+ # The event end date and time
32
+ # For all_day-events it is the day, when
33
+ # the event ends (and not the day after
34
+ # as in the google api)
35
+ #
36
+ def end_time=(t)
37
+ tc = convert_to_time(t)
38
+ # if @all_day
39
+ # self.end_time_orig = tc + DAY_SECS
40
+ # else
41
+ self.end_time_orig = tc
42
+ # end
43
+ end
44
+ #
45
+ # see end_time=()
46
+ def end_time
47
+ # if @all_day
48
+ # return (self.end_time_orig-DAY_SECS)
49
+ # else
50
+ return self.end_time_orig()
51
+ # end
52
+ end
53
+
54
+ def convert_to_time(t)
55
+ if t.is_a?String
56
+ return Time.parse(t)
57
+ elsif t.is_a?Time
58
+ return t
59
+ else
60
+ raise "Time must be either Time or String"
61
+ end
62
+ end
63
+
64
+ end
65
+ end
66
+ # ---
@@ -0,0 +1,45 @@
1
+ #encoding: utf-8
2
+ require 'rubygems'
3
+ require 'outlook2gcal/gcal4ruby_gateway'
4
+
5
+
6
+ module Outlook2GCal
7
+
8
+ class GoogleCalendar
9
+ attr_accessor :user, :password, :calendar
10
+ attr_reader :events
11
+ def initialize(params)
12
+ @user = params[:gmail_user]
13
+ @password = params[:gmail_password]
14
+ @calendar = params[:gmail_calendar] || "LotusNotes"
15
+
16
+ @service = GCal4Ruby::Service.new
17
+ @service.authenticate(@user, @password )
18
+ @cal = GCal4Ruby::Calendar.find(@service, @calendar, {:scope => :first})[0]
19
+
20
+ end
21
+ def find_event(id)
22
+ return GCal4Ruby::Event.find(@service, {:id=>id}, {:calendar=>@calendar})
23
+ end
24
+ def new_event
25
+ event = GCal4Ruby::Event.new(@service)
26
+ event.calendar = @cal
27
+ event
28
+ end
29
+ def del_event(id)
30
+ begin
31
+ event = find_event(id)
32
+ if event
33
+ return event.delete unless event == []
34
+ end
35
+ rescue StandardError => e
36
+ print 'X'
37
+ $logger.error DateTime.now.to_s
38
+ $logger.error id
39
+ $logger.error e
40
+ end
41
+ return false
42
+ end
43
+ end
44
+
45
+ end
@@ -0,0 +1,86 @@
1
+ #encoding: utf-8
2
+ require 'rubygems'
3
+ require 'log4r'
4
+ require 'fileutils'
5
+
6
+ module Outlook2GCal
7
+
8
+ def Outlook2GCal::install(conf)
9
+ raise "environment variable %APPDATA% not set" unless ENV['APPDATA']
10
+ puts File.realdirpath(__FILE__+ '/../..')
11
+ puts File.realdirpath($0+ '/../..')
12
+ puts "---"
13
+
14
+ src_dir = File.realdirpath($0 + '/../..') + '/.'
15
+ dest_dir = File.realdirpath(ENV['APPDATA']+"/outlook2gcal_standalone_0.0.1")
16
+ if !File.exists?(dest_dir)
17
+ copy_files(src_dir,dest_dir)
18
+ create_licence_file(dest_dir)
19
+ create_start_file(dest_dir)
20
+ else
21
+ puts "Dir (#{dest_dir}) already exists. "
22
+ end
23
+
24
+ end
25
+
26
+ def Outlook2GCal::copy_files(src_dir, dest_dir)
27
+ puts "Copying files from " + src_dir
28
+ puts "to " + dest_dir
29
+ FileUtils::mkdir_p(dest_dir)
30
+ FileUtils::cp_r(src_dir, dest_dir)
31
+ puts "Install successful!"
32
+ end
33
+ def Outlook2GCal::create_licence_file(dest_dir)
34
+ licence =<<__END__
35
+ Copyright (c) 2012 Elias Kugler
36
+
37
+ Permission is hereby granted, free of charge, to any person obtaining
38
+ a copy of this software and associated documentation files (the
39
+ "Software"), to deal in the Software without restriction, including
40
+ without limitation the rights to use, copy, modify, merge, publish,
41
+ distribute, sublicense, and/or sell copies of the Software, and to
42
+ permit persons to whom the Software is furnished to do so, subject to
43
+ the following conditions:
44
+
45
+ The above copyright notice and this permission notice shall be
46
+ included in all copies or substantial portions of the Software.
47
+
48
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
49
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
50
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
51
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
52
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
53
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
54
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
55
+ __END__
56
+
57
+ f= File.new(dest_dir+"/MIT-LICENSE",'w+')
58
+ f.write(licence)
59
+ f.close
60
+ end
61
+
62
+ def Outlook2GCal::create_start_file(dest_dir)
63
+ cmd =<<__END__
64
+ @goto :end ;@rem TODO: delete this line, edit the next one !!!
65
+ .\\bin\\ruby .\\src\\outlook2gcal_portable sync -U <google-user> -P <google-password> -C <calendar-name> -D 30 --sync-desc --sync-alarm
66
+ @echo.
67
+ @pause
68
+ @rem TODO: delete the following lines !!!
69
+
70
+ :end
71
+ @echo off
72
+ echo --------------------------
73
+ echo TODO!!! Edit this file!!
74
+ echo --------------------------
75
+ .\\bin\\ruby .\\src\\outlook2gcal_portable
76
+ echo --------------------------
77
+ echo TODO!!! Edit this file!!
78
+ echo --------------------------
79
+ @pause
80
+ __END__
81
+
82
+ f= File.new(dest_dir+"/start_outlook2gcal_portable.bat",'w+')
83
+ f.write(cmd)
84
+ f.close
85
+ end
86
+ end
@@ -0,0 +1,234 @@
1
+ #encoding: utf-8
2
+ require 'win32ole'
3
+
4
+ # Info:
5
+ # http://msdn.microsoft.com/en-us/library/dd469461(v=office.12).aspx
6
+ # http://msdn.microsoft.com/en-us/library/ff870661.aspx
7
+
8
+ module Outlook2GCal
9
+ class EventRepeat
10
+ attr_accessor :start_time, :end_time
11
+ def initialize
12
+ end
13
+ end
14
+ class OutlookCalendar
15
+ attr_accessor :server, :user, :password, :db
16
+ attr_reader :events
17
+ def initialize(params)
18
+ @server = params[:notes_server] || '' # local
19
+ @user = params[:notes_user]
20
+ @password = params[:notes_password]
21
+ @db = params[:notes_db]
22
+
23
+ created = false
24
+ @ol = nil
25
+ begin
26
+ @ol = WIN32OLE.connect("Outlook.Application")
27
+ rescue
28
+ @created = true
29
+ @ol = WIN32OLE.new("Outlook.Application")
30
+ end
31
+ ns = @ol.GetNameSpace("MAPI")
32
+ folder = ns.GetDefaultFolder(9) #olFolderCalendar
33
+ raise "olFolderCalendar View not found" unless folder
34
+ @events = OutlookEvents.new(folder)
35
+
36
+ end
37
+ def quit
38
+ @ol.Quit if @created # TODO
39
+ end
40
+ end
41
+ class OutlookEvents
42
+ def initialize(folder)
43
+ @calendar_items = folder.Items
44
+ @calendar_items.Sort("[Start]")
45
+ @calendar_items.IncludeRecurrences = true
46
+ end
47
+ def each
48
+ @calendar_items.each do |event|
49
+ begin
50
+ yield OutlookEvent.new(event)
51
+ rescue StandardError => e
52
+ print 'X'
53
+ $logger.error DateTime.now.to_s
54
+ $logger.error e
55
+ $logger.error event
56
+ end
57
+ end
58
+ end
59
+ end
60
+ class OutlookEvent
61
+ # Note: The value of the minutes can be any arbitrary number of minutes between 5 minutes to 4 weeks.
62
+ GCAL_MAX_REMINDER = 40320 # 4 Weeks in minutes
63
+ GCAL_MIN_REMINDER = 5
64
+ APPOINTMENTTYPE = {'0' =>:appointment,
65
+ '1' => :anniversary,
66
+ '2' => :all_day_event,
67
+ '3' => :meeting,
68
+ '4' => :reminder}
69
+ attr_accessor :uid,
70
+ :subject,
71
+ :where,
72
+ :start_time, :end_time,
73
+ :last_modified, #
74
+ :appointmenttype,
75
+ :content,
76
+ :repeats,
77
+ :alarm, :alarm_offset,
78
+ :required_names, :optional_names, :chair # todo ???
79
+
80
+ def initialize(outlook_event)
81
+ fill_alarm(outlook_event) #TODO
82
+
83
+ # Notes Id
84
+ @uid = outlook_event.GlobalAppointmentID
85
+
86
+ # Subject
87
+ if outlook_event.Subject
88
+ @subject = outlook_event.Subject
89
+ else
90
+ @subject = ''
91
+ $logger.warn 'no subject. uid: '+@uid
92
+ end
93
+
94
+ # Room/Location
95
+ @where = outlook_event.Location || ''
96
+
97
+ # start date + time
98
+ @start_time = outlook_event.Start.to_s
99
+
100
+ # end date + time
101
+ @end_time = outlook_event.End.to_s
102
+
103
+ # event type # TODO -> remove
104
+ @appointmenttype = APPOINTMENTTYPE['3'] # Meeting
105
+
106
+ #p outlook_event.LastModificationTime.to_s
107
+ @last_modified = DateTime.parse(outlook_event.LastModificationTime.to_s)
108
+ @content = outlook_event.Body
109
+
110
+ # -- neue
111
+ @all_day_event = outlook_event.AllDayEvent
112
+
113
+ # fill_repeats(outlook_event)
114
+
115
+ @chair = outlook_event.Organizer
116
+ @required_names = outlook_event.RequiredAttendees
117
+ @optional_names = outlook_event.OptionalAttendees
118
+ end
119
+ def all_day?
120
+ @all_day_event
121
+ end
122
+ def meeting?
123
+ true # TODO
124
+ end
125
+
126
+ def supported?
127
+ # anniversaries are now (v.0.0.7) supported
128
+ @appointmenttype #and (@appointmenttype != :anniversary)
129
+ end
130
+ def repeats?
131
+ @repeats.size > 1
132
+ end
133
+ def fill_alarm(outlook_event)
134
+ # Alarm
135
+ @alarm = false
136
+ @alarm_offset = 0
137
+ if outlook_event.ReminderSet
138
+ then
139
+ @alarm = true
140
+ @alarm_offset = outlook_event.ReminderMinutesBeforeStart
141
+ end
142
+
143
+ end
144
+ def fill_repeats(outlook_event)
145
+ @repeats = []
146
+ end
147
+ def formatted_names
148
+ names = "Chair: #{@chair}\nRequired: "
149
+ names += (@required_names || "")
150
+ names += "\nOptional: "+ (@optional_names || "")
151
+ names += "\n---\n"
152
+ #p names
153
+ names
154
+ end
155
+
156
+
157
+ # obsolete methods -> TODO remove!!
158
+ def _names_to_str_name_email(names)
159
+ (names.inject([]) do |x,y|
160
+ email, name = '', ''
161
+ email = "<#{y[:email]}>" if (y[:email] and y[:email] != '')
162
+ name = "\"#{y[:name]}\" " if (y[:name] and y[:name] != '')
163
+
164
+ x << name + email
165
+ x
166
+ end).join(', ')
167
+ end
168
+
169
+ def _names_to_str(names)
170
+ (names.inject([]) do |x,y|
171
+ if y[:email] and y[:email] != ''
172
+ x << y[:email] if y[:email]
173
+ elsif y[:name]
174
+ x << y[:name] if y[:name]
175
+ end
176
+ x
177
+ end).join(', ')
178
+ end
179
+ def _all_names
180
+ names = []
181
+ names += @chair || []
182
+ names += @required_names || []
183
+ names += @optional_names || []
184
+
185
+ names.uniq
186
+ end
187
+ def _fill_names(outlook_event)
188
+ @chair = outlook_event.Organizer
189
+ @required_names = outlook_event.RequiredAttendees
190
+ @optional_names = outlook_event.OptionalAttendees
191
+ end
192
+ def _fill_chair(outlook_event)
193
+ chair = []
194
+ chair << outlook_event.Organizer
195
+ chair
196
+ end
197
+ def _find_email(names, idx)
198
+ email = names[idx]
199
+ if email
200
+ email = nil if email == '.'
201
+ email = nil if email == ''
202
+ # email =~ /^CN=(.*)\/O=(.*)\/C=(.*)/
203
+ # email = $1 if $1
204
+ end
205
+ return email
206
+ end
207
+
208
+ def _fill_notes_names(outlook_event, notes_attr_name, notes_attr_email = nil)
209
+ names = []
210
+ notes_names = []
211
+ notes_names1 = outlook_event.GetFirstItem(notes_attr_name)
212
+ if notes_attr_email
213
+ notes_names2 = outlook_event.GetFirstItem(notes_attr_email)
214
+ notes_names2 = nil unless (notes_names2 and notes_names2.Values.size == notes_names1.Values.size)
215
+ end
216
+ if notes_names1
217
+ notes_names1.Values.each_with_index do |name, idx|
218
+ email = find_email(notes_names2.Values, idx) if notes_names2
219
+ #name =~ /^CN=(.*)\/O=(.*)\/C=(.*)/
220
+ #email ? short_name = name.split('/')[0] : short_name # use name+domain if email missing
221
+ short_name = name.split('/')[0]
222
+ # check if name is an email adress
223
+ if !email and short_name =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\Z/i
224
+ email = short_name
225
+ short_name = ''
226
+ end
227
+ names << {:name => (short_name || ''), :email => (email || '')}
228
+ end
229
+ end
230
+ names
231
+ end
232
+
233
+ end
234
+ end
@@ -0,0 +1,24 @@
1
+ #encoding: utf-8
2
+ #
3
+ class String
4
+ def asciify()
5
+ str = self
6
+ # ----------------------------------------------------------------
7
+ # Umlauts
8
+ # ----------------------------------------------------------------
9
+ =begin
10
+ str = str.gsub(/\334/,"Ü") #
11
+ str = str.gsub(/\374/,"ü") #
12
+ str = str.gsub(/\326/,"Ö") #
13
+ str = str.gsub(/\366/,"ö") #
14
+ str = str.gsub(/\304/,"Ä") #
15
+ str = str.gsub(/\344/,"ä") #
16
+ str = str.gsub(/\337/,"ß") #
17
+ str = str.gsub(/>/,"&gt;")
18
+ str = str.gsub(/</,"&lt;")
19
+ # bez_neu = Iconv.conv('UTF-8','CP850', bez_neu)
20
+ #str = str.gsub(/([^\d\w\s\.\[\]-])/, '')
21
+ =end
22
+ return str.encode('UTF-8')
23
+ end
24
+ end
@@ -0,0 +1,206 @@
1
+ #encoding: utf-8
2
+ require 'rubygems'
3
+ require 'log4r'
4
+ require 'uri'
5
+ require 'date'
6
+ require 'fileutils'
7
+ require 'outlook2gcal/outlook_calendar'
8
+ require 'outlook2gcal/google_calendar'
9
+ require 'outlook2gcal/string'
10
+ require 'outlook2gcal/counter'
11
+ require 'outlook2gcal/sync_entry'
12
+
13
+
14
+ module Outlook2GCal
15
+
16
+ class OutlookGoogleSync
17
+ def initialize(params)
18
+ @params = params
19
+ @google_calendar = Outlook2GCal::GoogleCalendar.new(params)
20
+
21
+ @sync_time = nil
22
+ if params[:days]
23
+ @min_sync_time = DateTime.now-params[:days] #*86400
24
+ end
25
+ if params[:days_max]
26
+ @max_sync_time = DateTime.now+params[:days_max] #*86400
27
+ else
28
+ @max_sync_time = DateTime.now+400 #*86400
29
+ end
30
+ @max_time = DateTime.parse("2038-01-18")
31
+ # do not sync the description unless the users wants to
32
+ @sync_desc = params[:sync_desc] # || true
33
+ @sync_alarm = params[:sync_alarm]
34
+ @sync_names = params[:sync_names]
35
+
36
+ init_logger
37
+ end
38
+ def init_logger
39
+ FileUtils::mkdir_p('Outlook2GCal')
40
+ $logger = Log4r::Logger.new("sync_logger")
41
+ Log4r::FileOutputter.new('logfile',
42
+ :filename=>"#{Dir.pwd}/Outlook2GCal/Outlook2GCal.log",
43
+ :trunc=>false,
44
+ :level=>Log4r::WARN)
45
+ $logger.add('logfile')
46
+ end
47
+ def sync_events
48
+ @counter = Counter.new
49
+ @sync_time = DateTime.now
50
+ sleep(0)
51
+ @outlook_calendar = Outlook2GCal::OutlookCalendar.new(@params)
52
+ @outlook_calendar.events.each do |outlook_event|
53
+ @counter.selects += 1
54
+ # if outlook_event.repeats?
55
+ # outlook_event.repeats.each do |r|
56
+ # sync_event(outlook_event, r.start_time, r.end_time, outlook_event.uid+'_'+r.start_time)
57
+ # end
58
+ # else
59
+ #p outlook_event.repeats
60
+ sync_event(outlook_event, outlook_event.start_time, outlook_event.end_time, outlook_event.uid)
61
+ # end
62
+ end
63
+ @outlook_calendar.quit
64
+ del_events()
65
+ @counter.end
66
+ return @counter
67
+ end
68
+
69
+ def sync_event(outlook_event, start_time, end_time, key)
70
+ sdt = DateTime.parse(start_time)
71
+ return unless sdt < @max_time # workaround
72
+
73
+ if end_time
74
+ edt = DateTime.parse(end_time)
75
+ return unless edt < @max_time # workaround
76
+ end
77
+
78
+ #puts DateTime.parse(outlook_event.end_time)
79
+
80
+ if (@min_sync_time and end_time and
81
+ @min_sync_time > DateTime.parse(end_time)) or
82
+ (@max_sync_time and start_time and
83
+ @max_sync_time < DateTime.parse(start_time))
84
+ then
85
+ @counter.ignored +=1
86
+ else
87
+ #p key
88
+ sync_entry = Outlook2GCal::SyncEntry.first(:outlook_uid => key)
89
+ if sync_entry
90
+ then
91
+ #puts DateTime.parse(outlook_event.end_time)
92
+ #p sync_entry.outlook_last_modified.to_s
93
+ #p outlook_event.last_modified
94
+ if sync_entry.outlook_last_modified < outlook_event.last_modified
95
+ then
96
+ #!!insert_update(sync_entry,outlook_event)
97
+ insert_update(sync_entry,outlook_event, start_time, end_time, key)
98
+ else
99
+ print "."
100
+ @counter.ignored +=1
101
+ sync_entry.sync_time = @sync_time
102
+ sync_entry.sync_action = 'N' # none
103
+ sync_entry.save
104
+ end
105
+ else
106
+ add_event(outlook_event, start_time, end_time, key)
107
+ end
108
+ end
109
+ end
110
+
111
+ def del_events
112
+ Outlook2GCal::SyncEntry.all(:sync_time.lt => @sync_time).each do |sync_entry|
113
+ @counter.deletes += 1
114
+ if @google_calendar.del_event(sync_entry.gcal_id)
115
+ print "D"
116
+ sync_entry.destroy
117
+ else
118
+ sync_entry.sync_time = @sync_time
119
+ sync_entry.sync_action = 'E'
120
+ sync_entry.save
121
+ print "E"
122
+ end
123
+ end
124
+ end
125
+ def init_google_event(outlook_event,start_time,end_time)
126
+ event = @google_calendar.new_event
127
+ google_event= set_google_event_attrs(outlook_event, event,start_time,end_time )
128
+ google_event.start_time = start_time
129
+ google_event.end_time = end_time
130
+ return google_event
131
+ end
132
+ def set_google_event_attrs(outlook_event, google_event,start_time=nil,end_time=nil)
133
+ google_event.title = outlook_event.subject.asciify if outlook_event.subject
134
+ if start_time
135
+ google_event.start_time = start_time
136
+ else
137
+ google_event.start_time = outlook_event.start_time
138
+ end
139
+ if end_time
140
+ google_event.end_time = end_time
141
+ else
142
+ google_event.end_time = outlook_event.end_time
143
+ end
144
+ google_event.where = outlook_event.where.asciify if outlook_event.where
145
+ google_event.all_day = outlook_event.all_day?
146
+
147
+ if (@sync_desc || @sync_names)
148
+ content = ''
149
+ content += outlook_event.formatted_names.asciify if @sync_names
150
+ content += outlook_event.content.asciify if @sync_desc
151
+ google_event.content = content
152
+ #puts content
153
+ end
154
+
155
+ if @sync_alarm and outlook_event.alarm
156
+ google_event.reminder = [{:method =>'alert', :minutes => outlook_event.alarm_offset }]
157
+ end
158
+
159
+ return google_event
160
+ end
161
+ def get_sync_entry_by_notes_uid(uid)
162
+ e1 = Outlook2GCal::SyncEntry.first(:outlook_uid => uid)
163
+ return e1
164
+ end
165
+ def insert_update(sync_entry,outlook_event, start_time, end_time, key)
166
+ gcal_event = @google_calendar.find_event(sync_entry.gcal_id)
167
+ if gcal_event == []
168
+ $logger.warn "Event not found for update"
169
+ add_event(outlook_event,start_time, end_time,key)
170
+ else
171
+ update_event(sync_entry, outlook_event, gcal_event, start_time, end_time)
172
+ end
173
+ end
174
+
175
+ def add_event(outlook_event, start_time, end_time, key)
176
+ print "I"
177
+ google_event=init_google_event(outlook_event, start_time, end_time)
178
+ #p google_event
179
+ ret = google_event.save
180
+ $logger.fatal "insert: cannot save gcal event" unless ret
181
+ raise "cannot save gcal event" unless ret
182
+ @counter.inserts +=1
183
+ sync_entry = Outlook2GCal::SyncEntry.new
184
+ sync_entry.outlook_uid = key #outlook_event.uid
185
+ sync_entry.sync_time = @sync_time
186
+ sync_entry.outlook_last_modified = outlook_event.last_modified
187
+ sync_entry.gcal_id = google_event.id
188
+ sync_entry.sync_action = 'I' # insert
189
+ sync_entry.save
190
+ end
191
+ def update_event(sync_entry, outlook_event, gcal_event, start_time, end_time)
192
+ print "U"
193
+ @counter.updates +=1
194
+ set_google_event_attrs(outlook_event, gcal_event, start_time, end_time)
195
+ ret = gcal_event.save
196
+ $logger.fatal "update: cannot save gcal event" unless ret
197
+ raise "cannot save gcal event" unless ret
198
+ sync_entry.sync_time = @sync_time
199
+ sync_entry.gcal_id = gcal_event.id
200
+ sync_entry.outlook_last_modified = outlook_event.last_modified
201
+ sync_entry.sync_action = 'U' # none
202
+ sync_entry.save
203
+ end
204
+ end
205
+
206
+ end
@@ -0,0 +1,31 @@
1
+ #encoding: utf-8
2
+ require 'dm-core'
3
+ require 'dm-migrations'
4
+ require 'fileutils'
5
+
6
+ module Outlook2GCal
7
+ raise "environment variable %APPDATA% not set" unless ENV['APPDATA']
8
+ FileUtils::cd(ENV['APPDATA'])
9
+ FileUtils::mkdir_p('outlook2gcal')
10
+
11
+ db_file = "#{Dir.pwd}/outlook2gcal/outlook2gcal.sqlite"
12
+ DataMapper::setup(:default, "sqlite3://#{db_file}")
13
+
14
+
15
+ class SyncEntry
16
+ include DataMapper::Resource
17
+ #storage_names[:repo] = 'ncal2gal_sync_entries'
18
+
19
+ property :id, Serial
20
+ property :sync_time, DateTime
21
+ property :sync_action, String
22
+
23
+ property :outlook_uid, Text, :index=>true
24
+ property :outlook_last_modified, DateTime
25
+ property :gcal_id, Text
26
+
27
+ end
28
+
29
+ # automatically create the SyncEntry table
30
+ SyncEntry.auto_migrate! unless File.exists?(db_file) #SyncEntry.table_exists?
31
+ end
@@ -0,0 +1,33 @@
1
+ require "rubygems"
2
+
3
+ spec = Gem::Specification.new do |s|
4
+ s.name = %q{outlook2gcal}
5
+ s.version = "0.0.1"
6
+ s.authors = ["Elias Kugler"]
7
+ s.email = %q{groesser3@gmail.com}
8
+ s.files = Dir["lib/**/*"] + Dir["bin/**/*"] + Dir["*.rb"] + ["MIT-LICENSE","outlook2gcal.gemspec"]
9
+ s.platform = Gem::Platform::RUBY
10
+ s.has_rdoc = true
11
+ s.extra_rdoc_files = ["README.rdoc", "CHANGELOG.rdoc"]
12
+ s.require_paths = ["lib"]
13
+ s.summary = %q{Sync your Outlook calendar with your Google calendar}
14
+ s.description = %q{This lib/tool syncs your Outlook calendar with your (private) Google calendar. The synchronisation is only one-way: Outlook events are pushed to your Google Calendar. All types of events (including recurring events like anniversaries) are supported.}
15
+ s.files.reject! { |fn| fn.include? "CVS" }
16
+ s.require_path = "lib"
17
+ s.default_executable = %q{outlook2gcal}
18
+ s.executables = ["outlook2gcal"]
19
+ s.homepage = %q{http://rubyforge.org/projects/outlook2gcal/}
20
+ s.rubyforge_project = %q{outlook2gcal}
21
+ s.add_dependency("dm-core", ">= 1.2.0")
22
+ s.add_dependency("dm-migrations", ">= 1.2.0")
23
+ s.add_dependency("dm-do-adapter", ">= 1.2.0")
24
+ s.add_dependency("dm-sqlite-adapter", ">= 1.2.0")
25
+ s.add_dependency("do_sqlite3", ">= 0.10.8")
26
+ s.add_dependency("sqlite3", ">= 1.3.6")
27
+ s.add_dependency("gdata4ruby", "=0.1.5")
28
+ s.add_dependency("groesser3-gcal4ruby", "=0.5.51") #!!! you need a modified version of this gem !!!
29
+ s.add_dependency("log4r", ">=1.1.10")
30
+
31
+ end
32
+
33
+
metadata ADDED
@@ -0,0 +1,208 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: outlook2gcal
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Elias Kugler
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: dm-core
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: 1.2.0
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: 1.2.0
30
+ - !ruby/object:Gem::Dependency
31
+ name: dm-migrations
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: 1.2.0
38
+ type: :runtime
39
+ prerelease: false
40
+ version_requirements: !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ! '>='
44
+ - !ruby/object:Gem::Version
45
+ version: 1.2.0
46
+ - !ruby/object:Gem::Dependency
47
+ name: dm-do-adapter
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: 1.2.0
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: 1.2.0
62
+ - !ruby/object:Gem::Dependency
63
+ name: dm-sqlite-adapter
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: 1.2.0
70
+ type: :runtime
71
+ prerelease: false
72
+ version_requirements: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ! '>='
76
+ - !ruby/object:Gem::Version
77
+ version: 1.2.0
78
+ - !ruby/object:Gem::Dependency
79
+ name: do_sqlite3
80
+ requirement: !ruby/object:Gem::Requirement
81
+ none: false
82
+ requirements:
83
+ - - ! '>='
84
+ - !ruby/object:Gem::Version
85
+ version: 0.10.8
86
+ type: :runtime
87
+ prerelease: false
88
+ version_requirements: !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ! '>='
92
+ - !ruby/object:Gem::Version
93
+ version: 0.10.8
94
+ - !ruby/object:Gem::Dependency
95
+ name: sqlite3
96
+ requirement: !ruby/object:Gem::Requirement
97
+ none: false
98
+ requirements:
99
+ - - ! '>='
100
+ - !ruby/object:Gem::Version
101
+ version: 1.3.6
102
+ type: :runtime
103
+ prerelease: false
104
+ version_requirements: !ruby/object:Gem::Requirement
105
+ none: false
106
+ requirements:
107
+ - - ! '>='
108
+ - !ruby/object:Gem::Version
109
+ version: 1.3.6
110
+ - !ruby/object:Gem::Dependency
111
+ name: gdata4ruby
112
+ requirement: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - '='
116
+ - !ruby/object:Gem::Version
117
+ version: 0.1.5
118
+ type: :runtime
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ none: false
122
+ requirements:
123
+ - - '='
124
+ - !ruby/object:Gem::Version
125
+ version: 0.1.5
126
+ - !ruby/object:Gem::Dependency
127
+ name: groesser3-gcal4ruby
128
+ requirement: !ruby/object:Gem::Requirement
129
+ none: false
130
+ requirements:
131
+ - - '='
132
+ - !ruby/object:Gem::Version
133
+ version: 0.5.51
134
+ type: :runtime
135
+ prerelease: false
136
+ version_requirements: !ruby/object:Gem::Requirement
137
+ none: false
138
+ requirements:
139
+ - - '='
140
+ - !ruby/object:Gem::Version
141
+ version: 0.5.51
142
+ - !ruby/object:Gem::Dependency
143
+ name: log4r
144
+ requirement: !ruby/object:Gem::Requirement
145
+ none: false
146
+ requirements:
147
+ - - ! '>='
148
+ - !ruby/object:Gem::Version
149
+ version: 1.1.10
150
+ type: :runtime
151
+ prerelease: false
152
+ version_requirements: !ruby/object:Gem::Requirement
153
+ none: false
154
+ requirements:
155
+ - - ! '>='
156
+ - !ruby/object:Gem::Version
157
+ version: 1.1.10
158
+ description: ! 'This lib/tool syncs your Outlook calendar with your (private) Google
159
+ calendar. The synchronisation is only one-way: Outlook events are pushed to your
160
+ Google Calendar. All types of events (including recurring events like anniversaries)
161
+ are supported.'
162
+ email: groesser3@gmail.com
163
+ executables:
164
+ - outlook2gcal
165
+ extensions: []
166
+ extra_rdoc_files:
167
+ - README.rdoc
168
+ - CHANGELOG.rdoc
169
+ files:
170
+ - lib/outlook2gcal/counter.rb
171
+ - lib/outlook2gcal/gcal4ruby_gateway.rb
172
+ - lib/outlook2gcal/google_calendar.rb
173
+ - lib/outlook2gcal/install.rb
174
+ - lib/outlook2gcal/outlook_calendar.rb
175
+ - lib/outlook2gcal/string.rb
176
+ - lib/outlook2gcal/sync.rb
177
+ - lib/outlook2gcal/sync_entry.rb
178
+ - bin/build_outlook2gcal_portable.cmd
179
+ - bin/outlook2gcal
180
+ - MIT-LICENSE
181
+ - outlook2gcal.gemspec
182
+ - README.rdoc
183
+ - CHANGELOG.rdoc
184
+ homepage: http://rubyforge.org/projects/outlook2gcal/
185
+ licenses: []
186
+ post_install_message:
187
+ rdoc_options: []
188
+ require_paths:
189
+ - lib
190
+ required_ruby_version: !ruby/object:Gem::Requirement
191
+ none: false
192
+ requirements:
193
+ - - ! '>='
194
+ - !ruby/object:Gem::Version
195
+ version: '0'
196
+ required_rubygems_version: !ruby/object:Gem::Requirement
197
+ none: false
198
+ requirements:
199
+ - - ! '>='
200
+ - !ruby/object:Gem::Version
201
+ version: '0'
202
+ requirements: []
203
+ rubyforge_project: outlook2gcal
204
+ rubygems_version: 1.8.24
205
+ signing_key:
206
+ specification_version: 3
207
+ summary: Sync your Outlook calendar with your Google calendar
208
+ test_files: []