atom-tools 0.9.1 → 0.9.2

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 (42) hide show
  1. data/Rakefile +1 -1
  2. data/doc/classes/Atom/Author.html +130 -0
  3. data/doc/classes/Atom/Category.html +128 -0
  4. data/doc/classes/Atom/Collection.html +322 -0
  5. data/doc/classes/Atom/Content.html +129 -0
  6. data/doc/classes/Atom/Contributor.html +119 -0
  7. data/doc/classes/Atom/DigestAuth.html +285 -0
  8. data/doc/classes/Atom/Element.html +325 -0
  9. data/doc/classes/Atom/Entry.html +369 -0
  10. data/doc/classes/Atom/Feed.html +595 -0
  11. data/doc/classes/Atom/HTTP.html +436 -0
  12. data/doc/classes/Atom/HTTPResponse.html +149 -0
  13. data/doc/classes/Atom/Link.html +137 -0
  14. data/doc/classes/Atom/Service.html +260 -0
  15. data/doc/classes/Atom/Text.html +245 -0
  16. data/doc/classes/Atom/Workspace.html +121 -0
  17. data/doc/classes/XHTML.html +118 -0
  18. data/doc/created.rid +1 -0
  19. data/doc/files/README.html +213 -0
  20. data/doc/files/lib/atom/collection_rb.html +110 -0
  21. data/doc/files/lib/atom/element_rb.html +109 -0
  22. data/doc/files/lib/atom/entry_rb.html +111 -0
  23. data/doc/files/lib/atom/feed_rb.html +112 -0
  24. data/doc/files/lib/atom/http_rb.html +112 -0
  25. data/doc/files/lib/atom/service_rb.html +111 -0
  26. data/doc/files/lib/atom/text_rb.html +109 -0
  27. data/doc/files/lib/atom/xml_rb.html +110 -0
  28. data/doc/files/lib/atom/yaml_rb.html +109 -0
  29. data/doc/fr_class_index.html +42 -0
  30. data/doc/fr_file_index.html +36 -0
  31. data/doc/fr_method_index.html +69 -0
  32. data/doc/index.html +24 -0
  33. data/doc/rdoc-style.css +208 -0
  34. data/lib/atom/collection.rb +0 -21
  35. data/lib/atom/entry.rb +13 -0
  36. data/lib/atom/feed.rb +6 -7
  37. data/lib/atom/http.rb +97 -19
  38. data/lib/atom/service.rb +2 -22
  39. data/lib/atom/yaml.rb +1 -1
  40. data/test/test_feed.rb +27 -4
  41. data/test/test_http.rb +153 -34
  42. metadata +39 -2
@@ -0,0 +1,36 @@
1
+
2
+ <?xml version="1.0" encoding="iso-8859-1"?>
3
+ <!DOCTYPE html
4
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
5
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
6
+
7
+ <!--
8
+
9
+ Files
10
+
11
+ -->
12
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
13
+ <head>
14
+ <title>Files</title>
15
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
16
+ <link rel="stylesheet" href="rdoc-style.css" type="text/css" />
17
+ <base target="docwin" />
18
+ </head>
19
+ <body>
20
+ <div id="index">
21
+ <h1 class="section-bar">Files</h1>
22
+ <div id="index-entries">
23
+ <a href="files/README.html">README</a><br />
24
+ <a href="files/lib/atom/collection_rb.html">lib/atom/collection.rb</a><br />
25
+ <a href="files/lib/atom/element_rb.html">lib/atom/element.rb</a><br />
26
+ <a href="files/lib/atom/entry_rb.html">lib/atom/entry.rb</a><br />
27
+ <a href="files/lib/atom/feed_rb.html">lib/atom/feed.rb</a><br />
28
+ <a href="files/lib/atom/http_rb.html">lib/atom/http.rb</a><br />
29
+ <a href="files/lib/atom/service_rb.html">lib/atom/service.rb</a><br />
30
+ <a href="files/lib/atom/text_rb.html">lib/atom/text.rb</a><br />
31
+ <a href="files/lib/atom/xml_rb.html">lib/atom/xml.rb</a><br />
32
+ <a href="files/lib/atom/yaml_rb.html">lib/atom/yaml.rb</a><br />
33
+ </div>
34
+ </div>
35
+ </body>
36
+ </html>
@@ -0,0 +1,69 @@
1
+
2
+ <?xml version="1.0" encoding="iso-8859-1"?>
3
+ <!DOCTYPE html
4
+ PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
5
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
6
+
7
+ <!--
8
+
9
+ Methods
10
+
11
+ -->
12
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
13
+ <head>
14
+ <title>Methods</title>
15
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
16
+ <link rel="stylesheet" href="rdoc-style.css" type="text/css" />
17
+ <base target="docwin" />
18
+ </head>
19
+ <body>
20
+ <div id="index">
21
+ <h1 class="section-bar">Methods</h1>
22
+ <div id="index-entries">
23
+ <a href="classes/Atom/Feed.html#M000019"><< (Atom::Feed)</a><br />
24
+ <a href="classes/Atom/Element.html#M000039">[] (Atom::Element)</a><br />
25
+ <a href="classes/Atom/Element.html#M000040">[]= (Atom::Element)</a><br />
26
+ <a href="classes/Atom/HTTP.html#M000026">delete (Atom::HTTP)</a><br />
27
+ <a href="classes/Atom/Collection.html#M000033">delete! (Atom::Collection)</a><br />
28
+ <a href="classes/Atom/DigestAuth.html#M000004">digest_authenticate (Atom::DigestAuth)</a><br />
29
+ <a href="classes/Atom/Feed.html#M000013">each (Atom::Feed)</a><br />
30
+ <a href="classes/Atom/Entry.html#M000010">edit_url (Atom::Entry)</a><br />
31
+ <a href="classes/Atom/Entry.html#M000006">from_yaml (Atom::Entry)</a><br />
32
+ <a href="classes/Atom/HTTP.html#M000023">get (Atom::HTTP)</a><br />
33
+ <a href="classes/Atom/HTTP.html#M000028">get_atom_entry (Atom::HTTP)</a><br />
34
+ <a href="classes/Atom/Feed.html#M000014">get_everything! (Atom::Feed)</a><br />
35
+ <a href="classes/Atom/DigestAuth.html#M000002">h (Atom::DigestAuth)</a><br />
36
+ <a href="classes/Atom/Text.html#M000021">html (Atom::Text)</a><br />
37
+ <a href="classes/Atom/DigestAuth.html#M000003">kd (Atom::DigestAuth)</a><br />
38
+ <a href="classes/Atom/Feed.html#M000017">merge (Atom::Feed)</a><br />
39
+ <a href="classes/Atom/Feed.html#M000016">merge! (Atom::Feed)</a><br />
40
+ <a href="classes/Atom/Feed.html#M000015">merge_entries! (Atom::Feed)</a><br />
41
+ <a href="classes/Atom/Feed.html#M000012">new (Atom::Feed)</a><br />
42
+ <a href="classes/Atom/Collection.html#M000030">new (Atom::Collection)</a><br />
43
+ <a href="classes/Atom/Service.html#M000036">new (Atom::Service)</a><br />
44
+ <a href="classes/Atom/Feed.html#M000011">parse (Atom::Feed)</a><br />
45
+ <a href="classes/Atom/Service.html#M000037">parse (Atom::Service)</a><br />
46
+ <a href="classes/Atom/Entry.html#M000007">parse (Atom::Entry)</a><br />
47
+ <a href="classes/Atom/DigestAuth.html#M000001">parse_wwwauth_digest (Atom::DigestAuth)</a><br />
48
+ <a href="classes/Atom/HTTP.html#M000024">post (Atom::HTTP)</a><br />
49
+ <a href="classes/Atom/Collection.html#M000031">post! (Atom::Collection)</a><br />
50
+ <a href="classes/Atom/Collection.html#M000034">post_media! (Atom::Collection)</a><br />
51
+ <a href="classes/Atom/HTTP.html#M000025">put (Atom::HTTP)</a><br />
52
+ <a href="classes/Atom/Collection.html#M000032">put! (Atom::Collection)</a><br />
53
+ <a href="classes/Atom/HTTP.html#M000029">put_atom_entry (Atom::HTTP)</a><br />
54
+ <a href="classes/Atom/Collection.html#M000035">put_media! (Atom::Collection)</a><br />
55
+ <a href="classes/Atom/Entry.html#M000009">tag_with (Atom::Entry)</a><br />
56
+ <a href="classes/Atom/Element.html#M000041">to_element (Atom::Element)</a><br />
57
+ <a href="classes/Atom/Text.html#M000020">to_s (Atom::Text)</a><br />
58
+ <a href="classes/Atom/Element.html#M000043">to_s (Atom::Element)</a><br />
59
+ <a href="classes/Atom/Element.html#M000042">to_xml (Atom::Element)</a><br />
60
+ <a href="classes/Atom/Service.html#M000038">to_xml (Atom::Service)</a><br />
61
+ <a href="classes/Atom/Feed.html#M000018">update! (Atom::Feed)</a><br />
62
+ <a href="classes/Atom/Entry.html#M000008">updated! (Atom::Entry)</a><br />
63
+ <a href="classes/Atom/HTTPResponse.html#M000005">validate_content_type (Atom::HTTPResponse)</a><br />
64
+ <a href="classes/Atom/HTTP.html#M000027">when_auth (Atom::HTTP)</a><br />
65
+ <a href="classes/Atom/Text.html#M000022">xml (Atom::Text)</a><br />
66
+ </div>
67
+ </div>
68
+ </body>
69
+ </html>
@@ -0,0 +1,24 @@
1
+ <?xml version="1.0" encoding="iso-8859-1"?>
2
+ <!DOCTYPE html
3
+ PUBLIC "-//W3C//DTD XHTML 1.0 Frameset//EN"
4
+ "http://www.w3.org/TR/xhtml1/DTD/xhtml1-frameset.dtd">
5
+
6
+ <!--
7
+
8
+ atom-tools documentation
9
+
10
+ -->
11
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
12
+ <head>
13
+ <title>atom-tools documentation</title>
14
+ <meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1" />
15
+ </head>
16
+ <frameset rows="20%, 80%">
17
+ <frameset cols="25%,35%,45%">
18
+ <frame src="fr_file_index.html" title="Files" name="Files" />
19
+ <frame src="fr_class_index.html" name="Classes" />
20
+ <frame src="fr_method_index.html" name="Methods" />
21
+ </frameset>
22
+ <frame src="files/README.html" name="docwin" />
23
+ </frameset>
24
+ </html>
@@ -0,0 +1,208 @@
1
+
2
+ body {
3
+ font-family: Verdana,Arial,Helvetica,sans-serif;
4
+ font-size: 90%;
5
+ margin: 0;
6
+ margin-left: 40px;
7
+ padding: 0;
8
+ background: white;
9
+ }
10
+
11
+ h1,h2,h3,h4 { margin: 0; color: #efefef; background: transparent; }
12
+ h1 { font-size: 150%; }
13
+ h2,h3,h4 { margin-top: 1em; }
14
+
15
+ a { background: #eef; color: #039; text-decoration: none; }
16
+ a:hover { background: #039; color: #eef; }
17
+
18
+ /* Override the base stylesheet's Anchor inside a table cell */
19
+ td > a {
20
+ background: transparent;
21
+ color: #039;
22
+ text-decoration: none;
23
+ }
24
+
25
+ /* and inside a section title */
26
+ .section-title > a {
27
+ background: transparent;
28
+ color: #eee;
29
+ text-decoration: none;
30
+ }
31
+
32
+ /* === Structural elements =================================== */
33
+
34
+ div#index {
35
+ margin: 0;
36
+ margin-left: -40px;
37
+ padding: 0;
38
+ font-size: 90%;
39
+ }
40
+
41
+
42
+ div#index a {
43
+ margin-left: 0.7em;
44
+ }
45
+
46
+ div#index .section-bar {
47
+ margin-left: 0px;
48
+ padding-left: 0.7em;
49
+ background: #ccc;
50
+ font-size: small;
51
+ }
52
+
53
+
54
+ div#classHeader, div#fileHeader {
55
+ width: auto;
56
+ color: white;
57
+ padding: 0.5em 1.5em 0.5em 1.5em;
58
+ margin: 0;
59
+ margin-left: -40px;
60
+ border-bottom: 3px solid #006;
61
+ }
62
+
63
+ div#classHeader a, div#fileHeader a {
64
+ background: inherit;
65
+ color: white;
66
+ }
67
+
68
+ div#classHeader td, div#fileHeader td {
69
+ background: inherit;
70
+ color: white;
71
+ }
72
+
73
+
74
+ div#fileHeader {
75
+ background: #057;
76
+ }
77
+
78
+ div#classHeader {
79
+ background: #048;
80
+ }
81
+
82
+
83
+ .class-name-in-header {
84
+ font-size: 180%;
85
+ font-weight: bold;
86
+ }
87
+
88
+
89
+ div#bodyContent {
90
+ padding: 0 1.5em 0 1.5em;
91
+ }
92
+
93
+ div#description {
94
+ padding: 0.5em 1.5em;
95
+ background: #efefef;
96
+ border: 1px dotted #999;
97
+ }
98
+
99
+ div#description h1,h2,h3,h4,h5,h6 {
100
+ color: #125;;
101
+ background: transparent;
102
+ }
103
+
104
+ div#validator-badges {
105
+ text-align: center;
106
+ }
107
+ div#validator-badges img { border: 0; }
108
+
109
+ div#copyright {
110
+ color: #333;
111
+ background: #efefef;
112
+ font: 0.75em sans-serif;
113
+ margin-top: 5em;
114
+ margin-bottom: 0;
115
+ padding: 0.5em 2em;
116
+ }
117
+
118
+
119
+ /* === Classes =================================== */
120
+
121
+ table.header-table {
122
+ color: white;
123
+ font-size: small;
124
+ }
125
+
126
+ .type-note {
127
+ font-size: small;
128
+ color: #DEDEDE;
129
+ }
130
+
131
+ .xxsection-bar {
132
+ background: #eee;
133
+ color: #333;
134
+ padding: 3px;
135
+ }
136
+
137
+ .section-bar {
138
+ color: #333;
139
+ border-bottom: 1px solid #999;
140
+ margin-left: -20px;
141
+ }
142
+
143
+
144
+ .section-title {
145
+ background: #79a;
146
+ color: #eee;
147
+ padding: 3px;
148
+ margin-top: 2em;
149
+ margin-left: -30px;
150
+ border: 1px solid #999;
151
+ }
152
+
153
+ .top-aligned-row { vertical-align: top }
154
+ .bottom-aligned-row { vertical-align: bottom }
155
+
156
+ /* --- Context section classes ----------------------- */
157
+
158
+ .context-row { }
159
+ .context-item-name { font-family: monospace; font-weight: bold; color: black; }
160
+ .context-item-value { font-size: small; color: #448; }
161
+ .context-item-desc { color: #333; padding-left: 2em; }
162
+
163
+ /* --- Method classes -------------------------- */
164
+ .method-detail {
165
+ background: #efefef;
166
+ padding: 0;
167
+ margin-top: 0.5em;
168
+ margin-bottom: 1em;
169
+ border: 1px dotted #ccc;
170
+ }
171
+ .method-heading {
172
+ color: black;
173
+ background: #ccc;
174
+ border-bottom: 1px solid #666;
175
+ padding: 0.2em 0.5em 0 0.5em;
176
+ }
177
+ .method-signature { color: black; background: inherit; }
178
+ .method-name { font-weight: bold; }
179
+ .method-args { font-style: italic; }
180
+ .method-description { padding: 0 0.5em 0 0.5em; }
181
+
182
+ /* --- Source code sections -------------------- */
183
+
184
+ a.source-toggle { font-size: 90%; }
185
+ div.method-source-code {
186
+ background: #262626;
187
+ color: #ffdead;
188
+ margin: 1em;
189
+ padding: 0.5em;
190
+ border: 1px dashed #999;
191
+ overflow: hidden;
192
+ }
193
+
194
+ div.method-source-code pre { color: #ffdead; overflow: hidden; }
195
+
196
+ /* --- Ruby keyword styles --------------------- */
197
+
198
+ .standalone-code { background: #221111; color: #ffdead; overflow: hidden; }
199
+
200
+ .ruby-constant { color: #7fffd4; background: transparent; }
201
+ .ruby-keyword { color: #00ffff; background: transparent; }
202
+ .ruby-ivar { color: #eedd82; background: transparent; }
203
+ .ruby-operator { color: #00ffee; background: transparent; }
204
+ .ruby-identifier { color: #ffdead; background: transparent; }
205
+ .ruby-node { color: #ffa07a; background: transparent; }
206
+ .ruby-comment { color: #b22222; font-weight: bold; background: transparent; }
207
+ .ruby-regexp { color: #ffa07a; background: transparent; }
208
+ .ruby-value { color: #7fffd4; background: transparent; }
@@ -51,25 +51,4 @@ module Atom
51
51
  @http.put(url, data, headers)
52
52
  end
53
53
  end
54
-
55
- class HTTP
56
- # GET a URL and turn it into an Atom::Entry
57
- def get_atom_entry(url)
58
- res = get(url)
59
-
60
- if res.code != "200" or res.content_type != "application/atom+xml"
61
- raise Atom::HTTPException, "expected Atom::Entry, didn't get it"
62
- end
63
-
64
- Atom::Entry.parse(res.body, url)
65
- end
66
-
67
- # PUT an Atom::Entry to a URL
68
- def put_atom_entry(entry, url = entry.edit_url)
69
- raise "Cowardly refusing to PUT a non-Atom::Entry (#{entry.class})" unless entry.is_a? Atom::Entry
70
- headers = {"Content-Type" => "application/atom+xml" }
71
-
72
- put(url, entry.to_s, headers)
73
- end
74
- end
75
54
  end
@@ -89,6 +89,19 @@ module Atom
89
89
  end
90
90
  end
91
91
 
92
+ # the @href of an entry's link[@rel="edit"]
93
+ def edit_url
94
+ begin
95
+ edit_link = self.links.find do |link|
96
+ link["rel"] == "edit"
97
+ end
98
+
99
+ edit_link["href"]
100
+ rescue
101
+ nil
102
+ end
103
+ end
104
+
92
105
  # XXX this needs a test suite before it can be trusted.
93
106
  =begin
94
107
  # tests the entry's validity
@@ -5,8 +5,6 @@ require "atom/entry"
5
5
  require "atom/http"
6
6
 
7
7
  module Atom
8
- class HTTPException < RuntimeError # :nodoc:
9
- end
10
8
  class FeedGone < RuntimeError # :nodoc:
11
9
  end
12
10
 
@@ -163,11 +161,17 @@ module Atom
163
161
  raise(RuntimeError, "can't fetch without a uri.") unless @uri
164
162
 
165
163
  headers = {}
164
+ headers["Accept"] = "application/atom+xml"
166
165
  headers["If-None-Match"] = @etag if @etag
167
166
  headers["If-Modified-Since"] = @last_modified if @last_modified
168
167
 
169
168
  res = @http.get(@uri, headers)
170
169
 
170
+ # we'll be forgiving about feed content types.
171
+ res.validate_content_type(["application/atom+xml",
172
+ "application/xml",
173
+ "text/xml"])
174
+
171
175
  if res.code == "304"
172
176
  # we're already all up to date
173
177
  return self
@@ -177,11 +181,6 @@ module Atom
177
181
  raise Atom::HTTPException, "Unexpected HTTP response code: #{res.code}"
178
182
  end
179
183
 
180
- media_type = res.content_type.split(";").first
181
- unless ["application/atom+xml", "application/xml", "text/xml"].member? media_type
182
- raise Atom::HTTPException, "An atom:feed shouldn't have Content-Type: #{res.content_type}"
183
- end
184
-
185
184
  @etag = res["Etag"] if res["Etag"]
186
185
  @last_modified = res["Last-Modified"] if res["Last-Modified"]
187
186
 
@@ -1,5 +1,6 @@
1
1
  require "net/http"
2
- require 'uri'
2
+ require "net/https"
3
+ require "uri"
3
4
 
4
5
  require "sha1"
5
6
  require "md5"
@@ -13,7 +14,7 @@ class String # :nodoc:
13
14
  end
14
15
 
15
16
  module Atom
16
- UA = "atom-tools 0.9.1"
17
+ UA = "atom-tools 0.9.2"
17
18
 
18
19
  module DigestAuth
19
20
  CNONCE = Digest::MD5.new("%x" % (Time.now.to_i + rand(65535))).hexdigest
@@ -92,7 +93,11 @@ module Atom
92
93
  end
93
94
  end
94
95
 
95
- class Unauthorized < RuntimeError # :nodoc:
96
+ class HTTPException < RuntimeError # :nodoc:
97
+ end
98
+ class Unauthorized < Atom::HTTPException # :nodoc:
99
+ end
100
+ class WrongMimetype < Atom::HTTPException # :nodoc:
96
101
  end
97
102
 
98
103
  # An object which handles the details of HTTP - particularly
@@ -108,10 +113,29 @@ module Atom
108
113
  # used by the default #when_auth
109
114
  attr_accessor :user, :pass
110
115
 
111
- # XXX doc me
112
- # :basic, :wsse, nil
116
+ # the token used for Google's AuthSub authentication
117
+ attr_accessor :token
118
+
119
+ # when set to :basic, :wsse or :authsub, this will send an
120
+ # Authentication header with every request instead of waiting for a
121
+ # challenge from the server.
122
+ #
123
+ # be careful; always_auth :basic will send your username and
124
+ # password in plain text to every URL this object requests.
125
+ #
126
+ # :digest won't work, since Digest authentication requires an
127
+ # initial challenge to generate a response
128
+ #
129
+ # defaults to nil
113
130
  attr_accessor :always_auth
114
131
 
132
+ # automatically handle redirects, even for POST/PUT/DELETE requests?
133
+ #
134
+ # defaults to false, which will transparently redirect GET requests
135
+ # but return a Net::HTTPRedirection object when the server
136
+ # indicates to redirect a POST/PUT/DELETE
137
+ attr_accessor :allow_all_redirects
138
+
115
139
  def initialize # :nodoc:
116
140
  @get_auth_details = lambda do |abs_url, realm|
117
141
  if @user and @pass
@@ -158,6 +182,29 @@ module Atom
158
182
  @get_auth_details = block
159
183
  end
160
184
 
185
+ # GET a URL and turn it into an Atom::Entry
186
+ def get_atom_entry(url)
187
+ res = get(url, "Accept" => "application/atom+xml")
188
+
189
+ # be picky for atom:entrys
190
+ res.validate_content_type( [ "application/atom+xml" ] )
191
+
192
+ # XXX handle other HTTP codes
193
+ if res.code != "200"
194
+ raise Atom::HTTPException, "expected Atom::Entry, didn't get it"
195
+ end
196
+
197
+ Atom::Entry.parse(res.body, url)
198
+ end
199
+
200
+ # PUT an Atom::Entry to a URL
201
+ def put_atom_entry(entry, url = entry.edit_url)
202
+ raise "Cowardly refusing to PUT a non-Atom::Entry (#{entry.class})" unless entry.is_a? Atom::Entry
203
+ headers = {"Content-Type" => "application/atom+xml" }
204
+
205
+ put(url, entry.to_s, headers)
206
+ end
207
+
161
208
  private
162
209
  # parses plain quoted-strings
163
210
  def parse_quoted_wwwauth param_string
@@ -177,22 +224,25 @@ module Atom
177
224
  req.basic_auth user, pass
178
225
  end
179
226
 
180
- # WSSE authentication <http://www.xml.com/pub/a/2003/12/17/dive.html>
227
+ # WSSE authentication
228
+ # <http://www.xml.com/pub/a/2003/12/17/dive.html>
181
229
  def wsse_authenticate(req, url, params = {})
182
- # from <http://www.koders.com/ruby/fidFB0C7F9A0F36CB0F30B2280BDDC4F43FF1FA4589.aspx?s=ruby+cgi>.
183
- # (thanks midore!)
184
230
  user, pass = username_and_password_for_realm(url, params["realm"])
185
231
 
186
232
  nonce = Array.new(10){ rand(0x100000000) }.pack('I*')
187
- nonce_base64 = [nonce].pack("m").chomp
188
- now = Time.now.utc.iso8601
233
+ nonce_b64 = [nonce].pack("m").chomp
234
+
235
+ now = Time.now.iso8601
189
236
  digest = [Digest::SHA1.digest(nonce + now + pass)].pack("m").chomp
190
- credentials = sprintf(%Q<UsernameToken Username="%s", PasswordDigest="%s", Nonce="%s", Created="%s">,
191
- user, digest, nonce_base64, now)
192
- req['X-WSSE'] = credentials
237
+
238
+ req['X-WSSE'] = %Q<UsernameToken Username="#{user}", PasswordDigest="#{digest}", Nonce="#{nonce_b64}", Created="#{now}">
193
239
  req["Authorization"] = 'WSSE profile="UsernameToken"'
194
240
  end
195
241
 
242
+ def authsub_authenticate req, url
243
+ req["Authorization"] = %{AuthSub token="#{@token}"}
244
+ end
245
+
196
246
  def username_and_password_for_realm(url, realm)
197
247
  abs_url = (url + "/").to_s
198
248
  user, pass = @get_auth_details.call(abs_url, realm)
@@ -205,7 +255,7 @@ module Atom
205
255
  end
206
256
 
207
257
  # performs a generic HTTP request.
208
- def http_request(url_s, method, body = nil, init_headers = {}, www_authenticate = nil)
258
+ def http_request(url_s, method, body = nil, init_headers = {}, www_authenticate = nil, redirect_limit = 5)
209
259
  req, url = new_request(url_s, method, init_headers)
210
260
 
211
261
  # two reasons to authenticate;
@@ -217,23 +267,38 @@ module Atom
217
267
  auth_type = $~[1]
218
268
  self.send("#{auth_type.downcase}_authenticate", req, url, param_string)
219
269
  end
220
-
221
- res = Net::HTTP.start(url.host, url.port) { |h| h.request(req, body) }
222
270
 
223
- if res.kind_of? Net::HTTPUnauthorized
271
+ http_obj = Net::HTTP.new(url.host, url.port)
272
+ http_obj.use_ssl = true if url.scheme == "https"
273
+
274
+ res = http_obj.start do |h|
275
+ h.request(req, body)
276
+ end
277
+
278
+ case res
279
+ when Net::HTTPUnauthorized
224
280
  if @always_auth or www_authenticate # XXX and not stale (Digest only)
225
281
  # we've tried the credentials you gave us once and failed
226
- raise Unauthorized, "Your username and password were rejected"
282
+ raise Unauthorized, "Your authorization was rejected"
227
283
  else
228
284
  # once more, with authentication
229
285
  res = http_request(url_s, method, body, init_headers, res["WWW-Authenticate"])
230
286
 
231
287
  if res.kind_of? Net::HTTPUnauthorized
232
- raise Unauthorized, "Your username and password were rejected"
288
+ raise Unauthorized, "Your authorization was rejected"
233
289
  end
234
290
  end
291
+ when Net::HTTPRedirection
292
+ if res["Location"] and (allow_all_redirects or [Net::HTTP::Get, Net::HTTP::Head].member? method)
293
+ raise HTTPException, "Too many redirects" if redirect_limit.zero?
294
+
295
+ res = http_request res["Location"], method, body, init_headers, nil, (redirect_limit - 1)
296
+ end
235
297
  end
236
298
 
299
+ # a bit of added convenience
300
+ res.extend Atom::HTTPResponse
301
+
237
302
  res
238
303
  end
239
304
 
@@ -248,4 +313,17 @@ module Atom
248
313
  [method.new(rel, headers), url]
249
314
  end
250
315
  end
316
+
317
+ module HTTPResponse
318
+ # this should probably support ranges (eg. text/*)
319
+ def validate_content_type( valid )
320
+ raise Atom::HTTPException, "HTTP response contains no Content-Type!" unless self.content_type
321
+
322
+ media_type = self.content_type.split(";").first
323
+
324
+ unless valid.member? media_type.downcase
325
+ raise Atom::WrongMimetype, "unexpected response Content-Type: #{media_type.inspect}. should be one of: #{valid.inspect}"
326
+ end
327
+ end
328
+ end
251
329
  end