simple_dav 0.0.1

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