Ejemplo n.º 1
0
def task_due_date_from_airsync(ctx):
    
    parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
    src_node = transform_ctx.current()
    
    asdate = tzutils.TaskTextToDate(xml2util.GetNodeValue(transform_ctx.current()))

    if tzconv.curtz() != None:

        # if we have a tz, we must insert the ID and convert to it

        dst_node.newChild(None,"TimezoneID",tzconv.curtz().name)
        asdate = tzconv.ConvertToLocal(asdate,tzconv.curtz())

        result = asdate.strftime(DATE_FORMAT_VCALTASKLOCAL)
    else:
        # if not, does the source have a UtcStartDate element?
        nd = xml2util.FindChildNode(src_node.parent,"UtcDueDate")
        if nd != None:
            result = tzutils.TaskTextToDate(xml2util.GetNodeValue(nd)).strftime(DATE_FORMAT_VCALTASK)

        else:
            # we don't have this either. Better hope that the DueDate value
            # is correct.

            result = asdate.strftime(DATE_FORMAT_VCALTASKLOCAL)

    dst_node = transform_ctx.insertNode()
    dst_node.newChild(None,"Content",result)
    return ""
Ejemplo n.º 2
0
def ExceptionDateTimeFromAirsync(ctx):

    parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
    src_node = transform_ctx.current()
    dst_node = transform_ctx.insertNode()

    exception_deleted = xml2util.GetNodeValue(
        xml2util.FindChildNode(src_node, "Deleted"))
    exception_date = xml2util.GetNodeValue(
        xml2util.FindChildNode(src_node, "ExceptionStartTime"))

    if exception_deleted != "1":
        raise ValueError(
            "Opensync does not support exceptions for modified occurrences")

    # We need to convert to current timezone if one is provided, else
    # we can assume UTC

    date = dateutil.TextToDate(exception_date)
    curtz = tzdatabase.tzdb.GetCurrentTimezone()
    if curtz != None:
        date = date.astimezone(curtz)
        dst_node.setProp("TimezoneID", curtz.name)

    dst_node = transform_ctx.insertNode()
    dst_node.setProp("Value", "DATE")
    tznode = dst_node.newChild(None, "Content",
                               date.strftime(DATE_FORMAT_SHORT))

    return ""
Ejemplo n.º 3
0
def TZFromAirsync(ctx):
	
	parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
	
	tznode = transform_ctx.current()
	tzdata = base64.b64decode(xml2util.GetNodeValue(tznode))	
	
	# Some AS timezones fail to include a year. See if we can
	# graft this from the AS body
	
	datenode = xml2util.FindChildNode(tznode.parent,"StartTime")
	if datenode == None:
		datenode = xml2util.FindChildNode(tznode.parent,"DtStamp")
	if datenode == None:
		year = datetime.datetime.now().year
	else:
		txtyear = xml2util.GetNodeValue(datenode)
		year = int(txtyear[0:4])
		
	tz = tzdatabase.TZFromAirsync(tzdata,year)
	
	# only add it if we have a valid timezone
	
	if tz!=None:
		tzdatabase.tzdb.PutTimezone(tz)
		
	return ""
Ejemplo n.º 4
0
def event_reminder_to_airsync(ctx):
    parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
    src_node = transform_ctx.current()
    content_node = xml2util.FindChildNode(src_node, "Content")
    value_node = xml2util.FindChildNode(src_node, "Value")
    related_node = xml2util.FindChildNode(src_node, "Related")
    if value_node == None or xml2util.GetNodeValue(value_node).lower() != "duration":
        return ""
    if related_node != None and xml2util.GetNodeValue(related_node).lower() != "start":
        return ""
    s = xml2util.GetNodeValue(content_node)
    s = s.lstrip("-PT")
    s = s.upper()
    days=0
    hours=0
    minutes=0
    if s.rfind("D") != -1:
        days = int(s[:s.rfind("D")])
        s = s[s.rfind("D")+1:]
    if s.rfind("H") != -1:
        hours = int(s[:s.rfind("H")])
        s = s[s.rfind("H")+1:]
    if s.rfind("M") != -1:
        minutes = int(s[:s.rfind("M")])
        s = s[s.rfind("M")+1:]

    return str(days * MINUTES_PER_DAY + hours * MINUTES_PER_HOUR + minutes)
Ejemplo n.º 5
0
def contact_position(ctx):
    parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
    curnode = transform_ctx.current()
    s=xml2util.GetNodeValue(curnode)
    # Extract the types
    types = []
    for child in curnode.children:
        if child.name == "Type":
            types.append(xml2util.GetNodeValue(child).upper())
    types.sort()
    position = 1
    prevnode = curnode.prev
    while prevnode != None:
        if prevnode.name == curnode.name:
            #print "found another", curnode.name
            # Now compare types
            prev_types = []
            for child in prevnode.children:
                if child.name == "Type":
                    prev_types.append(xml2util.GetNodeValue(child).upper())
            prev_types.sort()
            #print "prev types:",prev_types
            if prev_types == types:
                position += 1
        prevnode = prevnode.prev
    return position
Ejemplo n.º 6
0
def contact_has_type(ctx, type_string):
    parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
    curnode = transform_ctx.current()
    s=xml2util.GetNodeValue(curnode)
    for child in curnode.children:
        if child.name != "Type":
            continue
        if xml2util.GetNodeValue(child).upper() == type_string:
            return True
    return False
Ejemplo n.º 7
0
def AttendeeFromAirsync(ctx):
    parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
    src_node = transform_ctx.current()
    dst_node = transform_ctx.insertNode()

    email = xml2util.GetNodeValue(xml2util.FindChildNode(src_node, "Email"))
    name = xml2util.GetNodeValue(xml2util.FindChildNode(src_node, "Name"))
    if email != "":
        dst_node.newChild(None, "Content", "MAILTO:%s" % email)
    dst_node.newChild(None, "CommonName", name)
    return ""
Ejemplo n.º 8
0
def AttendeeToAirsync(ctx):

    parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
    src_node = transform_ctx.current()
    dst_node = transform_ctx.insertNode()

    email = xml2util.GetNodeValue(xml2util.FindChildNode(src_node,
                                                         "Content"))[7:]
    name = xml2util.GetNodeValue(xml2util.FindChildNode(
        src_node, "CommonName"))
    if name != "":
        dst_node.newChild(None, "Name", name)
    dst_node.newChild(None, "Email", email)
    return ""
Ejemplo n.º 9
0
def FindASDateElementContent(doc,element):
	
	up = xml2util.FindChildNode(doc,element)
	if up != None:
		return xml2util.GetNodeValue(up)
	else:
		return None
Ejemplo n.º 10
0
def ConvertASTimezoneToVcal(ctx):
	
    parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
    src_node = transform_ctx.current()
    dst_node = transform_ctx.insertNode()
    s=xml2util.GetNodeValue(transform_ctx.current())
    if len(s) > 0:
        dcas = base64.b64decode(s)
	base_node = transform_ctx.insertNode()
	astd = ASTimezoneData()
	astd.UnpackFromAirsync(dcas)
	astd.dump()

	sdate = FindASDateElementContent(src_node.parent,"StartTime")
	
	if sdate ==None:
		sdate=datetime.datetime.now()
		year = sdate.year
	else:
		year = int(sdate[0:4])
	
	astd.ToVcalTZ(dst_node,year)
	CUR_TZ["current"] = astd.tzcurrent
		
	# We now have a (hopefully valid) timezone entry as the current 
	# timezone. We need to back-convert the timezoneable dates as 
	# they appear.
	
    return ""
Ejemplo n.º 11
0
def event_datetime_from_airsync(ctx, node=None):
    parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
    if node:
        node = libxml2.xmlNode(_obj=node[0])
    else:
        node = transform_ctx.current()
    return tzutils.TextToDate(xml2util.GetNodeValue(node)).strftime(DATE_FORMAT_EVENT)
Ejemplo n.º 12
0
def ConvertDateNodeToUTC(node):
	date = None
	tdate = None
	udate = None
	ctnode = xml2util.FindChildNode(node,"Content")
	if ctnode != None:
		tdate = xml2util.GetNodeValue(ctnode).upper()
		if tdate != "":
			date = tzutils.TextToDate(tdate)
			udate = date
			if date.tzname() != "UTC":
				tzn=xml2util.FindChildNode(node,"TimezoneID")
				if tzn!=None:
					udate = ConvertToUTC(date,xml2util.GetNodeValue(tzn))
	else:
		raise ValueError("Bad date content")
	return date,udate
Ejemplo n.º 13
0
def StatusToAirsync(ctx):
    parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
    curnode = transform_ctx.current()
    s = xml2util.GetNodeValue(curnode)

    if s == "COMPLETED":
        return "1"
    else:
        # check that PercentComplete == 100% - mark it completed if
        # this is the case.
        up = xml2util.FindChildNode(curnode.parent.parent, "PercentComplete")
        if up != None:
            ct = xml2util.FindChildNode(up, "Content")
            if ct != None:
                if xml2util.GetNodeValue(ct) == "100":
                    return "1"
    return "0"
Ejemplo n.º 14
0
def TimeTransparencyFromAirsync(ctx):

    parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)

    if xml2util.GetNodeValue(transform_ctx.current()) == "0":
        return "TRANSPARENT"
    else:
        return "OPAQUE"  # 'Busy' is our default value
Ejemplo n.º 15
0
def event_reminder_from_airsync(ctx):
    parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
    s = int(xml2util.GetNodeValue(transform_ctx.current()))
    if s % MINUTES_PER_DAY == 0:
        return "-P%iD" % (s / MINUTES_PER_DAY)
    elif s % MINUTES_PER_HOUR == 0:
        return "-PT%iH" % (s / MINUTES_PER_HOUR)
    else:
        return "-PT%iM" % s
Ejemplo n.º 16
0
def AllDayEventToAirsync(ctx):

    parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
    src_node = transform_ctx.current()
    datetext = xml2util.GetNodeValue(
        xml2util.FindChildNode(src_node, "Content"))
    if datetext.find("T") >= 0:
        return "0"
    else:
        return "1"
Ejemplo n.º 17
0
def all_description_from_airsync(ctx):
    parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
    src_node = transform_ctx.current()
    s=xml2util.GetNodeValue(transform_ctx.current())
    asnote = ""
    if len(s) > 0:
        dc = base64.b64decode(s)
        try:
            asnote = pyrtfcomp.RTFConvertToUTF8(dc,1)
        except pyrtfcomp.RTFException, ConvErr:
            pass
Ejemplo n.º 18
0
def ClassFromAirsync(ctx):
	
	parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
	s = xml2util.GetNodeValue(transform_ctx.current())
    
	if s == "2":
		return "PRIVATE"
	elif s == "3":
		return "CONFIDENTIAL"
	else:
		return "PUBLIC" # 'PUBLIC' is our default value
Ejemplo n.º 19
0
def all_description_to_airsync(ctx):
    parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
    src_node = transform_ctx.current()
    content_node = xml2util.FindChildNode(src_node, "Content")
    s = xml2util.GetNodeValue(content_node)
    ec = ""
    if len(s) > 0:
        try:
            asnote = pyrtfcomp.RTFConvertFromUTF8(s,RTFHDR,1)
            ec=base64.b64encode(asnote)
        except pyrtfcomp.RTFException, ConvErr:
            pass
Ejemplo n.º 20
0
def StatusFromAirsync(ctx):

    parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
    src_node = transform_ctx.current()
    s = xml2util.GetNodeValue(transform_ctx.current())
    if s == "1":
        base_node = transform_ctx.insertNode()
        stat_node = base_node.newChild(None, "Status", None)
        stat_node.newChild(None, "Content", "COMPLETED")
        pcnt_node = base_node.newChild(None, "PercentComplete", None)
        pcnt_node.newChild(None, "Content", "100")
    return ""
Ejemplo n.º 21
0
def AlarmFromAirsync(ctx):
	
	parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
	src_node = transform_ctx.current()
	
	s = int(xml2util.GetNodeValue(src_node))
	
	if s % MINUTES_PER_DAY == 0:
		return "-P%iD" % (s / MINUTES_PER_DAY)
	elif s % MINUTES_PER_HOUR == 0:
		return "-PT%iH" % (s / MINUTES_PER_HOUR)
	else:
		return "-PT%iM" % s
Ejemplo n.º 22
0
def PriorityFromAirsync(ctx):

    parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
    s = xml2util.GetNodeValue(transform_ctx.current())

    if s == "0":
        return "7"
    elif s == "1":
        return "5"
    elif s == "2":
        return "3"
    else:
        return "0"  # We can use the unspecced one here if we get such an one from Airsync
Ejemplo n.º 23
0
def OSTextToAirsyncRTF(ctx):
	
	parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
	src_node = transform_ctx.current()

	s=xml2util.GetNodeValue(transform_ctx.current())
	
	ec = ""
	if len(s) > 0:
		try:
			asnote = pyrtfcomp.RTFConvertFromUTF8(s,RTFHDR,1)
			ec=base64.b64encode(asnote)
		except pyrtfcomp.RTFException, ConvErr:
			pass
Ejemplo n.º 24
0
def event_starttime_from_airsync(ctx):
    parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
    src_node = transform_ctx.current()
    dst_node = transform_ctx.insertNode()

    allday_node = xml2util.FindChildNode(src_node.parent, "AllDayEvent")
    asdate = tzutils.TextToDate(xml2util.GetNodeValue(transform_ctx.current()))

    if tzconv.curtz() != None:

        # if we have a tz, we must insert the ID and convert to it

        dst_node.newChild(None,"TimezoneID",tzconv.curtz().name)
        asdate = tzconv.ConvertToLocal(asdate,tzconv.curtz())
        result = asdate.strftime(DATE_FORMAT_EVLOCAL)
    else:
        result = asdate.strftime(DATE_FORMAT_EVENT)

    if allday_node != None and xml2util.GetNodeValue(allday_node) != "0":
        result=result[0:8]

    dst_node.newChild(None,"Content",result)
    return ""
Ejemplo n.º 25
0
def DateFromAirsync(ctx):

    parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
    src_node = transform_ctx.current()

    asdate = dateutil.TaskTextToDate(xml2util.GetNodeValue(src_node))
    datetext = asdate.strftime(DATE_FORMAT_VCALTASK)

    dst_node = transform_ctx.insertNode()

    curtz = tzdatabase.tzdb.GetCurrentTimezone()
    if curtz != None:

        # if we have a tz, we must insert the ID and convert to it

        dst_node.setProp("TimezoneID", curtz.name)

    else:
        # if not, does the source have a UtcStartDate or UtcDueDate element?

        if src_node.name == "StartDate":
            utcdatenode = xml2util.FindChildNode(src_node.parent,
                                                 "UtcStartDate")
        elif src_node.name == "DueDate":
            utcdatenode = xml2util.FindChildNode(src_node.parent, "UtcDueDate")
        else:
            utcdatenode = None

        if utcdatenode != None:

            asdate = dateutil.TaskTextToDate(
                xml2util.GetNodeValue(utcdatenode))
            datetext = asdate.strftime(DATE_FORMAT_VCALTASK) + "Z"

    dst_node = transform_ctx.insertNode()
    dst_node.newChild(None, "Content", datetext)
    return ""
Ejemplo n.º 26
0
def PriorityToAirsync(ctx):

    parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)

    # Here. let us not destroy the 'unspecified' priority when going
    # _to_ airsync. We can't really help reassigning this as 'low'
    # in the other direction.

    d = "0"
    s = xml2util.GetNodeValue(transform_ctx.current())
    if s > "0":
        if s == "7":
            d = "0"
        elif s == "5":
            d = "1"
        elif s == "3":
            d = "2"
        else:
            d = "0"
    return d
Ejemplo n.º 27
0
def OSDateFromAirsync(ctx):

	parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
	tznode = transform_ctx.current()
		
	date = dateutil.TextToDate(xml2util.GetNodeValue(transform_ctx.current()))
	date.replace(tzinfo=tzutils.tzone.utc)
	
	# We are going from AirSync, so 'date' will be in UTC
	# If we have a current timezone, set it.
	
	dst_node = transform_ctx.insertNode()
	
	dst_node.setProp("Value","DATE-TIME")
	curtz = tzdatabase.tzdb.GetCurrentTimezone()
	if curtz!=None:
		date = date.astimezone(curtz)
		dst_node.setProp("TimezoneID",curtz.name)
		
	tznode=dst_node.newChild(None,"Content",date.strftime(DATE_FORMAT_EVENT))
	
	return ""
Ejemplo n.º 28
0
def AlarmToAirsync(ctx):

	parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
	src_node = transform_ctx.current()
    
	desc_node = xml2util.FindChildNode(src_node,"Description")
	content_node = xml2util.FindChildNode(src_node, "AlarmTrigger")

	# value attribute is mandatory. Convert to blank string if not 
	# present. We only handle 'DURATION' values here, DATE-TIME is a 
	# TODO (we can convert this into a duration for AS)
    
	value_node = xml2util.GetNodeAttr(src_node,"Value")
	
	if value_node == None:
        	return ""

	if value_node.lower() != "duration":
		return ""
	
	s = xml2util.GetNodeValue(content_node).lstrip("-PT").upper()

	days=0
	hours=0
	minutes=0
	
	if s.rfind("D") != -1:
		days = int(s[:s.rfind("D")])
		s = s[s.rfind("D")+1:]
	if s.rfind("H") != -1:
		hours = int(s[:s.rfind("H")])
		s = s[s.rfind("H")+1:]
	if s.rfind("M") != -1:
		minutes = int(s[:s.rfind("M")])
		s = s[s.rfind("M")+1:]

	return str(days * MINUTES_PER_DAY + hours * MINUTES_PER_HOUR + minutes)
Ejemplo n.º 29
0
def event_recurrence_from_airsync(ctx):
    parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
    src_node = transform_ctx.current()
    dst_node = transform_ctx.insertNode()

    interval_node    = xml2util.FindChildNode(src_node, "Interval")
    until_node       = xml2util.FindChildNode(src_node, "Until")
    occurrences_node = xml2util.FindChildNode(src_node, "Occurrences")
    type_node        = xml2util.FindChildNode(src_node, "Type")
    dayofweek_node   = xml2util.FindChildNode(src_node, "DayOfWeek")
    dayofmonth_node  = xml2util.FindChildNode(src_node, "DayOfMonth")
    weekofmonth_node = xml2util.FindChildNode(src_node, "WeekOfMonth")
    monthofyear_node = xml2util.FindChildNode(src_node, "MonthOfYear")

    # Add the common nodes that don't really require conversion
    if interval_node != None:
        dst_node.newChild(None, "Rule", "INTERVAL=%s" % xml2util.GetNodeValue(interval_node))
    if until_node != None:
        dst_node.newChild(None, "Rule", "UNTIL=%s" % xml2util.GetNodeValue(until_node))
    if occurrences_node != None:
        dst_node.newChild(None, "Rule", "COUNT=%s" % xml2util.GetNodeValue(occurrences_node))

    if type_node != None:
        type = int(xml2util.GetNodeValue(type_node))

        # Special case: we can treat this as simple weekly event
        if type == 0 and dayofweek_node != None:
            type = 1

        if type == 0:
            dst_node.newChild(None, "Rule", "FREQ=DAILY")
        elif type == 1:
            dst_node.newChild(None, "Rule", "FREQ=WEEKLY")
            dst_node.newChild(None, "Rule", "BYDAY=%s" % airsync_days_to_vcal_days(xml2util.GetNodeValue(dayofweek_node)))
        elif type == 2:
            dst_node.newChild(None, "Rule", "FREQ=MONTHLY")
            dst_node.newChild(None, "Rule", "BYMONTHDAY=%s" % xml2util.GetNodeValue(dayofmonth_node))
        elif type == 3:
            dst_node.newChild(None, "Rule", "FREQ=MONTHLY")
            dst_node.newChild(None, "Rule", "BYDAY=%s" % generate_vcal_byday(xml2util.GetNodeValue(weekofmonth_node), xml2util.GetNodeValue(dayofweek_node)))
        elif type == 5:
            dst_node.newChild(None, "Rule", "FREQ=YEARLY")
            dst_node.newChild(None, "Rule", "BYMONTH=%s" % xml2util.GetNodeValue(monthofyear_node))
            dst_node.newChild(None, "Rule", "BYMONTHDAY=%s" % xml2util.GetNodeValue(dayofmonth_node))
        elif type == 6:
            dst_node.newChild(None, "Rule", "FREQ=YEARLY")
            dst_node.newChild(None, "Rule", "BYMONTH=%s" % xml2util.GetNodeValue(monthofyear_node))
            dst_node.newChild(None, "Rule", "BYDAY=%s" % generate_vcal_byday(xml2util.GetNodeValue(weekofmonth_node), xml2util.GetNodeValue(dayofweek_node)))
        else:
            # Unsupported type
            raise ValueError("Unknown recurrence type %d from Airsync" % type)

    else:
        # If we don't know what type of recurrence it is, we
        # can't construct its vcal rules
        raise ValueError("No recurrence type specified from Airsync")
    return ""
Ejemplo n.º 30
0
def event_recurrence_to_airsync(ctx):

    parser_ctx, transform_ctx = xml2util.ExtractContexts(ctx)
    src_node = transform_ctx.current()
    dst_node = transform_ctx.insertNode()

    # Extract the rules

    src_rules = {}
    child = src_node.children

    while child != None:
        if child.name == "Rule":
            rrule_val = xml2util.GetNodeValue(child)
            sep = rrule_val.index("=")
            key = rrule_val[:sep]
            val = rrule_val[sep+1:]
            src_rules[key.lower()] = val.lower()
        child = child.next


    # Interval, Count, and Until rules have straightforward conversions

    if src_rules.has_key("interval"):
        dst_node.newChild(None, "Interval", src_rules["interval"])
    if src_rules.has_key("until"):
        dst_node.newChild(None, "Until", tzutils.TextToDate(src_rules["until"]).strftime(DATE_FORMAT_EVENT))
    if src_rules.has_key("count"):
        dst_node.newChild(None, "Occurrences", src_rules["count"])

    # Handle different types of recurrences on a case-by-case basis

    if src_rules["freq"].lower() == "daily":

        # There really isn't much to convert in this case..
        dst_node.newChild(None, "Type", "0")

    elif src_rules["freq"].lower() == "weekly":
        dst_node.newChild(None, "Type", "1")
        dst_node.newChild(None, "DayOfWeek", str(vcal_days_to_airsync_days(src_rules["byday"])))

    elif src_rules["freq"].lower() == "monthly":
        if src_rules.has_key("bymonthday"):
            dst_node.newChild(None, "Type", "2")
            dst_node.newChild(None, "DayOfMonth", src_rules["bymonthday"])

        elif src_rules.has_key("byday"):

            week, day = vcal_split_byday(src_rules["byday"])
            dst_node.newChild(None, "Type", "3")
            dst_node.newChild(None, "DayOfWeek", str(vcal_days_to_airsync_days(day)))
            if week >= 0:
                dst_node.newChild(None, "WeekOfMonth", week)
            elif week == -1:
                # Airsync deals with this as a special case
                dst_node.newChild(None, "WeekOfMonth", "5")
            else:
                # Not supported (as far as I can tell...)
                # Airsync cannot count backwards from the end of the
                # month in general
                raise ValueError("Airsync does not support counting from end of month")
        else:
            # It seems like this should be against the rules, and filling in
            # a default might not make sense because either of the above interpretations
            # is equally valid.
            raise ValueError("Monthly events must either specify BYMONTHDAY or BYDAY rules")

    elif src_rules["freq"].lower() == "yearly":

        if src_rules.has_key("bymonth"):

            if src_rules.has_key("bymonthday"):

                dst_node.newChild(None, "Type", "5")
                dst_node.newChild(None, "MonthOfYear", src_rules["bymonth"])
                dst_node.newChild(None, "DayOfMonth", src_rules["bymonthday"])

            elif src_rules.has_key("byday"):

                week, day = vcal_split_byday(src_rules["byday"])
                dst_node.newChild(None, "Type", "6")
                dst_node.newChild(None, "MonthOfYear", src_rules["bymonth"])
                dst_node.newChild(None, "DayOfWeek", str(vcal_days_to_airsync_days(day)))

                if week >= 0:
                    dst_node.newChild(None, "WeekOfMonth", week)
                elif week == -1:
                    # Airsync deals with this as a special case
                    dst_node.newChild(None, "WeekOfMonth", "5")
                else:
                    # Not supported (as far as I can tell...)
                    # Airsync cannot count backwards from the end of the
                    # month in general
                    raise ValueError("Airsync does not support counting from end of month")
            else:

                # It seems like this should be against the rules, and filling in
                # a default might not make sense because either of the above interpretations
                # is equally valid.

                raise ValueError("Yearly events which are by month must either specify BYMONTHDAY or BYDAY rules")

        elif src_rules.has_key("byyearday"):

            # Not supported (as far as I can tell...)
            # Airsync does not have a 'DayOfYear' field
            raise ValueError("Airsync does not support day-of-year yearly events")

        else:
            # we need to get the start/recurrence date from the start date
            # Get the start date - we need this to expand yearly rules
            # We should always have a start date in a legal event!

            start = xml2util.FindChildNode(src_node.parent,"DateStarted")
            utcdate=tzconv.ConvertDateNodeToUTC(start)[1]

            # We need the month and the day

            dst_node.newChild(None,"Type", "5")
            dst_node.newChild(None,"MonthOfYear",str(utcdate.month))
            dst_node.newChild(None,"DayOfMonth",str(utcdate.day))


    return ""