Package pyxmpp :: Package jabber :: Module vcard
[hide private]

Source Code for Module pyxmpp.jabber.vcard

   1  # 
   2  # (C) Copyright 2003-2010 Jacek Konieczny <jajcus@jajcus.net> 
   3  # 
   4  # This program is free software; you can redistribute it and/or modify 
   5  # it under the terms of the GNU Lesser General Public License Version 
   6  # 2.1 as published by the Free Software Foundation. 
   7  # 
   8  # This program is distributed in the hope that it will be useful, 
   9  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
  10  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  11  # GNU Lesser General Public License for more details. 
  12  # 
  13  # You should have received a copy of the GNU Lesser General Public 
  14  # License along with this program; if not, write to the Free Software 
  15  # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  16  # 
  17  # pylint: disable-msg=W0302 
  18  """Jabber vCard and MIME (RFC 2426) vCard implementation. 
  19   
  20  Normative reference: 
  21    - `JEP 54 <http://www.jabber.org/jeps/jep-0054.html>`__ 
  22    - `RFC 2425 <http://www.ietf.org/rfc/rfc2425.txt>`__ 
  23    - `RFC 2426 <http://www.ietf.org/rfc/rfc2426.txt>`__ 
  24  """ 
  25   
  26   
  27  __revision__="$Id: vcard.py 714 2010-04-05 10:20:10Z jajcus $" 
  28  __docformat__="restructuredtext en" 
  29   
  30  import base64 
  31  import binascii 
  32  import libxml2 
  33  import re 
  34   
  35  import pyxmpp.jid 
  36  from pyxmpp.utils import to_utf8,from_utf8 
  37  from pyxmpp.xmlextra import get_node_ns 
  38  from pyxmpp.objects import StanzaPayloadObject 
  39  from pyxmpp.exceptions import BadRequestProtocolError, JIDMalformedProtocolError, JIDError 
  40   
  41  VCARD_NS="vcard-temp" 
  42   
43 -class Empty(Exception):
44 """Exception raised when parsing empty vcard element. Such element will 45 be ignored.""" 46 pass
47 48 valid_string_re=re.compile(r"^[\w\d \t]*$") 49 non_quoted_semicolon_re=re.compile(r'(?<!\\);') 50
51 -def quote_semicolon(value):
52 return value.replace(r';', r'\;')
53
54 -def unquote_semicolon(value):
55 return value.replace(r'\;', r';')
56
57 -def rfc2425encode(name,value,parameters=None,charset="utf-8"):
58 """Encodes a vCard field into an RFC2425 line. 59 60 :Parameters: 61 - `name`: field type name 62 - `value`: field value 63 - `parameters`: optional parameters 64 - `charset`: encoding of the output and of the `value` (if not 65 `unicode`) 66 :Types: 67 - `name`: `str` 68 - `value`: `unicode` or `str` 69 - `parameters`: `dict` of `str` -> `str` 70 - `charset`: `str` 71 72 :return: the encoded RFC2425 line (possibly folded) 73 :returntype: `str`""" 74 if not parameters: 75 parameters={} 76 if type(value) is unicode: 77 value=value.replace(u"\r\n",u"\\n") 78 value=value.replace(u"\n",u"\\n") 79 value=value.replace(u"\r",u"\\n") 80 value=value.encode(charset,"replace") 81 elif type(value) is not str: 82 raise TypeError,"Bad type for rfc2425 value" 83 elif not valid_string_re.match(value): 84 parameters["encoding"]="b" 85 value=binascii.b2a_base64(value) 86 87 ret=str(name).lower() 88 for k,v in parameters.items(): 89 ret+=";%s=%s" % (str(k),str(v)) 90 ret+=":" 91 while(len(value)>70): 92 ret+=value[:70]+"\r\n " 93 value=value[70:] 94 ret+=value+"\r\n" 95 return ret
96
97 -class VCardField:
98 """Base class for vCard fields. 99 100 :Ivariables: 101 - `name`: name of the field. 102 """
103 - def __init__(self,name):
104 """Initialize a `VCardField` object. 105 106 Set its name. 107 108 :Parameters: 109 - `name`: field name 110 :Types: 111 - `name`: `str`""" 112 self.name=name
113 - def __repr__(self):
114 return "<%s %r>" % (self.__class__,self.rfc2426())
115 - def rfc2426(self):
116 """RFC2426-encode the field content. 117 118 :return: the field in the RFC 2426 format. 119 :returntype: `str`""" 120 return ""
121
122 -class VCardString(VCardField):
123 """Generic class for all standard text fields in the vCard. 124 125 :Ivariables: 126 - `value`: field value. 127 :Types: 128 - `value`: `unicode`"""
129 - def __init__(self,name, value, rfc2425parameters = None, empty_ok = False):
130 """Initialize a `VCardString` object. 131 132 :Parameters: 133 - `name`: field name 134 - `value`: field value as string or an XML node 135 - `rfc2425parameters`: optional RFC 2425 parameters 136 :Types: 137 - `name`: `str` 138 - `value`: `str` or `libxml2.xmlNode` 139 - `rfc2425parameters`: `dict`""" 140 _unused = rfc2425parameters 141 VCardField.__init__(self,name) 142 if isinstance(value,libxml2.xmlNode): 143 value=value.getContent() 144 if value: 145 self.value=unicode(value,"utf-8","replace").strip() 146 else: 147 self.value=u"" 148 else: 149 self.value=value 150 if not self.value and not empty_ok: 151 raise Empty,"Empty string value"
152 - def rfc2426(self):
153 """RFC2426-encode the field content. 154 155 :return: the field in the RFC 2426 format. 156 :returntype: `str`""" 157 return rfc2425encode(self.name,self.value)
158 - def as_xml(self,parent):
159 """Create vcard-tmp XML representation of the field. 160 161 :Parameters: 162 - `parent`: parent node for the element 163 :Types: 164 - `parent`: `libxml2.xmlNode` 165 166 :return: xml node with the field data. 167 :returntype: `libxml2.xmlNode`""" 168 return parent.newTextChild(None, to_utf8(self.name.upper()), to_utf8(self.value))
169 - def __unicode__(self):
170 return self.value
171 - def __str__(self):
172 return self.value.encode("utf-8")
173
174 -class VCardXString(VCardString):
175 """Generic class for all text vCard fields not defined in RFC 2426. 176 177 In the RFC 2425 representation field name will be prefixed with 'x-'. 178 179 :Ivariables: 180 - `value`: field value. 181 :Types: 182 - `value`: `unicode`"""
183 - def rfc2426(self):
184 """RFC2426-encode the field content. 185 186 :return: the field in the RFC 2426 format. 187 :returntype: `str`""" 188 return rfc2425encode("x-"+self.name,self.value)
189
190 -class VCardJID(VCardField):
191 """JID vCard field. 192 193 This field is not defined in RFC 2426, so it will be named 'x-jabberid' 194 in RFC 2425 output. 195 196 :Ivariables: 197 - `value`: field value. 198 :Types: 199 - `value`: `JID`"""
200 - def __init__(self,name,value,rfc2425parameters=None):
201 """Initialize a `VCardJID` object. 202 203 :Parameters: 204 - `name`: field name 205 - `value`: field value as string or an XML node 206 - `rfc2425parameters`: optional RFC 2425 parameters 207 :Types: 208 - `name`: `str` 209 - `value`: `str` or `libxml2.xmlNode` 210 - `rfc2425parameters`: `dict`""" 211 _unused = rfc2425parameters 212 VCardField.__init__(self,name) 213 if isinstance(value,libxml2.xmlNode): 214 try: 215 self.value=pyxmpp.jid.JID(value.getContent()) 216 except JIDError: 217 raise JIDMalformedProtocolError, "JID malformed" 218 else: 219 self.value=pyxmpp.jid.JID(value) 220 if not self.value: 221 raise Empty,"Empty JID value"
222 - def rfc2426(self):
223 """RFC2426-encode the field content. 224 225 :return: the field in the RFC 2426 format. 226 :returntype: `str`""" 227 return rfc2425encode("x-jabberid",self.value.as_unicode())
228 - def as_xml(self,parent):
229 """Create vcard-tmp XML representation of the field. 230 231 :Parameters: 232 - `parent`: parent node for the element 233 :Types: 234 - `parent`: `libxml2.xmlNode` 235 236 :return: xml node with the field data. 237 :returntype: `libxml2.xmlNode`""" 238 name=to_utf8(self.name.upper()) 239 content=self.value.as_utf8() 240 return parent.newTextChild(None, name, content)
241 - def __unicode__(self):
242 return self.value.as_unicode()
243 - def __str__(self):
244 return self.value.as_string()
245
246 -class VCardName(VCardField):
247 """Name vCard field. 248 249 :Ivariables: 250 - `family`: family name. 251 - `given`: given name. 252 - `middle`: middle name. 253 - `prefix`: name prefix. 254 - `suffix`: name suffix. 255 :Types: 256 - `family`: `unicode` 257 - `given`: `unicode` 258 - `middle`: `unicode` 259 - `prefix`: `unicode` 260 - `suffix`: `unicode`"""
261 - def __init__(self,name,value,rfc2425parameters=None):
262 """Initialize a `VCardName` object. 263 264 :Parameters: 265 - `name`: field name 266 - `value`: field value as string or an XML node 267 - `rfc2425parameters`: optional RFC 2425 parameters 268 :Types: 269 - `name`: `str` 270 - `value`: `str` or `libxml2.xmlNode` 271 - `rfc2425parameters`: `dict`""" 272 _unused = rfc2425parameters 273 VCardField.__init__(self,name) 274 if self.name.upper()!="N": 275 raise RuntimeError,"VCardName handles only 'N' type" 276 if isinstance(value,libxml2.xmlNode): 277 self.family,self.given,self.middle,self.prefix,self.suffix=[u""]*5 278 empty=1 279 n=value.children 280 vns=get_node_ns(value) 281 while n: 282 if n.type!='element': 283 n=n.next 284 continue 285 ns=get_node_ns(n) 286 if (ns and vns and ns.getContent()!=vns.getContent()): 287 n=n.next 288 continue 289 if n.name=='FAMILY': 290 self.family=unicode(n.getContent(),"utf-8") 291 empty=0 292 if n.name=='GIVEN': 293 self.given=unicode(n.getContent(),"utf-8") 294 empty=0 295 if n.name=='MIDDLE': 296 self.middle=unicode(n.getContent(),"utf-8") 297 empty=0 298 if n.name=='PREFIX': 299 self.prefix=unicode(n.getContent(),"utf-8") 300 empty=0 301 if n.name=='SUFFIX': 302 self.suffix=unicode(n.getContent(),"utf-8") 303 empty=0 304 n=n.next 305 if empty: 306 raise Empty, "Empty N value" 307 else: 308 v=non_quoted_semicolon_re.split(value) 309 value=[u""]*5 310 value[:len(v)]=v 311 self.family,self.given,self.middle,self.prefix,self.suffix=( 312 unquote_semicolon(val) for val in value)
313 - def rfc2426(self):
314 """RFC2426-encode the field content. 315 316 :return: the field in the RFC 2426 format. 317 :returntype: `str`""" 318 return rfc2425encode("n",u';'.join(quote_semicolon(val) for val in 319 (self.family,self.given,self.middle,self.prefix,self.suffix)))
320 - def as_xml(self,parent):
321 """Create vcard-tmp XML representation of the field. 322 323 :Parameters: 324 - `parent`: parent node for the element 325 :Types: 326 - `parent`: `libxml2.xmlNode` 327 328 :return: xml node with the field data. 329 :returntype: `libxml2.xmlNode`""" 330 n=parent.newChild(None,"N",None) 331 n.newTextChild(None,"FAMILY",to_utf8(self.family)) 332 n.newTextChild(None,"GIVEN",to_utf8(self.given)) 333 n.newTextChild(None,"MIDDLE",to_utf8(self.middle)) 334 n.newTextChild(None,"PREFIX",to_utf8(self.prefix)) 335 n.newTextChild(None,"SUFFIX",to_utf8(self.suffix)) 336 return n
337 - def __unicode__(self):
338 r=[] 339 if self.prefix: 340 r.append(self.prefix.replace(u",",u" ")) 341 if self.given: 342 r.append(self.given.replace(u",",u" ")) 343 if self.middle: 344 r.append(self.middle.replace(u",",u" ")) 345 if self.family: 346 r.append(self.family.replace(u",",u" ")) 347 if self.suffix: 348 r.append(self.suffix.replace(u",",u" ")) 349 return u" ".join(r)
350 - def __str__(self):
351 return self.__unicode__().encode("utf-8")
352
353 -class VCardImage(VCardField):
354 """Image vCard field. 355 356 :Ivariables: 357 - `image`: image binary data (when `uri` is None) 358 - `uri`: image URI (when `image` is None) 359 - `type`: optional image type 360 :Types: 361 - `image`: `str` 362 - `uri`: `unicode` 363 - `type`: `unicode`"""
364 - def __init__(self,name,value,rfc2425parameters=None):
365 """Initialize a `VCardImage` object. 366 367 :Parameters: 368 - `name`: field name 369 - `value`: field value as string or an XML node 370 - `rfc2425parameters`: optional RFC 2425 parameters 371 :Types: 372 - `name`: `str` 373 - `value`: `str` or `libxml2.xmlNode` 374 - `rfc2425parameters`: `dict`""" 375 VCardField.__init__(self,name) 376 if not rfc2425parameters: 377 rfc2425parameters={} 378 self.uri,self.type,self.image=[None]*3 379 if isinstance(value,libxml2.xmlNode): 380 n=value.children 381 vns=get_node_ns(value) 382 while n: 383 if n.type!='element': 384 n=n.next 385 continue 386 ns=get_node_ns(n) 387 if (ns and vns and ns.getContent()!=vns.getContent()): 388 n=n.next 389 continue 390 if n.name=='TYPE': 391 self.type=unicode(n.getContent(),"utf-8","replace") 392 if n.name=='BINVAL': 393 self.image=base64.decodestring(n.getContent()) 394 if n.name=='EXTVAL': 395 self.uri=unicode(n.getContent(),"utf-8","replace") 396 n=n.next 397 if (self.uri and self.image) or (not self.uri and not self.image): 398 raise ValueError,"Bad %s value in vcard" % (name,) 399 if (not self.uri and not self.image): 400 raise Empty,"Bad %s value in vcard" % (name,) 401 else: 402 if rfc2425parameters.get("value", "").lower()=="uri": 403 self.uri=value 404 self.type=None 405 else: 406 self.type=rfc2425parameters.get("type") 407 self.image=value
408 - def rfc2426(self):
409 """RFC2426-encode the field content. 410 411 :return: the field in the RFC 2426 format. 412 :returntype: `str`""" 413 if self.uri: 414 return rfc2425encode(self.name,self.uri,{"value":"uri"}) 415 elif self.image: 416 if self.type: 417 p={"type":self.type} 418 else: 419 p={} 420 return rfc2425encode(self.name,self.image,p)
421 - def as_xml(self,parent):
422 """Create vcard-tmp XML representation of the field. 423 424 :Parameters: 425 - `parent`: parent node for the element 426 :Types: 427 - `parent`: `libxml2.xmlNode` 428 429 :return: xml node with the field data. 430 :returntype: `libxml2.xmlNode`""" 431 n=parent.newChild(None,self.name.upper(),None) 432 if self.uri: 433 n.newTextChild(None,"EXTVAL",to_utf8(self.uri)) 434 else: 435 if self.type: 436 n.newTextChild(None,"TYPE",self.type) 437 n.newTextChild(None,"BINVAL",binascii.b2a_base64(self.image)) 438 return n
439 - def __unicode__(self):
440 if self.uri: 441 return self.uri 442 if self.type: 443 return u"(%s data)" % (self.type,) 444 return u"(binary data)"
445 - def __str__(self):
446 return self.__unicode__().encode("utf-8")
447 448
449 -class VCardAdr(VCardField):
450 """Address vCard field. 451 452 :Ivariables: 453 - `type`: type of the address. 454 - `pobox`: the post office box. 455 - `extadr`: the extended address. 456 - `street`: the street address. 457 - `locality`: the locality (e.g. city). 458 - `region`: the region. 459 - `pcode`: the postal code. 460 - `ctry`: the country. 461 :Types: 462 - `type`: `list` of "home","work","postal","parcel","dom","intl" or "pref" 463 - `pobox`: `unicode` 464 - `extadr`: `unicode` 465 - `street`: `unicode` 466 - `locality`: `unicode` 467 - `region`: `unicode` 468 - `pcode`: `unicode` 469 - `ctry`: `unicode`"""
470 - def __init__(self,name,value,rfc2425parameters=None):
471 """Initialize a `VCardAdr` object. 472 473 :Parameters: 474 - `name`: field name 475 - `value`: field value as string or an XML node 476 - `rfc2425parameters`: optional RFC 2425 parameters 477 :Types: 478 - `name`: `str` 479 - `value`: `str` or `libxml2.xmlNode` 480 - `rfc2425parameters`: `dict`""" 481 VCardField.__init__(self,name) 482 if not rfc2425parameters: 483 rfc2425parameters={} 484 if self.name.upper()!="ADR": 485 raise RuntimeError,"VCardAdr handles only 'ADR' type" 486 (self.pobox,self.extadr,self.street,self.locality, 487 self.region,self.pcode,self.ctry)=[""]*7 488 self.type=[] 489 if isinstance(value,libxml2.xmlNode): 490 self.__from_xml(value) 491 else: 492 t=rfc2425parameters.get("type") 493 if t: 494 self.type=t.split(",") 495 else: 496 self.type=["intl","postal","parcel","work"] 497 v=non_quoted_semicolon_re.split(value) 498 value=[""]*7 499 value[:len(v)]=v 500 (self.pobox,self.extadr,self.street,self.locality, 501 self.region,self.pcode,self.ctry)=( 502 unquote_semicolon(val) for val in value)
503
504 - def __from_xml(self,value):
505 """Initialize a `VCardAdr` object from and XML element. 506 507 :Parameters: 508 - `value`: field value as an XML node 509 :Types: 510 - `value`: `libxml2.xmlNode`""" 511 n=value.children 512 vns=get_node_ns(value) 513 while n: 514 if n.type!='element': 515 n=n.next 516 continue 517 ns=get_node_ns(n) 518 if (ns and vns and ns.getContent()!=vns.getContent()): 519 n=n.next 520 continue 521 if n.name=='POBOX': 522 self.pobox=unicode(n.getContent(),"utf-8","replace") 523 elif n.name in ('EXTADR', 'EXTADD'): 524 self.extadr=unicode(n.getContent(),"utf-8","replace") 525 elif n.name=='STREET': 526 self.street=unicode(n.getContent(),"utf-8","replace") 527 elif n.name=='LOCALITY': 528 self.locality=unicode(n.getContent(),"utf-8","replace") 529 elif n.name=='REGION': 530 self.region=unicode(n.getContent(),"utf-8","replace") 531 elif n.name=='PCODE': 532 self.pcode=unicode(n.getContent(),"utf-8","replace") 533 elif n.name=='CTRY': 534 self.ctry=unicode(n.getContent(),"utf-8","replace") 535 elif n.name in ("HOME","WORK","POSTAL","PARCEL","DOM","INTL", 536 "PREF"): 537 self.type.append(n.name.lower()) 538 n=n.next 539 if self.type==[]: 540 self.type=["intl","postal","parcel","work"] 541 elif "dom" in self.type and "intl" in self.type: 542 raise ValueError,"Both 'dom' and 'intl' specified in vcard ADR"
543
544 - def rfc2426(self):
545 """RFC2426-encode the field content. 546 547 :return: the field in the RFC 2426 format. 548 :returntype: `str`""" 549 return rfc2425encode("adr",u';'.join(quote_semicolon(val) for val in 550 (self.pobox,self.extadr,self.street,self.locality, 551 self.region,self.pcode,self.ctry)), 552 {"type":",".join(self.type)})
553
554 - def as_xml(self,parent):
555 """Create vcard-tmp XML representation of the field. 556 557 :Parameters: 558 - `parent`: parent node for the element 559 :Types: 560 - `parent`: `libxml2.xmlNode` 561 562 :return: xml node with the field data. 563 :returntype: `libxml2.xmlNode`""" 564 n=parent.newChild(None,"ADR",None) 565 for t in ("home","work","postal","parcel","dom","intl","pref"): 566 if t in self.type: 567 n.newChild(None,t.upper(),None) 568 n.newTextChild(None,"POBOX",to_utf8(self.pobox)) 569 n.newTextChild(None,"EXTADD",to_utf8(self.extadr)) 570 n.newTextChild(None,"STREET",to_utf8(self.street)) 571 n.newTextChild(None,"LOCALITY",to_utf8(self.locality)) 572 n.newTextChild(None,"REGION",to_utf8(self.region)) 573 n.newTextChild(None,"PCODE",to_utf8(self.pcode)) 574 n.newTextChild(None,"CTRY",to_utf8(self.ctry)) 575 return n
576
577 -class VCardLabel(VCardField):
578 """Address label vCard field. 579 580 :Ivariables: 581 - `lines`: list of label text lines. 582 - `type`: type of the label. 583 :Types: 584 - `lines`: `list` of `unicode` 585 - `type`: `list` of "home","work","postal","parcel","dom","intl" or "pref" 586 """
587 - def __init__(self,name,value,rfc2425parameters=None):
588 """Initialize a `VCardLabel` object. 589 590 :Parameters: 591 - `name`: field name 592 - `value`: field value as string or an XML node 593 - `rfc2425parameters`: optional RFC 2425 parameters 594 :Types: 595 - `name`: `str` 596 - `value`: `str` or `libxml2.xmlNode` 597 - `rfc2425parameters`: `dict`""" 598 VCardField.__init__(self,name) 599 if not rfc2425parameters: 600 rfc2425parameters={} 601 if self.name.upper()!="LABEL": 602 raise RuntimeError,"VCardAdr handles only 'LABEL' type" 603 if isinstance(value,libxml2.xmlNode): 604 self.lines=[] 605 self.type=[] 606 n=value.children 607 vns=get_node_ns(value) 608 while n: 609 if n.type!='element': 610 n=n.next 611 continue 612 ns=get_node_ns(n) 613 if (ns and vns and ns.getContent()!=vns.getContent()): 614 n=n.next 615 continue 616 if n.name=='LINE': 617 l=unicode(n.getContent(),"utf-8","replace").strip() 618 l=l.replace("\n"," ").replace("\r"," ") 619 self.lines.append(l) 620 elif n.name in ("HOME","WORK","POSTAL","PARCEL","DOM","INTL", 621 "PREF"): 622 self.type.append(n.name.lower()) 623 n=n.next 624 if self.type==[]: 625 self.type=["intl","postal","parcel","work"] 626 elif "dom" in self.type and "intl" in self.type: 627 raise ValueError,"Both 'dom' and 'intl' specified in vcard LABEL" 628 if not self.lines: 629 self.lines=[""] 630 else: 631 t=rfc2425parameters.get("type") 632 if t: 633 self.type=t.split(",") 634 else: 635 self.type=["intl","postal","parcel","work"] 636 self.lines=value.split("\\n")
637
638 - def rfc2426(self):
639 """RFC2426-encode the field content. 640 641 :return: the field in the RFC 2426 format. 642 :returntype: `str`""" 643 return rfc2425encode("label",u"\n".join(self.lines), 644 {"type":",".join(self.type)})
645 - def as_xml(self,parent):
646 """Create vcard-tmp XML representation of the field. 647 648 :Parameters: 649 - `parent`: parent node for the element 650 :Types: 651 - `parent`: `libxml2.xmlNode` 652 653 :return: xml node with the field data. 654 :returntype: `libxml2.xmlNode`""" 655 n=parent.newChild(None,"ADR",None) 656 for t in ("home","work","postal","parcel","dom","intl","pref"): 657 if t in self.type: 658 n.newChild(None,t.upper(),None) 659 for l in self.lines: 660 n.newTextChild(None,"LINE",l) 661 return n
662
663 -class VCardTel(VCardField):
664 """Telephone vCard field. 665 666 :Ivariables: 667 - `number`: phone number. 668 - `type`: type of the phone number. 669 :Types: 670 - `number`: `unicode` 671 - `type`: `list` of "home","work","voice","fax","pager","msg","cell","video","bbs","modem","isdn","pcs" or "pref". 672 """
673 - def __init__(self,name,value,rfc2425parameters=None):
674 """Initialize a `VCardTel` object. 675 676 :Parameters: 677 - `name`: field name 678 - `value`: field value as string or an XML node 679 - `rfc2425parameters`: optional RFC 2425 parameters 680 :Types: 681 - `name`: `str` 682 - `value`: `str` or `libxml2.xmlNode` 683 - `rfc2425parameters`: `dict`""" 684 VCardField.__init__(self,name) 685 if not rfc2425parameters: 686 rfc2425parameters={} 687 if self.name.upper()!="TEL": 688 raise RuntimeError,"VCardTel handles only 'TEL' type" 689 if isinstance(value,libxml2.xmlNode): 690 self.number=None 691 self.type=[] 692 n=value.children 693 vns=get_node_ns(value) 694 while n: 695 if n.type!='element': 696 n=n.next 697 continue 698 ns=get_node_ns(n) 699 if (ns and vns and ns.getContent()!=vns.getContent()): 700 n=n.next 701 continue 702 if n.name=='NUMBER': 703 self.number=unicode(n.getContent(),"utf-8","replace") 704 elif n.name in ("HOME","WORK","VOICE","FAX","PAGER","MSG", 705 "CELL","VIDEO","BBS","MODEM","ISDN","PCS", 706 "PREF"): 707 self.type.append(n.name.lower()) 708 n=n.next 709 if self.type==[]: 710 self.type=["voice"] 711 if not self.number: 712 raise Empty,"No tel number" 713 else: 714 t=rfc2425parameters.get("type") 715 if t: 716 self.type=t.split(",") 717 else: 718 self.type=["voice"] 719 self.number=value
720 - def rfc2426(self):
721 """RFC2426-encode the field content. 722 723 :return: the field in the RFC 2426 format. 724 :returntype: `str`""" 725 return rfc2425encode("tel",self.number,{"type":",".join(self.type)})
726 - def as_xml(self,parent):
727 """Create vcard-tmp XML representation of the field. 728 729 :Parameters: 730 - `parent`: parent node for the element 731 :Types: 732 - `parent`: `libxml2.xmlNode` 733 734 :return: xml node with the field data. 735 :returntype: `libxml2.xmlNode`""" 736 n=parent.newChild(None,"TEL",None) 737 for t in ("home","work","voice","fax","pager","msg","cell","video", 738 "bbs","modem","isdn","pcs","pref"): 739 if t in self.type: 740 n.newChild(None,t.upper(),None) 741 n.newTextChild(None,"NUMBER",to_utf8(self.number)) 742 return n
743
744 -class VCardEmail(VCardField):
745 """E-mail vCard field. 746 747 :Ivariables: 748 - `address`: e-mail address. 749 - `type`: type of the address. 750 :Types: 751 - `address`: `unicode` 752 - `type`: `list` of "home","work","internet" or "x400". 753 """
754 - def __init__(self,name,value,rfc2425parameters=None):
755 """Initialize a `VCardEmail` object. 756 757 :Parameters: 758 - `name`: field name 759 - `value`: field value as string or an XML node 760 - `rfc2425parameters`: optional RFC 2425 parameters 761 :Types: 762 - `name`: `str` 763 - `value`: `str` or `libxml2.xmlNode` 764 - `rfc2425parameters`: `dict`""" 765 VCardField.__init__(self,name) 766 if not rfc2425parameters: 767 rfc2425parameters={} 768 if self.name.upper()!="EMAIL": 769 raise RuntimeError,"VCardEmail handles only 'EMAIL' type" 770 if isinstance(value,libxml2.xmlNode): 771 self.address=None 772 self.type=[] 773 n=value.children 774 vns=get_node_ns(value) 775 while n: 776 if n.type!='element': 777 n=n.next 778 continue 779 ns=get_node_ns(n) 780 if (ns and vns and ns.getContent()!=vns.getContent()): 781 n=n.next 782 continue 783 if n.name=='USERID': 784 self.address=unicode(n.getContent(),"utf-8","replace") 785 elif n.name in ("HOME","WORK","INTERNET","X400"): 786 self.type.append(n.name.lower()) 787 n=n.next 788 if self.type==[]: 789 self.type=["internet"] 790 if not self.address: 791 raise Empty,"No USERID" 792 else: 793 t=rfc2425parameters.get("type") 794 if t: 795 self.type=t.split(",") 796 else: 797 self.type=["internet"] 798 self.address=value
799 - def rfc2426(self):
800 """RFC2426-encode the field content. 801 802 :return: the field in the RFC 2426 format. 803 :returntype: `str`""" 804 return rfc2425encode("email",self.address,{"type":",".join(self.type)})
805 - def as_xml(self,parent):
806 """Create vcard-tmp XML representation of the field. 807 808 :Parameters: 809 - `parent`: parent node for the element 810 :Types: 811 - `parent`: `libxml2.xmlNode` 812 813 :return: xml node with the field data. 814 :returntype: `libxml2.xmlNode`""" 815 n=parent.newChild(None,"EMAIL",None) 816 for t in ("home","work","internet","x400"): 817 if t in self.type: 818 n.newChild(None,t.upper(),None) 819 n.newTextChild(None,"USERID",to_utf8(self.address)) 820 return n
821
822 -class VCardGeo(VCardField):
823 """Geographical location vCard field. 824 825 :Ivariables: 826 - `lat`: the latitude. 827 - `lon`: the longitude. 828 :Types: 829 - `lat`: `unicode` 830 - `lon`: `unicode` 831 """
832 - def __init__(self,name,value,rfc2425parameters=None):
833 """Initialize a `VCardGeo` object. 834 835 :Parameters: 836 - `name`: field name 837 - `value`: field value as string or an XML node 838 - `rfc2425parameters`: optional RFC 2425 parameters 839 :Types: 840 - `name`: `str` 841 - `value`: `str` or `libxml2.xmlNode` 842 - `rfc2425parameters`: `dict`""" 843 _unused = rfc2425parameters 844 VCardField.__init__(self,name) 845 if self.name.upper()!="GEO": 846 raise RuntimeError,"VCardName handles only 'GEO' type" 847 if isinstance(value,libxml2.xmlNode): 848 self.lat,self.lon=[None]*2 849 n=value.children 850 vns=get_node_ns(value) 851 while n: 852 if n.type!='element': 853 n=n.next 854 continue 855 ns=get_node_ns(n) 856 if (ns and vns and ns.getContent()!=vns.getContent()): 857 n=n.next 858 continue 859 if n.name=='LAT': 860 self.lat=unicode(n.getContent(),"utf-8") 861 if n.name=='LON': 862 self.lon=unicode(n.getContent(),"utf-8") 863 n=n.next 864 if not self.lat or not self.lon: 865 raise ValueError,"Bad vcard GEO value" 866 else: 867 self.lat,self.lon=(unquote_semicolon(val) for val in non_quoted_semicolon_re.split(value))
868 - def rfc2426(self):
869 """RFC2426-encode the field content. 870 871 :return: the field in the RFC 2426 format. 872 :returntype: `str`""" 873 return rfc2425encode("geo",u';'.join(quote_semicolon(val) for val in 874 (self.lat,self.lon)))
875 - def as_xml(self,parent):
876 """Create vcard-tmp XML representation of the field. 877 878 :Parameters: 879 - `parent`: parent node for the element 880 :Types: 881 - `parent`: `libxml2.xmlNode` 882 883 :return: xml node with the field data. 884 :returntype: `libxml2.xmlNode`""" 885 n=parent.newChild(None,"GEO",None) 886 n.newTextChild(None,"LAT",to_utf8(self.lat)) 887 n.newTextChild(None,"LON",to_utf8(self.lon)) 888 return n
889
890 -class VCardOrg(VCardField):
891 """Organization vCard field. 892 893 :Ivariables: 894 - `name`: organization name. 895 - `unit`: organizational unit. 896 :Types: 897 - `name`: `unicode` 898 - `unit`: `unicode` 899 """
900 - def __init__(self,name,value,rfc2425parameters=None):
901 """Initialize a `VCardOrg` object. 902 903 :Parameters: 904 - `name`: field name 905 - `value`: field value as string or an XML node 906 - `rfc2425parameters`: optional RFC 2425 parameters 907 :Types: 908 - `name`: `str` 909 - `value`: `str` or `libxml2.xmlNode` 910 - `rfc2425parameters`: `dict`""" 911 _unused = rfc2425parameters 912 VCardField.__init__(self,name) 913 if self.name.upper()!="ORG": 914 raise RuntimeError,"VCardName handles only 'ORG' type" 915 if isinstance(value,libxml2.xmlNode): 916 self.name,self.unit=None,"" 917 n=value.children 918 vns=get_node_ns(value) 919 while n: 920 if n.type!='element': 921 n=n.next 922 continue 923 ns=get_node_ns(n) 924 if (ns and vns and ns.getContent()!=vns.getContent()): 925 n=n.next 926 continue 927 if n.name=='ORGNAME': 928 self.name=unicode(n.getContent(),"utf-8") 929 if n.name=='ORGUNIT': 930 self.unit=unicode(n.getContent(),"utf-8") 931 n=n.next 932 if not self.name: 933 raise Empty,"Bad vcard ORG value" 934 else: 935 sp=non_quoted_semicolon_re.split(value,1) 936 if len(sp)>1: 937 self.name,self.unit=(unquote_semicolon(val) for val in sp) 938 else: 939 self.name=unquote_semicolon(sp[0]) 940 self.unit=None
941 - def rfc2426(self):
942 """RFC2426-encode the field content. 943 944 :return: the field in the RFC 2426 format. 945 :returntype: `str`""" 946 if self.unit: 947 return rfc2425encode("org",u';'.join(quote_semicolon(val) for val in 948 (self.name,self.unit))) 949 else: 950 return rfc2425encode("org",unicode(quote_semicolon(self.name)))
951 - def as_xml(self,parent):
952 """Create vcard-tmp XML representation of the field. 953 954 :Parameters: 955 - `parent`: parent node for the element 956 :Types: 957 - `parent`: `libxml2.xmlNode` 958 959 :return: xml node with the field data. 960 :returntype: `libxml2.xmlNode`""" 961 n=parent.newChild(None,"ORG",None) 962 n.newTextChild(None,"ORGNAME",to_utf8(self.name)) 963 n.newTextChild(None,"ORGUNIT",to_utf8(self.unit)) 964 return n
965
966 -class VCardCategories(VCardField):
967 """Categories vCard field. 968 969 :Ivariables: 970 - `keywords`: category keywords. 971 :Types: 972 - `keywords`: `list` of `unicode` 973 """
974 - def __init__(self,name,value,rfc2425parameters=None):
975 """Initialize a `VCardCategories` object. 976 977 :Parameters: 978 - `name`: field name 979 - `value`: field value as string or an XML node 980 - `rfc2425parameters`: optional RFC 2425 parameters 981 :Types: 982 - `name`: `str` 983 - `value`: `str` or `libxml2.xmlNode` 984 - `rfc2425parameters`: `dict`""" 985 _unused = rfc2425parameters 986 VCardField.__init__(self,name) 987 self.name=name 988 if self.name.upper()!="CATEGORIES": 989 raise RuntimeError,"VCardName handles only 'CATEGORIES' type" 990 if isinstance(value,libxml2.xmlNode): 991 self.keywords=[] 992 n=value.children 993 vns=get_node_ns(value) 994 while n: 995 if n.type!='element': 996 n=n.next 997 continue 998 ns=get_node_ns(n) 999 if (ns and vns and ns.getContent()!=vns.getContent()): 1000 n=n.next 1001 continue 1002 if n.name=='KEYWORD': 1003 self.keywords.append(unicode(n.getContent(),"utf-8")) 1004 n=n.next 1005 if not self.keywords: 1006 raise Empty,"Bad vcard CATEGORIES value" 1007 else: 1008 self.keywords=value.split(",")
1009 - def rfc2426(self):
1010 """RFC2426-encode the field content. 1011 1012 :return: the field in the RFC 2426 format. 1013 :returntype: `str`""" 1014 return rfc2425encode("keywords",u",".join(self.keywords))
1015 - def as_xml(self,parent):
1016 """Create vcard-tmp XML representation of the field. 1017 1018 :Parameters: 1019 - `parent`: parent node for the element 1020 :Types: 1021 - `parent`: `libxml2.xmlNode` 1022 1023 :return: xml node with the field data. 1024 :returntype: `libxml2.xmlNode`""" 1025 n=parent.newChild(None,"CATEGORIES",None) 1026 for k in self.keywords: 1027 n.newTextChild(None,"KEYWORD",to_utf8(k)) 1028 return n
1029
1030 -class VCardSound(VCardField):
1031 """Sound vCard field. 1032 1033 :Ivariables: 1034 - `sound`: binary sound data (when `uri` is None) 1035 - `uri`: sound URI (when `sound` is None) 1036 - `phonetic`: phonetic transcription 1037 :Types: 1038 - `sound`: `str` 1039 - `uri`: `unicode` 1040 - `phonetic`: `unicode`"""
1041 - def __init__(self,name,value,rfc2425parameters=None):
1042 """Initialize a `VCardSound` object. 1043 1044 :Parameters: 1045 - `name`: field name 1046 - `value`: field value as string or an XML node 1047 - `rfc2425parameters`: optional RFC 2425 parameters 1048 :Types: 1049 - `name`: `str` 1050 - `value`: `str` or `libxml2.xmlNode` 1051 - `rfc2425parameters`: `dict`""" 1052 VCardField.__init__(self,name) 1053 if not rfc2425parameters: 1054 rfc2425parameters={} 1055 self.uri,self.sound,self.phonetic=[None]*3 1056 if isinstance(value,libxml2.xmlNode): 1057 n=value.children 1058 vns=get_node_ns(value) 1059 while n: 1060 if n.type!='element': 1061 n=n.next 1062 continue 1063 ns=get_node_ns(n) 1064 if (ns and vns and ns.getContent()!=vns.getContent()): 1065 n=n.next 1066 continue 1067 if n.name=='BINVAL': 1068 if (self.phonetic or self.uri): 1069 raise ValueError,"Bad SOUND value in vcard" 1070 self.sound=base64.decodestring(n.getContent()) 1071 if n.name=='PHONETIC': 1072 if (self.sound or self.uri): 1073 raise ValueError,"Bad SOUND value in vcard" 1074 self.phonetic=unicode(n.getContent(),"utf-8","replace") 1075 if n.name=='EXTVAL': 1076 if (self.phonetic or self.sound): 1077 raise ValueError,"Bad SOUND value in vcard" 1078 self.uri=unicode(n.getContent(),"utf-8","replace") 1079 n=n.next 1080 if (not self.phonetic and not self.uri and not self.sound): 1081 raise Empty,"Bad SOUND value in vcard" 1082 else: 1083 if rfc2425parameters.get("value", "").lower()=="uri": 1084 self.uri=value 1085 self.sound=None 1086 self.phonetic=None 1087 else: 1088 self.sound=value 1089 self.uri=None 1090 self.phonetic=None
1091 - def rfc2426(self):
1092 """RFC2426-encode the field content. 1093 1094 :return: the field in the RFC 2426 format. 1095 :returntype: `str`""" 1096 if self.uri: 1097 return rfc2425encode(self.name,self.uri,{"value":"uri"}) 1098 elif self.sound: 1099 return rfc2425encode(self.name,self.sound)
1100 - def as_xml(self,parent):
1101 """Create vcard-tmp XML representation of the field. 1102 1103 :Parameters: 1104 - `parent`: parent node for the element 1105 :Types: 1106 - `parent`: `libxml2.xmlNode` 1107 1108 :return: xml node with the field data. 1109 :returntype: `libxml2.xmlNode`""" 1110 n=parent.newChild(None,self.name.upper(),None) 1111 if self.uri: 1112 n.newTextChild(None,"EXTVAL",to_utf8(self.uri)) 1113 elif self.phonetic: 1114 n.newTextChild(None,"PHONETIC",to_utf8(self.phonetic)) 1115 else: 1116 n.newTextChild(None,"BINVAL",binascii.b2a_base64(self.sound)) 1117 return n
1118
1119 -class VCardPrivacy(VCardField):
1120 """Privacy vCard field. 1121 1122 :Ivariables: 1123 - `value`: privacy information about the vcard data ("public", "private" 1124 or "confidental") 1125 :Types: 1126 - `value`: `str` """
1127 - def __init__(self,name,value,rfc2425parameters=None):
1128 """Initialize a `VCardPrivacy` object. 1129 1130 :Parameters: 1131 - `name`: field name 1132 - `value`: field value as string or an XML node 1133 - `rfc2425parameters`: optional RFC 2425 parameters 1134 :Types: 1135 - `name`: `str` 1136 - `value`: `str` or `libxml2.xmlNode` 1137 - `rfc2425parameters`: `dict`""" 1138 _unused = rfc2425parameters 1139 VCardField.__init__(self,name) 1140 if isinstance(value,libxml2.xmlNode): 1141 self.value=None 1142 n=value.children 1143 vns=get_node_ns(value) 1144 while n: 1145 if n.type!='element': 1146 n=n.next 1147 continue 1148 ns=get_node_ns(n) 1149 if (ns and vns and ns.getContent()!=vns.getContent()): 1150 n=n.next 1151 continue 1152 if n.name=='PUBLIC': 1153 self.value="public" 1154 elif n.name=='PRIVATE': 1155 self.value="private" 1156 elif n.name=='CONFIDENTAL': 1157 self.value="confidental" 1158 n=n.next 1159 if not self.value: 1160 raise Empty 1161 else: 1162 self.value=value
1163 - def rfc2426(self):
1164 """RFC2426-encode the field content. 1165 1166 :return: the field in the RFC 2426 format. 1167 :returntype: `str`""" 1168 return rfc2425encode(self.name,self.value)
1169 - def as_xml(self,parent):
1170 """Create vcard-tmp XML representation of the field. 1171 1172 :Parameters: 1173 - `parent`: parent node for the element 1174 :Types: 1175 - `parent`: `libxml2.xmlNode` 1176 1177 :return: xml node with the field data. 1178 :returntype: `libxml2.xmlNode`""" 1179 if self.value in ("public","private","confidental"): 1180 n=parent.newChild(None,self.name.upper(),None) 1181 n.newChild(None,self.value.upper(),None) 1182 return n 1183 return None
1184
1185 -class VCardKey(VCardField):
1186 """Key vCard field. 1187 1188 :Ivariables: 1189 - `type`: key type. 1190 - `cred`: key data. 1191 :Types: 1192 - `type`: `unicode` 1193 - `cred`: `str` """
1194 - def __init__(self,name,value,rfc2425parameters=None):
1195 """Initialize a `VCardKey` object. 1196 1197 :Parameters: 1198 - `name`: field name 1199 - `value`: field value as string or an XML node 1200 - `rfc2425parameters`: optional RFC 2425 parameters 1201 :Types: 1202 - `name`: `str` 1203 - `value`: `str` or `libxml2.xmlNode` 1204 - `rfc2425parameters`: `dict`""" 1205 VCardField.__init__(self,name) 1206 if not rfc2425parameters: 1207 rfc2425parameters={} 1208 if isinstance(value,libxml2.xmlNode): 1209 self.type,self.cred=None,None 1210 n=value.children 1211 vns=get_node_ns(value) 1212 while n: 1213 if n.type!='element': 1214 n=n.next 1215 continue 1216 ns=get_node_ns(n) 1217 if (ns and vns and ns.getContent()!=vns.getContent()): 1218 n=n.next 1219 continue 1220 if n.name=='TYPE': 1221 self.type=unicode(n.getContent(),"utf-8","replace") 1222 if n.name=='CRED': 1223 self.cred=base64.decodestring(n.getContent()) 1224 n=n.next 1225 if not self.cred: 1226 raise Empty,"Bad %s value in vcard" % (name,) 1227 else: 1228 self.type=rfc2425parameters.get("type") 1229 self.cred=value
1230 - def rfc2426(self):
1231 """RFC2426-encode the field content. 1232 1233 :return: the field in the RFC 2426 format. 1234 :returntype: `str`""" 1235 if self.type: 1236 p={"type":self.type} 1237 else: 1238 p={} 1239 return rfc2425encode(self.name,self.cred,p)
1240 - def as_xml(self,parent):
1241 """Create vcard-tmp XML representation of the field. 1242 1243 :Parameters: 1244 - `parent`: parent node for the element 1245 :Types: 1246 - `parent`: `libxml2.xmlNode` 1247 1248 :return: xml node with the field data. 1249 :returntype: `libxml2.xmlNode`""" 1250 n=parent.newChild(None,self.name.upper(),None) 1251 if self.type: 1252 n.newTextChild(None,"TYPE",self.type) 1253 n.newTextChild(None,"CRED",binascii.b2a_base64(self.cred)) 1254 return n
1255
1256 -class VCard(StanzaPayloadObject):
1257 """Jabber (vcard-temp) or RFC2426 vCard. 1258 1259 :Ivariables: 1260 - `fn`: full name. 1261 - `n`: structural name. 1262 - `nickname`: nickname(s). 1263 - `photo`: photo(s). 1264 - `bday`: birthday date(s). 1265 - `adr`: address(es). 1266 - `label`: address label(s). 1267 - `tel`: phone number(s). 1268 - `email`: e-mail address(es). 1269 - `jabberid`: JID(s). 1270 - `mailer`: mailer(s). 1271 - `tz`: timezone(s). 1272 - `geo`: geolocation(s). 1273 - `title`: title(s). 1274 - `role`: role(s). 1275 - `logo`: logo(s). 1276 - `org`: organization(s). 1277 - `categories`: categories. 1278 - `note`: note(s). 1279 - `prodid`: product id(s). 1280 - `rev`: revision(s). 1281 - `sort-string`: sort string(s). 1282 - `sound`: sound(s). 1283 - `uid`: user identifier(s). 1284 - `url`: URL(s). 1285 - `class`: class(es). 1286 - `key`: key(s). 1287 - `desc`: description. 1288 :Types: 1289 - `fn`: `VCardString`, 1290 - `n`: `VCardName`, 1291 - `nickname`: `list` of `VCardString` 1292 - `photo`: `list` of `VCardImage` 1293 - `bday`: `list` of `VCardString` 1294 - `adr`: `list` of `VCardAdr` 1295 - `label`: `list` of `VCardLabel` 1296 - `tel`: `list` of `VCardTel` 1297 - `email`: `list` of `VCardEmail` 1298 - `jabberid`: `list` of `VCardJID` 1299 - `mailer`: `list` of `VCardString` 1300 - `tz`: `list` of `VCardString` 1301 - `geo`: `list` of `VCardGeo` 1302 - `title`: `list` of `VCardString` 1303 - `role`: `list` of `VCardString` 1304 - `logo`: `list` of `VCardImage` 1305 - `org`: `list` of `VCardOrg` 1306 - `categories`: `list` of `VCardCategories` 1307 - `note`: `list` of `VCardString` 1308 - `prodid`: `list` of `VCardString` 1309 - `rev`: `list` of `VCardString` 1310 - `sort-string`: `list` of `VCardString` 1311 - `sound`: `list` of `VCardSound` 1312 - `uid`: `list` of `VCardString` 1313 - `url`: `list` of `VCardString` 1314 - `class`: `list` of `VCardString` 1315 - `key`: `list` of `VCardKey` 1316 - `desc`: `list` of `VCardXString` 1317 """ 1318 1319 xml_element_name = "vCard" 1320 xml_element_namespace = VCARD_NS 1321 1322 components={ 1323 #"VERSION": (VCardString,"optional"), 1324 "FN": (VCardString,"required"), 1325 "N": (VCardName,"required"), 1326 "NICKNAME": (VCardString,"multi"), 1327 "PHOTO": (VCardImage,"multi"), 1328 "BDAY": (VCardString,"multi"), 1329 "ADR": (VCardAdr,"multi"), 1330 "LABEL": (VCardLabel,"multi"), 1331 "TEL": (VCardTel,"multi"), 1332 "EMAIL": (VCardEmail,"multi"), 1333 "JABBERID": (VCardJID,"multi"), 1334 "MAILER": (VCardString,"multi"), 1335 "TZ": (VCardString,"multi"), 1336 "GEO": (VCardGeo,"multi"), 1337 "TITLE": (VCardString,"multi"), 1338 "ROLE": (VCardString,"multi"), 1339 "LOGO": (VCardImage,"multi"), 1340 "AGENT": ("VCardAgent","ignore"), #FIXME: agent field 1341 "ORG": (VCardOrg,"multi"), 1342 "CATEGORIES": (VCardCategories,"multi"), 1343 "NOTE": (VCardString,"multi"), 1344 "PRODID": (VCardString,"multi"), 1345 "REV": (VCardString,"multi"), 1346 "SORT-STRING": (VCardString,"multi"), 1347 "SOUND": (VCardSound,"multi"), 1348 "UID": (VCardString,"multi"), 1349 "URL": (VCardString,"multi"), 1350 "CLASS": (VCardString,"multi"), 1351 "KEY": (VCardKey,"multi"), 1352 "DESC": (VCardXString,"multi"), 1353 };
1354 - def __init__(self,data):
1355 """Initialize a VCard object from data which may be XML node 1356 or an RFC2426 string. 1357 1358 :Parameters: 1359 - `data`: vcard to parse. 1360 :Types: 1361 - `data`: `libxml2.xmlNode`, `unicode` or `str`""" 1362 1363 # to make pylint happy 1364 self.n = None 1365 del self.n 1366 1367 self.content={} 1368 if isinstance(data,libxml2.xmlNode): 1369 self.__from_xml(data) 1370 else: 1371 self.__from_rfc2426(data) 1372 if not self.content.get("N") and self.content.get("FN"): 1373 s=self.content['FN'].value.replace(";",",") 1374 s=s.split(None,2) 1375 if len(s)==2: 1376 s=u"%s;%s;;;" % (s[1],s[0]) 1377 elif len(s)==3: 1378 s=u"%s;%s;%s" % (s[2],s[0],s[1]) 1379 else: 1380 s=u"%s;;;;" % (s[0],) 1381 self.content["N"]=VCardName("N",s) 1382 elif not self.content.get("FN") and self.content.get("N"): 1383 self.__make_fn() 1384 for c, (_unused, tp) in self.components.items(): 1385 if self.content.has_key(c): 1386 continue 1387 if tp=="required": 1388 raise ValueError,"%s is missing" % (c,) 1389 elif tp=="multi": 1390 self.content[c]=[] 1391 elif tp=="optional": 1392 self.content[c]=None 1393 else: 1394 continue
1395
1396 - def __make_fn(self):
1397 """Initialize the mandatory `self.fn` from `self.n`. 1398 1399 This is a workaround for buggy clients which set only one of them.""" 1400 s=[] 1401 if self.n.prefix: 1402 s.append(self.n.prefix) 1403 if self.n.given: 1404 s.append(self.n.given) 1405 if self.n.middle: 1406 s.append(self.n.middle) 1407 if self.n.family: 1408 s.append(self.n.family) 1409 if self.n.suffix: 1410 s.append(self.n.suffix) 1411 s=u" ".join(s) 1412 self.content["FN"]=VCardString("FN", s, empty_ok = True)
1413
1414 - def __from_xml(self,data):
1415 """Initialize a VCard object from XML node. 1416 1417 :Parameters: 1418 - `data`: vcard to parse. 1419 :Types: 1420 - `data`: `libxml2.xmlNode`""" 1421 ns=get_node_ns(data) 1422 if ns and ns.getContent()!=VCARD_NS: 1423 raise ValueError, "Not in the %r namespace" % (VCARD_NS,) 1424 if data.name!="vCard": 1425 raise ValueError, "Bad root element name: %r" % (data.name,) 1426 n=data.children 1427 dns=get_node_ns(data) 1428 while n: 1429 if n.type!='element': 1430 n=n.next 1431 continue 1432 ns=get_node_ns(n) 1433 if (ns and dns and ns.getContent()!=dns.getContent()): 1434 n=n.next 1435 continue 1436 if not self.components.has_key(n.name): 1437 n=n.next 1438 continue 1439 cl,tp=self.components[n.name] 1440 if tp in ("required","optional"): 1441 if self.content.has_key(n.name): 1442 raise ValueError,"Duplicate %s" % (n.name,) 1443 try: 1444 self.content[n.name]=cl(n.name,n) 1445 except Empty: 1446 pass 1447 elif tp=="multi": 1448 if not self.content.has_key(n.name): 1449 self.content[n.name]=[] 1450 try: 1451 self.content[n.name].append(cl(n.name,n)) 1452 except Empty: 1453 pass 1454 n=n.next
1455
1456 - def __from_rfc2426(self,data):
1457 """Initialize a VCard object from an RFC2426 string. 1458 1459 :Parameters: 1460 - `data`: vcard to parse. 1461 :Types: 1462 - `data`: `libxml2.xmlNode`, `unicode` or `str`""" 1463 data=from_utf8(data) 1464 lines=data.split("\n") 1465 started=0 1466 current=None 1467 for l in lines: 1468 if not l: 1469 continue 1470 if l[-1]=="\r": 1471 l=l[:-1] 1472 if not l: 1473 continue 1474 if l[0] in " \t": 1475 if current is None: 1476 continue 1477 current+=l[1:] 1478 continue 1479 if not started and current and current.upper().strip()=="BEGIN:VCARD": 1480 started=1 1481 elif started and current.upper().strip()=="END:VCARD": 1482 current=None 1483 break 1484 elif current and started: 1485 self._process_rfc2425_record(current) 1486 current=l 1487 if started and current: 1488 self._process_rfc2425_record(current)
1489
1490 - def _process_rfc2425_record(self,data):
1491 """Parse single RFC2425 record and update attributes of `self`. 1492 1493 :Parameters: 1494 - `data`: the record (probably multiline) 1495 :Types: 1496 - `data`: `unicode`""" 1497 label,value=data.split(":",1) 1498 value=value.replace("\\n","\n").replace("\\N","\n") 1499 psplit=label.lower().split(";") 1500 name=psplit[0] 1501 params=psplit[1:] 1502 if u"." in name: 1503 name=name.split(".",1)[1] 1504 name=name.upper() 1505 if name in (u"X-DESC",u"X-JABBERID"): 1506 name=name[2:] 1507 if not self.components.has_key(name): 1508 return 1509 if params: 1510 params=dict([p.split("=",1) for p in params]) 1511 cl,tp=self.components[name] 1512 if tp in ("required","optional"): 1513 if self.content.has_key(name): 1514 raise ValueError,"Duplicate %s" % (name,) 1515 try: 1516 self.content[name]=cl(name,value,params) 1517 except Empty: 1518 pass 1519 elif tp=="multi": 1520 if not self.content.has_key(name): 1521 self.content[name]=[] 1522 try: 1523 self.content[name].append(cl(name,value,params)) 1524 except Empty: 1525 pass 1526 else: 1527 return
1528 - def __repr__(self):
1529 return "<vCard of %r>" % (self.content["FN"].value,)
1530 - def rfc2426(self):
1531 """Get the RFC2426 representation of `self`. 1532 1533 :return: the UTF-8 encoded RFC2426 representation. 1534 :returntype: `str`""" 1535 ret="begin:VCARD\r\n" 1536 ret+="version:3.0\r\n" 1537 for _unused, value in self.content.items(): 1538 if value is None: 1539 continue 1540 if type(value) is list: 1541 for v in value: 1542 ret+=v.rfc2426() 1543 else: 1544 v=value.rfc2426() 1545 ret+=v 1546 return ret+"end:VCARD\r\n"
1547
1548 - def complete_xml_element(self, xmlnode, _unused):
1549 """Complete the XML node with `self` content. 1550 1551 Should be overriden in classes derived from `StanzaPayloadObject`. 1552 1553 :Parameters: 1554 - `xmlnode`: XML node with the element being built. It has already 1555 right name and namespace, but no attributes or content. 1556 - `_unused`: document to which the element belongs. 1557 :Types: 1558 - `xmlnode`: `libxml2.xmlNode` 1559 - `_unused`: `libxml2.xmlDoc`""" 1560 for _unused1, value in self.content.items(): 1561 if value is None: 1562 continue 1563 if type(value) is list: 1564 for v in value: 1565 v.as_xml(xmlnode) 1566 else: 1567 value.as_xml(xmlnode)
1568
1569 - def __getattr__(self,name):
1570 try: 1571 return self.content[name.upper().replace("_","-")] 1572 except KeyError: 1573 raise AttributeError,"Attribute %r not found" % (name,)
1574 - def __getitem__(self,name):
1575 return self.content[name.upper()]
1576 1577 # vi: sts=4 et sw=4 1578