twisted-caldav 0.0.0.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NTc4MTIxMzFiZmM0MTgwZWJiZDNkN2E2ZjFhYWVjMDJjMTMzMjhhMQ==
5
+ data.tar.gz: !binary |-
6
+ NDUwNzIxZWIzNzcwYmUxZDQ0MjM4NjYyZGEwMGE0NGRhMWMxZTJhMw==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ YzdiODY5Zjc2M2JjY2U2OWVlNzdjN2RjMzE2YTYyZDYzZTJkYjI0YTgyYmFi
10
+ NTI0NGFiYmIxYjlkODFhYThkZjlmNzJkYTQzMmQwNDAyMzU2Yjg3N2ZhZmVl
11
+ ZDJmMDg5OTQ0ZmY4NjY5N2I1NjQ2ZWIyZDU1ZGE2MDgxZGIxYmE=
12
+ data.tar.gz: !binary |-
13
+ MzlmNzZkMWFhNzliNmFiNjU3OTBjODQzYjNlZTdlNTdhYjFmNjFiNTE1Mzc0
14
+ YzNjZDkzYzhjMWU1NDc3ZThjM2YyM2QzYTlkMGNlNjllN2NiMGI4ZWE3OTNi
15
+ NmMwZGM3ZDYyNTQ5MDg4NzkwOGI0NzdiZWQ2NDA0ZTFhMWI0MWE=
data/.gitignore ADDED
@@ -0,0 +1,34 @@
1
+ *.gem
2
+ *.rbc
3
+ /.config
4
+ /coverage/
5
+ /InstalledFiles
6
+ /pkg/
7
+ /spec/reports/
8
+ /test/tmp/
9
+ /test/version_tmp/
10
+ /tmp/
11
+
12
+ ## Specific to RubyMotion:
13
+ .dat*
14
+ .repl_history
15
+ build/
16
+
17
+ ## Documentation cache and generated files:
18
+ /.yardoc/
19
+ /_yardoc/
20
+ /doc/
21
+ /rdoc/
22
+
23
+ ## Environment normalisation:
24
+ /.bundle/
25
+ /lib/bundler/man/
26
+
27
+ # for a library or gem, you might want to ignore these files since the code is
28
+ # intended to run in multiple environments; otherwise, check them in:
29
+ # Gemfile.lock
30
+ # .ruby-version
31
+ # .ruby-gemset
32
+
33
+ # unless supporting rvm < 1.11.0 or doing something fancy, ignore this:
34
+ .rvmrc
data/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2014 Siddhartha Mukherjee
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,6 @@
1
+ twisted-caldav
2
+ ==============
3
+
4
+ Ruby client for searching, creating, editing calendar and tasks. Tested with ubuntu based calendar server installation.
5
+
6
+ It is a modified version of original caldav client 4fthawaiian/ruby-caldav
@@ -0,0 +1,12 @@
1
+ require 'net/https'
2
+ require 'net/http/digest_auth'
3
+ require 'uuid'
4
+ require 'rexml/document'
5
+ require 'rexml/xpath'
6
+ require 'icalendar'
7
+ require 'time'
8
+ require 'date'
9
+
10
+ ['client.rb', 'request.rb', 'net.rb', 'query.rb', 'filter.rb', 'event.rb', 'todo.rb', 'format.rb'].each do |f|
11
+ require File.join( File.dirname(__FILE__), 'twisted-caldav', f )
12
+ end
@@ -0,0 +1,328 @@
1
+ module TwistedCaldav
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( data )
15
+ unless data[:proxy_uri].nil?
16
+ proxy_uri = URI(data[:proxy_uri])
17
+ @proxy_host = proxy_uri.host
18
+ @proxy_port = proxy_uri.port.to_i
19
+ end
20
+
21
+ uri = URI(data[:uri])
22
+ @host = uri.host
23
+ @port = uri.port.to_i
24
+ @url = uri.path
25
+ @user = data[:user]
26
+ @password = data[:password]
27
+ @ssl = uri.scheme == 'https'
28
+
29
+ unless data[:authtype].nil?
30
+ @authtype = data[:authtype]
31
+ if @authtype == 'digest'
32
+
33
+ @digest_auth = Net::HTTP::DigestAuth.new
34
+ @duri = URI.parse data[:uri]
35
+ @duri.user = @user
36
+ @duri.password = @password
37
+
38
+ elsif @authtype == 'basic'
39
+ # this is fine for us
40
+ else
41
+ raise "Please use basic or digest"
42
+ end
43
+ else
44
+ @authtype = 'basic'
45
+ end
46
+ end
47
+
48
+ def __create_http
49
+ if @proxy_uri.nil?
50
+ http = Net::HTTP.new(@host, @port)
51
+ else
52
+ http = Net::HTTP.new(@host, @port, @proxy_host, @proxy_port)
53
+ end
54
+ if @ssl
55
+ http.use_ssl = @ssl
56
+ http.verify_mode = OpenSSL::SSL::VERIFY_NONE
57
+ end
58
+ http
59
+ end
60
+
61
+ def find_events data
62
+ result = ""
63
+ events = []
64
+ res = nil
65
+ __create_http.start {|http|
66
+
67
+ req = Net::HTTP::Report.new(@url, initheader = {'Content-Type'=>'application/xml'} )
68
+
69
+ if not @authtype == 'digest'
70
+ req.basic_auth @user, @password
71
+ else
72
+ req.add_field 'Authorization', digestauth('REPORT')
73
+ end
74
+ if data[:start].is_a? Integer
75
+ req.body = TwistedCaldav::Request::ReportVEVENT.new(Time.at(data[:start]).utc.strftime("%Y%m%dT%H%M%S"),
76
+ Time.at(data[:end]).utc.strftime("%Y%m%dT%H%M%S") ).to_xml
77
+ else
78
+ req.body = TwistedCaldav::Request::ReportVEVENT.new(Time.parse(data[:start]).utc.strftime("%Y%m%dT%H%M%S"),
79
+ Time.parse(data[:end]).utc.strftime("%Y%m%dT%H%M%S") ).to_xml
80
+ end
81
+ res = http.request(req)
82
+ }
83
+ errorhandling res
84
+ result = ""
85
+ #puts res.body
86
+ xml = REXML::Document.new(res.body)
87
+ REXML::XPath.each( xml, '//c:calendar-data/', {"c"=>"urn:ietf:params:xml:ns:caldav"} ){|c| result << c.text}
88
+ r = Icalendar.parse(result)
89
+ unless r.empty?
90
+ r.each do |calendar|
91
+ calendar.events.each do |event|
92
+ events << event
93
+ end
94
+ end
95
+ events
96
+ else
97
+ return false
98
+ end
99
+ end
100
+
101
+ def find_event uuid
102
+ res = nil
103
+ __create_http.start {|http|
104
+ req = Net::HTTP::Get.new("#{@url}/#{uuid}.ics")
105
+ if not @authtype == 'digest'
106
+ req.basic_auth @user, @password
107
+ else
108
+ req.add_field 'Authorization', digestauth('GET')
109
+ end
110
+ res = http.request( req )
111
+ }
112
+ errorhandling res
113
+ begin
114
+ r = Icalendar.parse(res.body)
115
+ rescue
116
+ return false
117
+ else
118
+ r.first.events.first
119
+ end
120
+
121
+
122
+ end
123
+
124
+ def delete_event uuid
125
+ res = nil
126
+ __create_http.start {|http|
127
+ req = Net::HTTP::Delete.new("#{@url}/#{uuid}.ics")
128
+ if not @authtype == 'digest'
129
+ req.basic_auth @user, @password
130
+ else
131
+ req.add_field 'Authorization', digestauth('DELETE')
132
+ end
133
+ res = http.request( req )
134
+ }
135
+ errorhandling res
136
+ # accept any success code
137
+ if res.code.to_i.between?(200,299)
138
+ return true
139
+ else
140
+ return false
141
+ end
142
+ end
143
+
144
+ def create_event event
145
+ c = Calendar.new
146
+ c.events = []
147
+ uuid = UUID.new.generate
148
+ raise DuplicateError if entry_with_uuid_exists?(uuid)
149
+ c.event do
150
+ uid uuid
151
+ dtstart DateTime.parse(event[:start])
152
+ dtend DateTime.parse(event[:end])
153
+ categories event[:categories]# Array
154
+ contacts event[:contacts] # Array
155
+ attendees event[:attendees]# Array
156
+ duration event[:duration]
157
+ summary event[:title]
158
+ description event[:description]
159
+ klass event[:accessibility] #PUBLIC, PRIVATE, CONFIDENTIAL
160
+ location event[:location]
161
+ geo_location event[:geo_location]
162
+ status event[:status]
163
+ url event[:url]
164
+ end
165
+ cstring = c.to_ical
166
+ res = nil
167
+ http = Net::HTTP.new(@host, @port)
168
+ __create_http.start { |http|
169
+ req = Net::HTTP::Put.new("#{@url}/#{uuid}.ics")
170
+ req['Content-Type'] = 'text/calendar'
171
+ if not @authtype == 'digest'
172
+ req.basic_auth @user, @password
173
+ else
174
+ req.add_field 'Authorization', digestauth('PUT')
175
+ end
176
+ req.body = cstring
177
+ res = http.request( req )
178
+ }
179
+ errorhandling res
180
+ find_event uuid
181
+ end
182
+
183
+ def update_event event
184
+ #TODO... fix me
185
+ if delete_event event[:uid]
186
+ create_event event
187
+ else
188
+ return false
189
+ end
190
+ end
191
+
192
+ def add_alarm tevent, altCal="Calendar"
193
+
194
+ end
195
+
196
+ def find_todo uuid
197
+ res = nil
198
+ __create_http.start {|http|
199
+ req = Net::HTTP::Get.new("#{@url}/#{uuid}.ics")
200
+ if not @authtype == 'digest'
201
+ req.basic_auth @user, @password
202
+ else
203
+ req.add_field 'Authorization', digestauth('GET')
204
+ end
205
+ res = http.request( req )
206
+ }
207
+ errorhandling res
208
+ r = Icalendar.parse(res.body)
209
+ r.first.todos.first
210
+ end
211
+
212
+
213
+
214
+
215
+
216
+ def create_todo todo
217
+ c = Calendar.new
218
+ uuid = UUID.new.generate
219
+ raise DuplicateError if entry_with_uuid_exists?(uuid)
220
+ c.todo do
221
+ uid uuid
222
+ start DateTime.parse(todo[:start])
223
+ duration todo[:duration]
224
+ summary todo[:title]
225
+ description todo[:description]
226
+ klass todo[:accessibility] #PUBLIC, PRIVATE, CONFIDENTIAL
227
+ location todo[:location]
228
+ percent todo[:percent]
229
+ priority todo[:priority]
230
+ url todo[:url]
231
+ geo todo[:geo_location]
232
+ status todo[:status]
233
+ end
234
+ c.todo.uid = uuid
235
+ cstring = c.to_ical
236
+ res = nil
237
+ http = Net::HTTP.new(@host, @port)
238
+ __create_http.start { |http|
239
+ req = Net::HTTP::Put.new("#{@url}/#{uuid}.ics")
240
+ req['Content-Type'] = 'text/calendar'
241
+ if not @authtype == 'digest'
242
+ req.basic_auth @user, @password
243
+ else
244
+ req.add_field 'Authorization', digestauth('PUT')
245
+ end
246
+ req.body = cstring
247
+ res = http.request( req )
248
+ }
249
+ errorhandling res
250
+ find_todo uuid
251
+ end
252
+
253
+ def create_todo
254
+ res = nil
255
+ raise DuplicateError if entry_with_uuid_exists?(uuid)
256
+
257
+ __create_http.start {|http|
258
+ req = Net::HTTP::Report.new(@url, initheader = {'Content-Type'=>'application/xml'} )
259
+ if not @authtype == 'digest'
260
+ req.basic_auth @user, @password
261
+ else
262
+ req.add_field 'Authorization', digestauth('REPORT')
263
+ end
264
+ req.body = TwistedCaldav::Request::ReportVTODO.new.to_xml
265
+ res = http.request( req )
266
+ }
267
+ errorhandling res
268
+ format.parse_todo( res.body )
269
+ end
270
+
271
+ private
272
+
273
+ def digestauth method
274
+
275
+ h = Net::HTTP.new @duri.host, @duri.port
276
+ if @ssl
277
+ h.use_ssl = @ssl
278
+ h.verify_mode = OpenSSL::SSL::VERIFY_NONE
279
+ end
280
+ req = Net::HTTP::Get.new @duri.request_uri
281
+
282
+ res = h.request req
283
+ # res is a 401 response with a WWW-Authenticate header
284
+
285
+ auth = @digest_auth.auth_header @duri, res['www-authenticate'], method
286
+
287
+ return auth
288
+ end
289
+
290
+ def entry_with_uuid_exists? uuid
291
+ res = nil
292
+
293
+ __create_http.start {|http|
294
+ req = Net::HTTP::Get.new("#{@url}/#{uuid}.ics")
295
+ if not @authtype == 'digest'
296
+ req.basic_auth @user, @password
297
+ else
298
+ req.add_field 'Authorization', digestauth('GET')
299
+ end
300
+
301
+ res = http.request( req )
302
+
303
+ }
304
+ begin
305
+ errorhandling res
306
+ Icalendar.parse(res.body)
307
+ rescue
308
+ return false
309
+ else
310
+ return true
311
+ end
312
+ end
313
+ def errorhandling response
314
+ raise NotExistError if response.code.to_i == 404
315
+ raise AuthenticationError if response.code.to_i == 401
316
+ raise NotExistError if response.code.to_i == 410
317
+ raise APIError if response.code.to_i >= 500
318
+ end
319
+ end
320
+
321
+
322
+ class TwistedCaldavError < StandardError
323
+ end
324
+ class AuthenticationError < TwistedCaldavError; end
325
+ class DuplicateError < TwistedCaldavError; end
326
+ class APIError < TwistedCaldavError; end
327
+ class NotExistError < TwistedCaldavError; end
328
+ end
@@ -0,0 +1,121 @@
1
+
2
+
3
+ module Icalendar
4
+ # A Event calendar component is a grouping of component
5
+ # properties, and possibly including Alarm calendar components, that
6
+ # represents a scheduled amount of time on a calendar. For example, it
7
+ # can be an activity; such as a one-hour long, department meeting from
8
+ # 8:00 AM to 9:00 AM, tomorrow. Generally, an event will take up time
9
+ # on an individual calendar.
10
+ class Event < Component
11
+ ical_component :alarms
12
+
13
+ ## Single instance properties
14
+
15
+ # Access classification (PUBLIC, PRIVATE, CONFIDENTIAL...)
16
+ ical_property :ip_class, :klass
17
+
18
+ # Date & time of creation
19
+ ical_property :created
20
+
21
+ # Complete description of the calendar component
22
+ ical_property :description
23
+
24
+ attr_accessor :tzid
25
+
26
+ # Specifies date-time when calendar component begins
27
+ ical_property :dtstart, :start
28
+
29
+ # Latitude & longitude for specified activity
30
+ ical_property :geo, :geo_location
31
+
32
+ # Date & time this item was last modified
33
+ ical_property :last_modified
34
+
35
+ # Specifies the intended venue for this activity
36
+ ical_property :location
37
+
38
+ # Defines organizer of this item
39
+ ical_property :organizer
40
+
41
+ # Defines relative priority for this item (1-9... 1 = best)
42
+ ical_property :priority
43
+
44
+ # Indicate date & time when this item was created
45
+ ical_property :dtstamp, :timestamp
46
+
47
+ # Revision sequence number for this item
48
+ ical_property :sequence, :seq
49
+
50
+ # Defines overall status or confirmation of this item
51
+ ical_property :status
52
+ ical_property :summary
53
+ ical_property :transp, :transparency
54
+
55
+ # Defines a persistent, globally unique id for this item
56
+ ical_property :uid, :unique_id
57
+
58
+ # Defines a URL associated with this item
59
+ ical_property :url
60
+ ical_property :recurrence_id, :recurid
61
+
62
+ ## Single but mutually exclusive properties (Not testing though)
63
+
64
+ # Specifies a date and time that this item ends
65
+ ical_property :dtend, :end
66
+
67
+ # Specifies a positive duration time
68
+ ical_property :duration
69
+
70
+ ## Multi-instance properties
71
+
72
+ # Associates a URI or binary blob with this item
73
+ ical_multi_property :attach, :attachment, :attachments
74
+
75
+ # Defines an attendee for this calendar item
76
+ ical_multiline_property :attendee, :attendee, :attendees
77
+
78
+ # Defines the categories for a calendar component (school, work...)
79
+ ical_multi_property :categories, :category, :categories
80
+
81
+ # Simple comment for the calendar user.
82
+ ical_multi_property :comment, :comment, :comments
83
+
84
+ # Contact information associated with this item.
85
+ ical_multi_property :contact, :contact, :contacts
86
+ ical_multi_property :exdate, :exception_date, :exception_dates
87
+ ical_multi_property :exrule, :exception_rule, :exception_rules
88
+ ical_multi_property :rstatus, :request_status, :request_statuses
89
+
90
+ # Used to represent a relationship between two calendar items
91
+ ical_multi_property :related_to, :related_to, :related_tos
92
+ ical_multi_property :resources, :resource, :resources
93
+
94
+ # Used with the UID & SEQUENCE to identify a specific instance of a
95
+ # recurring calendar item.
96
+ ical_multi_property :rdate, :recurrence_date, :recurrence_dates
97
+ ical_multi_property :rrule, :recurrence_rule, :recurrence_rules
98
+
99
+ def initialize()
100
+ super("VEVENT")
101
+
102
+ # Now doing some basic initialization
103
+ sequence 0
104
+ timestamp DateTime.now
105
+ end
106
+
107
+ def alarm(&block)
108
+ a = Alarm.new
109
+ self.add a
110
+
111
+ a.instance_eval(&block) if block
112
+
113
+ a
114
+ end
115
+
116
+ def occurrences_starting(time)
117
+ recurrence_rules.first.occurrences_of_event_starting(self, time)
118
+ end
119
+
120
+ end
121
+ end
@@ -0,0 +1,78 @@
1
+ module TwistedCaldav
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,54 @@
1
+ module TwistedCaldav
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(s)
14
+ result = ""
15
+ xml = REXML::Document.new(s)
16
+
17
+ REXML::XPath.each( xml, '//c:calendar-data/', {"c"=>"urn:ietf:params:xml:ns:caldav"} ){|c| result << c.text}
18
+ r = Icalendar.parse(result)
19
+ r
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
+ Icalendar.parse(vcal)
46
+ end
47
+
48
+ def parse_single( body )
49
+ # FIXME: parse event/todo/vcard
50
+ parse_events( body )
51
+ end
52
+ end
53
+ end
54
+ end
@@ -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 TwistedCaldav
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", TwistedCaldav::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 TwistedCaldav
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,48 @@
1
+ module Icalendar
2
+ class Todo < Component
3
+ ical_component :alarms
4
+
5
+ ical_property :ip_class
6
+ ical_property :completed
7
+ ical_property :created
8
+ ical_property :description
9
+ ical_property :dtstamp, :timestamp
10
+ ical_property :dtstart, :start
11
+ ical_property :geo
12
+ ical_property :last_modified
13
+ ical_property :location
14
+ ical_property :organizer
15
+ ical_property :percent_complete, :percent
16
+ ical_property :priority
17
+ ical_property :recurid, :recurrence_id
18
+ ical_property :sequence, :seq
19
+ ical_property :status
20
+ ical_property :summary
21
+ ical_property :uid, :user_id
22
+ ical_property :url
23
+
24
+ ical_property :due
25
+ ical_property :duration
26
+
27
+ ical_multi_property :attach, :attachment, :attachments
28
+ ical_multiline_property :attendee, :attendee, :attendees
29
+ ical_multi_property :categories, :category, :categories
30
+ ical_multi_property :comment, :comment, :comments
31
+ ical_multi_property :contact, :contact, :contacts
32
+ ical_multi_property :exdate, :exception_date, :exception_dates
33
+ ical_multi_property :exrule, :exception_rule, :exception_rules
34
+ ical_multi_property :rstatus, :request_status, :request_statuses
35
+ ical_multi_property :related_to, :related_to, :related_tos
36
+ ical_multi_property :resources, :resource, :resources
37
+ ical_multi_property :rdate, :recurrence_date, :recurrence_dates
38
+ ical_multi_property :rrule, :recurrence_rule, :recurrence_rules
39
+
40
+ def initialize()
41
+ super("VTODO")
42
+
43
+ sequence 0
44
+ timestamp DateTime.now
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,3 @@
1
+ module TwistedCaldav
2
+ VERSION="0.0.0.1"
3
+ end
data/twisted-caldav ADDED
File without changes
@@ -0,0 +1,30 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require File.expand_path('../lib/twisted-caldav/version', __FILE__)
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "twisted-caldav"
7
+ s.version = TwistedCaldav::VERSION
8
+ s.summary = "Ruby calender server client"
9
+ s.description = "Ruby client for searching, creating, editing calendar and tasks. Tested with ubuntu based calendar server installation."
10
+
11
+ s.required_ruby_version = '>= 1.9.3'
12
+
13
+ s.license = 'MIT'
14
+
15
+ s.homepage = %q{https://github.com/siddhartham/twisted-caldav}
16
+ s.authors = [%q{Siddhartha Mukherjee}]
17
+ s.email = [%q{mukherjee.siddhartha@gmail.com}]
18
+ s.add_runtime_dependency 'icalendar', '~> 1.x'
19
+ s.add_runtime_dependency 'uuid', '~> 2.x'
20
+ s.add_runtime_dependency 'net-http-digest_auth', '~> 1.x'
21
+ s.add_runtime_dependency 'builder', '~> 3.x'
22
+
23
+ s.description = <<-DESC
24
+ Ruby client for searching, creating, editing calendar and tasks. Tested with ubuntu based calendar server installation.
25
+ It is a modified version of original caldav client 4fthawaiian/ruby-caldav
26
+ DESC
27
+
28
+ s.files = `git ls-files`.split("\n")
29
+ s.require_paths = ["lib"]
30
+ end
metadata ADDED
@@ -0,0 +1,117 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: twisted-caldav
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Siddhartha Mukherjee
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-04-12 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: icalendar
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ~>
18
+ - !ruby/object:Gem::Version
19
+ version: 1.x
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ~>
25
+ - !ruby/object:Gem::Version
26
+ version: 1.x
27
+ - !ruby/object:Gem::Dependency
28
+ name: uuid
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ~>
32
+ - !ruby/object:Gem::Version
33
+ version: 2.x
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ~>
39
+ - !ruby/object:Gem::Version
40
+ version: 2.x
41
+ - !ruby/object:Gem::Dependency
42
+ name: net-http-digest_auth
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ version: 1.x
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ~>
53
+ - !ruby/object:Gem::Version
54
+ version: 1.x
55
+ - !ruby/object:Gem::Dependency
56
+ name: builder
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ version: 3.x
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ~>
67
+ - !ruby/object:Gem::Version
68
+ version: 3.x
69
+ description: ! " Ruby client for searching, creating, editing calendar and tasks.
70
+ Tested with ubuntu based calendar server installation.\n It is a modified version
71
+ of original caldav client 4fthawaiian/ruby-caldav\n"
72
+ email:
73
+ - mukherjee.siddhartha@gmail.com
74
+ executables: []
75
+ extensions: []
76
+ extra_rdoc_files: []
77
+ files:
78
+ - .gitignore
79
+ - LICENSE
80
+ - README.md
81
+ - lib/twisted-caldav.rb
82
+ - lib/twisted-caldav/client.rb
83
+ - lib/twisted-caldav/event.rb
84
+ - lib/twisted-caldav/filter.rb
85
+ - lib/twisted-caldav/format.rb
86
+ - lib/twisted-caldav/net.rb
87
+ - lib/twisted-caldav/query.rb
88
+ - lib/twisted-caldav/request.rb
89
+ - lib/twisted-caldav/todo.rb
90
+ - lib/twisted-caldav/version.rb
91
+ - twisted-caldav
92
+ - twisted-caldav.gemspec
93
+ homepage: https://github.com/siddhartham/twisted-caldav
94
+ licenses:
95
+ - MIT
96
+ metadata: {}
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ! '>='
104
+ - !ruby/object:Gem::Version
105
+ version: 1.9.3
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ! '>='
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubyforge_project:
113
+ rubygems_version: 2.2.1
114
+ signing_key:
115
+ specification_version: 4
116
+ summary: Ruby calender server client
117
+ test_files: []