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