idnits 2.17.1 draft-sayre-atompub-xhtml-micro-00.txt: Checking boilerplate required by RFC 5378 and the IETF Trust (see https://trustee.ietf.org/license-info): ---------------------------------------------------------------------------- ** It looks like you're using RFC 3978 boilerplate. You should update this to the boilerplate described in the IETF Trust License Policy document (see https://trustee.ietf.org/license-info), which is required now. -- Found old boilerplate from RFC 3978, Section 5.1 on line 14. -- Found old boilerplate from RFC 3978, Section 5.5 on line 1145. -- Found old boilerplate from RFC 3979, Section 5, paragraph 1 on line 1117. -- Found old boilerplate from RFC 3979, Section 5, paragraph 2 on line 1124. -- Found old boilerplate from RFC 3979, Section 5, paragraph 3 on line 1130. ** This document has an original RFC 3978 Section 5.4 Copyright Line, instead of the newer IETF Trust Copyright according to RFC 4748. ** This document has an original RFC 3978 Section 5.5 Disclaimer, instead of the newer disclaimer which includes the IETF Trust according to RFC 4748. Checking nits according to https://www.ietf.org/id-info/1id-guidelines.txt: ---------------------------------------------------------------------------- == No 'Intended status' indicated for this document; assuming Proposed Standard Checking nits according to https://www.ietf.org/id-info/checklist : ---------------------------------------------------------------------------- ** The document seems to lack an IANA Considerations section. (See Section 2.2 of https://www.ietf.org/id-info/checklist for how to handle the case when there are no actions for IANA.) ** The abstract seems to contain references ([1]), which it shouldn't. Please replace those with straight textual mentions of the documents in question. Miscellaneous warnings: ---------------------------------------------------------------------------- == The copyright year in the RFC 3978 Section 5.4 Copyright Line does not match the current year -- The document seems to lack a disclaimer for pre-RFC5378 work, but may have content which was first submitted before 10 November 2008. If you have contacted all the original authors and they are all willing to grant the BCP78 rights to the IETF Trust, then this is fine, and you can ignore this comment. If not, you may need to add the pre-RFC5378 disclaimer. (See the Legal Provisions document at https://trustee.ietf.org/license-info for more information.) -- The document date (September 3, 2005) is 6809 days in the past. Is this intentional? -- Found something which looks like a code comment -- if you have code sections in the document, please surround them with '' 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
121 122 123 124
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
373
374 Term:
375 Label:
376 Scheme:
377 378
379
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" " 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.