simple_dav 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/simple_dav.rb +267 -0
- 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: []
|