1
0
Fork 0
mirror of git://git.code.sf.net/p/cdesktopenv/code synced 2025-03-09 15:50:02 +00:00
cde/cde/programs/dtcm/libDtCmP/cm_tty.c
2018-07-04 23:23:32 +01:00

2782 lines
72 KiB
C

/*
* CDE - Common Desktop Environment
*
* Copyright (c) 1993-2012, The Open Group. All rights reserved.
*
* These libraries and programs are free software; you can
* redistribute them and/or modify them under the terms of the GNU
* Lesser General Public License as published by the Free Software
* Foundation; either version 2 of the License, or (at your option)
* any later version.
*
* These libraries and programs are distributed in the hope that
* they will be useful, but WITHOUT ANY WARRANTY; without even the
* implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
* PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with these libraries and programs; if not, write
* to the Free Software Foundation, Inc., 51 Franklin Street, Fifth
* Floor, Boston, MA 02110-1301 USA
*/
/*******************************************************************************
**
** cm_tty.c
**
** $TOG: cm_tty.c /main/9 1998/04/17 11:22:38 mgreess $
**
** RESTRICTED CONFIDENTIAL INFORMATION:
**
** The information in this document is subject to special
** restrictions in a confidential disclosure agreement between
** HP, IBM, Sun, USL, SCO and Univel. Do not distribute this
** document outside HP, IBM, Sun, USL, SCO, or Univel without
** Sun's specific written approval. This document and all copies
** and derivative works thereof must be returned or destroyed at
** Sun's request.
**
** Copyright 1993 Sun Microsystems, Inc. All rights reserved.
**
*******************************************************************************/
/* *
* (c) Copyright 1993, 1994 Hewlett-Packard Company *
* (c) Copyright 1993, 1994 International Business Machines Corp. *
* (c) Copyright 1993, 1994 Sun Microsystems, Inc. *
* (c) Copyright 1993, 1994 Novell, Inc. *
*/
#ifndef lint
static char sccsid[] = "@(#)cm_tty.c 1.91 95/07/27 Copyr 1993 Sun Microsystems, Inc.";
#endif
#include <EUSCompat.h>
#include <sys/types.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <nl_types.h>
#include <sys/param.h>
#include <sys/types.h>
#include "cm_tty.h"
#include "getdate.h"
#include "util.h"
/*******************************************************************************
**
** Globals
**
*******************************************************************************/
static char *separator_strs[] = {
" ",
"/",
".",
"-"
};
static char *repeat_strs[11];
static char *default_repeat_cnt_strs[] = {
"\0",
"365",
"52",
"26",
"12",
"12",
"2",
"52",
"52",
"52",
"5"
};
static char *default_repeat_scope_strs[11];
static char *for_strs[] = {
"2",
"3",
"4",
"5",
"6",
"7",
"8",
"9",
"10",
"11",
"12",
"13",
"14",
"forever"
};
static char *time_scope_strs_i18n[3];
static char *time_scope_strs[] = {
"Mins",
"Hrs",
"Days"
};
static char *repeat_scope_strs[3];
static char *day_strs[] = {
"Sunday",
"Monday",
"Tuesday",
"Wednesday",
"Thursday",
"Friday",
"Saturday",
"Sunday"
};
static char *month_strs[] = {
"January",
"February",
"March",
"April",
"May",
"June",
"July",
"August",
"September",
"October",
"November",
"December"
};
typedef enum {
TTY_Delete,
TTY_Insert,
TTY_Lookup
} Op_Type;
nl_catd catd_global;
static char *new_appt_begin_delimiter = NULL;
static char *new_appt_end_delimiter = NULL;
extern int _csa_iso8601_to_tick(char *, time_t*);
extern int _csa_tick_to_iso8601(time_t, char *);
extern int _csa_iso8601_to_duration(char *, int*);
extern int _csa_duration_to_iso8601(int, char *);
/*******************************************************************************
**
** Static functions
**
*******************************************************************************/
static void
copy_and_pad_newlines(char *dest, char *source) {
while (*source)
if ((*dest++ = *source++) == '\n')
*dest++ = '\t';
}
static int
count_newlines(char *string) {
int count = 0;
if (string == NULL)
return(0);
while (*string)
if (*string++ == '\n')
count++;
return count;
}
static void
mini_err_msg(
nl_catd catd,
char *appt_what,
Op_Type type) {
char *buf, *ptr;
if (type == TTY_Insert)
fprintf(stderr, "%s",
catgets(catd, 1, 1042, "Insert Access Denied: "));
else if (type == TTY_Delete)
fprintf(stderr, "%s",
catgets(catd, 1, 1043, "Delete Access Denied: "));
else
fprintf(stderr, "%s",
catgets(catd, 1, 1044, "Lookup Access Denied: "));
if (appt_what && appt_what[0] != '\0') {
buf = cm_strdup(appt_what);
if (ptr = strrchr(buf, '\n'))
*ptr = '\0';
fprintf(stderr, "%s '%s'\n",
catgets(catd, 1, 1045, "Cancelled for"),
buf);
free(buf);
} else
fprintf(stderr, "%s\n",
catgets(catd, 1, 1046,
"Appointment Cancelled\n"));
}
static boolean_t
query_user(void *client_data) {
char ans[MAXNAMELEN], *what_str = (char *)client_data;
/* NL_COMMENT
The following four messages (1047-1050) will be printed to stdout
and can have the following two forms:
"This appointment: '<appt text>' has an end
time earlier than its begin time. Do you
want to schedule it into the next day? [Y/N] "
or
"This appointment has an end
time earlier than its begin time. Do you
want to schedule it into the next day? [Y/N] "
The text <appt text> and [Y/N] should not be translated.
*/
if (what_str && what_str[0] != '\0')
fprintf(stdout, catgets(catd_global, 1, 1047,
"This appointment: '%s' has an end\n"), what_str);
else
fprintf(stdout, "%s", catgets(catd_global, 1, 1048,
"This appointment has an end\n"));
fprintf(stdout, "%s", catgets(catd_global, 1, 1049,
"time earlier than its begin time. Do you\n"));
fprintf(stdout, "%s", catgets(catd_global, 1, 1050,
"want to schedule it into the next day? [Y/N] "));
fgets(ans, sizeof(ans)-1, stdin);
fprintf(stdout, "\n");
if (*ans == 'y' || *ans == 'Y')
return B_TRUE;
return B_FALSE;
}
/*******************************************************************************
**
** External functions
**
*******************************************************************************/
extern char*
boolean_str(boolean_t val) {
return (val ? "True" : "False");
}
/*
* Delete an appointment. index is the nth appointment in the passed array.
*/
extern int
cm_tty_delete(
nl_catd catd,
CSA_session_handle session,
int version,
int index,
CSA_entry_handle *list) {
char ans[10];
Dtcm_appointment *appt;
if (index < 0 || !list[index])
return -1;
appt = allocate_appt_struct(appt_read,
version,
CSA_ENTRY_ATTR_SUMMARY_I,
CSA_X_DT_ENTRY_ATTR_REPEAT_TYPE_I,
NULL);
if (query_appt_struct(session, list[index], appt) != CSA_SUCCESS) {
mini_err_msg(catd, appt->what->value->item.string_value,
TTY_Delete);
return(0);
}
if (appt->repeat_type->value->item.sint32_value == CSA_X_DT_REPEAT_ONETIME)
*ans = '1';
else {
/* NL_COMMENT
Message numbers 1051-1057 are printed to stdout and
should appear like:
"The appointment '<appt text>' is part of a repeating series. Do you want to:
1. Delete all of them
2. Delete this on only
3. Delete forward
4. Cancel
Option [1-4]: "
*/
fprintf(stdout, catgets(catd, 1, 1051,
"The appointment '%s' is part of a repeating series. "),
appt->what->value->item.string_value);
fprintf(stdout, "%s", catgets(catd, 1, 1052, "Do you want to:"));
fprintf(stdout, "%s", catgets(catd, 1, 1053,
"\n\t1. Delete all of them"));
fprintf(stdout, "%s", catgets(catd, 1, 1054,
"\n\t2. Delete this one only"));
fprintf(stdout, "%s", catgets(catd, 1, 1055, "\n\t3. Delete forward"));
fprintf(stdout, "%s", catgets(catd, 1, 1056, "\n\t4. Cancel"));
fprintf(stdout, "%s", catgets(catd, 1, 1057, "\n\tOption [1-4]: "));
fgets(ans, sizeof(ans)-1, stdin);
fprintf(stdout, "\n");
}
switch(*ans) {
case '1':
if (csa_delete_entry(session, list[index], CSA_SCOPE_ALL, NULL)
!= CSA_SUCCESS)
mini_err_msg(catd, appt->what->value->item.string_value,
TTY_Delete);
break;
case '2':
if (csa_delete_entry(session, list[index], CSA_SCOPE_ONE, NULL)
!= CSA_SUCCESS)
mini_err_msg(catd, appt->what->value->item.string_value,
TTY_Delete);
break;
case '3':
if (csa_delete_entry(session, list[index], CSA_SCOPE_FORWARD,
NULL) != CSA_SUCCESS)
mini_err_msg(catd, appt->what->value->item.string_value,
TTY_Delete);
break;
case '4':
default:
break;
}
free_appt_struct(&appt);
return 0;
}
/*
* Build ascii date/time line from integer (tick)
*/
extern void
cm_tty_format_header(Props *p, Tick tick, char *buf) {
Days_op d_op;
Months_op m_op;
if (!buf)
return;
d_op = (Days_op)dow(tick);
m_op = (Months_op)(month(tick) - 1);
switch(get_int_prop(p, CP_DATEORDERING)) {
case ORDER_MDY:
sprintf(buf, "%s %s %d, %d", day_str(d_op), month_str(m_op),
dom(tick), year(tick));
break;
case ORDER_DMY:
sprintf(buf, "%s %d %s, %d", day_str(d_op), dom(tick),
month_str(m_op), year(tick));
break;
case ORDER_YMD:
sprintf(buf, "%s, %d %s %d", day_str(d_op), year(tick),
month_str(m_op), dom(tick));
break;
default:
buf[0] = '\0';
break;
}
}
extern void
scrub_attr_list(Dtcm_appointment *appt) {
int i;
for (i = 0; i < appt->count; i++) {
if (appt->attrs[i].value->type == CSA_VALUE_REMINDER) {
if ((appt->attrs[i].value->item.reminder_value->lead_time == NULL) ||
(appt->attrs[i].value->item.reminder_value->lead_time[0] == '\0')) {
free(appt->attrs[i].name);
appt->attrs[i].name = NULL;
}
}
else if ((appt->attrs[i].value->type == CSA_VALUE_ACCESS_LIST) && (appt->attrs[i].value->item.access_list_value == NULL)) {
free(appt->attrs[i].name);
appt->attrs[i].name = NULL;
}
else if ((appt->attrs[i].value->type == CSA_VALUE_STRING) && (appt->attrs[i].value->item.string_value == NULL)) {
free(appt->attrs[i].name);
appt->attrs[i].name = NULL;
}
else if ((appt->attrs[i].value->type == CSA_VALUE_DATE_TIME) && (appt->attrs[i].value->item.date_time_value == NULL)) {
free(appt->attrs[i].name);
appt->attrs[i].name = NULL;
}
}
}
/*
* Insert an appointment!
*/
extern int
cm_tty_insert(nl_catd catd, CSA_session_handle target, int version,
char *date, char *start, char *end,
char *repeat, char *repeatfor, char *what, char *filename,
Props *p) {
int ret_stat = 0, cnt;
char *t1 = NULL;
CSA_entry_handle new_entry;
CmDataList *list = CmDataListCreate();
Validate_op op;
CSA_attribute *attrs;
CSA_return_code status;
Dtcm_appointment *appt;
/* XXX: This is ugly but the query_user() function needs the catd
* and this is the easiest way to get it there.
*/
catd_global = catd;
if (filename) {
op = parse_appt_from_file(catd, filename, list, p, query_user,
NULL, version);
appt = (Dtcm_appointment *)CmDataListGetData(list, 1);
} else {
appt = allocate_appt_struct(appt_write, version, NULL);
load_appt_defaults(appt, p);
op = validate_appt(catd, appt, start, end, date, 0, what,
repeat, repeatfor, query_user, what, version);
CmDataListAdd(list, (void *)appt, 0);
}
for (cnt = 1; cnt <= list->count; cnt++) {
if ((appt = (Dtcm_appointment *)CmDataListGetData(list, cnt)) == NULL)
continue;
switch(op) {
case INVALID_DATE:
t1 = catgets(catd, 1, 1058, "Invalid Date specified.\n");
break;
case INVALID_START:
t1 = catgets(catd, 1, 1059,
"Invalid Start time specified.\n");
break;
case INVALID_TIME:
t1 = "Invalid Due time specified.\n";
break;
case INVALID_STOP:
t1 = catgets(catd, 1, 1060,
"Invalid Stop time specified.\n");
break;
case MISSING_DATE:
t1 = catgets(catd, 1, 1061,
"Empty or missing Date field.\n");
break;
case MISSING_START:
t1 = catgets(catd, 1, 1062,
"Empty or missing Start field.\n");
break;
case MISSING_TIME:
t1 = "Empty or missing Due time field.\n";
break;
case MISSING_WHAT:
t1 = catgets(catd, 1, 1063,
"Empty or missing What field.\n");
break;
case REPEAT_FOR_MISMATCH:
t1 = catgets(catd, 1, 1064,
"Repeat and For field mismatch.\n");
break;
case VALID_APPT:
case CANCEL_APPT:
t1 = "";
break;
default:
op = CANCEL_APPT;
t1 = catgets(catd, 1, 1065,
"Insert appointment was cancelled\n");
break;
}
if (op == VALID_APPT) {
scrub_attr_list(appt);
if ((status = csa_add_entry(target, appt->count, appt->attrs, &new_entry, NULL)) != CSA_SUCCESS) {
mini_err_msg(catd,
appt->what->value->item.string_value,
TTY_Insert);
ret_stat = -1;
} else
csa_free((CSA_buffer)new_entry);
} else {
char *msg = strdup(t1);
fprintf(stderr, "%s%s\n", msg, catgets(catd, 1, 1066,
"Appointment was not inserted."));
free(msg);
ret_stat = -1;
}
}
if ((list->count == 0) && (op != VALID_APPT || op != CANCEL_APPT))
ret_stat = -1;
for (cnt = 1; cnt <= list->count; cnt++)
if (appt = (Dtcm_appointment *)CmDataListGetData(list, cnt))
free_appt_struct(&appt);
CmDataListDestroy(list, B_FALSE);
return ret_stat;
}
void
cm_tty_load_props(Props **p) {
int start, stop;
if (*p)
free(*p);
*p = (Props *)ckalloc(sizeof(Props));
read_props(*p);
cal_convert_cmrc(*p);
if ((start = get_int_prop(*p, CP_DAYBEGIN)) < 0)
start = 0;
else if (start > 22)
start = 22;
if ((stop = get_int_prop(*p, CP_DAYEND)) <= start)
stop = start + 1;
else if (stop > 23)
stop = 23;
set_int_prop(*p, CP_DAYBEGIN, start);
set_int_prop(*p, CP_DAYEND, stop);
}
extern int
cm_tty_lookup(nl_catd catd, CSA_session_handle target, int version, char *date, char *view, CSA_entry_handle **list,
Props *p) {
int span, day, lineno = 1, last_day = -1, i;
CSA_uint32 a_total;
char start_buf[MAXNAMELEN], end_buf[MAXNAMELEN];
char buf[MAXNAMELEN], date_str[MAXNAMELEN], *what;
time_t tick, start, stop;
Lines *lines = NULL, *next_line;
DisplayType dt;
CSA_enum *ops;
CSA_attribute *range_attrs;
Dtcm_appointment *appt;
Tick start_tick, end_tick = 0;
CSA_return_code status;
/*
* Preliminary stuff - set defaults
*/
if (!view) {
switch(get_int_prop(p, CP_DEFAULTVIEW)) {
case 1:
view = "month";
break;
case 2:
view = "week";
break;
default:
view = "day";
break;
}
}
if (!date)
tick = now();
else if ((tick = cm_getdate(date, NULL)) < 0) {
fprintf(stdout, "\n%s %s\n\n",
catgets(catd, 1, 1067, "Invalid date specified:"),
date);
return(0);
}
/*
* Compute day and span for view specified
*/
if (strncasecmp(view, "week", 4) == 0) {
day = dow(tick);
span = 7;
} else if (strncasecmp(view, "month", 5) == 0) {
day = dom(tick) - 1;
span = monthlength(tick);
} else {
day = 0;
span = 1;
}
start = lowerbound(tick - (day * daysec));
stop = next_ndays(start, span) - 1;
setup_range(&range_attrs, &ops, &i, start, stop, CSA_TYPE_EVENT,
0, B_FALSE, version);
status = csa_list_entries(target, i, range_attrs, ops, &a_total, list, NULL);
free_range(&range_attrs, &ops, i);
appt = allocate_appt_struct(appt_read,
version,
CSA_ENTRY_ATTR_START_DATE_I,
CSA_ENTRY_ATTR_SUMMARY_I,
CSA_X_DT_ENTRY_ATTR_SHOWTIME_I,
CSA_ENTRY_ATTR_END_DATE_I,
NULL);
for (i = 0; i < a_total; i++) {
if (query_appt_struct(target, (*list)[i], appt) != CSA_SUCCESS) {
mini_err_msg(catd, appt->what->value->item.string_value,
TTY_Lookup);
continue;
}
_csa_iso8601_to_tick(appt->time->value->item.date_time_value, &start_tick);
if (appt->end_time)
_csa_iso8601_to_tick(appt->end_time->value->item.\
date_time_value, &end_tick);
day = dom(start_tick);
if (day != last_day) {
cm_tty_format_header(p, start_tick, date_str);
fprintf(stdout, "\n%s %s:\n",
catgets(catd, 1, 1068, "Appointments for"),
date_str);
}
last_day = day;
memset(buf, '\0', MAXNAMELEN);
if (appt->show_time->value->item.sint32_value &&
!magic_time(start_tick)) {
dt = get_int_prop(p, CP_DEFAULTDISP);
format_time(start_tick, dt, start_buf);
if (appt->end_time)
format_time(end_tick, dt, end_buf);
else
*end_buf = '\0';
sprintf(buf, "%s%c%7s ", start_buf,
(*end_buf ? '-' : ' '), end_buf);
}
fprintf(stdout, "\t%3d) %s", lineno++, buf);
if ((lines = text_to_lines(appt->what->value->item.string_value, 5)) && lines->s) {
fprintf(stdout, "%s\n", lines->s);
next_line = lines->next;
while(next_line) {
fprintf(stdout,
"\t%21s%s\n", " ", next_line->s);
next_line = next_line->next;
}
} else
fprintf(stdout, "\n");
destroy_lines(lines);
fprintf(stdout, "\n");
}
free_appt_struct(&appt);
if (*list == NULL) {
cm_tty_format_header(p, start + 1, date_str);
fprintf(stdout, "\n%s %s\n\n",
catgets(catd, 1, 1069, "No Appointments for"),
date_str);
}
return a_total;
}
/*
* These functions will convert a string to the enumerated value
*/
extern boolean_t
convert_boolean_str(char *val) {
if (strncasecmp(val, "T", 1) == 0 || strcasecmp(val, "B_TRUE") == 0)
return B_TRUE;
return B_FALSE;
}
extern int
convert_privacy_str_to_op(char *val) {
int i = 2;
/*
* i defaults to 1 = CSA_CLASS_PRIVATE, so no need to check for that
* string.
*/
if (strcmp(val, privacy_str(0)) == 0 ||
strcmp(val, privacy_str_old(0)) == 0 ||
strcmp(val, privacy_str_411(0)) == 0)
i = 0;
else if (strcmp(val, privacy_str(1)) == 0 ||
strcmp(val, privacy_str_old(1)) == 0 ||
strcmp(val, privacy_str_411(1)) == 0)
i = 1;
return i;
}
extern CSA_sint32
convert_privacy_str(char *val) {
CSA_sint32 ret_val = CSA_CLASS_PRIVATE;
if ((strcasecmp(val, privacy_str(0)) == 0) ||
(strcasecmp(val, privacy_str_old(0)) == 0) ||
(strcasecmp(val, privacy_str_411(0)) == 0))
ret_val = CSA_CLASS_PUBLIC;
else if ((strcasecmp(val, privacy_str(1)) == 0) ||
(strcasecmp(val, privacy_str_old(1)) == 0) ||
(strcasecmp(val, privacy_str_411(1)) == 0))
ret_val = CSA_CLASS_CONFIDENTIAL;
else if ((strcasecmp(val, privacy_str(2)) == 0) ||
(strcasecmp(val, privacy_str_old(2)) == 0) ||
(strcasecmp(val, privacy_str_411(2)) == 0))
ret_val = CSA_CLASS_PRIVATE;
return ret_val;
}
extern SeparatorType
convert_separator_str(char *val) {
SeparatorType op = SEPARATOR_BLANK;
char *search_val = separator_str(op);
while (search_val && strcasecmp(search_val, val) != 0)
search_val = separator_str(++op);
return op;
}
extern Time_scope_menu_op
convert_time_scope_str(char *val) {
Time_scope_menu_op op = TIME_MINS;
char *search_val = time_scope_str(op);
while(search_val && strcasecmp(search_val, val) != 0)
search_val = time_scope_str(++op);
return op;
}
extern char*
day_str(Days_op op) {
if (op >= SUNDAY && op <= SATURDAY)
return day_strs[op];
return NULL;
}
extern char*
default_repeat_cnt_str(Repeat_menu_op op) {
if (op >= ONE_TIME && op <= REPEAT_EVERY)
return default_repeat_cnt_strs[op];
return NULL;
}
extern char*
default_repeat_scope_str(
nl_catd catd,
Repeat_menu_op op)
{
if (!default_repeat_scope_strs[DAILY]) {
default_repeat_scope_strs[ONE_TIME] = strdup("\0");
default_repeat_scope_strs[DAILY] =
strdup(catgets(catd, 1, 994, "days"));
default_repeat_scope_strs[WEEKLY] =
strdup(catgets(catd, 1, 995, "weeks"));
default_repeat_scope_strs[EVERY_TWO_WEEKS] =
strdup(catgets(catd, 1, 996, "biweeks"));
default_repeat_scope_strs[MONTHLY_BY_DATE] =
strdup(catgets(catd, 1, 997, "months"));
default_repeat_scope_strs[MONTHLY_BY_WEEKDAY] =
default_repeat_scope_strs[MONTHLY_BY_DATE];
default_repeat_scope_strs[YEARLY] =
strdup(catgets(catd, 1, 998, "years"));
default_repeat_scope_strs[MONDAY_THRU_FRIDAY] =
default_repeat_scope_strs[WEEKLY];
default_repeat_scope_strs[MON_WED_FRI] =
default_repeat_scope_strs[WEEKLY];
default_repeat_scope_strs[TUESDAY_THURSDAY] =
default_repeat_scope_strs[WEEKLY];
default_repeat_scope_strs[REPEAT_EVERY] = strdup("\0");
}
if (op >= ONE_TIME && op <= REPEAT_EVERY)
return default_repeat_scope_strs[op];
return NULL;
}
extern char*
for_str(For_menu_op op) {
if (op >= TWO && op <= FOR_EVER)
return for_strs[op];
return NULL;
}
/*
** Return a date label according to order and sep
*/
extern char*
get_datemsg(OrderingType order, SeparatorType sep) {
char *str = separator_str(sep);
char buf[20];
switch (order) {
case ORDER_DMY:
sprintf(buf, "%s %s %s %s %s", "Day", str, "Month", str, "Year");
break;
case ORDER_YMD:
sprintf(buf, "%s %s %s %s %s", "Year", str, "Month", str, "Day");
break;
case ORDER_MDY:
default:
sprintf(buf, "%s %s %s %s %s", "Month", str, "Day", str, "Year");
break;
}
return(cm_strdup(buf));
}
/*
* This function is used by the appointment file parsing routines to return
* whether or not the value in the passed buffer is a key word
*/
extern Parse_key_op
identify_parse_key(char *str) {
if (!new_appt_begin_delimiter) {
new_appt_begin_delimiter = malloc(strlen(CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER) + 14);
sprintf(new_appt_begin_delimiter, "%s%s",
CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER,
":string:begin");
}
if (strncasecmp(str, "** Calendar Appointment **",
cm_strlen("** Calendar Appointment **")) == 0)
return APPOINTMENT_START;
if (strncasecmp(str, "date:", cm_strlen("date:")) == 0)
return DATE_KEY;
if (strncasecmp(str, "time:", cm_strlen("time:")) == 0 ||
strncasecmp(str, "start:", cm_strlen("start:")) == 0 ||
strncasecmp(str, "from:", cm_strlen("from:")) == 0)
return START_KEY;
if(strncasecmp(str, "end:", cm_strlen("end:")) == 0 ||
strncasecmp(str, "until:", cm_strlen("until:")) == 0 ||
strncasecmp(str, "stop:", cm_strlen("stop:")) == 0 ||
strncasecmp(str, "to:", cm_strlen("to:")) == 0)
return STOP_KEY;
if (strncasecmp(str, "duration:", cm_strlen("duration:")) == 0)
return DURATION_KEY;
if (strncasecmp(str, "what:", cm_strlen("what:")) == 0)
return WHAT_KEY;
if (strncasecmp(str, "repeat:", cm_strlen("repeat:")) == 0)
return REPEAT_KEY;
if (strncasecmp(str, "for:", cm_strlen("for:")) == 0)
return FOR_KEY;
if (strncasecmp(str, new_appt_begin_delimiter, cm_strlen(new_appt_begin_delimiter)) == 0)
return NEW_APPT_KEY;
return NOT_A_KEY;
}
/*
* This function will fill in the default values using the properties database
* for a given appointment
*/
extern void
load_appt_defaults(Dtcm_appointment *a, Props *p) {
a->type->value->item.sint32_value = CSA_TYPE_EVENT;
a->subtype->value->item.string_value = strdup(CSA_SUBTYPE_APPOINTMENT);
a->state->value->item.sint32_value = CSA_X_DT_STATUS_ACTIVE;
if (a->repeat_type && a->repeat_type->value)
a->repeat_type->value->item.sint32_value = CSA_X_DT_REPEAT_ONETIME;
if (a->repeat_times && a->repeat_times->value)
a->repeat_times->value->item.uint32_value = 0;
a->private->value->item.sint32_value =
convert_privacy_str(get_char_prop(p, CP_PRIVACY));
load_reminder_props(a, p);
}
extern void
load_reminder_props(Dtcm_appointment *a, Props *p) {
if (convert_boolean_str(get_char_prop(p, CP_BEEPON))) {
a->beep->value->item.reminder_value->lead_time = malloc(BUFSIZ);
_csa_duration_to_iso8601(get_int_prop(p, CP_BEEPADV) *
timescopestring_to_tick(get_char_prop(p, CP_BEEPUNIT)),
a->beep->value->item.reminder_value->lead_time);
a->beep->value->item.reminder_value->reminder_data.data = NULL;
a->beep->value->item.reminder_value->reminder_data.size = 0;
}
if (convert_boolean_str(get_char_prop(p, CP_FLASHON))) {
a->flash->value->item.reminder_value->lead_time = malloc(BUFSIZ);
_csa_duration_to_iso8601(get_int_prop(p, CP_FLASHADV) *
timescopestring_to_tick(get_char_prop(p, CP_FLASHUNIT)),
a->flash->value->item.reminder_value->lead_time);
a->flash->value->item.reminder_value->reminder_data.data = NULL;
a->flash->value->item.reminder_value->reminder_data.size = 0;
}
if (convert_boolean_str(get_char_prop(p, CP_OPENON))) {
a->popup->value->item.reminder_value->lead_time = malloc(BUFSIZ);
_csa_duration_to_iso8601(get_int_prop(p, CP_OPENADV) *
timescopestring_to_tick(get_char_prop(p, CP_OPENUNIT)),
a->popup->value->item.reminder_value->lead_time);
a->popup->value->item.reminder_value->reminder_data.data = NULL;
a->popup->value->item.reminder_value->reminder_data.size = 0;
}
if (convert_boolean_str(get_char_prop(p, CP_MAILON))) {
a->mail->value->item.reminder_value->lead_time = malloc(BUFSIZ);
_csa_duration_to_iso8601(get_int_prop(p, CP_MAILADV) *
timescopestring_to_tick(get_char_prop(p, CP_MAILUNIT)),
a->mail->value->item.reminder_value->lead_time);
a->mail->value->item.reminder_value->reminder_data.data = (CSA_uint8 *) strdup(get_char_prop(p, CP_MAILTO));
a->mail->value->item.reminder_value->reminder_data.size = strlen(get_char_prop(p, CP_MAILTO)) + 1;
}
}
extern char*
month_str(Months_op op) {
if (op >= JANUARY && op <= DECEMBER)
return month_strs[op];
return NULL;
}
void
build_new_attrval(CSA_attribute *attrval, char *name, char *tag, char *value)
{
CSA_attribute_value *vptr = calloc(sizeof(CSA_attribute_value), 1);
char *s_ptr, *b_ptr;
CSA_access_list l_ptr;
boolean_t done = B_FALSE;
CSA_access_list *link_location;
attrval->name = cm_strdup(name);
attrval->value = vptr;
if (!strcmp(tag, "string")) {
vptr->type = CSA_VALUE_STRING;
vptr->item.string_value = cm_strdup(value);
}
if (!strcmp(tag, "datetime")) {
vptr->type = CSA_VALUE_DATE_TIME;
vptr->item.date_time_value = cm_strdup(value);
}
if (!strcmp(tag, "caluser")) {
vptr->type = CSA_VALUE_CALENDAR_USER;
s_ptr = strchr(value, ':');
*s_ptr = '\0';
vptr->item.calendar_user_value = calloc(sizeof(CSA_calendar_user), 1);
vptr->item.calendar_user_value->user_name = cm_strdup(s_ptr + 1);
vptr->item.calendar_user_value->user_type = atoi(value);
}
if (!strcmp(tag, "uinteger")) {
vptr->type = CSA_VALUE_UINT32;
vptr->item.sint32_value = atoi(value);
}
if (!strcmp(tag, "sinteger")) {
vptr->type = CSA_VALUE_SINT32;
vptr->item.sint32_value = atoi(value);
}
if (!strcmp(tag, "reminder")) {
vptr->type = CSA_VALUE_REMINDER;
s_ptr = strchr(value, ':');
*s_ptr = '\0';
vptr->item.reminder_value = calloc(sizeof(CSA_reminder), 1);
vptr->item.reminder_value->lead_time = malloc(BUFSIZ);
_csa_duration_to_iso8601(atoi(value), vptr->item.reminder_value->lead_time);
vptr->item.reminder_value->reminder_data.data = (CSA_uint8 *) strdup(s_ptr + 1);
vptr->item.reminder_value->reminder_data.size = strlen(s_ptr + 1) + 1;
}
if (!strcmp(tag, "accesslist")) {
/* The access list format is that each member in the
list is written out on a separate line. So after
the initial newline in the value, the pattern is a
TAB followed by the mask value followed by a colon,
followed by the string. */
vptr->type = CSA_VALUE_ACCESS_LIST;
link_location = &vptr->item.access_list_value;
b_ptr = value + 1;
if (value && *b_ptr) {
/* we have an initial value. */
while (!done) {
l_ptr = calloc(sizeof(CSA_access_rights), 1);
s_ptr = strchr(b_ptr, ':');
*s_ptr = '\0';
l_ptr->rights = atoi(b_ptr);
b_ptr = s_ptr + 1;
if (s_ptr = strchr(b_ptr, '\n'))
*s_ptr = '\0';
l_ptr->user->user_name = cm_strdup(b_ptr);
if (s_ptr)
b_ptr = s_ptr + 1;
else
done = B_TRUE;
*link_location = l_ptr;
link_location = &l_ptr->next;
}
}
}
}
static void
read_new_appt(FILE *fp, Dtcm_appointment **appt, Props *p, int version)
{
int attrs_allocated, attrs_written = 0;
char line[MAXNAMELEN], *b_ptr, *c_ptr;
char *a_name = NULL, *a_tag = NULL, *a_value = NULL;
Dtcm_appointment *avlist;
if (!new_appt_end_delimiter) {
new_appt_end_delimiter = malloc(strlen(CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER) + 14);
sprintf(new_appt_end_delimiter, "%s%s",
CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER,
":string:end");
}
avlist = allocate_appt_struct(appt_write, DATAVER_ARCHIVE, NULL);
load_appt_defaults(avlist, p);
/* At this point, we really want to negate all of the links set
up by tha appointment allocation routine. The following code is
almost certain to invalidate them, and some trailing links would
be harmful */
avlist->identifier = NULL;
avlist->modified_time = NULL;
avlist->author = NULL;
avlist->time = NULL;
avlist->type = NULL;
avlist->subtype = NULL;
avlist->private = NULL;
avlist->end_time = NULL;
avlist->show_time = NULL;
avlist->what = NULL;
avlist->state = NULL;
avlist->repeat_type = NULL;
avlist->repeat_times = NULL;
avlist->repeat_interval = NULL;
avlist->repeat_week_num = NULL;
avlist->recurrence_rule = NULL;
avlist->beep = NULL;
avlist->flash = NULL;
avlist->mail = NULL;
avlist->popup = NULL;
attrs_allocated = avlist->count;
/* should be starting a new attribute definition */
while (fgets(line, MAXNAMELEN - 1, fp))
{
/* look for new end marker on appointment */
if (strncmp(line, new_appt_end_delimiter, strlen(new_appt_end_delimiter)) == 0)
break;
if (line[0] == '\t') {
/* This is a continuation line from the previous
attribute definition. The value here should
be catenated onto the previously yanked value */
b_ptr = line + 1;
/* a line with only a tab on it, and no text
means that a newline should be inserted
into the stream */
if (!*b_ptr)
b_ptr = "\n";
a_value = realloc(a_value, strlen(a_value) +
strlen(b_ptr) + 2);
strcat(a_value, "\n");
strcat(a_value, b_ptr);
a_value[strlen(a_value) - 1] = '\0';
continue;
}
else if (line[0] == '\n') {
/* An empty line. This means the end of
the appointment definition */
break;
}
/* if the line is neither a termination line, nor
a continuation line, then the entry must be a new
attribute name. We should declare the previous
triple complete. */
if (a_name && a_tag && a_value)
{
/* see if the allocated buffer is large enough to
contain another attibute. If not, make it a bit
larger, and then copy the attribute. */
if (ident_name_ro(a_name, DATAVER_ARCHIVE) == B_FALSE) {
if ((attrs_written) == attrs_allocated) {
attrs_allocated += 10;
avlist->attrs = realloc(avlist->attrs,
sizeof(CSA_attribute) *
attrs_allocated);
}
build_new_attrval(&avlist->attrs[attrs_written],
a_name, a_tag, a_value);
++attrs_written;
}
free(a_name);
free(a_tag);
free(a_value);
}
/* this should pull off the new attribute name */
a_name = a_tag = a_value = NULL;
b_ptr = line;
if (c_ptr = strchr(line, ':'))
{
*c_ptr = '\0';
a_name = cm_strdup(b_ptr);
}
else
/* big problem. Malformed attribute specification */
break;
b_ptr = c_ptr + 1;
if (!*b_ptr)
break;
if (c_ptr = strchr(b_ptr, ':'))
{
*c_ptr = '\0';
a_tag = cm_strdup(b_ptr);
}
else
/* big problem. Malformed attribute specification */
break;
b_ptr = c_ptr + 1;
if (!*b_ptr)
break;
else
b_ptr[strlen(b_ptr) - 1] = '\0';
a_value = cm_strdup(b_ptr);
}
/* finished due to end of file. Early termination, but not too bad */
if (a_name && a_tag && a_value)
{
/* see if the allocated buffer is large enough to
contain another attibute. If not, make it a bit
larger, and then copy the attribute. */
if (ident_name_ro(a_name, DATAVER_ARCHIVE) == B_FALSE) {
if ((attrs_written) == attrs_allocated) {
attrs_allocated += 10;
avlist->attrs = realloc(avlist->attrs,
sizeof(CSA_attribute) *
attrs_allocated);
}
build_new_attrval(&avlist->attrs[attrs_written],
a_name, a_tag, a_value);
++attrs_written;
}
}
free(a_name);
free(a_tag);
free(a_value);
avlist->count = attrs_written;
set_appt_links(avlist);
*appt = avlist;
}
static char *
dow_str(
time_t tick)
{
switch (dow(tick)) {
case 0:
return (cm_strdup("SU"));
case 1:
return (cm_strdup("MO"));
case 2:
return (cm_strdup("TU"));
case 3:
return (cm_strdup("WE"));
case 4:
return (cm_strdup("TH"));
case 5:
return (cm_strdup("FR"));
case 6:
default:
return (cm_strdup("SA"));
}
}
/* this routine is designed to take in an appintment, and generate the
DATAVER4 recurrence rule for that appointment. It is also responsible
for crushing out the old style attributes that may not be inserted
into a newer style data model. */
static void
generate_recurrence_rule(Dtcm_appointment *appt, int version) {
char *str,
rule_buf1[32],
rule_buf2[32];
CSA_sint32 repeat_type;
CSA_uint32 repeat_nth;
CSA_uint32 repeat_for;
Tick appt_time;
int wk;
rule_buf1[0] = '\0';
rule_buf2[0] = '\0';
if (appt->repeat_type && appt->repeat_type->value)
repeat_type = appt->repeat_type->value->item.sint32_value;
else
repeat_type = CSA_X_DT_REPEAT_ONETIME;
if (appt->repeat_interval && appt->repeat_interval->value)
repeat_nth = appt->repeat_interval->value->item.uint32_value;
if (appt->repeat_times && appt->repeat_times->value)
repeat_for = appt->repeat_times->value->item.uint32_value;
_csa_iso8601_to_tick(appt->time->value->item.date_time_value, &appt_time);
if (!appt->recurrence_rule || !appt->recurrence_rule->value) {
switch (repeat_type) {
case CSA_X_DT_REPEAT_ONETIME :
strcpy(rule_buf1, "D1 ");
break;
case CSA_X_DT_REPEAT_DAILY :
strcpy(rule_buf1, "D1 ");
break;
case CSA_X_DT_REPEAT_WEEKLY :
strcpy(rule_buf1, "W1 ");
break;
case CSA_X_DT_REPEAT_BIWEEKLY :
strcpy(rule_buf1, "W2 ");
break;
case CSA_X_DT_REPEAT_MONTHLY_BY_WEEKDAY :
if (weekofmonth(appt_time, &wk) && wk == 5)
sprintf(rule_buf1, "MP1 1- %s ", dow_str(appt_time));
else
strcpy(rule_buf1, "MP1 ");
break;
case CSA_X_DT_REPEAT_MONTHLY_BY_DATE :
strcpy(rule_buf1, "MD1 ");
break;
case CSA_X_DT_REPEAT_YEARLY :
strcpy(rule_buf1, "YM1 ");
break;
case CSA_X_DT_REPEAT_MON_TO_FRI :
strcpy(rule_buf1, "W1 MO TU WE TH FR ");
break;
case CSA_X_DT_REPEAT_MONWEDFRI :
strcpy(rule_buf1, "W1 MO WE FR ");
break;
case CSA_X_DT_REPEAT_TUETHUR :
strcpy(rule_buf1, "W1 TU TH ");
break;
case CSA_X_DT_REPEAT_EVERY_NDAY :
sprintf(rule_buf1, "D%ld ", repeat_nth);
break;
case CSA_X_DT_REPEAT_EVERY_NWEEK :
sprintf(rule_buf1, "W%ld ", repeat_nth);
break;
case CSA_X_DT_REPEAT_EVERY_NMONTH :
sprintf(rule_buf1, "MD%ld ", repeat_nth);
break;
}
if (repeat_for == 0)
strcat(rule_buf2, "#1");
else
sprintf(rule_buf2, "#%ld", repeat_for);
strcat (rule_buf1, rule_buf2);
appt->attrs = realloc(appt->attrs,
sizeof(CSA_attribute) * (appt->count + 1));
build_new_attrval(&appt->attrs[appt->count],
CSA_ENTRY_ATTR_RECURRENCE_RULE,
"string",
rule_buf1);
appt->count++;
set_appt_links(appt);
}
if (version == DATAVER_ARCHIVE)
return;
/* crush out the old values, if they exist */
if (appt->repeat_type && appt->repeat_type->name) {
free(appt->repeat_type->name);
appt->repeat_type->name = NULL;
}
if (appt->repeat_times && appt->repeat_times->name) {
free(appt->repeat_times->name);
appt->repeat_times->name = NULL;
}
if (appt->repeat_interval && appt->repeat_interval->name) {
free(appt->repeat_interval->name);
appt->repeat_interval->name = NULL;
}
if (appt->repeat_week_num && appt->repeat_week_num->name) {
free(appt->repeat_week_num->name);
appt->repeat_week_num->name = NULL;
}
}
/*
* Given a file name, we're going to parse the file and create an appointment
* for the calling routine.
*
* This function will scan for X number of appointments in the file and add
* each to the linked list unless the validation routine returns an error.
* If this happens, the invalid appointment is still added to the list so the
* calling routine can do further processing if necessary, but the remaining
* appointments in the file (if any) are not read.
*/
extern Validate_op
parse_appt_from_file(nl_catd catd, char *file, CmDataList *list, Props *p,
boolean_t(*query)(), void *key_data, int version) {
int len, dur;
char line[MAXNAMELEN], *tmp, *key_str, *val_str,
s_buf[MAXNAMELEN], e_buf[MAXNAMELEN],
d_buf[MAXNAMELEN], r_buf[MAXNAMELEN],
f_buf[MAXNAMELEN], *w_buf = NULL;
FILE *fp;
boolean_t processing_appt = B_FALSE,
processing_what = B_FALSE;
int what_lines = 0;
Validate_op valid_op = VALID_APPT;
Parse_key_op pko = NOT_A_KEY;
Dtcm_appointment *appt;
boolean_t found_appt = B_FALSE;
if (!file || *file == '\0' || (fp = fopen(file, "r")) == NULL)
return COULD_NOT_OPEN_FILE;
appt = allocate_appt_struct(appt_write, version, NULL);
load_appt_defaults(appt, p);
while (valid_op == VALID_APPT && fgets(line, MAXNAMELEN - 1, fp)) {
/*
* Point key_str at the first non-space character
*/
key_str = line;
if ((*line != ' ' && *line != '\t') || blank_buf(line)) {
processing_what = B_FALSE;
what_lines = 0;
}
/*
* The check for '*' handles the case when the appt header
* ``** Calendar Appointment **'' has no white space to its
* left.
*/
if (*key_str == '\0' ||
(!isspace((u_char)*key_str) && (u_char)*key_str != '*'))
continue;
while (*key_str != '\0' && isspace((u_char)*key_str))
++key_str;
pko = identify_parse_key(key_str);
/*
* Remove any ending white space
*/
tmp = strrchr(line, '\0'); --tmp;
while(isspace((u_char)*tmp)) {
*tmp = '\0';
--tmp;
}
/*
* If we're not currently processing the what string and we
* haven't identified a key, continue the loop. Otherwise,
* we've identified a key, so we can't be in the middle of
* processing the what string -- set the flag accordingly.
*
*/
if (pko == NOT_A_KEY) {
if (!processing_what || what_lines >= 5)
continue;
pko = WHAT_KEY;
} else
{
processing_what = B_FALSE;
what_lines = 0;
}
/*
* Point val_str at the next non-space character after the key
*/
val_str = key_str;
while(*val_str != '\0' && !isspace((u_char)*val_str))
++val_str;
while(*val_str != '\0' && isspace((u_char)*val_str))
++val_str;
/*
* Now, based on the keyword, set the necessary stuff in the
* new appointment structure.
*/
switch(pko) {
case APPOINTMENT_START:
if (processing_appt) {
/*
* We've reached another appointment. Add the
* current to the linked list, reset flags, and
* check it's validity ...
*/
CmDataListAdd(list, (void *)appt, 0);
valid_op = validate_appt(catd, appt, s_buf,
e_buf, d_buf, dur, w_buf, r_buf, f_buf,
query, key_data, version);
if (valid_op == VALID_APPT) {
appt = allocate_appt_struct(appt_write, version, NULL);
load_appt_defaults(appt, p);
}
else
processing_appt = B_FALSE;
} else
processing_appt = B_TRUE;
found_appt = B_TRUE;
dur = 0;
memset(s_buf, '\0', MAXNAMELEN);
memset(e_buf, '\0', MAXNAMELEN);
memset(d_buf, '\0', MAXNAMELEN);
memset(r_buf, '\0', MAXNAMELEN);
memset(f_buf, '\0', MAXNAMELEN);
if (w_buf)
free(w_buf);
w_buf = NULL;
break;
case DATE_KEY:
cm_strcpy(d_buf, val_str);
break;
case START_KEY:
cm_strcpy(s_buf, val_str);
len = cm_strlen(s_buf) - 1;
if (s_buf[len] == 'a' || s_buf[len] == 'p')
cm_strcat(s_buf, "m");
break;
case STOP_KEY:
cm_strcpy(e_buf, val_str);
len = cm_strlen(e_buf) - 1;
if (e_buf[len] == 'a' || e_buf[len] == 'p')
cm_strcat(e_buf, "m");
break;
case DURATION_KEY:
dur = atoi(val_str);
/*
* Check for a unit specification
*/
while(*val_str != '\0' && !isspace((u_char)*val_str))
++val_str;
while(*val_str != '\0' && isspace((u_char)*val_str))
++val_str;
switch(*val_str) {
case 'h':
case 'H':
dur *= 3600;
break;
case 'd':
case 'D':
dur *= 86400;
break;
case 's':
case 'S':
break;
case 'm':
case 'M':
default:
dur *= 60;
break;
}
break;
case WHAT_KEY:
/*
* If we're not currently processing a what string
* and we've got a what value, there were more than one
* what strings in the file - delete the first one and
* take the second.
*
* Otherwise, we in process, so alloc more space and
* concatinate the new string to the end of the last.
*/
if (!processing_what) {
if (w_buf != NULL)
free(w_buf);
w_buf = cm_strdup(val_str);
what_lines = 1;
} else {
tmp = w_buf;
w_buf = (char *)ckalloc(cm_strlen(tmp)
+ cm_strlen(key_str) + 2);
cm_strcpy(w_buf, tmp);
cm_strcat(w_buf, "\n");
cm_strcat(w_buf, key_str);
free(tmp);
what_lines++;
}
processing_what = B_TRUE;
break;
case REPEAT_KEY:
cm_strcpy(r_buf, val_str);
break;
case FOR_KEY:
cm_strcpy(f_buf, val_str);
break;
case NEW_APPT_KEY:
if ((version >= DATAVER4) ||
(version == DATAVER_ARCHIVE)) {
read_new_appt(fp, &appt, p, version);
generate_recurrence_rule(appt, version);
CmDataListAdd(list, (void *)appt, 0);
processing_appt = B_FALSE;
found_appt = B_TRUE;
}
break;
case NOT_A_KEY:
default:
break;
}
}
/*
* Because the appointment read is only saved when we start reading the
* next appointment, the last appointment (which doesn't have a next)
* will always be left off ... so, providing we're processing an
* appointment -- meaning we've found at least one header -- save the
* appointment in process.
*/
if (found_appt == B_FALSE) {
fclose(fp);
return(INVALID_DATE);
}
if (processing_appt) {
CmDataListAdd(list, (void *)appt, 0);
valid_op = validate_appt(catd, appt, s_buf, e_buf, d_buf, dur,
w_buf, r_buf, f_buf, query, key_data,
version);
if (w_buf)
free(w_buf);
}
scrub_attr_list(appt);
fclose(fp);
return valid_op;
}
void
growcat(char **source, char *new)
{
*source = (char *) realloc(*source, strlen(*source) + strlen(new) + 2);
strcat(*source, new);
}
void
cat_indented_string(char **catbuf, char *string)
{
char *p_str = string;
int nl_count = 0;
char *buf;
char *b_ptr;
nl_count = count_newlines(string);
b_ptr = buf = malloc(strlen(string) + nl_count + 1);
while (*p_str) {
*b_ptr++ = *p_str;
if (*p_str == '\n')
*b_ptr++ = '\t';
p_str++;
}
*b_ptr = '\0';
growcat(catbuf, buf);
free(buf);
}
char *
attrs_to_string(CSA_attribute * attrs, int num_attrs)
{
int i;
CSA_access_list a_ptr;
char *buffer = malloc(1);
char tmp_buf[MAXNAMELEN];
int advance_time;
buffer[0] = '\0';
for (i = 0; i < num_attrs; i++) {
if (!attrs[i].name || (attrs[i].value == NULL))
continue;
tmp_buf[0] = '\0';
sprintf(tmp_buf, "%s:", attrs[i].name);
switch (attrs[i].value->type) {
case CSA_VALUE_SINT32:
growcat(&buffer, tmp_buf);
sprintf(tmp_buf, "sinteger:%ld\n",
attrs[i].value->item.sint32_value);
growcat(&buffer, tmp_buf);
break;
case CSA_VALUE_UINT32:
growcat(&buffer, tmp_buf);
sprintf(tmp_buf, "uinteger:%ld\n",
attrs[i].value->item.uint32_value);
growcat(&buffer, tmp_buf);
break;
case CSA_VALUE_DATE_TIME:
if (attrs[i].value->item.string_value == NULL)
continue;
growcat(&buffer, tmp_buf);
growcat(&buffer, "datetime:");
if (attrs[i].value->item.date_time_value)
cat_indented_string(&buffer,
attrs[i].value->item.string_value);
growcat(&buffer, "\n");
break;
case CSA_VALUE_STRING:
if (attrs[i].value->item.string_value == NULL)
continue;
growcat(&buffer, tmp_buf);
growcat(&buffer, "string:");
if (attrs[i].value->item.string_value)
cat_indented_string(&buffer,
attrs[i].value->item.string_value);
growcat(&buffer, "\n");
break;
case CSA_VALUE_CALENDAR_USER:
if (attrs[i].value->item.calendar_user_value == NULL)
continue;
growcat(&buffer, tmp_buf);
sprintf(tmp_buf, "caluser:%ld:", attrs[i].value->item.calendar_user_value->user_type);
growcat(&buffer, tmp_buf);
if (attrs[i].value->item.calendar_user_value->user_name)
cat_indented_string(&buffer,
attrs[i].value->item.calendar_user_value->user_name);
growcat(&buffer, "\n");
break;
case CSA_VALUE_REMINDER:
if (attrs[i].value->item.reminder_value->lead_time == NULL)
continue;
growcat(&buffer, tmp_buf);
_csa_iso8601_to_duration(attrs[i].value->item.reminder_value->lead_time, &advance_time);
sprintf(tmp_buf, "reminder:%d:", advance_time);
growcat(&buffer, tmp_buf);
if (attrs[i].value->item.reminder_value->reminder_data.data)
cat_indented_string(&buffer,
(char *) attrs[i].value->item.reminder_value->reminder_data.data);
growcat(&buffer, "\n");
break;
case CSA_VALUE_ACCESS_LIST:
if (attrs[i].value->item.access_list_value == NULL)
continue;
growcat(&buffer, tmp_buf);
growcat(&buffer, "accesslist:\n");
a_ptr = attrs[i].value->item.access_list_value;
while (a_ptr) {
sprintf(tmp_buf, "\t%d:%s\n",
(int) a_ptr->rights, a_ptr->user->user_name);
growcat(&buffer, tmp_buf);
a_ptr = a_ptr->next;
}
break;
}
}
return(buffer);
}
char *
entry_to_attrval_string(CSA_session_handle target, CSA_entry_handle entry)
{
int i;
char *ptr;
CSA_attribute_reference *names;
CSA_attribute *attrs_ret;
CSA_uint32 num_attrs, num_attrs_ret;
csa_list_entry_attributes(target, entry, &num_attrs, &names, NULL);
csa_read_entry_attributes(target, entry, num_attrs, names, &num_attrs_ret,
&attrs_ret, NULL);
ptr = attrs_to_string(attrs_ret, num_attrs_ret);
/* PORTING NOTE:
** Need to check that library's memory management will
** properly free this. Old scheme had a special fn to
** to it, which took a pointer and a count. - dac
**
** [old version: DtCmFreeAttributeValues(attrs, num_attrs); ]
*/
csa_free(attrs_ret);
csa_free(names);
return(ptr);
}
char *
calendar_to_attrval_string(CSA_session_handle cal)
{
int i;
CSA_uint32 num_attrs, num_attrs_ret;
CSA_attribute_reference *names;
char *ptr;
CSA_attribute *attrs_ret;
csa_list_calendar_attributes(cal, &num_attrs, &names, NULL);
csa_read_calendar_attributes(cal, num_attrs, names, &num_attrs_ret,
&attrs_ret, NULL);
ptr = attrs_to_string(attrs_ret, num_attrs_ret);
csa_free(attrs_ret);
csa_free(names);
return(ptr);
}
/*
* Given an appointment structure, return a character string that can be used
* for DnD or written to a file.
*/
extern char*
parse_appt_to_string(CSA_session_handle target, CSA_entry_handle entry, Props *p, int version) {
char *ret_val, *attr_string;
Dtcm_appointment *appt;
CSA_uint32 na_ret; /* num of attributes actually read */
CSA_attribute a_ret; /* list of attrs actually read */
attr_string = entry_to_attrval_string(target, entry);
/*
* Extract the info we need from the back-end entry
*/
appt = allocate_appt_struct(appt_read,
version,
NULL);
query_appt_struct(target, entry, appt);
ret_val = parse_attrs_to_string(appt, p, attr_string);
free_appt_struct(&appt);
free (attr_string);
return ret_val;
}
extern char*
parse_attrs_to_string(Dtcm_appointment *appt, Props *p, char *attr_string) {
int nlcount, duration, repeat_nth, repeat_wk, wk;
char *whatstr, d_buf[MAXNAMELEN],
s_buf[MAXNAMELEN], e_buf[MAXNAMELEN], w_buf[MAXNAMELEN],
r_buf[MAXNAMELEN], f_buf[MAXNAMELEN], *appt_what,
*b_ptr;
time_t tick, end_tick = 0;
CSA_sint32 repeat_type;
CSA_uint32 repeat_times;
static char *format_string = "\n\n\t** Calendar Appointment **\n%s:string:begin\n%s%s:string:end\n\tDate: %s\n\tStart: %s\n\tEnd: %s\n\tRepeat: %s\n\tFor: %s\n\tWhat: %s";
s_buf[0] = '\0';
e_buf[0] = '\0';
w_buf[0] = '\0';
r_buf[0] = '\0';
f_buf[0] = '\0';
_csa_iso8601_to_tick(appt->time->value->item.date_time_value, &tick);
if (appt->end_time)
if (appt->end_time->value != NULL)
_csa_iso8601_to_tick(appt->end_time->value->item.\
date_time_value, &end_tick);
appt_what = appt->what->value->item.string_value;
if (appt->repeat_type && appt->repeat_type->value)
repeat_type = appt->repeat_type->value->item.sint32_value;
if (appt->repeat_times && appt->repeat_times->value)
repeat_times = appt->repeat_times->value->item.uint32_value;
else
repeat_times = 0;
if (appt->repeat_interval && appt->repeat_interval->value)
repeat_nth = appt->repeat_interval->value->item.uint32_value;
else
repeat_nth = 0;
if (appt->repeat_week_num && appt->repeat_week_num->value)
repeat_wk = appt->repeat_week_num->value->item.sint32_value;
else
repeat_wk = 0;
/*
* Format the date and start/stop strings
*/
format_tick(tick, ORDER_MDY, SEPARATOR_SLASH, d_buf);
format_time(tick, get_int_prop(p, CP_DEFAULTDISP), s_buf);
format_time(end_tick, get_int_prop(p, CP_DEFAULTDISP), e_buf);
/*
* Handle the what string
*/
whatstr = appt_what;
if ((nlcount = count_newlines(appt_what)) > 0) {
whatstr = (char *)ckalloc(cm_strlen(appt_what) + nlcount + 1);
copy_and_pad_newlines(whatstr, appt_what);
}
if (whatstr && !blank_buf(whatstr)) {
cm_strcat(w_buf, whatstr);
cm_strcat(w_buf, "\n\t");
}
/*
* Repeat and For stuff
*/
cm_strcpy(r_buf, periodstr_from_period(repeat_type, repeat_nth));
if (repeat_type == CSA_X_DT_REPEAT_MONTHLY_BY_WEEKDAY) {
if (weekofmonth(tick, &wk) && wk == 4) {
if (repeat_wk == -1)
strcat(r_buf, ", last");
else if (repeat_wk == 4)
strcat(r_buf, ", 4th");
}
}
sprintf(f_buf, "%ld", repeat_times);
/*
* Put it all together
*/
b_ptr = malloc(cm_strlen(d_buf) +
(2 * cm_strlen(CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER)) +
cm_strlen(s_buf) +
cm_strlen(e_buf) +
cm_strlen(r_buf) +
cm_strlen(f_buf) +
cm_strlen(w_buf) +
cm_strlen(format_string) +
cm_strlen(attr_string) +
1);
sprintf(b_ptr, format_string,
CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER,
attr_string,
CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER,
d_buf, s_buf, e_buf, r_buf, f_buf, w_buf);
if (nlcount > 0)
free(whatstr);
return(b_ptr);
}
/* This routine takes a list of buffers that represent a number
of message attachments, and glues them into a mime format
message for eventual submission to the dtmail mailer. These
objects do nor *need* to be cm appointment objects. */
char *
create_rfc_message(char *address_list,
char *subject,
char **appointment_objects,
int num_objects) {
char *unique_label;
/* do *not* put these header strings in a message catalog.
These are invariants specified by MIME */
char *address_header = "To: ";
char *subject_header = "Subject: ";
char *x_content_name = "X-Content-Name: CalendarAppointment\n";
char *content_label = "Mime-Version: 1.0\nContent-Type: multipart/mixed;boundary=";
char divider_string[32];
int buffer_size;
int i;
boolean_t done = B_FALSE;
char *return_buffer;
/* A MIME message is a rather specialized beast. It consists of
a series of headers describing the mail message, followed by
the message, and then followed by a set of attachments.
Each attachments is separated by a magic unique string
flagging the boundaries between the attached objects. The
first header is the address list. The second header is the
subject line for the message. The third header describes the
MIME version of the message. The forth describes the type of
information within the message and the magic string used for
part divisions.
Each division is two dashes, the unique string, and a newline
The last dividing string is trailed by two more dashes. */
/* we need to generate a unique dividing string that will
not be found in any of the parts of the MIME message.
The following printf will be tried until it generates
something that isn't in any of the body parts. */
while (done != B_TRUE) {
sprintf(divider_string, "%x_%x-%x_%x-%x_%x", rand(), rand(),
rand(), rand(), rand(), rand());
done = B_TRUE;
for (i = 0; i < num_objects; i++) {
if (strstr(appointment_objects[i], divider_string) != NULL)
done = B_FALSE;
}
}
buffer_size = strlen(address_header) +
strlen(address_list) +
1 + /* newline */
strlen(subject_header) +
strlen(subject) +
1 + /* newline */
strlen(content_label) + 2 +
(num_objects + 2) * strlen(divider_string) +
/* one definition copy,
one terminating copy,
and one per body
part */
5 + strlen(divider_string) + /* empty body part */
num_objects * strlen(x_content_name) +
/* one X-Content-Name
line for each
body part */
(2 * num_objects) + 4 + /* bracketing on unique
strings */
(num_objects * 3) + 2; /* newlines...3 per
body part boundary
and 2 for the
terminating boundary */
for (i = 0; i < num_objects; i++)
buffer_size += strlen(appointment_objects[i]);
/* extra byte is added for null char */
return_buffer = (char *)calloc(buffer_size + 1, 1);
sprintf(return_buffer, "%s%s\n%s%s\n%s%s\n\n",
address_header,
address_list,
subject_header,
subject,
content_label,
divider_string);
/*
* Add an empty body part. This is a hack to get dtmail to
* display the object(s) as an attachment.
*/
strcat(return_buffer, "\n--");
strcat(return_buffer, divider_string);
strcat(return_buffer, "\n\n");
for (i = 0; i <num_objects; i++) {
strcat(return_buffer, "\n--");
strcat(return_buffer, divider_string);
strcat(return_buffer, "\n");
strcat(return_buffer, x_content_name);
strcat(return_buffer, "\n");
strcat(return_buffer, appointment_objects[i]);
}
strcat(return_buffer, "\n--");
strcat(return_buffer, divider_string);
strcat(return_buffer, "--");
strcat(return_buffer, "\n");
return(return_buffer);
}
boolean_t
appointments_to_file(CSA_session_handle target, CSA_entry_handle *appointment_list,
int num_appts,
char *file_name) {
int i;
char *entry_string;
FILE *f_ptr = fopen(file_name, "w");
if (f_ptr == NULL)
return(B_FALSE);
if (num_appts == 0)
return(B_FALSE);
fprintf(f_ptr, "DTCM Archive 1.0\n");
for (i = 0; i < num_appts; i++) {
entry_string = entry_to_attrval_string(target, appointment_list[i]);
fprintf(f_ptr, "\n-%s:string:begin\n%s%s:string:end\n\n",
CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER,
entry_string,
CSA_X_DT_ENTRY_ATTR_ENTRY_DELIMITER);
free(entry_string);
}
fclose(f_ptr);
return(B_TRUE);
}
/*
* NOTE!! These are strings used in versions 1-4 - the repeat strings read
* from a file or passed to the cm_tty_insert routine are checked against
* these strings as well as the V5 API strings.
*/
static char *periodstrings[] = {
"One Time",
"Daily",
"Weekly",
"Every Two Weeks",
"Monthly By Date",
"Yearly",
"Monthly By Weekday",
"days",
"weeks",
"months",
"other",
"Monday thru Friday",
"Mon, Wed, Fri",
"Tuesday, Thursday",
"Weekday Combo",
"Every"
};
extern void
str_to_period(char *ps, CSA_sint32 *repeat_type, int *repeat_nth) {
boolean_t compute_times = B_FALSE;
char *ps2, *ptr, *ptr2, *unit;
*repeat_type = '\0';
*repeat_nth = 0;
if (ps == NULL)
return;
if (strcasecmp(ps, periodstrings[0]) == 0)
*repeat_type = CSA_X_DT_REPEAT_ONETIME;
else if (strcasecmp(ps, periodstrings[1]) == 0)
*repeat_type = CSA_X_DT_REPEAT_DAILY;
else if (strcasecmp(ps, periodstrings[2]) == 0)
*repeat_type = CSA_X_DT_REPEAT_WEEKLY;
else if (strcasecmp(ps, periodstrings[3]) == 0)
*repeat_type = CSA_X_DT_REPEAT_BIWEEKLY;
else if (strcasecmp(ps, periodstrings[4]) == 0)
*repeat_type = CSA_X_DT_REPEAT_MONTHLY_BY_DATE;
else if (strcasecmp(ps, periodstrings[5]) == 0)
*repeat_type = CSA_X_DT_REPEAT_YEARLY;
else if (strncasecmp(ps, periodstrings[6],
strlen(periodstrings[6])) == 0)
*repeat_type = CSA_X_DT_REPEAT_MONTHLY_BY_WEEKDAY;
else if (strcasecmp(ps, periodstrings[10]) == 0)
*repeat_type = CSA_X_DT_REPEAT_OTHER;
else if (strcasecmp(ps, periodstrings[11]) == 0)
*repeat_type = CSA_X_DT_REPEAT_MON_TO_FRI;
else if (strcasecmp(ps, periodstrings[12]) == 0)
*repeat_type = CSA_X_DT_REPEAT_MONWEDFRI;
else if (strcasecmp(ps, periodstrings[13]) == 0)
*repeat_type = CSA_X_DT_REPEAT_TUETHUR;
else if (strcasecmp(ps, periodstrings[14]) == 0)
*repeat_type = CSA_X_DT_REPEAT_WEEKDAYCOMBO;
else if (strncasecmp(ps, periodstrings[15], strlen(periodstrings[15])) == 0) {
compute_times = B_TRUE;
}
else
*repeat_type = CSA_X_DT_REPEAT_ONETIME;
if ((compute_times) && (unit = strchr(ps, ' '))) {
while (*unit == ' ')
unit++;
ps2 = cm_strdup(unit);
ptr = strchr(ps2, ' ');
if (ptr != NULL) {
*ptr = '\0';
} else {
free(ps2);
return;
}
ptr++;
while (*ptr == ' ')
ptr++;
*repeat_nth = atoi(ps2);
if (strcasecmp(ptr, periodstrings[7]) == 0) {
*repeat_type = CSA_X_DT_REPEAT_EVERY_NDAY;
}
else if (strcasecmp(ptr, periodstrings[8]) == 0) {
*repeat_type = CSA_X_DT_REPEAT_EVERY_NWEEK;
}
else if (strcasecmp(ptr, periodstrings[9]) == 0) {
*repeat_type = CSA_X_DT_REPEAT_EVERY_NMONTH;
}
free(ps2);
}
}
extern char*
periodstr_from_period(CSA_sint32 repeat_type, int repeat_nth) {
static char pstr[80];
switch (repeat_type) {
case CSA_X_DT_REPEAT_ONETIME:
sprintf(pstr, "%s", periodstrings[0]);
break;
case CSA_X_DT_REPEAT_DAILY:
sprintf(pstr, "%s", periodstrings[1]);
break;
case CSA_X_DT_REPEAT_WEEKLY:
sprintf(pstr, "%s", periodstrings[2]);
break;
case CSA_X_DT_REPEAT_BIWEEKLY:
sprintf(pstr, "%s", periodstrings[3]);
break;
case CSA_X_DT_REPEAT_MONTHLY_BY_DATE:
sprintf(pstr, "%s", periodstrings[4]);
break;
case CSA_X_DT_REPEAT_YEARLY:
sprintf(pstr, "%s", periodstrings[5]);
break;
case CSA_X_DT_REPEAT_MONTHLY_BY_WEEKDAY:
sprintf(pstr, "%s", periodstrings[6]);
break;
case CSA_X_DT_REPEAT_EVERY_NDAY:
sprintf(pstr, "Every %d %s", repeat_nth, periodstrings[7]);
break;
case CSA_X_DT_REPEAT_EVERY_NWEEK:
sprintf(pstr, "Every %d %s", repeat_nth, periodstrings[8]);
break;
case CSA_X_DT_REPEAT_EVERY_NMONTH:
sprintf(pstr, "Every %d %s", repeat_nth, periodstrings[9]);
break;
case CSA_X_DT_REPEAT_OTHER:
sprintf(pstr, "%s", periodstrings[10]);
break;
case CSA_X_DT_REPEAT_MON_TO_FRI:
sprintf(pstr, "%s", periodstrings[11]);
break;
case CSA_X_DT_REPEAT_MONWEDFRI:
sprintf(pstr, "%s", periodstrings[12]);
break;
case CSA_X_DT_REPEAT_TUETHUR:
sprintf(pstr, "%s", periodstrings[13]);
break;
case CSA_X_DT_REPEAT_WEEKDAYCOMBO:
sprintf(pstr, "%s", periodstrings[14]);
break;
default:
sprintf(pstr, "Unknown repeat type");
break;
}
return pstr;
}
/*
* NOTE!! These first set of these strings are used in versions 1-4 - the
* privacy strings read from a file or passed to the cm_tty_insert routine are
* checked against these strings as well as the V5 API strings and the new
* strings.
*/
static char *privacy_strs_old[] = {
"Show Time And Text",
"Show Time Only",
"Show Nothing"
};
static char *privacy_strs[] = {
"Others See Time And Text",
"Others See Time Only",
"Others See Nothing"
};
static char *privacy_strs_411[] = {
"none",
"cm_what",
"all"
};
extern char*
privacy_str_old(int op) {
if (op >= 0 && op <= 2)
return privacy_strs_old[op];
return NULL;
}
extern char*
privacy_str(int op) {
if (op >= 0 && op <= 2)
return privacy_strs[op];
return NULL;
}
static void
init_repeat_strs(
nl_catd catd,
char **repeat_strs)
{
repeat_strs[ONE_TIME] = strdup(catgets(catd, 1, 852, "One Time"));
repeat_strs[DAILY] = strdup(catgets(catd, 1, 853, "Daily"));
repeat_strs[WEEKLY] = strdup(catgets(catd, 1, 854, "Weekly"));
repeat_strs[EVERY_TWO_WEEKS] =
strdup(catgets(catd, 1, 855, "Every Two Weeks"));
repeat_strs[MONTHLY_BY_DATE] =
strdup(catgets(catd, 1, 856, "Monthly By Date"));
repeat_strs[MONTHLY_BY_WEEKDAY] =
strdup(catgets(catd, 1, 857, "Monthly By Weekday"));
repeat_strs[YEARLY] =
strdup(catgets(catd, 1, 858, "Yearly"));
repeat_strs[MONDAY_THRU_FRIDAY] =
strdup(catgets(catd, 1, 859, "Monday Thru Friday"));
repeat_strs[MON_WED_FRI] =
strdup(catgets(catd, 1, 860, "Mon, Wed, Fri"));
repeat_strs[TUESDAY_THURSDAY] =
strdup(catgets(catd, 1, 861, "Tuesday, Thursday"));
repeat_strs[REPEAT_EVERY] =
strdup(catgets(catd, 1, 862, "Repeat Every..."));
}
extern char*
repeat_str(
nl_catd catd,
Repeat_menu_op op)
{
if (!repeat_strs[0])
init_repeat_strs(catd, repeat_strs);
if (op >= ONE_TIME && op <= REPEAT_EVERY)
return repeat_strs[op];
return NULL;
}
extern char*
privacy_str_411(int op) {
if (op >= 0 && op <= 2)
return privacy_strs_411[op];
return NULL;
}
extern char*
repeat_scope_str(
nl_catd catd,
Repeat_scope_menu_op op)
{
if (!repeat_scope_strs[0]) {
repeat_scope_strs[REPEAT_DAYS] =
strdup(catgets(catd, 1, 994, "days"));
repeat_scope_strs[REPEAT_WEEKS] =
strdup(catgets(catd, 1, 995, "weeks"));
repeat_scope_strs[REPEAT_MONTHS] =
strdup(catgets(catd, 1, 997, "months"));
}
if (op >= REPEAT_DAYS && op <= REPEAT_MONTHS)
return repeat_scope_strs[op];
return NULL;
}
extern char*
separator_str(SeparatorType op) {
if (op >= SEPARATOR_BLANK && op <= SEPARATOR_DASH)
return separator_strs[op];
return NULL;
}
extern int
timescopestring_to_tick(char *str) {
if (strcasecmp(time_scope_strs[1], str) == 0)
return hrsec;
else if (strcasecmp(time_scope_strs[2], str) == 0)
return daysec;
return minsec;
}
extern char*
time_scope_str(Time_scope_menu_op op) {
if (op >= TIME_MINS && op <= TIME_DAYS)
return time_scope_strs[op];
return NULL;
}
extern char*
time_scope_str_i18n(
nl_catd catd,
Time_scope_menu_op op)
{
if (!time_scope_strs_i18n[0]) {
time_scope_strs_i18n[TIME_MINS] =
strdup(catgets(catd, 1, 877, "Mins"));
time_scope_strs_i18n[TIME_HRS] =
strdup(catgets(catd, 1, 878, "Hrs"));
time_scope_strs_i18n[TIME_DAYS] =
strdup(catgets(catd, 1, 879, "Days"));
}
if (op >= TIME_MINS && op <= TIME_DAYS)
return time_scope_strs_i18n[op];
return NULL;
}
/*
** Determine whether or not the time passed is a valid format
*/
extern boolean_t
valid_time(Props *p, char *time_str) {
char *ptr;
int num_minutes = 0, num_colons = 0;
boolean_t after_colon = B_FALSE;
DisplayType dt = get_int_prop(p, CP_DEFAULTDISP);
for (ptr = time_str; ptr != NULL && *ptr != '\0'; ptr++) {
if (dt == HOUR12) {
if (*ptr == ':') {
after_colon = B_TRUE;
if ((++num_colons) > 1)
return B_FALSE;
if (*(ptr+1) == '\0')
return B_FALSE;
}
else if (*ptr != ' ' && (*ptr < '0' || *ptr > '9') )
return B_FALSE;
if ((after_colon) && (*ptr != ':'))
num_minutes++;
if (num_minutes > 2)
return B_FALSE;
else if (num_minutes == 2) {
++ptr;
if (strncasecmp(ptr, "am", 2) == 0 ||
strncasecmp(ptr, "pm", 2) == 0)
*ptr = '\0';
--ptr;
}
}
else if (dt == HOUR24) {
if (*ptr != ' ' && (*ptr < '0' || *ptr > '9') )
return B_FALSE;
if (++num_minutes > 4)
return B_FALSE;
}
}
if (dt == HOUR12 && ((int)atoi(time_str) > 12))
return B_FALSE;
else if ((dt == HOUR24)
&& ((int)atoi(time_str) > 2359))
return B_FALSE;
return B_TRUE;
}
/*
* This method will validate the passed appointment data.
*
* Checks made:
* Date string incorrect INVALID_DATE
* Tick for start is < 0 INVALID_START
* Tick for stop is < 0 INVALID_STOP
* Blank date MISSING_DATE
* End time but no start time MISSING_START
* Blank what with no times MISSING_WHAT
* Period = single & for > 0 REPEAT_FOR_MISMATCH
* Period != single & for = 0 REPEAT_FOR_MISMATCH
*
* Note the function pointer passed to this function - if the end time is
* before the start time, this function will be executed and should return
* B_TRUE if the appointment should be scheduled to the next day, B_FALSE if
* it should be canceled.
*/
extern Validate_op
validate_appt(nl_catd catd, Dtcm_appointment *a, char *s_buf, char *e_buf,
char *d_buf, int dur, char *w_buf, char *r_buf, char *f_buf,
boolean_t(*query)(void*), void *key_data, int version) {
Validate_op op;
if ((op = validate_dssw(a, s_buf, e_buf, d_buf, dur, w_buf, query,
key_data)) != VALID_APPT)
return op;
if ((op = validate_rfp(catd, a, r_buf, f_buf, version)) != VALID_APPT)
return op;
if ((op = validate_reminders(a)) != VALID_APPT)
return op;
return VALID_APPT;
}
extern Validate_op
validate_dssw(Dtcm_appointment *a, char *s_buf, char *e_buf, char *d_buf,
int dur, char *w_buf, boolean_t(*query)(), void *key_data) {
Tick end_tick = 0;
char buf[MAXNAMELEN];
Tick appt_time = 0;
if (blank_buf(d_buf))
return MISSING_DATE;
a->time->value->item.date_time_value = malloc(BUFSIZ);
_csa_tick_to_iso8601(0, a->time->value->item.date_time_value);
a->show_time->value->item.sint32_value = B_TRUE;
if (w_buf && w_buf[0] != '\0') {
a->what->value->item.string_value = cm_strdup(w_buf);
expand_esc_chars(a->what->value->item.string_value);
} else
a->what->value->item.string_value = NULL;
if (!blank_buf(s_buf)) {
/*
* We have something in the start buffer, is it valid?
*/
sprintf(buf, "%s %s", d_buf, s_buf);
if ((appt_time = cm_getdate(buf, NULL)) < 0)
return INVALID_START;
_csa_tick_to_iso8601(appt_time, a->time->value->item.date_time_value);
/*
* Okay, we have a valid start time - do we have a duration
* specified?
*/
if (dur > 0) {
/*
* Duration is specified - add duration to start time
*/
end_tick = appt_time + dur;
a->end_time->value->item.date_time_value = malloc(BUFSIZ);
_csa_tick_to_iso8601(end_tick, a->end_time->value->item.date_time_value);
} else if (!blank_buf(e_buf)) {
/*
* No duration, but something in the end buffer. If
* it's valid set the end tick to it's value.
*/
sprintf(buf, "%s %s", d_buf, e_buf);
if ((end_tick = cm_getdate(buf, NULL)) < 0)
return INVALID_STOP;
a->end_time->value->item.date_time_value = malloc(BUFSIZ);
_csa_tick_to_iso8601(end_tick, a->end_time->value->item.date_time_value);
} else {
/*
* No duration or end buffer - set end_tick to starting
* tick and duration to 0
*/
a->end_time->value->item.date_time_value =
strdup(a->time->value->item.date_time_value);
}
} else if (dur > 0 || !blank_buf(e_buf))
return MISSING_START;
else {
if (blank_buf(a->what->value->item.string_value))
return MISSING_WHAT;
/*
* If we're here, there was a date with no start or stop time,
* so set time to magic time (3:41 am - don't ask where that
* came from, 'cause I certainly don't know) and make sure the
* date was correct. If so, set duration to 1 minute and
* showtime to false.
*/
sprintf(buf, "%s 3:41am", d_buf);
if ((appt_time = cm_getdate(buf, NULL)) < 0)
return INVALID_DATE;
a->time->value->item.date_time_value = malloc(BUFSIZ);
_csa_tick_to_iso8601(appt_time, a->time->value->item.date_time_value);
end_tick = appt_time + minsec;
a->end_time->value->item.date_time_value = malloc(BUFSIZ);
_csa_tick_to_iso8601(end_tick, a->end_time->value->item.date_time_value);
a->show_time->value->item.sint32_value = B_FALSE;
}
/*
* Finally, if the ending tick is before the starting tick, execute the
* passed function which should return B_TRUE if we should schedule
* this into the next day and B_FALSE if not.
*
* This allows for methods calling this function to be UI oriented or
* command line oriented as they can "query" the user appropriately.
*/
if (end_tick < appt_time) {
if ((*query)(key_data) == B_TRUE) {
while (end_tick < appt_time)
end_tick += daysec;
_csa_tick_to_iso8601(end_tick, a->end_time->value->item.date_time_value);
}
else
return CANCEL_APPT;
}
return VALID_APPT;
}
extern Validate_op
validate_reminders(Dtcm_appointment *a) {
return VALID_APPT;
}
extern Validate_op
validate_rfp(
nl_catd catd,
Dtcm_appointment *a,
char *r_buf,
char *f_buf,
int version)
{
int repeat_nth,
repeat_wk = -1,
repeat_every = 0,
repeat_forever = False;
CSA_sint32 repeat_type;
CSA_uint32 repeat_times = 0;
char rule_buf[32];
if (r_buf) {
str_to_period(r_buf, &repeat_type, &repeat_nth);
if (repeat_type == CSA_X_DT_REPEAT_MONTHLY_BY_WEEKDAY) {
r_buf += strlen(periodstrings[6]);
while(*r_buf != '\0' && !isspace((u_char)*r_buf))
++r_buf;
while(*r_buf != '\0' && isspace((u_char)*r_buf))
++r_buf;
if (strncasecmp(r_buf, "last", 4) != 0)
repeat_wk = atoi(r_buf);
}
}
if (f_buf) {
/* Repeat forever is represented by either:
f_buf == ``forever'' or
f_buf == ``0''.
If it is a CSA_X_DT_REPEAT_ONETIME then
f_buf == ``0''.
*/
if (strcasecmp(f_buf, catgets(catd, 1, 876, "forever")) == 0) {
repeat_times = CSA_X_DT_DT_REPEAT_FOREVER;
repeat_forever = True;
} else {
repeat_times = atoi(f_buf);
if (repeat_times == CSA_X_DT_DT_REPEAT_FOREVER &&
repeat_type != CSA_X_DT_REPEAT_ONETIME)
repeat_forever = True;
}
}
/* If it is a onetime event then it cannot repeat.
* If it is a repeating event then repeat_times must be greater
* than 0 unless it is supposed to repeat forever.
*/
if (((repeat_type != CSA_X_DT_REPEAT_ONETIME) &&
(repeat_times == 0) && (repeat_forever != True)) ||
((repeat_type == CSA_X_DT_REPEAT_ONETIME) &&
((repeat_times != 0) || (repeat_forever == True))))
return REPEAT_FOR_MISMATCH;
if (a->repeat_type && a->repeat_type->value)
a->repeat_type->value->item.sint32_value = repeat_type;
if (a->repeat_times && a->repeat_times->value)
a->repeat_times->value->item.uint32_value = repeat_times;
if (a->repeat_interval && a->repeat_interval->value)
a->repeat_interval->value->item.uint32_value = repeat_nth;
if (a->repeat_week_num && a->repeat_week_num->value)
a->repeat_week_num->value->item.sint32_value = repeat_wk;
/* If we are less than data version 4, we're done. */
if (version < DATAVER4)
return VALID_APPT;
/* Data version 4 appts use a recurrence rule. */
memset (rule_buf, 0, 32);
switch(repeat_type) {
case CSA_X_DT_REPEAT_ONETIME:
case CSA_X_DT_REPEAT_DAILY:
strcpy(rule_buf, "D1 ");
break;
case CSA_X_DT_REPEAT_WEEKLY:
strcpy(rule_buf, "W1 ");
break;
case CSA_X_DT_REPEAT_BIWEEKLY:
strcpy(rule_buf, "W2 ");
break;
case CSA_X_DT_REPEAT_MONTHLY_BY_DATE:
strcpy(rule_buf, "MD1 ");
break;
case CSA_X_DT_REPEAT_MONTHLY_BY_WEEKDAY: {
int wk;
Tick tick = 0;
if (a && a->time && a->time->value) {
_csa_iso8601_to_tick(
a->time->value->item.date_time_value,
&tick);
}
/*
* The current behavior of cm/dtcm is that if an appt is
* scheduled for the 5 wk of the month, it repeats on the
* last week of the month.
*/
if (tick && weekofmonth(tick, &wk) && wk == 5)
sprintf(rule_buf, "MP1 1- %s ", dow_str(tick));
else
strcpy(rule_buf, "MP1 ");
break;
}
case CSA_X_DT_REPEAT_YEARLY:
strcpy(rule_buf, "YM1 ");
break;
case CSA_X_DT_REPEAT_MON_TO_FRI:
strcpy(rule_buf, "W1 MO TU WE TH FR ");
break;
case CSA_X_DT_REPEAT_MONWEDFRI:
strcpy(rule_buf, "W1 MO WE FR ");
break;
case CSA_X_DT_REPEAT_TUETHUR:
strcpy(rule_buf, "W1 TU TH ");
break;
case CSA_X_DT_REPEAT_EVERY_NDAY:
sprintf(rule_buf, "D%d ", repeat_nth);
repeat_every = True;
break;
case CSA_X_DT_REPEAT_EVERY_NWEEK:
sprintf(rule_buf, "W%d ", repeat_nth);
repeat_every = True;
break;
case CSA_X_DT_REPEAT_EVERY_NMONTH:
sprintf(rule_buf, "MD%d ", repeat_nth);
repeat_every = True;
break;
default:
return CANCEL_APPT;
}
/* If the for buffer is NULL then we default to repeating one time.
* If the repeat_type is onetime then we default repeat times to
* one time.
*/
if (!f_buf || repeat_type == CSA_X_DT_REPEAT_ONETIME)
repeat_times = 1;
if (repeat_times == CSA_X_DT_DT_REPEAT_FOREVER) {
strcat(rule_buf, "#0");
} else {
char buf[16];
if (repeat_every) {
int duration;
if (repeat_times % repeat_nth)
duration = 1 + repeat_times/repeat_nth;
else
duration = repeat_times/repeat_nth;
sprintf(buf, "#%d", duration);
} else
sprintf(buf, "#%ld", repeat_times);
strcat(rule_buf, buf);
}
a->recurrence_rule->value->item.string_value = strdup(rule_buf);
return VALID_APPT;
}