File: vcalendar_r.c

package info (click to toggle)
plan 1.10.1-2
  • links: PTS
  • area: main
  • in suites: squeeze, wheezy
  • size: 2,228 kB
  • ctags: 1,995
  • sloc: ansic: 25,050; perl: 1,361; sh: 1,003; makefile: 528; yacc: 121; sed: 17
file content (227 lines) | stat: -rw-r--r-- 6,194 bytes parent folder | download | duplicates (3)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
/*
 * read a calendar file in vCalendar format. vCalendar files are used by many
 * calendar programs such as Apple iCal, Zimbra, Google Calendar, Lotus, and
 * many others. The format is defined in RFC 2445, but only a small subset of
 * that monster of a standard is implemented here.
 *
 *	read_vcal_file()	read an vCalendar files into an entry list.
 */

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <assert.h>
#include <errno.h>
#include <time.h>
#include <Xm/Xm.h>
#include "cal.h"
#include "config.h"
#include "proto.h"

static void   parse_vcal_line(struct plist **, char *, char *);
static char  *parse_vcal_command_arg(char *);
static time_t parse_vcal_datetime(char *, BOOL *);
static time_t parse_vcal_duration(char *);

static BOOL in_entry;			/* between begin..end entry */
extern struct user *user;		/* user list (from file_r.c) */
extern struct config config;		/* global configuration data */


/*
 * read a list from an vCalendar file. If <list> points to a non-null pointer,
 * the list in the file is merged into the list. As with add_entry(), <list>
 * is a pointer to a pointer to the list because the list might grow and
 * must be re-allocated. If the file is empty, no new list is allocated,
 * and <list> continues to point to a null pointer.
 */

void read_vcal_file(
	struct plist	**list,		/* list to add to */
	FILE		*fp,		/* file to read list from */
	int		u)		/* user number */
{
	char		line[1024], *p;	/* line buffer */

	in_entry = FALSE;
	rewind(fp);
	while (fgets(line, 1024, fp)) {
		if ((p = strchr(line, '\r'))) *p = 0;
		if ((p = strchr(line, '\n'))) *p = 0;
		parse_vcal_line(list, user ? user[u].name : 0, line);
	}
}


/*
 * parse a line read from the vCalendar file. Parsing is rather simple: just
 * waand ignore everything not known.
 */

static void parse_vcal_line(
	struct plist		**list,		/* list to add to */
	char			*name,		/* user name, 0=not yet known*/
	char			*line)		/* line to be parsed */
{
	static struct entry	entry;		/* temp entry before adding */

	char *arg = parse_vcal_command_arg(line);
	if (!arg)
		return;

	if (!strcasecmp(line, "begin") && !strcasecmp(arg, "vevent")) {
		clone_entry(&entry, (struct entry *)0);
		entry.user = name ? mystrdup(name) : 0;
		in_entry = TRUE;

	} else if (!strcasecmp(line, "begin") && !strcasecmp(arg, "vtodo")) {
		clone_entry(&entry, (struct entry *)0);
		entry.user = name ? mystrdup(name) : 0;
		in_entry = TRUE;
	}
	if (!in_entry)		/* ignore any entries other than the above */
		return;

	if (!strcasecmp(line, "dtstart")) {
		entry.time = parse_vcal_datetime(arg, &entry.notime);
		entry.noalarm = TRUE;

	} else if (!strcasecmp(line, "dtend")) {
		time_t end = parse_vcal_datetime(arg, 0);
		if (end - entry.time < 86400)
			entry.length = end - entry.time;
		else {
			entry.rep_every = 86400;
			entry.rep_last  = end;
		}

	} else if (!strcasecmp(line, "duration")) {
		time_t dur = parse_vcal_duration(arg);
		if (dur < 86400)
			entry.length = dur;
		else {
			entry.rep_every = 86400;
			entry.rep_last  = entry.time + dur;
		}

	} else if (!strcasecmp(line, "summary")) {
		entry.note = mystrdup(arg);

	} else if (!strcasecmp(line, "trigger")) {
		entry.time = parse_vcal_datetime(arg, &entry.notime);

	} else if (!strcasecmp(line, "organizer")	||
		   !strcasecmp(line, "attendee")	||
		   !strcasecmp(line, "due")		||
		   !strcasecmp(line, "status")		||
		   !strcasecmp(line, "class")		||
		   !strcasecmp(line, "category")	||
		   !strcasecmp(line, "description")) {

		char *msg, *p;
		for (p=line+1; *p; p++)
			*p = tolower(*p);
		if (entry.message) {
			msg = malloc(strlen(entry.message) + strlen(line)+2 +
					strlen(arg) + 1);
			sprintf(msg, "%s%s: %s\n", entry.message, line, arg);
			free(entry.message);
		} else {
			msg = malloc(strlen(line)+2 + strlen(arg) + 1);
			sprintf(msg, "%s: %s\n", line, arg);
		}
		entry.message = msg;

	} else if (!strcasecmp(line, "end") && (!strcasecmp(arg, "vevent") ||
						!strcasecmp(arg, "vtodo"))) {
		if (!entry.note)
			entry.note = mystrdup("(none)");
		add_entry(list, &entry);
		in_entry = FALSE;
	}
}


/*
 * put a \0 at the end of the command, and return a pointer to the argument.
 * Lines have the form "command[;junk]*:arg". Return 0 for lines that don't
 * have this form; they will be ignored.
 */

static char *parse_vcal_command_arg(
	char		*line)		/* input line to break up */
{
	char *p;
	for (p=line; *p; p++)
		if (*p == ';')
			*p++ = 0;
		else if (*p == ':') {
			*p = 0;
			return p+1;
		}
	return 0;
}


/*
 * parse an ICS date/time string of the form dateTtime, with date=YYYYMMDD and
 * time=HHMMSS. If a 'Z' follows, the date is UTC and must be converted to
 * local time.
 */

static time_t parse_vcal_datetime(
	char		*str,		/* parse this ICS date/time string */
	BOOL		*notime)	/* set to true if Ttime is missing */
{
	struct tm tm;
	memset(&tm, 0, sizeof(tm));
	sscanf(str, "%04d%02d%02d", &tm.tm_year, &tm.tm_mon, &tm.tm_mday);
	tm.tm_year -= 1900;
	tm.tm_mon--;
	if (notime)
		*notime = str[8] != 'T';
	if (str[8] == 'T')
		sscanf(str+9, "%02d%02d%02d",
					&tm.tm_hour, &tm.tm_min, &tm.tm_sec);
	if (str[15] == 'Z')
		return(tm_to_time(&tm) + config.tzone);
	else
		return(tm_to_time(&tm));
}


/*
 * parse an ICS suration string of the form [+|-]PdatesTtimes, where dates is
 * a sequence of nW (weeks) or nD; and times is a sequence of nH, nM, or nS.
 * For example, P15DT5H30M0S means 15 days and 5:30:00.
 */

static time_t parse_vcal_duration(
	char		*str)		/* parse this ICS duration string */
{
	int dur = 0, n = 0, sign = 1;
	for (; *str; str++)
		switch (*str | 0x20) {
		  case '+': sign = 1;				break;
		  case '-': sign = -1;				break;
		  case 'p':					break;
		  case 't':					break;
		  case 'w': dur += n * 7 * 86400; n = 0;	break;
		  case 'd': dur += n * 86400;	  n = 0;	break;
		  case 'h': dur += n * 3600;	  n = 0;	break;
		  case 'm': dur += n * 60;	  n = 0;	break;
		  case 's': dur += n;		  n = 0;	break;
		  case '0':
		  case '1':
		  case '2':
		  case '3':
		  case '4':
		  case '5':
		  case '6':
		  case '7':
		  case '9': n = n * 10 + *str - '0';		break;
		}
	return((time_t)(dur * sign));
}