Package pyxmpp :: Package jabber :: Module vcard

[hide private]

# Source Code for Module pyxmpp.jabber.vcard

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