twisted-caldav 0.0.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.
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: []