' and
'' lines.
Checking references for intended status: Proposed Standard
----------------------------------------------------------------------------
(See RFCs 3967 and 4897 for information about using normative references
to lower-maturity documents in RFCs)
== Missing Reference: '8' is mentioned on line 503, but not defined
== Missing Reference: '0' is mentioned on line 1087, but not defined
== Missing Reference: '-1' is mentioned on line 783, but not defined
== Missing Reference: '2' is mentioned on line 1044, but not defined
== Missing Reference: '3' is mentioned on line 1044, but not defined
-- Possible downref: Non-RFC (?) normative reference: ref. 'APP'
-- Possible downref: Non-RFC (?) normative reference: ref. 'AtomFormat'
-- Possible downref: Non-RFC (?) normative reference: ref. 'XHTML'
Summary: 5 errors (**), 0 flaws (~~), 7 warnings (==), 11 comments (--).
Run idnits with the --verbose option for more detailed information about
the items above.
--------------------------------------------------------------------------------
2 Network Working Group R. Sayre
3 Internet-Draft September 3, 2005
4 Expires: March 7, 2006
6 XHTML Microformats for the Atom Publishing Protocol
7 draft-sayre-atompub-xhtml-micro-00.txt
9 Status of this Memo
11 By submitting this Internet-Draft, each author represents that any
12 applicable patent or other IPR claims of which he or she is aware
13 have been or will be disclosed, and any of which he or she becomes
14 aware will be disclosed, in accordance with Section 6 of BCP 79.
16 Internet-Drafts are working documents of the Internet Engineering
17 Task Force (IETF), its areas, and its working groups. Note that
18 other groups may also distribute working documents as Internet-
19 Drafts.
21 Internet-Drafts are draft documents valid for a maximum of six months
22 and may be updated, replaced, or obsoleted by other documents at any
23 time. It is inappropriate to use Internet-Drafts as reference
24 material or to cite them other than as "work in progress."
26 The list of current Internet-Drafts can be accessed at
27 http://www.ietf.org/ietf/1id-abstracts.txt.
29 The list of Internet-Draft Shadow Directories can be accessed at
30 http://www.ietf.org/shadow.html.
32 This Internet-Draft will expire on March 7, 2006.
34 Copyright Notice
36 Copyright (C) The Internet Society (2005).
38 Abstract
40 This memo presents a number of XHTML microformats for use with the
41 Atom Publishing Protocol.
43 Editorial Note
45 To provide feedback on this Internet-Draft, join the atom-protocol
46 mailing list (http://www.imc.org/atom-protocol/index.html) [1].
48 Table of Contents
50 1. Introduction . . . . . . . . . . . . . . . . . . . . . . . . . 3
51 2. Notational Conventions . . . . . . . . . . . . . . . . . . . . 4
52 3. hCat: Atom Categories . . . . . . . . . . . . . . . . . . . . 5
53 3.1 Creating and Editing hCat Categories . . . . . . . . . . . 7
54 4. hError: Error Documents . . . . . . . . . . . . . . . . . . . 9
55 5. Security Considerations . . . . . . . . . . . . . . . . . . . 11
56 6. References . . . . . . . . . . . . . . . . . . . . . . . . . . 12
57 6.1 Normative References . . . . . . . . . . . . . . . . . . . 12
58 6.2 Informative References . . . . . . . . . . . . . . . . . . 12
59 Author's Address . . . . . . . . . . . . . . . . . . . . . . . 13
60 A. An Example hCat and hError Server . . . . . . . . . . . . . . 14
61 B. An Example hCat and hError Client . . . . . . . . . . . . . . 20
62 Intellectual Property and Copyright Statements . . . . . . . . 31
64 1. Introduction
66 Atom Publishing Protocol [APP] client implementations require a fair
67 amount of ancillary server-provided data in order to provide a smooth
68 user experience. Rather than invent a plethora of new XML formats,
69 this specification chooses to present a number of XHTML profiles
70 [XHTML], colloquially known as "microformats". Visit
71 for more information.
73 2. Notational Conventions
75 The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
76 "SHOULD", "SHOULD NOT", "RECOMMENDED", "MAY", and "OPTIONAL" in this
77 document are to be interpreted as described in [RFC2119].
79 3. hCat: Atom Categories
81 hCat is an XHTML profile for encoding the three standard attributes
82 of Atom category elements [AtomFormat]. By providing a definition
83 list containing encoded category information, servers can present
84 clients with a list of known categories in an XHTML definition list.
85 hCat also allows description of endpoints for category editing
86 through a simple HTTP-based protocol, described in Section 3.1.
88 A sample category definition
89 ...
90 birds
91
92 ISBN birds
93 Edit
94
95
96 ...
98 The three standard properties of an Atom category are 'term',
99 'label', and 'scheme'. The 'term' attribute is required. hCat
100 encodes the categories provided by a server within an xhtml:dl
101 element with a 'class' attribute value of 'category'. Each category
102 is presented as a pair of elements: xhtml:dt and xhtml:dd. Each
103 element pair MAY contain or be enclosed by additional markup, but
104 this specification assigns it no significance.
106 hCat XHTML documents MUST indicate their profile in the 'head'
107 element. The profile URI for hCat documents is
108 "http://www.example.org/2005/Atom/hCat" [example URI--do not deploy].
110 An example hCat document
111
113
114
115 Atom Category Demo
116
117
118 Atom Category Demo
120
126
128 - cats
129 -
130 Edit
131
133 - dogs
134 -
135 Edit
136
137
139 - atom
140 -
141 The Atom Syndication Format
142 Edit
143
145 - birds
146 -
147 ISBN birds
148 Edit
149
150
152
153
154
156 The REQUIRED 'term' property is found in the content of each xhtml:dt
157 element. The content of the xhtml:dt element MUST NOT contain child
158 elements. Terms are also known as 'tags'.
160 The OPTIONAL 'label' property is found in an xhtml:span element
161 directly contained by each xhtml:dd element. The xhtml:span element
162 MUST have 'label' as one of its 'class' values. There MUST NOT be
163 more than one such xhtml:span within each xhtml:dd element.
165 The OPTIONAL 'scheme' property is found in an xhtml:a element
166 directly contained by each xhtml:dd element. The xhtml:a element
167 MUST have 'scheme' as one of its 'rel' values. There MUST NOT be
168 more than one such xhtml:a within each xhtml:dd element.
170 Each definition MAY contain an xhtml:a element with a 'rel' value of
171 'hCat'. This URI provided by the value of the element's 'href'
172 attribute indicates a URI that responds to a simple protocol for
173 editing of categories, which is described in the next section.
175 3.1 Creating and Editing hCat Categories
177 In addition to listing categories provided by a server, hCat XHTML
178 documents allow conforming clients to edit and add categories. The
179 server MAY communicate a URI where it accepts new categories by
180 including an xhtml:form element with a 'class' attribute of 'hCat' in
181 the hCat XHTML document. There MUST NOT be more than one xhtml:form
182 element containing a class of 'hCat'. It would be preferable to use
183 an 'id' attribute in this case, but many implementations depend on a
184 generated value for a form's 'id' attribute. The xhtml:form element
185 MUST be contained by the xhtml:body element, but MUST NOT be
186 contained by an hCat definition list.
188 The values provided by the attributes of the xhtml:form element
189 provide guidance for implementations wishing to submit content to the
190 server. Servers MUST accept the MIME-type 'application/
191 x-www-form-urlencoded', which is the default value for xhtml:forms.
193 o As in XHTML, the 'action' attribute indicates the URI where new
194 categories are submitted.
196 o The 'accept-charset' attribute of the xhtml:form element MUST be
197 present, and the list of accepted character sets SHOULD include
198 'utf-8'.
200 o The value of the 'method' attribute MUST NOT be 'get', and SHOULD
201 be 'post'. Naive user agents will likely treat other values as if
202 they were 'get'.
204 The 'application/x-www-form-urlencoded' MIME-type is a name/value
205 pair format. Servers identify their preferred name for each atom:
207 category property using xhtml:input elements contained by the hCat
208 xhtml:form element. The property is identified by the 'id' attribute
209 of the xhtml:input element. For example,
211
213 would indicate that the 'term' property is to be sent as the value of
214 the 'foo' parameter.
216 The information provided by the hCat xhtml:form also applies to
217 editing categories. To update a category, clients can send the
218 parameters indicated by the form in an HTTP PUT request directed to
219 the hCat URI conveyed by the relevant xhtml:a element in each
220 xhtml:dd element. HTTP DELETE requests sent to that URI remove the
221 category. This specification does not define a response to HTTP GET
222 for such URIs, but servers SHOULD provide one (see [WEBARCH]).
223 Servers MAY extend the hCat protocol with information in that
224 response. This specification does not define a response to HTTP
225 POST, but other specifications might do so. For example, a system
226 supporting hierarchical categories might use HTTP POST requests to
227 append a new category as a sub-category.
229 If the server encounters an error condition, the response body SHOULD
230 be an hError XHTML document (Section 4).
232 4. hError: Error Documents
234 HTTP provides response codes which indicate the success or failure of
235 a given request, but does not go into great detail on textual
236 diagnostics for the end-user (see [RFC3117], Section 3.3). hError is
237 an XHTML profile that encodes error information intended for the end-
238 user.
240 hCat XHTML documents MUST indicate their profile in the 'head'
241 element. The profile URI for hCat documents is
242 "http://www.example.org/2005/Atom/hError" [example URI--do not
243 deploy].
245 hCat provides three locations for error information:
247 1. The xhtml:title element MUST be present, and contains a short
248 description of the error. Longer titles risk truncation due to
249 GUI constraints.
251 2. An xhtml:p element with an 'id' attribute value of 'hError' MAY
252 be present, and contains a longer description of the issue. The
253 hError xhtml:p element MAY contain child elements, but many user
254 interfaces will not display HTML.
256 3. An xhtml:span element with an 'id' attribute value of 'hErrorId'
257 MAY be present, and contains a identifier for the error. This
258 error identifer could be a unique identifier useful for locating
259 a transaction during support requests, or a more general code
260 identifying a specific condition.
262 An example hError XHTML document
264
266
267
268 We're Sorry! File not found.
269
270
271
272 Hmm, can't find that entry.
273 You can contact an administrator for help:
274 admin@example.com.
275
276
277 The unique identifier for this error is
278 574DDDF2-A1E1-4898-B21B-EBB2DD16B38C.
279
280
281
283 5. Security Considerations
285 [[anchor5: What isn't a consideration with this? ...will fix]]
287 6. References
289 6.1 Normative References
291 [APP] Gregorio, J. and B. de hOra, "The Atom Publishing
292 Protocol", work-in-progress, July 2005.
294 [AtomFormat]
295 Nottingham, M. and R. Sayre, "The Atom Syndication
296 Format", IESG Approved, awaiting RFC number, July 2005.
298 [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
299 Requirement Levels", BCP 14, RFC 2119, March 1997.
301 [XHTML] Pemberton, S., "XHTML 1.0 The Extensible HyperText Markup
302 Language (Second Edition)", W3C REC REC-xhtml1-20020801,
303 August 2002,
304 .
306 6.2 Informative References
308 [RFC3117] Rose, M., "On the Design of Application Protocols",
309 RFC 3117, November 2001.
311 [WEBARCH] Walsh, N. and I. Jacobs, "Architecture of the World Wide
312 Web, Volume One", W3C REC REC-webarch-20041215,
313 December 2004,
314 .
316 URIs
318 [1]
320 Author's Address
322 Robert Sayre
324 Email: rfsayre@boswijck.com
325 URI: http://boswijck.com
327 Appendix A. An Example hCat and hError Server
329 You can run this server from the command line using the Python
330 programming language .
332 Syntax: "python this_script.py"
334 This will start a server running at 'http://localhost:8888'. You can
335 visit in your browser, and try the client in Appendix B.
337 On Microsoft Windows, the server may not respond to an interrupt
338 command (^Z CR) immediately. Visit the server one more time after
339 attempting to interrupt, and the server will quit.
341 Patches welcome :)
343 import BaseHTTPServer, cgi
345 categories = [{'term':u'\u201CHello World!\u201D',
346 'label':u'Not much to describe',
347 'scheme':None},
348 {'term':u'Hello World!',
349 'label':None,
350 'scheme':None},
351 {'term':u'Hello World!',
352 'label':u'Not much to describe',
353 'scheme':None},
354 {'term':u'Hello World!',
355 'label':u'An example label',
356 'scheme':u'http://example.com'}]
358 hcat_template = u"""
359
361
362
363 hCat Demo
364
365
367 hCat Category Demo
368 Warning: requires
369 I\u00F1t\u00EBrn\u00E2ti\u00F4n\u00E0liz\u00E6ti\u00F8n
371
381
382 %s
383
384
385
386 """
388 herror_template = u"""
389
391
392
393 Error %(code)d: %(shortDesc)s
394
395
396 Error %(code)d: %(shortDesc)s
397 %(hError)s
398 %(hErrorId)s
399
400
401 """
403 cat_template = u"""
404 Term: %(term)s
405 Label: %(label)s
406 Scheme: %(scheme)s"""
408 class HCatHandler(BaseHTTPServer.BaseHTTPRequestHandler):
410 def do_GET(self):
411 if self.path == "/":
412 self.send_response(200)
413 self.send_header('Content-type',self._gen_ctype_header())
414 self.end_headers()
415 resp = hcat_template % self.get_cats()
416 self.wfile.write(resp.encode('utf-8'))
417 elif self._is_cat(self.path):
418 cat = categories[int(self.path[8])]
419 self.send_response(200)
420 self.send_header('Content-Type',
421 'text/plain; charset=utf-8')
422 self.end_headers()
423 resp = cat_template % cat
424 self.wfile.write(resp.encode('utf-8'))
425 else:
426 self.send_herror(404, "File not found",
427 "Are you sure the address is right?",
428 self.gen_err_id())
430 def do_DELETE(self):
431 if (self._is_cat(self.path)
432 and categories[int(self.path[8])]):
433 categories[int(self.path[8])] = None
434 self.send_response(204)
435 self.end_headers()
436 else:
437 self.send_herror(404, "File not found",
438 "Category does not exist",
439 self.gen_err_id())
441 def do_PUT(self):
442 data = self._read_data()
443 if not data:
444 return
445 if (self._is_cat(self.path)
446 and categories[int(self.path[8])]):
447 form = cgi.parse_qs(data)
448 if not (form.has_key("term")):
449 self.send_herror(500, "Error",
450 "Term is required.",
451 self.gen_err_id())
452 return
453 categories[int(self.path[8])] = self.get_cat(form)
454 self.send_response(204)
455 self.end_headers()
456 else:
457 self.send_herror(404, "File not found",
458 "Category does not exist",
459 self.gen_err_id())
460 def do_POST(self):
461 redirect = "/"
462 host = self.headers.getheader('host')
463 if(host):
464 redirect = "http://" + host + redirect
465 data = self._read_data()
466 if not data:
467 return
468 if self.path == "/add":
469 form = cgi.parse_qs(data)
470 if not (form.has_key("term")):
472 self.send_herror(500, "Error",
473 "Term is required.",
474 self.gen_err_id())
475 return
476 categories.append(self.get_cat(form))
477 self.send_response(303)
478 self.send_header('Location',redirect)
479 self.end_headers()
480 else:
481 self.send_herror(404, "POSTed to incorrect location",
482 "Are you sure the address is right?",
483 self.gen_err_id())
485 def _read_data(self):
486 con_type = self.headers.getheader('content-type')
487 if con_type != 'application/x-www-form-urlencoded':
488 msg = "The content type was %s" % con_type
489 self.send_herror(415, "Unsupported Content-Type", msg,
490 self.gen_err_id())
491 return
492 clen = self.headers.getheader('content-length')
493 if clen:
494 clen = int(clen)
495 else:
496 self.send_herror(411, "Missing Content-Length", None,
497 self.gen_err_id())
498 return
499 return self.rfile.read(clen)
501 def _is_cat(self, path):
502 if (path.startswith('/ed?cat=') and int(path[8]) >= 0
503 and int(path[8]) < len(categories)):
504 return True
505 else:
506 return False
508 def _gen_ctype_header(self):
509 # we want to send XHTML, but will fudge with
510 # text/html if necessary (IE)
511 # TODO: parse these correctly
512 prefer = 'application/xhtml+xml'
513 template = '%s; charset=utf-8'
514 accept = self.headers.get('Accept')
515 if accept:
516 if prefer in accept:
517 ctype = prefer
518 else:
519 ctype = 'text/html'
521 else:
522 ctype = prefer
523 return '%s; charset=utf-8' % ctype
525 def get_cat(self,form):
526 def find_in_form(k):
527 if form.has_key(k):
528 return unicode(form[k][0],'utf-8')
529 else:
530 return None
532 return {'term':find_in_form('term'),
533 'label':find_in_form('label'),
534 'scheme':find_in_form('scheme')}
536 # loop through the categories in memory and return dt/dd pairs
537 def get_cats(self):
538 res = u""
539 for i in range(len(categories)):
540 if categories[i]:
541 cat = categories[i]
542 res += u" %s \n" % cat['term']
543 res += u" \n"
544 if cat['label']:
545 res += u" "
546 res += cat['label']
547 res += u"\n"
548 res += u" Edit\n"
550 if cat['scheme']:
551 res += u" "
554 res += u"\n"
555 res += u" \n\n"
557 return res
559 def send_herror(self, code, short_desc,
560 long_desc=None, err_id=None):
562 # log with the base class facilities
563 try:
564 short, long = self.responses[code]
565 except KeyError:
566 short, long = '???', '???'
567 message = "%s id: %s" % (short,err_id)
568 self.log_error("code %d, message %s", code, message)
569 # prepare hError doc
570 if long_desc:
571 hError = "%s
" % long_desc
572 else:
573 hError = ''
574 if err_id:
575 hErrorId = """The identifier for this error is:
576 %s
""" % err_id
577 else:
578 hErrorId = ''
579 content = (herror_template %
580 {'code':code,
581 'shortDesc': short_desc,
582 'hError': hError,
583 'hErrorId': hErrorId})
585 self.send_response(code, short_desc)
586 self.send_header("Content-Type", "text/html")
587 self.send_header('Connection', 'close')
588 self.end_headers()
589 if self.command != 'HEAD' and (code >= 200 and
590 code not in (204, 304)):
591 self.wfile.write(content)
593 def gen_err_id(self):
594 from random import choice
595 lnd='0123456789'
596 return ''.join(map(lambda x,y=lnd: choice(y), range(10)))
598 PORT = 8888
600 httpd = BaseHTTPServer.HTTPServer(("", PORT), HCatHandler)
601 print "serving at port", PORT
602 httpd.serve_forever()
604 Appendix B. An Example hCat and hError Client
606 You can run this client from the command line using the Python
607 programming language . A sample server is
608 included in Appendix A.
610 Syntax: "python this_script.py http://example.com"
612 This script uses a SAX handler in an
613 effort to test viability in performance constrained environments.
615 Patches welcome :)
617 import sys,os,urllib2,urllib,urlparse,httplib
618 import xml.sax
619 from xml.sax.handler import *
620 import cStringIO
622 class Category:
623 def __init__(self, term):
624 self.term = term
625 self.label = None
626 self.scheme = None
627 self.uri = None
629 def __str__(self):
630 s = u'term: %s label: %s, scheme: %s'
631 ret = s % (self.term,self.label,self.scheme)
632 return ret.encode('unicode-escape')
634 class HCatForm:
635 pass
637 HCAT_PROFILE = u'http://www.example.org/2005/Atom/hCat'
638 HERROR_PROFILE = u'http://www.example.org/2005/Atom/hError'
639 XHTML_NS = u'http://www.w3.org/1999/xhtml'
640 CLOSE = 0
641 UE = 'unicode-escape'
642 FORM_ENCODED='application/x-www-form-urlencoded'
644 # states
645 (START,END,IN_HTML,
646 IN_HEAD,IN_BODY,IN_DL,
647 IN_DT,IN_DD,IN_LABEL,
648 IN_FORM, IN_P, IN_SPAN, IN_TITLE) = (1,2,3,4,5,
649 6,7,8,9,10,
650 11,12,13)
652 class HCatHandler(ContentHandler):
653 def __init__(self, baseURI):
654 self._base = baseURI
655 self.state = START
656 self._buf = ''
657 self.cats = []
658 self._lists = []
659 self.form = None
660 self.transitions = {
661 START: {(XHTML_NS,u'html'):self.open_html},
662 IN_HTML: {(XHTML_NS,u'html',CLOSE):self.close_html,
663 (XHTML_NS,u'head'):self.open_head,
664 (XHTML_NS,u'body'):self.open_body},
665 IN_HEAD: {(XHTML_NS,u'head',CLOSE):self.close_head},
666 IN_BODY: {(XHTML_NS,u'body',CLOSE):self.close_body,
667 (XHTML_NS,u'dl'):self.open_dl,
668 (XHTML_NS,u'form'):self.open_form},
669 IN_DL: {(XHTML_NS,u'dl',CLOSE):self.close_dl,
670 (XHTML_NS,u'dt'):self.open_dt,
671 (XHTML_NS,u'dd'):self.open_dd},
672 IN_DT: {(XHTML_NS,u'dt',CLOSE):self.close_dt},
673 IN_DD: {(XHTML_NS,u'dd',CLOSE):self.close_dd,
674 (XHTML_NS,u'span'):self.open_span,
675 (XHTML_NS,u'a'):self.open_anchor,
676 (XHTML_NS,u'dl'):self.open_dl},
677 IN_LABEL: {(XHTML_NS,u'span',CLOSE):self.close_span},
678 IN_FORM: {(XHTML_NS,u'form',CLOSE):self.close_form,
679 (XHTML_NS,u'input'):self.open_input,
680 (XHTML_NS,u'dl'):self.open_dl}
681 }
683 def open_html(self, name, attrs):
684 return IN_HTML
686 def close_html(self, name):
687 return END
689 def open_head(self, name, attrs):
690 profile = attrs.get((None,u'profile'))
691 if profile and HCAT_PROFILE in profile.split():
692 return IN_HEAD
693 else:
694 return END
696 def close_head(self, name):
697 return IN_HTML
699 def open_body(self, name, attrs):
701 return IN_BODY
703 def close_body(self, name):
704 return IN_HTML
706 def open_dl(self, name, attrs):
707 if attrs.get((None,u'class'))==u'category':
708 self._lists.append(self.state)
709 return IN_DL
710 else:
711 return None
713 def close_dl(self, name):
714 return self._lists.pop(-1)
716 def open_dt(self, name, attrs):
717 self._buf = ""
718 return IN_DT
720 def close_dt(self, name):
721 cat = Category(self._buf)
722 self.cats.append(cat)
723 return IN_DL
725 def open_dd(self, name, attrs):
726 self._buf = ""
727 return IN_DD
729 def close_dd(self, name):
730 return IN_DL
732 def open_span(self, name, attrs):
733 if attrs.get((None,u'class'))==u'label':
734 self._buf=''
735 return IN_LABEL
736 else:
737 return None
739 def close_span(self, name):
740 self.cats[-1].label = self._buf
741 return IN_DD
743 def open_form(self, name, attrs):
744 class_att = attrs.get((None,u'class'))
745 if class_att and u'hCat' in class_att.split():
746 self.form = HCatForm()
747 uri = attrs.get((None,u'action'))
748 if uri:
750 self.form.uri = urlparse.urljoin(self._base,uri)
751 csets = attrs.get((None,u'accept-charset'))
752 # charsets are 'space and/or comma separated' :/
753 csets = [x for subseq in
754 [x.split(',') for x in csets.split()]
755 for x in subseq if x != '']
756 self.form.charsets = csets
757 return IN_FORM
758 else:
759 return None
761 def close_form(self, name):
762 return IN_BODY
764 def open_input(self, name, attrs):
765 params = ('term','scheme','label')
766 input_id = attrs.get((None,u'id'))
767 if input_id:
768 name = attrs.get((None,u'name'))
769 if input_id=='term':
770 self.form.term = name
771 elif input_id=='label':
772 self.form.label = name
773 elif input_id=='scheme':
774 self.form.scheme = name
775 return None
777 def open_anchor(self, name, attrs):
778 rel = attrs.get((None,u'rel'))
779 href = attrs.get((None,u'href'))
780 if rel and u'scheme' in rel.split():
781 self.cats[-1].scheme = href
782 elif rel and u'hCat' in rel.split():
783 self.cats[-1].uri = urlparse.urljoin(self._base,href)
784 return None
786 ## SAX Events
787 def startElementNS(self, name, qname, attrs):
788 try:
789 trans_func = self.transitions[self.state][name]
790 self.state = trans_func(name,attrs) or self.state
791 except KeyError:
792 pass
794 def endElementNS(self, name, qname):
795 try:
796 event = (name[0],name[1],CLOSE)
797 trans_func = self.transitions[self.state][event]
798 self.state = trans_func(name) or self.state
799 except KeyError:
800 pass
802 def characters(self, chars):
803 self._buf += chars
805 class HErrorHandler(ContentHandler):
806 def __init__(self):
807 self.state = START
808 self._buf = ''
810 self.title = None
811 self.hError = None
812 self.hErrorId = None
814 self.transitions = {
815 START: {(XHTML_NS,u'html'):self.open_html},
816 IN_HTML: {(XHTML_NS,u'html',CLOSE):self.close_html,
817 (XHTML_NS,u'head'):self.open_head,
818 (XHTML_NS,u'body'):self.open_body},
819 IN_HEAD: {(XHTML_NS,u'head',CLOSE):self.close_head,
820 (XHTML_NS,u'title'):self.open_title},
821 IN_TITLE: {(XHTML_NS,u'title',CLOSE):self.close_title},
822 IN_BODY: {(XHTML_NS,u'body',CLOSE):self.close_body,
823 (XHTML_NS,u'p'):self.open_p,
824 (XHTML_NS,u'span'):self.open_span},
825 IN_P: {(XHTML_NS,u'p',CLOSE):self.close_p},
826 IN_SPAN: {(XHTML_NS,u'span',CLOSE):self.close_span},
827 }
829 def open_html(self, name, attrs):
830 return IN_HTML
832 def close_html(self, name):
833 return END
835 def open_head(self, name, attrs):
836 profile = attrs.get((None,u'profile'))
837 if profile and HERROR_PROFILE in profile.split():
838 return IN_HEAD
839 else:
840 return END
842 def close_head(self, name):
843 return IN_HTML
845 def open_title(self, name, attrs):
847 self._buf = ''
848 return IN_TITLE
850 def close_title(self, name):
851 self.title = self._buf
852 return IN_HEAD
854 def open_body(self, name, attrs):
855 return IN_BODY
857 def close_body(self, name):
858 return IN_HTML
860 def open_p(self, name, attrs):
861 if attrs.get((None,u'id'))==u'hError':
862 self._buf = ''
863 return IN_P
864 else:
865 return None
867 def close_p(self, name):
868 self.hError = self._buf
869 return IN_BODY
871 def open_span(self, name, attrs):
872 if attrs.get((None,u'id'))==u'hErrorId':
873 self._buf = ''
874 return IN_SPAN
875 else:
876 return None
878 def close_span(self, name):
879 self.hErrorId = self._buf
880 return IN_BODY
882 ## SAX Events
883 def startElementNS(self, name, qname, attrs):
884 try:
885 trans_func = self.transitions[self.state][name]
886 self.state = trans_func(name,attrs) or self.state
887 except KeyError:
888 pass
890 def endElementNS(self, name, qname):
891 try:
892 event = (name[0],name[1],CLOSE)
893 trans_func = self.transitions[self.state][event]
894 self.state = trans_func(name) or self.state
896 except KeyError:
897 pass
899 def characters(self, chars):
900 self._buf += chars
902 def prompt(str):
903 if str[-1:] != ' ': str=str+': '
904 if len(str) > 40:
905 print str
906 sys.stdout.flush()
907 return raw_input('--> ')
908 else:
909 return raw_input(str)
911 class HCatEditor:
912 def __init__(self, uri):
913 self._cats = []
914 self.uri = uri
915 self._form = None
917 def refresh_cats(self):
918 request = urllib2.Request(self.uri)
919 try:
920 data = self.make_request(request).read()
921 self.parse(data)
922 except urllib2.HTTPError,e:
923 self.print_error(e.read())
925 def parse(self,data):
926 hCat_handler = HCatHandler(self.uri)
927 parser = xml.sax.make_parser()
928 parser.setFeature(xml.sax.handler.feature_namespaces, 1)
929 parser.setContentHandler(hCat_handler)
930 # you may want to provide a local resolver for
931 # the DTDs, so you don't hit w3.org
932 # parser.setEntityResolver(XhtmlResolver())
933 parser.parse(cStringIO.StringIO(data))
934 self._cats=hCat_handler.cats
935 self._form=hCat_handler.form
936 print '\n---- Server Categories ----'
937 for i in range(len(self._cats)):
938 print '[%d] %s' % (i+1,self._cats[i])
939 print '---------------------------\n'
941 def make_request(self, request):
943 request.add_header('User-agent',
944 'hCatDemo/00 +http://franklinmint.fm')
945 accept_list = 'application/xhtml+xml,text/html,'
946 accept_list += 'text/plain;q=0.9,*/*;q=0.5'
947 request.add_header('Accept', accept_list)
948 opener = urllib2.build_opener()
949 return opener.open(request)
951 def enter_field(self,noun,current):
952 if(current):
953 curr = current.encode(UE)
954 new = prompt('Enter a %s [%s]' % (noun,curr))
955 else:
956 new = prompt('Enter a %s' % noun)
957 if new != '':
958 return new
959 else:
960 return current
962 def enter_fields(self, term=None, label=None, scheme=None):
963 params = {}
964 term = unicode(self.enter_field('term',term),UE)
965 if self._form.label:
966 label = self.enter_field('label',label)
967 if label is not None:
968 label = unicode(label,UE)
969 if self._form.scheme:
970 scheme = self.enter_field('scheme',scheme)
971 if scheme is not None:
972 scheme = unicode(scheme,UE)
973 if label:
974 params[self._form.label] = label.encode('utf-8')
975 if scheme:
976 params[self._form.scheme] = scheme.encode('utf-8')
977 params[self._form.term] = term.encode('utf-8')
978 return params
980 def choose_add(self):
981 if not self._form:
982 print "No add capability"
983 return
984 if not 'utf-8' in self._form.charsets:
985 print "I can only use utf-8 for this demo..."
986 print "the form provided:",self._form.charsets
987 return
988 params = self.enter_fields()
989 request = urllib2.Request(self._form.uri,
990 urllib.urlencode(params))
992 resp = self.make_request(request)
993 self.uri = resp.url
994 self.parse(resp.read())
996 def pick_cat(self):
997 try:
998 cat_num = int(prompt('Pick a number from the list')) - 1
999 if cat_num < 0 or cat_num >= len(self._cats):
1000 raise ValueError
1001 except ValueError:
1002 print 'Value not in list.'
1003 return None
1004 if not self._cats[cat_num].uri:
1005 print "Category has no editing capability"
1006 return None
1007 return cat_num
1009 def choose_delete(self):
1010 cat_num = self.pick_cat()
1011 if cat_num is not None:
1012 parts = urlparse.urlsplit(self._cats[cat_num].uri)
1013 conn = httplib.HTTPConnection(parts[1])
1014 conn.request('DELETE', parts[2]+'?'+parts[3])
1015 response = conn.getresponse()
1016 if response.status in range(200,299):
1017 print "\nOK"
1018 self.refresh_cats()
1019 elif response.status in range(400,599):
1020 data = response.read()
1021 self.print_error(data)
1022 else:
1023 print response.reason
1025 return
1027 def choose_edit(self):
1028 cat_num = self.pick_cat()
1029 if cat_num is not None:
1030 label = self._cats[cat_num].label
1031 scheme = self._cats[cat_num].scheme
1032 if label:
1033 label = label.encode(UE)
1034 if scheme:
1035 scheme = scheme.encode(UE)
1036 term = self._cats[cat_num].term
1037 p = self.enter_fields(term.encode(UE),
1038 label, scheme)
1039 body = urllib.urlencode(p)
1040 headers = {'Content-Type':FORM_ENCODED,
1041 'Content-Length':len(body)}
1042 parts = urlparse.urlsplit(self._cats[cat_num].uri)
1043 conn = httplib.HTTPConnection(parts[1])
1044 conn.request('PUT', parts[2]+'?'+parts[3],
1045 body, headers)
1046 response = conn.getresponse()
1047 if response.status in range(200,299):
1048 print "\nOK"
1049 self.refresh_cats()
1050 elif response.status in range(400,599):
1051 data = response.read()
1052 self.print_error(data)
1053 else:
1054 print response.reason
1055 return
1057 def print_error(self,data):
1058 hError_handler = HErrorHandler()
1059 parser = xml.sax.make_parser()
1060 parser.setFeature(xml.sax.handler.feature_namespaces, 1)
1061 parser.setContentHandler(hError_handler)
1062 # you may want to provide a local resolver for
1063 # the DTDs, so you don't hit w3.org.
1064 # parser.setEntityResolver(XhtmlResolver())
1065 print "An Error occured.\n"
1066 try:
1067 parser.parse(cStringIO.StringIO(data))
1068 if hError_handler.title:
1069 print hError_handler.title
1070 if hError_handler.hError:
1071 print "Description:"
1072 print hError_handler.hError
1073 if hError_handler.hErrorId:
1074 print "ID:" + hError_handler.hErrorId
1075 except xml.sax.SAXException, e:
1076 pass
1077 print ""
1079 def usage():
1080 print "Usage: %s http_uri" % os.path.basename(sys.argv[0])
1082 def main():
1083 args = sys.argv[1:]
1084 if "-h" in args or "--help" in args or len(args)==0:
1085 usage()
1086 sys.exit(2)
1087 editor = HCatEditor(args[0])
1088 editor.refresh_cats()
1089 msg = "Choose: [a]dd, [e]dit, [d]elete, [r]efresh, [q]uit"
1090 while True:
1091 x = prompt(msg)
1092 if x == 'q':
1093 return 0
1094 elif x == 'r':
1095 editor.refresh_cats()
1096 elif x == 'e':
1097 editor.choose_edit()
1098 elif x == 'd':
1099 editor.choose_delete()
1100 elif x == 'a':
1101 editor.choose_add()
1102 else:
1103 print 'Unknown option.\n'
1105 if __name__ == "__main__":
1106 main()
1108 Intellectual Property Statement
1110 The IETF takes no position regarding the validity or scope of any
1111 Intellectual Property Rights or other rights that might be claimed to
1112 pertain to the implementation or use of the technology described in
1113 this document or the extent to which any license under such rights
1114 might or might not be available; nor does it represent that it has
1115 made any independent effort to identify any such rights. Information
1116 on the procedures with respect to rights in RFC documents can be
1117 found in BCP 78 and BCP 79.
1119 Copies of IPR disclosures made to the IETF Secretariat and any
1120 assurances of licenses to be made available, or the result of an
1121 attempt made to obtain a general license or permission for the use of
1122 such proprietary rights by implementers or users of this
1123 specification can be obtained from the IETF on-line IPR repository at
1124 http://www.ietf.org/ipr.
1126 The IETF invites any interested party to bring to its attention any
1127 copyrights, patents or patent applications, or other proprietary
1128 rights that may cover technology that may be required to implement
1129 this standard. Please address the information to the IETF at
1130 ietf-ipr@ietf.org.
1132 The IETF has been notified of intellectual property rights claimed in
1133 regard to some or all of the specification contained in this
1134 document. For more information consult the online list of claimed
1135 rights.
1137 Disclaimer of Validity
1139 This document and the information contained herein are provided on an
1140 "AS IS" basis and THE CONTRIBUTOR, THE ORGANIZATION HE/SHE REPRESENTS
1141 OR IS SPONSORED BY (IF ANY), THE INTERNET SOCIETY AND THE INTERNET
1142 ENGINEERING TASK FORCE DISCLAIM ALL WARRANTIES, EXPRESS OR IMPLIED,
1143 INCLUDING BUT NOT LIMITED TO ANY WARRANTY THAT THE USE OF THE
1144 INFORMATION HEREIN WILL NOT INFRINGE ANY RIGHTS OR ANY IMPLIED
1145 WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE.
1147 Copyright Statement
1149 Copyright (C) The Internet Society (2005). This document is subject
1150 to the rights, licenses and restrictions contained in BCP 78, and
1151 except as set forth therein, the authors retain all their rights.
1153 Acknowledgment
1155 Funding for the RFC Editor function is currently provided by the
1156 Internet Society.