pyxmpp.jabber.vcard

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