simple_dav 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.
Files changed (2) hide show
  1. data/lib/simple_dav.rb +267 -0
  2. metadata +45 -0
data/lib/simple_dav.rb ADDED
@@ -0,0 +1,267 @@
1
+ # Author:: od (mailto:od@idfuze.com)
2
+ # Copyright:: 2012 IDFUZE.COM Olivier DIRRENBERGER - Released under the terms of the MIT license
3
+ #
4
+ # This work is a part for PLUG&WORK project http://v4.myplugandwork.com
5
+ #
6
+ # :title:SimpleDav
7
+
8
+ require 'uri'
9
+ require 'httpclient'
10
+ require 'nokogiri'
11
+ require 'logger'
12
+
13
+ BASEVCF = <<EOF
14
+ BEGIN:VCARD
15
+ PRODID:-//IDFuze//SimpleDav//EN
16
+ VERSION:3.0
17
+ CLASS:PUBLIC
18
+ PROFILE:VCARD
19
+ END:VCARD
20
+ EOF
21
+
22
+ class SimpleDav
23
+ attr_reader :headers, :uri, :client
24
+
25
+ def gen_uid
26
+ "#{rand(100000)}-#{Time.now.to_i}-#{rand(100000)}"
27
+ end
28
+
29
+ def initialize(params = {})
30
+ begin
31
+ #https://serveur.ag-si.net/SOGo/dav/geraldine/Contacts/personal/
32
+ url = params[:ssl] ? "https://#{params[:server]}/" : "http://#{params[:server]}/"
33
+ url += case (@type = params[:type])
34
+ when "sogo" then "/SOGo/dav/#{params[:user]}/"
35
+ else ""
36
+ end
37
+ @uri = URI.parse(url)
38
+ @uid = nil
39
+ @headers = {}
40
+ #open(uri) if uri
41
+ proxy = ENV['HTTP_PROXY'] || ENV['http_proxy'] || nil
42
+ @client = HTTPClient.new(proxy)
43
+ @client.ssl_config.verify_mode = OpenSSL::SSL::VERIFY_NONE unless params[:verify]
44
+ if (@user = params[:user]) && params[:pass]
45
+ @client.set_basic_auth(@uri, @user, params[:pass])
46
+ end
47
+ rescue
48
+ raise RuntimeError.exception("Init Failed!! (#{$!.to_s})")
49
+ end
50
+ end
51
+
52
+ end
53
+
54
+ class AddressBook < SimpleDav
55
+
56
+ #PUT (Create) sends If-None-Match: *
57
+
58
+ #PUT (Replace) sends If-Match: <existing etag>
59
+
60
+ #DELETE sends If-Match: <existing etag>
61
+
62
+ def initialize(abu = "personal", params)
63
+ @vcard = nil
64
+ super(params)
65
+ abu = @type == "sogo" ? "Contacts/#{abu}/" : "#{abu}"
66
+ @uri.path += abu
67
+ end
68
+
69
+ def self.find(uid)
70
+ where(:uid => uid)
71
+ end
72
+
73
+ def create(params)
74
+ Vcard.create(self, params)
75
+ end
76
+
77
+ def vcard
78
+ @vcard
79
+ end
80
+
81
+ def where(conditions)
82
+ query = Nokogiri::XML::Builder.new(:encoding => "UTF-8") do |xml|
83
+ xml.send('C:addressbook-query', 'xmlns:D' => "DAV:", 'xmlns:C' => "urn:ietf:params:xml:ns:carddav") do
84
+ xml.send('D:prop') do
85
+ xml.send('D:getetag')
86
+ xml.send('C:address-data') do
87
+ xml.send('C:prop', 'name' => "UID")
88
+ conditions.each do |k,v|
89
+ xml.send('C:prop', 'name' => k.to_s.upcase)
90
+ end
91
+ end
92
+ end
93
+ xml.send('C:filter') do
94
+ conditions.each do |k,v|
95
+ xml.send('C:prop-filter', 'name' => k.to_s.upcase) do
96
+ xml.send('C:text-match', 'collation' => "i;unicode-casemap", 'match-type' => "equals") do
97
+ xml << v
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
104
+ headers = {
105
+ "content-Type" => "text/xml; charset=\"utf-8\"",
106
+ "depth" => 1
107
+ }
108
+
109
+ content = @client.request('REPORT', @uri, nil, query.to_xml.to_s, headers)
110
+ #puts "#{content.body}"
111
+ xml = Nokogiri::XML(content.body)
112
+ vcards = []
113
+ xml.xpath('//C:address-data').each do |card|
114
+ vcards << Vcard.new(self, card.text)
115
+ end
116
+ return vcards
117
+ end
118
+
119
+ def update(params)
120
+ #get card
121
+
122
+ #push card with new params
123
+ end
124
+
125
+ def method_missing(meth, *args, &block)
126
+ if meth.to_s =~ /^find_by_(.+)$/
127
+ run_find_by_method($1, *args, &block)
128
+ else
129
+ super
130
+ end
131
+ end
132
+
133
+ def run_find_by_method(attrs, *args, &block)
134
+ attrs = attrs.split('_and_')
135
+ attrs_with_args = [attrs, args].transpose
136
+ conditions = Hash[attrs_with_args]
137
+ where(conditions)
138
+ end
139
+
140
+ def debug_dev=(dev)
141
+ @client.debug_dev = dev
142
+ end
143
+
144
+ end
145
+
146
+ class Vcard
147
+ attr_reader :ab
148
+
149
+ def initialize(ab, text = BASEVCF)
150
+ @plain = text
151
+ @ab = ab
152
+ return self
153
+ end
154
+
155
+ def self.create(ab, params)
156
+ @vcard = Vcard.new(ab)
157
+
158
+ params.each do |k,v|
159
+ @vcard.update_attribute(k,v)
160
+ end
161
+
162
+ headers = {
163
+ "If-None-Match" => "*",
164
+ "Content-Type" => "text/vcard",
165
+ "Content-Length" => @vcard.to_s.size
166
+ }
167
+ uid = "#{ab.gen_uid}.vcf"
168
+
169
+ @vcard.update_attribute(:uid, uid)
170
+
171
+ unc = ab.uri.clone
172
+ unc.path += uid
173
+ res = @vcard.ab.client.request('PUT', unc, nil, @vcard.to_s, headers)
174
+
175
+ if res.status < 200 or res.status >= 300
176
+ @uid = nil
177
+ raise "create failed: #{res.inspect}"
178
+ else
179
+ @uid = uid
180
+ end
181
+ @vcard
182
+ end
183
+
184
+ def update(params)
185
+ params.each do |k,v|
186
+ update_attribute(k,v)
187
+ end
188
+
189
+ headers = {
190
+ "Content-Type" => "text/vcard",
191
+ "Content-Length" => @plain.size
192
+ }
193
+ uid = self.uid
194
+
195
+ unc = ab.uri.clone
196
+ unc.path += uid
197
+ res = @ab.client.request('PUT', unc, nil, @plain, headers)
198
+
199
+ if res.status < 200 or res.status >= 300
200
+ @uid = nil
201
+ raise "create failed: #{res.inspect}"
202
+ else
203
+ @uid = uid
204
+ end
205
+ self
206
+ end
207
+
208
+ def delete
209
+ if @uid && @ab
210
+
211
+ headers = {
212
+ #"If-None-Match" => "*",
213
+ "Content-Type" => "text/xml; charset=\"utf-8\""
214
+ }
215
+ unc = @ab.uri.clone
216
+ unc.path += @uid
217
+ res = @ab.client.request('DELETE', unc, nil, nil, headers)
218
+
219
+ if res.status < 200 or res.status >= 300
220
+ @uid = nil
221
+ raise "delete failed: #{res.inspect}"
222
+ else
223
+ @uid = nil
224
+ true
225
+ end
226
+ else
227
+ raise "Failed : no connection or null id"
228
+ end
229
+ end
230
+
231
+ def update_attribute(a, v)
232
+ @plain.match(/^#{a.to_s.upcase}:(.+)$/) ? @plain.gsub!(/^#{a.to_s.upcase}:(.+)$/, "#{a.to_s.upcase}:#{v}") : add_attribute(a, v)
233
+ end
234
+
235
+ def add_attribute(a, v)
236
+ @plain["END:VCARD"] = "#{a.to_s.upcase}:#{v}\nEND:VCARD"
237
+ end
238
+
239
+ def method_missing(meth, *args, &block)
240
+ if meth.to_s =~ /^((n|email|title|nickname|tel|bday|fn|org|note|uid)=?)$/
241
+ run_on_field($1, *args, &block)
242
+ else
243
+ super
244
+ end
245
+ end
246
+
247
+ def run_on_field(attrs, *args, &block)
248
+ field = attrs.upcase
249
+ field["EMAIL"] = "EMAIL;TYPE=work" if field.match("EMAIL")
250
+
251
+ if field =~ /=/
252
+ field = field[0..-2]
253
+ update_attribute(field, args)
254
+ else
255
+ if m = @plain.match(/#{field}:(.+)$/)
256
+ m[1]
257
+ else
258
+ nil
259
+ end
260
+ end
261
+ end
262
+
263
+ def to_s
264
+ @plain.to_s
265
+ end
266
+
267
+ end
metadata ADDED
@@ -0,0 +1,45 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simple_dav
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Olivier DIRRENBERGER
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-09-28 00:00:00.000000000Z
13
+ dependencies: []
14
+ description: Access your dav server from ruby
15
+ email: od@idfuze.com
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - lib/simple_dav.rb
21
+ homepage: https://github.com/reivilo/simple_dav
22
+ licenses: []
23
+ post_install_message:
24
+ rdoc_options: []
25
+ require_paths:
26
+ - lib
27
+ required_ruby_version: !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ required_rubygems_version: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ requirements: []
40
+ rubyforge_project:
41
+ rubygems_version: 1.8.6
42
+ signing_key:
43
+ specification_version: 3
44
+ summary: Ruby dav client
45
+ test_files: []