agcaldav 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.
data/.gitignore ADDED
@@ -0,0 +1 @@
1
+ caldaver-test.sh
data/README.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ CalDAV library
2
+
3
+ based on 4fthawaiian/ruby-caldav and collectiveidea/caldav and martinpovolny/ruby-caldav
data/bin/caldav ADDED
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'caldav'
4
+
5
+ prg = CalDAV::CalDAVer.new
6
+ prg.run_args( ARGV )
7
+
data/caldavtest.rb ADDED
@@ -0,0 +1,100 @@
1
+ require './lib/caldav.rb'
2
+
3
+ class CalDAVTester
4
+
5
+ def initialize
6
+ $caldav = {
7
+ :host => 'localhost',
8
+ :port => 5232,
9
+ :url => '/user/calendar/',
10
+ :user => 'user',
11
+ :password => 'yourpassword'
12
+ }
13
+ begin
14
+ require '~/.caldav_cred.rb'
15
+ rescue LoadError
16
+ end
17
+
18
+ @cal = CalDAV::Client.new $caldav[:host], $caldav[:port], $caldav[:url], $caldav[:user], $caldav[:password]
19
+ end
20
+
21
+ attr :cal
22
+
23
+ def test_create_event
24
+ myevent = Event.new
25
+ myevent.dtstart = DateTime.parse( '2012/08/11 09:45')
26
+ myevent.dtend = DateTime.parse( '2012/08/11 10:45')
27
+ myevent.summary = "Jo?"
28
+
29
+ uuid, response = cal.create myevent
30
+
31
+ puts "CREATE Response #{response.code} #{response.message}: #{response.body}"
32
+ puts "UUID: #{uuid}"
33
+
34
+ return uuid
35
+ end
36
+
37
+ def test_delete_event( uuid )
38
+ response = cal.delete uuid
39
+ puts "DETELE Response #{response.code} #{response.message}: #{response.body}"
40
+ end
41
+
42
+ def test_get_event( uuid )
43
+ #ev, response = cal.get uuid
44
+ ev = cal.get( uuid )
45
+ #puts "GET Response #{response.code} #{response.message}: #{response.body}"
46
+ puts ev
47
+ end
48
+
49
+ def test_read_todo
50
+ puts '*' * 20 + ' TODO ' + '*' * 20
51
+ res = cal.todo
52
+
53
+ res.each{ |todo|
54
+ p todo
55
+ }
56
+ end
57
+
58
+ def test_report
59
+ puts '*' * 20 + ' EVENTS ' + '*' * 20
60
+ p cal.report "20111201T000000", "20131231T000000"
61
+ end
62
+
63
+ def test_query0
64
+ puts CalDAV::Query.event.to_xml
65
+
66
+ time1 = DateTime.parse('2011/08/08 09:45')
67
+ time2 = DateTime.parse('2012/08/08 10:45')
68
+ puts CalDAV::Query.event(time1..time2).to_xml
69
+ puts CalDAV::Query.event.uid( UUID.generate ).to_xml
70
+ #puts CalDAV::Query.todo.alarm(time1..time2).to_xml
71
+ #puts CalDAV::Query.event.attendee(email).partstat('NEEDS-ACTION').to_xml
72
+ #puts CalDAV::Query.todo.completed(false).status(:cancelled => false).to_xml
73
+ end
74
+
75
+ def test_query
76
+ p cal.query( CalDAV::Query.event ) #=> All events
77
+ p cal.query( CalDAV::Query.event(time1..time2) )
78
+ p cal.query( CalDAV::Query.event.uid("UID") )
79
+ p cal.query( CalDAV::Query.todo.alarm(time1..time2) )
80
+ p cal.query( CalDAV::Query.event.attendee(email).partstat('NEEDS-ACTION') )
81
+ p cal.query( CalDAV::Query.todo.completed(false).status(:cancelled => false) )
82
+ end
83
+ end
84
+
85
+ #r = CalDAV::Request::Report.new( "20111201T000000", "20131231T000000" )
86
+ #puts r.to_xml
87
+ #exit
88
+
89
+ t = CalDAVTester.new
90
+
91
+ t.test_query0
92
+
93
+ uuid = t.test_create_event
94
+ t.test_get_event( uuid )
95
+ t.test_delete_event( uuid )
96
+
97
+ t.test_report
98
+ t.test_read_todo
99
+ #t.test_query
100
+
data/lib/caldav.rb ADDED
@@ -0,0 +1,19 @@
1
+ require 'net/https'
2
+ require 'uuid'
3
+ require 'rexml/document'
4
+ require 'rexml/xpath'
5
+ require 'date'
6
+ require 'icalendar'
7
+ require 'time'
8
+
9
+ ['client.rb', 'request.rb', 'net.rb', 'query.rb', 'filter.rb', 'caldaver.rb', 'format.rb'].each do |f|
10
+ require File.join( File.dirname(__FILE__), 'caldav', f )
11
+ end
12
+
13
+ class Event
14
+ attr_accessor :uid, :created, :dtstart, :dtend, :lastmodified, :summary, :description, :name, :action
15
+ end
16
+
17
+ class Todo
18
+ attr_accessor :uid, :created, :summary, :dtstart, :status, :completed
19
+ end
@@ -0,0 +1,103 @@
1
+ require 'optparse'
2
+
3
+ # caldav --user USER --password PASSWORD --uri URI --command COMMAND
4
+ # caldav --user martin@solnet.cz --password test --uri https://mail.solnet.cz/caldav.php/martin.povolny@solnet.cz/test --command create_event
5
+
6
+ # caldav report --begin 2012-01-01 --end 2012-07-01 --format raw --user USER --password PASSWORD --uri https://in.solnet.cz/caldav.php/martin.povolny@solnet.cz/public
7
+ # caldav get --user USER --password PASSWD --uri https://mail.solnet.cz/caldav.php/martin.povolny%40solnet.cz/public/ --uuid 64d0b933-e916-4755-9e56-2f4d0d9068cb
8
+
9
+ module CalDAV
10
+
11
+ class CalDAVer
12
+
13
+ def create_object( options )
14
+ if options[:raw]
15
+ # STDIN
16
+ return STDIN.read(nil)
17
+ else
18
+ # options[:subject]
19
+ # options[:login]
20
+ # options[:summary]
21
+ # options[:begin]
22
+ # options[:end]
23
+ # options[:due]
24
+ case options[:what].intern
25
+ when :task
26
+ # FIXME
27
+ when :event
28
+ # FIXME
29
+ when :contact
30
+ # FIXME
31
+ else
32
+ print_help_and_exit if options[:command].to_s.empty? or options[:uri].to_s.empty?
33
+ end
34
+ end
35
+ end
36
+
37
+ def print_help_and_exit
38
+ puts @o.help
39
+ exit 1
40
+ end
41
+
42
+ def run_args( args )
43
+ options = {}
44
+ @o = OptionParser.new do |o|
45
+ o.banner = "Usage: caldaver [command] [options]"
46
+ o.on('-p', '--password [STRING]', String, 'Password') { |p| options[:password] = p }
47
+ o.on('-u', '--user [STRING}', String, 'User (login)') { |l| options[:login] = l }
48
+ o.on('--uri [STRING]', String, 'Calendar URI') { |uri| options[:uri] = uri }
49
+ o.on('--format [STRING]', String, 'Format of output: raw,pretty,[debug]') {
50
+ |fmt| options[:fmt] = fmt }
51
+ ##o.on('--command [STRING]', String, 'Command') { |c| options[:command] = c }
52
+ o.on('--uuid [STRING]', String, 'UUID') { |u| options[:uuid] = u }
53
+ # what to create
54
+ o.on('--what [STRING]', String, 'Event/task/contact') { |c| options[:command] = command }
55
+ o.on('--raw', 'Read raw data (event/task/contact) from STDIN') { |raw| options[:raw] = true }
56
+ # report and event options
57
+ o.on('--begin [DATETIME]', String, 'Start time') { |dt| options[:begin] = dt }
58
+ o.on('--end [DATETIME]', String, 'End time') { |dt| options[:end] = dt }
59
+ o.on('--due [DATETIME]', String, 'Due time') { |dt| options[:due] = dt }
60
+ #o.on('--begin [DATETIME]', DateTime, 'Start time') { |dt| options[:begin] = dt }
61
+ #o.on('--end [DATETIME]', DateTime, 'End time') { |dt| options[:end] = dt }
62
+ # event options
63
+ o.on('--summary [string]', String, 'summary of event/task') { |s| options[:summary] = s }
64
+ o.on('--location [string]', String, 'location of event/task') { |s| options[:location] = s }
65
+ o.on('--subject [string]', String, 'subject of event/task') { |s| options[:subject] = s }
66
+ o.on('-h') { print_help_and_exit }
67
+ end
68
+
69
+ options[:command] = @o.parse( args )[0]
70
+
71
+ print_help_and_exit if options[:command].to_s.empty? or options[:uri].to_s.empty?
72
+ cal = CalDAV::Client.new( options[:uri], options[:login], options[:password] )
73
+
74
+ if options[:format]
75
+ cal.format = case options[:format].intern
76
+ when :raw
77
+ CalDAV::Format::Raw.new
78
+ when :pretty
79
+ CalDAV::Format::Pretty.new
80
+ when :debug
81
+ CalDAV::Format::Debug.new
82
+ else
83
+ nil
84
+ end
85
+ end
86
+
87
+ case options[:command].intern
88
+ when :create
89
+ obj = create_object( options )
90
+ when :delete
91
+ when :modify
92
+ obj = create_object( options )
93
+ when :get
94
+ puts cal.get( options[:uuid] )
95
+ when :report
96
+ puts cal.report( options[:begin], options[:end] )
97
+ else
98
+ print_help_and_exit
99
+ end
100
+ end
101
+ end
102
+
103
+ end
@@ -0,0 +1,220 @@
1
+ module CalDAV
2
+ class Client
3
+ include Icalendar
4
+ attr_accessor :host, :port, :url, :user, :password, :ssl
5
+
6
+ def format=( fmt )
7
+ @format = fmt
8
+ end
9
+
10
+ def format
11
+ @format ||= Format::Debug.new
12
+ end
13
+
14
+ def initialize( *args )
15
+ case args.length
16
+ when 3
17
+ __init_from_uri( *args )
18
+ when 5
19
+ __init_from_host_port( *args )
20
+ else
21
+ raise "#{self.class.to_s}: invalid number of arguments: #{args.length}"
22
+ end
23
+ end
24
+
25
+ def __init_from_uri( suri, user, password )
26
+ uri = URI( suri )
27
+ @host = uri.host
28
+ @port = uri.port
29
+ @url = [ uri.scheme, '://', uri.host, uri.path ].join('') # FIXME: port?
30
+ @user = user
31
+ @password = password
32
+ @ssl = uri.scheme == 'https'
33
+ end
34
+
35
+ def __init_from_host_port( host, port, url, user, password )
36
+ @host = host
37
+ @port = port
38
+ @url = url
39
+ @user = user
40
+ @password = password
41
+ @ssl = port == 443
42
+ end
43
+
44
+ def __create_http
45
+ http = Net::HTTP.new(@host, @port)
46
+ http.use_ssl = @ssl
47
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
48
+ #http.set_debug_output $stderr
49
+ http
50
+ end
51
+
52
+ def report start, stop
53
+ res = nil
54
+ __create_http.start {|http|
55
+ req = Net::HTTP::Report.new(@url, initheader = {'Content-Type'=>'application/xml'} )
56
+ req.basic_auth @user, @password
57
+ req.body = CalDAV::Request::ReportVEVENT.new( start, stop ).to_xml
58
+ res = http.request( req )
59
+ }
60
+ format.parse_calendar( res.body )
61
+ end
62
+
63
+ def get uuid
64
+ res = nil
65
+ __create_http.start {|http|
66
+ req = Net::HTTP::Get.new("#{@url}/#{uuid}.ics")
67
+ req.basic_auth @user, @password
68
+ res = http.request( req )
69
+ }
70
+
71
+ # FIXME: process HTTP code
72
+ format.parse_single( res.body )
73
+ end
74
+
75
+ def delete uuid
76
+ __create_http.start {|http|
77
+ req = Net::HTTP::Delete.new("#{@url}/#{uuid}.ics")
78
+ req.basic_auth @user, @password
79
+ res = http.request( req )
80
+ }
81
+ end
82
+
83
+ def create event
84
+ nowstr = DateTime.now.strftime "%Y%m%dT%H%M%SZ"
85
+ uuid = UUID.generate
86
+ dings = """BEGIN:VCALENDAR
87
+ PRODID:Caldav.rb
88
+ VERSION:2.0
89
+ BEGIN:VEVENT
90
+ CREATED:#{nowstr}
91
+ UID:#{uuid}
92
+ SUMMARY:#{event.summary}
93
+ DTSTART:#{event.dtstart.strftime("%Y%m%dT%H%M%S")}
94
+ DTEND:#{event.dtend.strftime("%Y%m%dT%H%M%S")}
95
+ END:VEVENT
96
+ END:VCALENDAR"""
97
+
98
+ res = nil
99
+ http = Net::HTTP.new(@host, @port)
100
+ __create_http.start { |http|
101
+ req = Net::HTTP::Put.new("#{@url}/#{uuid}.ics")
102
+ req['Content-Type'] = 'text/calendar'
103
+ req.basic_auth @user, @password
104
+ req.body = dings
105
+ res = http.request( req )
106
+ }
107
+ return uuid, res
108
+ end
109
+
110
+ def add_alarm tevent, altCal="Calendar"
111
+ #[#<Icalendar::Alarm:0x10b9d1b90 @name=\"VALARM\", @components={}, @properties={\"trigger\"=>\"-PT5M\", \"action\"=>\"DISPLAY\", \"description\"=>\"\"}>]
112
+ dtstart_string = ( Time.parse(tevent.dtstart.to_s) + Time.now.utc_offset.to_i.abs ).strftime "%Y%m%dT%H%M%S"
113
+ dtend_string = ( Time.parse(tevent.dtend.to_s) + Time.now.utc_offset.to_i.abs ).strftime "%Y%m%dT%H%M%S"
114
+ alarmText = <<EOL
115
+ BEGIN:VCALENDAR
116
+ VERSION:2.0
117
+ PRODID:Ruby iCalendar
118
+ BEGIN:VEVENT
119
+ UID:#{tevent.uid}
120
+ SUMMARY:#{tevent.summary}
121
+ DESCRIPTION:#{tevent.description}
122
+ DTSTART:#{dtstart_string}
123
+ DTEND:#{dtend_string}
124
+ BEGIN:VALARM
125
+ ACTION:DISPLAY
126
+ TRIGGER;RELATED=START:-PT5M
127
+ DESCRIPTION:Reminder
128
+ END:VALARM
129
+ BEGIN:VALARM
130
+ TRIGGER:-PT5M
131
+ ACTION:EMAIL
132
+ ATTENDEE:#{tevent.organizer}
133
+ SUMMARY:#{tevent.summary}
134
+ DESCRIPTION:#{tevent.description}
135
+ TRIGGER:-PT5M
136
+ END:VALARM
137
+ END:VEVENT
138
+ END:VCALENDAR
139
+ EOL
140
+ p alarmText
141
+ res = nil
142
+ puts "#{@url}/#{tevent.uid}.ics"
143
+ thttp = __create_http.start
144
+ #thttp.set_debug_output $stderr
145
+ req = Net::HTTP::Put.new("#{@url}/#{tevent.uid}.ics", initheader = {'Content-Type'=>'text/calendar'} )
146
+ req.basic_auth @user, @password
147
+ req.body = alarmText
148
+ res = thttp.request( req )
149
+ p res.inspect
150
+
151
+ return tevent.uid
152
+ end
153
+
154
+ def update event
155
+ dings = """BEGIN:VCALENDAR
156
+ PRODID:Caldav.rb
157
+ VERSION:2.0
158
+
159
+ BEGIN:VTIMEZONE
160
+ TZID:/Europe/Vienna
161
+ X-LIC-LOCATION:Europe/Vienna
162
+ BEGIN:DAYLIGHT
163
+ TZOFFSETFROM:+0100
164
+ TZOFFSETTO:+0200
165
+ TZNAME:CEST
166
+ DTSTART:19700329T020000
167
+ RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=3
168
+ END:DAYLIGHT
169
+ BEGIN:STANDARD
170
+ TZOFFSETFROM:+0200
171
+ TZOFFSETTO:+0100
172
+ TZNAME:CET
173
+ DTSTART:19701025T030000
174
+ RRULE:FREQ=YEARLY;INTERVAL=1;BYDAY=-1SU;BYMONTH=10
175
+ END:STANDARD
176
+ END:VTIMEZONE
177
+
178
+ BEGIN:VEVENT
179
+ CREATED:#{event.created}
180
+ UID:#{event.uid}
181
+ SUMMARY:#{event.summary}
182
+ DTSTART;TZID=Europe/Vienna:#{event.dtstart}
183
+ DTEND;TZID=Europe/Vienna:#{event.dtend.rfc3339}
184
+ END:VEVENT
185
+ END:VCALENDAR"""
186
+
187
+ res = nil
188
+ __create_http.start {|http|
189
+ req = Net::HTTP::Put.new("#{@url}/#{event.uid}.ics", initheader = {'Content-Type'=>'text/calendar'} )
190
+ req.basic_auth @user, @password
191
+ req.body = dings
192
+ res = http.request( req )
193
+ }
194
+ return event.uid
195
+ end
196
+
197
+ def todo
198
+ res = nil
199
+ __create_http.start {|http|
200
+ req = Net::HTTP::Report.new(@url, initheader = {'Content-Type'=>'application/xml'} )
201
+ req.basic_auth @user, @password
202
+ req.body = CalDAV::Request::ReportVTODO.new.to_xml
203
+ res = http.request( req )
204
+ }
205
+ # FIXME: process HTTP code
206
+ format.parse_todo( res.body )
207
+ end
208
+
209
+ def filterTimezone( vcal )
210
+ data = ""
211
+ inTZ = false
212
+ vcal.split("\n").each{ |l|
213
+ inTZ = true if l.index("BEGIN:VTIMEZONE")
214
+ data << l+"\n" unless inTZ
215
+ inTZ = false if l.index("END:VTIMEZONE")
216
+ }
217
+ return data
218
+ end
219
+ end
220
+ end
@@ -0,0 +1,78 @@
1
+ module CalDAV
2
+ module Filter
3
+ class Base
4
+ attr_accessor :parent, :child
5
+
6
+ def to_xml(xml = Builder::XmlMarkup.new(:indent => 2))
7
+ if parent
8
+ parent.to_xml
9
+ else
10
+ build_xml(xml)
11
+ end
12
+ end
13
+
14
+ def build_xml(xml)
15
+ #do nothing
16
+ end
17
+
18
+ def child=(child)
19
+ @child = child
20
+ child.parent = self
21
+ end
22
+ end
23
+
24
+ class Component < Base
25
+ attr_accessor :name
26
+
27
+ def initialize(name, parent = nil)
28
+ self.name = name
29
+ self.parent = parent
30
+ end
31
+
32
+ def time_range(range)
33
+ self.child = TimeRange.new(range, self)
34
+ end
35
+
36
+ def uid(uid)
37
+ self.child = Property.new("UID", uid, self)
38
+ end
39
+
40
+ def build_xml(xml)
41
+ xml.tag! "cal:comp-filter", :name => name do
42
+ child.build_xml(xml) unless child.nil?
43
+ end
44
+ end
45
+ end
46
+
47
+ class TimeRange < Base
48
+ attr_accessor :range
49
+
50
+ def initialize(range, parent = nil)
51
+ self.range = range
52
+ self.parent = parent
53
+ end
54
+
55
+ def build_xml(xml)
56
+ xml.tag! "cal:time-range",
57
+ :start => range.begin.to_ical,
58
+ :end => range.end.to_ical
59
+ end
60
+ end
61
+
62
+ class Property < Base
63
+ attr_accessor :name, :text
64
+
65
+ def initialize(name, text, parent = nil)
66
+ self.name = name
67
+ self.text = text
68
+ self.parent = parent
69
+ end
70
+
71
+ def build_xml(xml)
72
+ xml.tag! "cal:prop-filter", :name => self.name do
73
+ xml.tag! "cal:text-match", self.text, :collation => "i;octet"
74
+ end
75
+ end
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,64 @@
1
+ module CalDAV
2
+ module Format
3
+ class Raw
4
+ def method_missing(m, *args, &block)
5
+ return *args
6
+ end
7
+ end
8
+
9
+ class Debug < Raw
10
+ end
11
+
12
+ class Pretty < Raw
13
+ def parse_calendar( body )
14
+ result = []
15
+ xml = REXML::Document.new( res.body )
16
+ REXML::XPath.each( xml, '//c:calendar-data/', { "c"=>"urn:ietf:params:xml:ns:caldav"} ){ |c|
17
+ result += parse_events( c.text )
18
+ }
19
+ return result
20
+ end
21
+
22
+ def parse_todo( body )
23
+ result = []
24
+ xml = REXML::Document.new( body )
25
+ REXML::XPath.each( xml, '//c:calendar-data/', { "c"=>"urn:ietf:params:xml:ns:caldav"} ){ |c|
26
+ p c.text
27
+ p parse_tasks( c.text )
28
+ result += parse_tasks( c.text )
29
+ }
30
+ return result
31
+ end
32
+
33
+ def parse_tasks( vcal )
34
+ return_tasks = Array.new
35
+ cals = Icalendar.parse(vcal)
36
+ cals.each { |tcal|
37
+ tcal.todos.each { |ttask| # FIXME
38
+ return_tasks << ttask
39
+ }
40
+ }
41
+ return return_tasks
42
+ end
43
+
44
+ def parse_events( vcal )
45
+ return_events = Array.new
46
+ cals = Icalendar.parse(vcal)
47
+ cals.each { |tcal|
48
+ tcal.events.each { |tevent|
49
+ if tevent.recurrence_id.to_s.empty? # skip recurring events
50
+ return_events << tevent
51
+ end
52
+ }
53
+ }
54
+ return return_events
55
+ end
56
+
57
+ def parse_single( body )
58
+ # FIXME: parse event/todo/vcard
59
+ parse_events( body )
60
+ end
61
+ end
62
+ end
63
+ end
64
+
data/lib/caldav/net.rb ADDED
@@ -0,0 +1,16 @@
1
+ module Net
2
+ class HTTP
3
+ class Report < HTTPRequest
4
+ METHOD = 'REPORT'
5
+ REQUEST_HAS_BODY = true
6
+ RESPONSE_HAS_BODY = true
7
+ end
8
+
9
+ class Mkcalendar < HTTPRequest
10
+ METHOD = 'MKCALENDAR'
11
+ REQUEST_HAS_BODY = true
12
+ RESPONSE_HAS_BODY = true
13
+ end
14
+ end
15
+ end
16
+
@@ -0,0 +1,51 @@
1
+ module CalDAV
2
+ class Query
3
+ attr_accessor :child
4
+
5
+ #TODO: raise error if to_xml is called before child is assigned
6
+ def to_xml(xml = Builder::XmlMarkup.new(:indent => 2))
7
+ xml.instruct!
8
+ xml.tag! "cal:calendar-query", CalDAV::NAMESPACES do
9
+ xml.tag! "dav:prop" do
10
+ xml.tag! "dav:getetag"
11
+ xml.tag! "cal:calendar-data"
12
+ end
13
+ xml.tag! "cal:filter" do
14
+ cal = Filter::Component.new("VCALENDAR", self)
15
+ cal.child = self.child
16
+ cal.build_xml(xml)
17
+ end
18
+ end
19
+ end
20
+
21
+ def event(param = nil)
22
+ self.child = Filter::Component.new("VEVENT")
23
+ if param.is_a? Range
24
+ self.child.time_range(param)
25
+ elsif param.is_a? String
26
+ self.child.uid(param)
27
+ else
28
+ self.child
29
+ end
30
+ end
31
+
32
+ def todo(param = nil)
33
+ self.child = Filter::Component.new("VTODO")
34
+ self.child
35
+ end
36
+
37
+ def child=(child)
38
+ child.parent = self
39
+ @child = child
40
+ end
41
+
42
+ def self.event( param=nil )
43
+ self.new.event( param )
44
+ end
45
+
46
+
47
+ def self.todo( param=nil )
48
+ self.new.todo
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,76 @@
1
+ require 'builder'
2
+
3
+ module CalDAV
4
+ NAMESPACES = { "xmlns:d" => 'DAV:', "xmlns:c" => "urn:ietf:params:xml:ns:caldav" }
5
+ module Request
6
+ class Base
7
+ def initialize
8
+ @xml = Builder::XmlMarkup.new(:indent => 2)
9
+ @xml.instruct!
10
+ end
11
+ attr :xml
12
+ end
13
+
14
+ class Mkcalendar < Base
15
+ attr_accessor :displayname, :description
16
+
17
+ def initialize(displayname = nil, description = nil)
18
+ @displayname = displayname
19
+ @description = description
20
+ end
21
+
22
+ def to_xml
23
+ xml.c :mkcalendar, NAMESPACES do
24
+ xml.d :set do
25
+ xml.d :prop do
26
+ xml.d :displayname, displayname unless displayname.to_s.empty?
27
+ xml.tag! "c:calendar-description", description, "xml:lang" => "en" unless description.to_s.empty?
28
+ end
29
+ end
30
+ end
31
+ end
32
+ end
33
+
34
+ class ReportVEVENT < Base
35
+ attr_accessor :tstart, :tend
36
+
37
+ def initialize( tstart=nil, tend=nil )
38
+ @tstart = tstart
39
+ @tend = tend
40
+ super()
41
+ end
42
+
43
+ def to_xml
44
+ xml.c 'calendar-query'.intern, NAMESPACES do
45
+ xml.d :prop do
46
+ xml.d :getetag
47
+ xml.c 'calendar-data'.intern
48
+ end
49
+ xml.c :filter do
50
+ xml.c 'comp-filter'.intern, :name=> 'VCALENDAR' do
51
+ xml.c 'comp-filter'.intern, :name=> 'VEVENT' do
52
+ xml.c 'time-range'.intern, :start=> "#{tstart}Z", :end=> "#{tend}Z"
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+ class ReportVTODO < Base
61
+ def to_xml
62
+ xml.c 'calendar-query'.intern, NAMESPACES do
63
+ xml.d :prop do
64
+ xml.d :getetag
65
+ xml.c 'calendar-data'.intern
66
+ end
67
+ xml.c :filter do
68
+ xml.c 'comp-filter'.intern, :name=> 'VCALENDAR' do
69
+ xml.c 'comp-filter'.intern, :name=> 'VTODO'
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end
75
+ end
76
+ end
@@ -0,0 +1,3 @@
1
+ module CalDAV
2
+ VERSION=0.1
3
+ end
@@ -0,0 +1,20 @@
1
+ require File.expand_path('../lib/caldav/version', __FILE__)
2
+
3
+ Gem::Specification.new do |s|
4
+ s.name = "agcaldav"
5
+ s.version = CalDAV::VERSION
6
+ s.summary = "Ruby CalDAV client"
7
+ s.description = "Ruby client for CalDAV calendar and tasks."
8
+ s.homepage = "https://github.com/agilastic/ruby-caldav"
9
+ s.authors = ["Alex Ebeling-Hoppe"]
10
+ s.email = ["ebeling-hoppe@agilastic.de"]
11
+
12
+ s.add_runtime_dependency 'nokogiri'
13
+ s.add_runtime_dependency 'icalendar'
14
+ s.add_runtime_dependency 'uuid'
15
+ s.add_runtime_dependency 'builder'
16
+
17
+ s.files = `git ls-files`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{|f| File.basename(f)}
19
+ s.require_paths = ["lib"]
20
+ end
metadata ADDED
@@ -0,0 +1,124 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: agcaldav
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Alex Ebeling-Hoppe
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-12-26 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: nokogiri
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '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: '0'
30
+ - !ruby/object:Gem::Dependency
31
+ name: icalendar
32
+ requirement: !ruby/object:Gem::Requirement
33
+ none: false
34
+ requirements:
35
+ - - ! '>='
36
+ - !ruby/object:Gem::Version
37
+ version: '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: '0'
46
+ - !ruby/object:Gem::Dependency
47
+ name: uuid
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '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: '0'
62
+ - !ruby/object:Gem::Dependency
63
+ name: builder
64
+ requirement: !ruby/object:Gem::Requirement
65
+ none: false
66
+ requirements:
67
+ - - ! '>='
68
+ - !ruby/object:Gem::Version
69
+ version: '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: '0'
78
+ description: Ruby client for CalDAV calendar and tasks.
79
+ email:
80
+ - ebeling-hoppe@agilastic.de
81
+ executables:
82
+ - caldav
83
+ extensions: []
84
+ extra_rdoc_files: []
85
+ files:
86
+ - .gitignore
87
+ - README.rdoc
88
+ - bin/caldav
89
+ - caldavtest.rb
90
+ - lib/caldav.rb
91
+ - lib/caldav/caldaver.rb
92
+ - lib/caldav/client.rb
93
+ - lib/caldav/filter.rb
94
+ - lib/caldav/format.rb
95
+ - lib/caldav/net.rb
96
+ - lib/caldav/query.rb
97
+ - lib/caldav/request.rb
98
+ - lib/caldav/version.rb
99
+ - ruby-caldav.gemspec
100
+ homepage: https://github.com/agilastic/ruby-caldav
101
+ licenses: []
102
+ post_install_message:
103
+ rdoc_options: []
104
+ require_paths:
105
+ - lib
106
+ required_ruby_version: !ruby/object:Gem::Requirement
107
+ none: false
108
+ requirements:
109
+ - - ! '>='
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ required_rubygems_version: !ruby/object:Gem::Requirement
113
+ none: false
114
+ requirements:
115
+ - - ! '>='
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ requirements: []
119
+ rubyforge_project:
120
+ rubygems_version: 1.8.24
121
+ signing_key:
122
+ specification_version: 3
123
+ summary: Ruby CalDAV client
124
+ test_files: []