#include <time.h>
#include <strings.h>
#include <multisync.h>
#include <csa/csa.h>
#include "csa_plugin.h"

#define MODULE "csa"
#define STATE "state"

extern gboolean multisync_debug;

/* Load our configuration from a module config file */
 
void csa_load_config(csa_connection *conn)
{
  char *filename;
  FILE *f;
  char line[256];
                                                                                
  filename = g_strdup_printf("%s/%s%s", sync_get_datapath(conn->sync_pair),
		     (conn->conntype==CONNECTION_TYPE_LOCAL?"local":"remote"),
			MODULE);
                                                                                
  if((f = fopen(filename, "r")))
    {
      while(fgets(line, 256, f))
	{
	  char prop[128], data[256];
                                                                                
	  if(sscanf(line, "%128s = %256[^\n]", prop, data) == 2)
	    {
	      if(!strcmp(prop, "csaserver"))
		{
		  conn->csaserver = g_strdup(data);
		}
	    }
	}

      fclose(f);
    }

  g_free(filename);
}

/* Save our configuration from a module config file */

void csa_save_config(csa_connection *conn)
{
  char *filename;
  FILE *f;
                                                                                
  filename = g_strdup_printf("%s/%s%s", sync_get_datapath(conn->sync_pair),
		     (conn->conntype==CONNECTION_TYPE_LOCAL?"local":"remote"),
			MODULE);
                                                                                
  if((f = fopen(filename, "w")))
    {
      if(conn->csaserver)
	fprintf(f, "csaserver = %s\n", conn->csaserver);

      fclose(f);
    }
                                                                                
  g_free(filename);
}

/* Load in state file into a linked list */

void csa_load_state(csa_connection *conn, nvlist_t **list)
{
  char *filename;
  FILE *f;
  char line[256];

  if(nvlist_alloc(list, NV_UNIQUE_NAME, 0) != 0) {
	perror("csa_load_state:nvlist_alloc");
	return;
  }
  filename = g_strdup_printf("%s/%s%s", sync_get_datapath(conn->sync_pair),
		     (conn->conntype==CONNECTION_TYPE_LOCAL?"local":"remote"),
			STATE);
                                                                                
  if((f = fopen(filename, "r")))
    {
      while(fgets(line, 256, f))
	{
	  char prop[128], data[256];
                                                                                
	  if(sscanf(line, "%128s = %256[^\n]", prop, data) == 2)
	    {
		nvlist_add_string(*list, prop, data);
	    }
	}

      fclose(f);
    }

  g_free(filename);
}

/* Save our state to a file */

void csa_save_state(csa_connection *conn)
{
  char *filename;
  FILE *f;
                                                                                
  filename = g_strdup_printf("%s/%s%s", sync_get_datapath(conn->sync_pair),
		     (conn->conntype==CONNECTION_TYPE_LOCAL?"local":"remote"),
			STATE);
                                                                                
  if((f = fopen(filename, "w")) && conn->csa_entries) {
	nvpair_t * pair = NULL;
	char * name, * value;
	while((pair = nvlist_next_nvpair(conn->csa_entries, pair)) != NULL) {
		name = nvpair_name(pair);
		nvpair_value_string(pair, &value);
	
      		if((name != NULL) && (value != NULL)) {
			fprintf(f, "%s = %s\n", name, value);
		}
	}

        fclose(f);
  }
                                                                                
  g_free(filename);
}

/* Get a hash of csa entries from a server */

void csa_get_entries(csa_connection *conn, GList **changes) {

	CSA_uint32 num_entries;
	CSA_entry_handle *entries;

  	if(nvlist_alloc(&conn->csa_entries, NV_UNIQUE_NAME, 0) != 0) {
		perror("csa_get_entries:nvlist_alloc");
		return;
  	}

	if(csa_list_entries(conn->session, 0, NULL, NULL,
		&num_entries, &entries, NULL) == CSA_SUCCESS) {

		int i;

		for(i=0;i<num_entries;i++) {
			csa_add_to_list(conn, entries[i]);
		}
	}

	if(entries) csa_free(entries);
}

int csa_entry_exists(nvlist_t * list, nvpair_t * pair) {
	char * uid, * timestamp1, * timestamp2;

	uid = nvpair_name(pair);
	nvpair_value_string(pair, &timestamp1);

	if(!list ||
		(nvlist_lookup_string(list, uid, &timestamp2) != 0)) {
		return 0;
	} else {
		return 1;
	}
}

int csa_entry_matches(nvlist_t * list, nvpair_t * pair) {
	char * uid, * timestamp1, * timestamp2;

	uid = nvpair_name(pair);
	nvpair_value_string(pair, &timestamp1);

	if(!list ||
		(nvlist_lookup_string(list, uid, &timestamp2) != 0) ||
		(strcmp(timestamp1, timestamp2) != 0)) {
		return 0;
	} else {
		return 1;
	}
}

/* Get a CSA entry handle */

int csa_get_entry_handle(csa_connection *conn, char * uid,
						CSA_entry_handle ** handle) {
	CSA_attribute attrs[1];
	CSA_attribute_value attr_val[1];
	CSA_opaque_data opaque;
	CSA_enum ops[1];
	CSA_uint32 num_entries;
	CSA_entry_handle *entries;

	/* Get the csa entry from the server based on the uid */
	
	attrs[0].name = CSA_ENTRY_ATTR_REFERENCE_IDENTIFIER;
	attrs[0].value = &attr_val[0];
	attrs[0].attribute_extensions = NULL;
	attr_val[0].type = CSA_VALUE_OPAQUE_DATA;
	attr_val[0].item.opaque_data_value = &opaque;
	opaque.data = (unsigned char *)uid;
	opaque.size = strlen(uid);
	ops[0] = CSA_MATCH_EQUAL_TO;

	if(csa_list_entries(conn->session, 1, attrs, ops,
		&num_entries, &entries, NULL) == CSA_SUCCESS) {
		*handle=entries;
		return 1;
	}
	return 0;
}

/* Get a vCAL from a CSA entry */

GString * csa_get_entry_vcal(csa_connection *conn, nvpair_t * entry) {
	GString * vcal;
	CSA_entry_handle *handles;
	char * uid;

	uid=nvpair_name(entry);

	vcal = g_string_new("");

	/* Get the csa entry from the server based on the uid */

	if(csa_get_entry_handle(conn, uid, &handles)) {

		/* Found the uid */

		CSA_uint32 num_attributes;
		CSA_attribute *attributes;

		g_string_append(vcal, "BEGIN:VCALENDAR\n");
		g_string_append(vcal, "VERSION:2.0\n");
		g_string_append(vcal, "BEGIN:VEVENT\n");

		if(csa_read_entry_attributes(conn->session, handles[0], 0,
			NULL, &num_attributes, &attributes, NULL) == CSA_SUCCESS) {
			int i, showtime=1;
			for(i=0;i<num_attributes;i++) {
				add_attribute_to_vcal(&vcal, attributes[i],
						&showtime);
			}
			if(!showtime) {
				csa_remove_end_from_vcal(vcal);
			}
		} else {
			dd(printf("Unable to locate handle for %s\n", uid));
		}

		g_string_append(vcal, "END:VEVENT\n");
		g_string_append(vcal, "END:VCALENDAR\n");

		csa_free(handles);
	}
	
	/* Finish off the vCal entry */

	return vcal;
}

void add_attribute_to_vcal(GString **vcal, CSA_attribute attribute,
							 int *showtime) {
	GString * rrule;
	rrule = g_string_new("");

	if(strcmp(attribute.name, CSA_ENTRY_ATTR_DATE_CREATED) == 0) {
		g_string_append(*vcal,"CREATED:");
		g_string_append(*vcal,attribute.value->item.date_time_value);
		g_string_append(*vcal,"\n");
	}
	if(strcmp(attribute.name, CSA_ENTRY_ATTR_END_DATE) == 0) {
		g_string_append(*vcal,"DTEND:");
		g_string_append(*vcal,attribute.value->item.date_time_value);
		g_string_append(*vcal,"\n");
	}
	if(strcmp(attribute.name, CSA_ENTRY_ATTR_LAST_UPDATE) == 0) {
		g_string_append(*vcal,"DTSTAMP:");
		g_string_append(*vcal,attribute.value->item.date_time_value);
		g_string_append(*vcal,"\n");
	}
	if(strcmp(attribute.name, CSA_ENTRY_ATTR_ORGANIZER) == 0) {
		g_string_append(*vcal,"ORGANIZER:");
		g_string_append(*vcal,
			attribute.value->item.calendar_user_value->user_name);
		g_string_append(*vcal,"\n");
	}
	if(strcmp(attribute.name, CSA_ENTRY_ATTR_REFERENCE_IDENTIFIER) == 0) {
		g_string_append(*vcal,"UID:");
		g_string_append_len(*vcal,
		  (const char *)attribute.value->item.opaque_data_value->data,
		  attribute.value->item.opaque_data_value->size);
		g_string_append(*vcal,"\n");
	}
	if(strcmp(attribute.name, CSA_ENTRY_ATTR_START_DATE) == 0) {
		g_string_append(*vcal,"DTSTART:");
		g_string_append(*vcal,attribute.value->item.date_time_value);
		g_string_append(*vcal,"\n");
	}
	if(strcmp(attribute.name, CSA_ENTRY_ATTR_SUMMARY) == 0) {
		g_string_append(*vcal,"DESC:");
		g_string_append(*vcal,attribute.value->item.string_value);
		g_string_append(*vcal,"\n");
	}
	if(strcmp(attribute.name, CSA_X_DT_ENTRY_ATTR_SHOWTIME)==0) {
		*showtime=attribute.value->item.uint32_value;
	}

	if(strcmp(attribute.name, CSA_ENTRY_ATTR_CLASSIFICATION) == 0) {
		switch(attribute.value->item.uint32_value) {
			case CSA_CLASS_PUBLIC:
				g_string_append(*vcal,"CLASS:PUBLIC\n");
				break;
			case CSA_CLASS_PRIVATE:
				g_string_append(*vcal,"CLASS:PRIVATE\n");
				break;
			case CSA_CLASS_CONFIDENTIAL:
				g_string_append(*vcal,"CLASS:CONFIDENTIAL\n");
				break;
		}
	}

	if(strcmp(attribute.name, CSA_ENTRY_ATTR_EXCEPTION_DATES) == 0) {
		CSA_date_time_entry *entry;

		entry=attribute.value->item.date_time_list_value;

		g_string_append(*vcal, "EXDATE:");

		while(entry) {
			g_string_append(*vcal, entry->date_time);
			entry=entry->next;
			if(entry) {
				g_string_append(*vcal, ",");
			}
		}

		g_string_append(*vcal, "\n");
	}

	if(strcmp(attribute.name, CSA_ENTRY_ATTR_RECURRENCE_RULE) == 0) {
		csa_read_rr_entry_to_vcal(*vcal, 
			attribute.value->item.string_value);
	}
}

void csa_add_vcal_entry(csa_connection *conn, char * vcal, char * uid) {
	csa_attribute_bundle bundle;
	CSA_entry_handle new_handle;
	CSA_return_code stat;

	csa_vcal_to_attributes(vcal, &bundle, TRUE);

	if(bundle.count > 0) {
		if((stat = csa_add_entry(conn->session,
			bundle.count, bundle.attrs, &new_handle,
			NULL)) == CSA_SUCCESS) {

			/* We need to get the new uid to return */

			csa_get_uid_from_handle(conn, new_handle, uid);
			csa_add_to_list(conn, new_handle);

			dd(printf("Got new uid - %s\n", uid));
			csa_free(&new_handle);
		} else {
			switch(stat) {
				case CSA_E_READONLY:
					dd(printf("CSA: server %s is readonly; unable to add new entry\n", 
						conn->csaserver));
					break;
				default:
					dd(printf("CSA: unable to add new entry - %d\n", stat));
					break;
			}
			g_assert(0);
		}
		free(bundle.attrs);
		free(bundle.values);
	} else {
		dd(printf("CSA: unable to locate uid\n"));
	}
}

void csa_update_vcal_entry(csa_connection *conn, char * vcal, char * uid) {
	csa_attribute_bundle bundle;
	CSA_entry_handle *handles, new_handle;

	if(csa_get_entry_handle(conn, uid, &handles)) {

		csa_vcal_to_attributes(vcal, &bundle, FALSE);

		if(bundle.count > 0) {
			CSA_return_code stat;
			if((stat = csa_update_entry_attributes(conn->session,
				handles[0], CSA_SCOPE_ALL, FALSE,
				bundle.count, bundle.attrs, &new_handle,
				NULL)) == CSA_SUCCESS) {

				csa_remove_from_list(conn, uid);
				csa_add_to_list(conn,new_handle);

				csa_free(&new_handle);
			} else {
				int i;
				dd(printf("CSA: update entry attributes failed - %d\n", stat));
				for(i=0;i<bundle.count;i++) {
					dd(printf("CSA Attr: %s\n",
							bundle.attrs[i].name));
				}
				g_assert(0);
			}
			free(bundle.attrs);
			free(bundle.values);
		}
		csa_free(handles);
	}
}

void csa_vcal_to_attributes(char * vcal, csa_attribute_bundle * bundle, int newentry) {
#define	MAX_ATTRS 9

	char * data;

	bundle->count=0;
	bundle->attrs=(CSA_attribute *)calloc(MAX_ATTRS, sizeof(CSA_attribute));
	bundle->values=(CSA_attribute_value *)calloc(MAX_ATTRS, sizeof(CSA_attribute_value));

	/* 1: CSA_ENTRY_ATTR_TYPE isn't optional for an add... */

	if(newentry) {
		bundle->attrs[bundle->count].name=CSA_ENTRY_ATTR_TYPE;
		bundle->attrs[bundle->count].value=&(bundle->values[bundle->count]);
		bundle->attrs[bundle->count].attribute_extensions = NULL;
		bundle->values[bundle->count].type=CSA_VALUE_UINT32;
		bundle->values[bundle->count].item.sint32_value=CSA_TYPE_EVENT;
		bundle->count++;
	}

	/* 2: CSA_ENTRY_ATTR_END_DATE */

	data=sync_get_key_data(vcal, "DTEND");
	if(data) {
		bundle->attrs[bundle->count].name=CSA_ENTRY_ATTR_END_DATE;
		bundle->attrs[bundle->count].value=&(bundle->values[bundle->count]);
		bundle->attrs[bundle->count].attribute_extensions = NULL;
		bundle->values[bundle->count].type=CSA_VALUE_DATE_TIME;
		bundle->values[bundle->count].item.date_time_value=g_strdup(data);
		bundle->count++;
		g_free(data);
	}

	/* 3: CSA_ENTRY_ATTR_CLASSIFICATION */

	data=sync_get_key_data(vcal, "CLASS");
	if(data) {
		bundle->attrs[bundle->count].name=CSA_ENTRY_ATTR_CLASSIFICATION;
		bundle->attrs[bundle->count].value=&(bundle->values[bundle->count]);
		bundle->attrs[bundle->count].attribute_extensions = NULL;
		bundle->values[bundle->count].type=CSA_VALUE_UINT32;
		if(strcasecmp(data,"public")==0) {
			bundle->values[bundle->count].item.uint32_value=
				CSA_CLASS_PUBLIC;
		}
		if(strcasecmp(data,"private")==0) {
			bundle->values[bundle->count].item.uint32_value=
				CSA_CLASS_PRIVATE;
		}
		if(strcasecmp(data,"confidential")==0) {
			bundle->values[bundle->count].item.uint32_value=
				CSA_CLASS_CONFIDENTIAL;
		}
		bundle->count++;
		g_free(data);
	}

	/* 4: CSA_ENTRY_ATTR_START_DATE */

	data=sync_get_key_data(vcal, "DTSTART");
	if(data) {
		bundle->attrs[bundle->count].name=CSA_ENTRY_ATTR_START_DATE;
		bundle->attrs[bundle->count].value=&(bundle->values[bundle->count]);
		bundle->attrs[bundle->count].attribute_extensions = NULL;
		bundle->values[bundle->count].type=CSA_VALUE_DATE_TIME;
		bundle->values[bundle->count].item.date_time_value=g_strdup(data);
		bundle->count++;
		g_free(data);
	}

	/* 5: CSA_ENTRY_ATTR_SUMMARY */

	data=sync_get_key_data(vcal, "DESC");
	if(data) {
		bundle->attrs[bundle->count].name=CSA_ENTRY_ATTR_SUMMARY;
		bundle->attrs[bundle->count].value=&(bundle->values[bundle->count]);
		bundle->attrs[bundle->count].attribute_extensions = NULL;
		bundle->values[bundle->count].type=CSA_VALUE_STRING;
		bundle->values[bundle->count].item.string_value=g_strdup(data);
		bundle->count++;
		g_free(data);
	}

	/* 6: CSA_ENTRY_ATTR_EXCEPTION_DATES */

	data=sync_get_key_data(vcal, "EXDATE");
	if(data) {
		char *token, * value, *toknext;
		CSA_date_time_entry **entry, *entry2;

		value=strdup(data);
		bundle->attrs[bundle->count].name=CSA_ENTRY_ATTR_EXCEPTION_DATES;
		bundle->attrs[bundle->count].value=&(bundle->values[bundle->count]);
		bundle->attrs[bundle->count].attribute_extensions = NULL;
		bundle->values[bundle->count].type=CSA_VALUE_DATE_TIME_LIST;

		/* We need to loop around the dates and add them to the list */

		token=strtok_r(value, ",", &toknext);
		entry=&bundle->values[bundle->count].item.date_time_list_value;
		while(token) {
			*entry=(CSA_date_time_entry*)malloc(sizeof(CSA_date_time_entry));
			(*entry)->date_time=strdup(token);
			(*entry)->next=NULL;
			token=strtok_r(NULL, ",", &toknext);
			entry=&((*entry)->next);
		}
			
		bundle->count++;
		free(value);
		g_free(data);

	}

	/* 7: CSA_ENTRY_ATTR_RECURRENCE_RULE */

	data=sync_get_key_data(vcal, "RRULE");
	if(data) {
		GString *rrule = g_string_new("");

		csa_read_rr_entry_from_vcal(data, rrule);

		bundle->attrs[bundle->count].name=CSA_ENTRY_ATTR_RECURRENCE_RULE;
		bundle->attrs[bundle->count].value=&(bundle->values[bundle->count]);
		bundle->attrs[bundle->count].attribute_extensions = NULL;
		bundle->values[bundle->count].type=CSA_VALUE_STRING;
		bundle->values[bundle->count].item.string_value=strdup(rrule->str);
		bundle->count++;
		g_free(data);

		g_string_free(rrule,TRUE);
	}

	/* 8: CSA_X_DT_ENTRY_ATTR_SHOWTIME */

	data=sync_get_key_data(vcal, "DTEND");
	bundle->attrs[bundle->count].name=CSA_X_DT_ENTRY_ATTR_SHOWTIME;
	bundle->attrs[bundle->count].value=&(bundle->values[bundle->count]);
	bundle->attrs[bundle->count].attribute_extensions = NULL;
	bundle->values[bundle->count].type=CSA_VALUE_SINT32;
	bundle->values[bundle->count].item.sint32_value=(data?1:0);
	bundle->count++;
	g_free(data);

	g_assert(bundle->count <= MAX_ATTRS);
}

void csa_add_to_list(csa_connection * conn, CSA_entry_handle handle) {

	CSA_uint32 num_attnames = 2;
	CSA_attribute_reference attnames[2];
	CSA_uint32 num_attributes;		
	CSA_attribute *attributes;

	attnames[0] = CSA_ENTRY_ATTR_REFERENCE_IDENTIFIER;
	attnames[1] = CSA_ENTRY_ATTR_LAST_UPDATE;

	if(csa_read_entry_attributes(conn->session,
		handle, num_attnames, attnames,
		&num_attributes, &attributes, NULL) == CSA_SUCCESS) {

		attributes[0].value->item.opaque_data_value->data[
			attributes[0].value->item.opaque_data_value->size]='\0';

		nvlist_add_string(conn->csa_entries,
		         (char *)attributes[0].value->item.opaque_data_value->data,
			 (char *)attributes[1].value->item.string_value);
		csa_free(attributes);
	}
}

void csa_get_uid_from_handle(csa_connection * conn, CSA_entry_handle handle, char *uid) {

	CSA_uint32 num_attnames = 1;
	CSA_attribute_reference attnames[1];
	CSA_uint32 num_attributes;		
	CSA_attribute *attributes;

	attnames[0] = CSA_ENTRY_ATTR_REFERENCE_IDENTIFIER;

	if(csa_read_entry_attributes(conn->session,
		handle, num_attnames, attnames,
		&num_attributes, &attributes, NULL) == CSA_SUCCESS) {

		strncpy(uid,
			(const char *)attributes[0].value->item.opaque_data_value->data,
			attributes[0].value->item.opaque_data_value->size);
		uid[attributes[0].value->item.opaque_data_value->size]='\0';

		csa_free(attributes);
	}
}

void csa_remove_from_list(csa_connection * conn, char * uid) {
	nvlist_remove(conn->csa_entries, uid, DATA_TYPE_STRING);
}

void csa_read_rr_entry_to_vcal(GString *vcal, char *csa_rrule) {

	/* Recurrance rules */

	GString * rrule;
	char * value=strdup(csa_rrule);
	char * toklast = NULL;
	char * token;
	char dayprefix='\0';

	int byday=0;
	int factor=0;

	rrule=g_string_new("");

	/* Tokenize the input */
	token=strtok_r(value, " ", &toklast);
		
	if(strncmp(token, "D", 1)==0) {
		/* Daily */
		g_string_append(rrule,"FREQ=DAILY");
		g_string_append(rrule,";INTERVAL=");
		g_string_append_len(rrule, &token[1], 1);
	}

	if(strncmp(token, "W", 1)==0) {
		/* Weekly */
		g_string_append(rrule,"FREQ=WEEKLY");
		g_string_append(rrule,";INTERVAL=");
		g_string_append_len(rrule, &token[1], 1);
	}

	if(strncmp(token, "M", 1)==0) {
		/* Monthly */
		g_string_append(rrule,"FREQ=MONTHLY");
		g_string_append(rrule,";INTERVAL=");
		g_string_append_len(rrule, &token[2], 1);
	}

	if(strncmp(token, "Y", 1)==0) {
		/* Yearly */
		g_string_append(rrule,"FREQ=YEARLY");
		g_string_append(rrule,";INTERVAL=");
		g_string_append_len(rrule, &token[2], 1);
	}

	while(((token=strtok_r(NULL," ",&toklast))!=NULL) &&
		(token[0]!='#')) {
		
		/* Process non # token */

		if(token[1] == '-') {
			dayprefix=token[0];
		}
			
		if((strcmp(token, "MO")==0) ||
		   (strcmp(token, "TU")==0) ||
		   (strcmp(token, "WE")==0) ||
		   (strcmp(token, "TH")==0) ||
		   (strcmp(token, "FR")==0) ||
		   (strcmp(token, "SA")==0) ||
		   (strcmp(token, "SU")==0)) {
			if(!byday) {
				g_string_append(rrule,";WKST=SU;BYDAY=");
				byday=1;
			} else {
				g_string_append(rrule,",");
			}
			if(dayprefix != '\0') {
				g_string_append(rrule,"-");
				g_string_append_c(rrule,dayprefix);
			}
			g_string_append(rrule,token);
			factor+=1;
		}
		
	}

	/* Get the real count from the # */
	if(token && (token[0]=='#')) {
		int num;
		g_string_append(rrule,";COUNT=");
		sscanf(&token[1], "%d", &num);
		if(factor>0) { num=num*factor; }
		g_string_append_printf(rrule, "%d", num);
	}

	if(rrule->len > 0) {
		g_string_append(vcal,"RRULE:");
		g_string_append(vcal,rrule->str);
		g_string_append(vcal,"\n");
	}

	free(value);
	
	dd(printf("CSA: (%s) -> (%s)\n", csa_rrule, rrule->str));

	g_string_free(rrule,TRUE);
}

void csa_read_rr_entry_from_vcal(char *data, GString *rrule) {

	int factor=0;
	char * value, *toknext, *token;
	GString *freq = g_string_new("");
	GString *interval = g_string_new("");
	GString *byday = g_string_new("");
	int count = 0;

	value=strdup(data);

	token=strtok_r(value, ";", &toknext);

	while(token) {
		if(strncmp(token, "COUNT=", 6)==0) {
			sscanf(&token[6], "%d", &count);
		}

		if(strncmp(token, "FREQ=", 5)==0) {
			g_string_append(freq, &token[5]);
		}

		if(strncmp(token, "INTERVAL=", 9)==0) {
			g_string_append(interval, &token[9]);
		}

		if(strncmp(token, "BYDAY=", 6)==0) {
			g_string_append(byday, &token[6]);
		}

		token=strtok_r(NULL, ";", &toknext);
	}

	/* Process the entries and create an rrule */

	if(strcmp(freq->str, "DAILY")==0) {
		g_string_append(rrule, "D");
	}

	if(strcmp(freq->str, "WEEKLY")==0) {
		g_string_append(rrule, "W");
	}

	if(strcmp(freq->str, "MONTHLY")==0) {
		if((byday->len > 0) && (byday->str[0]=='-')) {
			g_string_append(rrule, "MP");
		} else {
			g_string_append(rrule, "MD");
		}
	}

	if(strcmp(freq->str, "YEARLY")==0) {
		g_string_append(rrule, "YM");
	}
	g_string_append(rrule, interval->str);

	if(byday->len > 0) {
		char *token, *toknext;
		token=strtok_r(byday->str, ",", &toknext);
		while(token) {
			g_string_append(rrule, " ");
			if(token[0] == '-') {
				g_string_append_c(rrule, token[1]);
				g_string_append(rrule, "- ");
				g_string_append(rrule, &token[2]);
			} else {
				g_string_append(rrule, token);
			}
			token=strtok_r(NULL, ",", &toknext);
			factor++;
		}
		count=count/factor;
	}

	g_string_append(rrule, " #");
	g_string_append_printf(rrule, "%d", count);

	g_string_free(freq, TRUE);
	g_string_free(interval, TRUE);
	g_string_free(byday, TRUE);

	dd(printf("CSA: (%s) -> (%s)\n", data, rrule->str));

	free(value);
}

void csa_remove_end_from_vcal(GString *vcal) {
	/* Remove the DTEND: entry from a vcal */
	char *end, *nl;
	int pos, len;

	end = strstr(vcal->str, "\nDTEND:");
	if(end) {
		pos=end-vcal->str;
		nl=strchr(&end[1], '\n');
		if(nl) {
			len=nl-end;
			g_string_erase(vcal, pos, len);
		}
	}
}
