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

# Source Code for Module pyxmpp.jabber.vcard

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