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