agcaldav 0.1

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