#include #include #include #include #include #include "global.h" #include "custom.h" /* for date FORMAT strings */ #include "calendar.h" #include "sqlite3.h" #include "manhat-lib/shared_cs_util.h" #include "manhat-lib/shared_util.h" #include "manhat-lib/shared_calendar_util.h" #include "manhat-lib/shared_super_util.h" #include "manhat-lib/shared_file_util.h" #include "manhat-lib/shared_strtrm.h" int check_end_date( int year, int month, int today); int check_end_time(int year, int month, int today); void read_parameters ( char *course, char *key) { cs_get_optional_parameter("crs", course, MAX_PATH); cs_get_required_parameter("id", key, MAX_KEY); } void set_end_hour( int error) { int e_hour; char *oper; oper = hdf_get_value(global_cgi->hdf, "Query.oper", 0); e_hour = hdf_get_int_value(global_cgi->hdf, "Query.e_hour", 0); if(oper && strlen(oper) ) { if(e_hour == 0 || error ==0) { cs_set_int_value("e_hourstar", 1); cs_set_int_value("error_endtime", 1); } } else cs_set_int_value("e_hourstar", 0); cs_set_int_value("end_event_hour", e_hour); } void set_end_min( int error) { int e_min; char *oper; oper = hdf_get_value(global_cgi->hdf, "Query.oper", 0); e_min = hdf_get_int_value(global_cgi->hdf, "Query.e_min", -1); if(oper && strlen(oper) ) { if(e_min == -1 || error ==0) { cs_set_int_value("e_minstar", 1); cs_set_int_value("error_endtime", 1); } else cs_set_int_value("e_minstar", 0); } cs_set_int_value("end_event_min", e_min); } void set_am_pm(int error) { char *oper; int am; oper = hdf_get_value(global_cgi->hdf, "Query.oper", 0); am = hdf_get_int_value(global_cgi->hdf, "Query.e_am", 0); if(oper && strlen(oper) ) { if(!am || error ==0) { cs_set_int_value( "e_amstar", 1); cs_set_int_value( "error_endtime", 1); } else cs_set_int_value( "e_amstar", 0); } if(am == 1) cs_set_value("end_event_am", "AM"); else if(am == 2) cs_set_value("end_event_am", "PM"); } void set_general_data( const char *prefix, char *query, char *value) { char *oper =0; char name[MAX_PATH +1]; int max =0; if(!strcmp(prefix, "title")) max = MAX_CALENDAR_TITLE; else max = MAX_CALENDAR_DESC; oper = hdf_get_value(global_cgi->hdf, "Query.oper", 0); snprintf(name, MAX_PATH +1, "%sstar", prefix); if(oper && strlen(oper)) { if( !value || (value && !strlen(value)) || (value && strlen(value) > max )) { cs_set_int_value( name, 1); cs_set_int_value("error_title", 0); } else cs_set_int_value( name, 0); } else cs_set_int_value( name, 0); } void set_enddate_data(int error, int year, int month, int day) { char *oper =0, *p =0; char name[MAX_PATH +1]; char de_value[MAX_PATH +1]; oper = hdf_get_value(global_cgi->hdf, "Query.oper", 0); p = hdf_get_value(global_cgi->hdf, "Query.enddate", 0); snprintf(name, MAX_PATH +1, "%sstar", "enddate"); if(oper && strlen(oper)) { if(!p || error == 0) { cs_set_int_value( name, 1); cs_set_int_value( "error_enddate", 4); } else cs_set_int_value( name, 0); snprintf(name, MAX_PATH +1, "%svalue", "enddate"); cs_set_value( name, p); } else { cs_set_int_value( name, 0); snprintf(name, MAX_PATH +1, "%svalue", "enddate"); snprintf(de_value, MAX_PATH +1, "%s%d/%s%d/%d", month <10 ? "0":"", month, day<10 ? "0":"",day,year); cs_set_value( name, de_value); } } void set_icon_freq( const char *prefix, char *query, int max) { char name[MAX_PATH +1]; char *oper =0; int freq =0, flag =0; if(!strcmp(prefix, "icon")) flag =1; oper = hdf_get_value(global_cgi->hdf, "Query.oper", 0); freq = hdf_get_int_value(global_cgi->hdf, query, 0); if(oper && strlen(oper)) { snprintf(name, MAX_PATH +1, "%sstar", prefix); if(freq == 0) { cs_set_int_value( name, 1); snprintf(name, MAX_PATH +1, "error_%s", prefix); if(flag == 1) cs_set_int_value( name, 3); else cs_set_int_value(name, 2); } else cs_set_int_value( name, 0); } if(flag) cs_set_int_value("icon", freq); else cs_set_int_value("freq", freq); } void set_layout_data(int today, int year, int month, const char *crs, const char *key, char *title, char *desc, SESSION *user) { char url[MAX_PATH +1]; int i, check =1; int freq; set_general_data( "title", "Query.title", title); set_general_data( "desc", "Query.desc", desc); if(user->group == FACULTY) { freq = hdf_get_int_value(global_cgi->hdf, "Query.freq", 0); /* freq = 1 mean event will happen once */ if(freq != 0 && freq != 1) check = check_end_date( year, month, today); set_enddate_data( check, year, month, today); i = check_end_time( year, month, today); set_end_hour( i); set_end_min( i); set_am_pm( i); set_icon_freq( "freq", "Query.freq", 6); snprintf(url, MAX_PATH +1, "%s?crs=%s&id=%s&year=%d&month=%d&today=%d", CALENDAR_PICK,crs,key, year, month, today); cs_set_value("pickdateurl", url); } set_icon_freq( "icon", "Query.icon", 4); } void set_para( CONFIG_STRUCT *conf, const char *crs, const char *key, char *title, char *desc, SESSION *user) { int year, month, today, hour, min; year = hdf_get_int_value (global_cgi->hdf, "Query.year", 0); month = hdf_get_int_value (global_cgi->hdf, "Query.month", 0); today = hdf_get_int_value (global_cgi->hdf, "Query.today", 0); hour = hdf_get_int_value (global_cgi->hdf, "Query.hour", 0); min = hdf_get_int_value (global_cgi->hdf, "Query.min", -1); if(!year || !month || !today || !hour || min == -1) cs_critical_error(ERR_PARAM_MISSING, "year, month, today, hour, min"); set_selected_time(today, year, month); /* shared_cs_util.c */ set_hour_min(hour, min, "hourmin", "hourmin_am"); cs_set_int_value("titlelen", MAX_CALENDAR_TITLE); set_layout_data(today, year, month, crs, key, title, desc, user); if(user->group == FACULTY) cs_set_int_value( "faculty", 1); } int count_slash(char *p) { int i, len, count =0; len = strlen(p); for(i =0; ihdf, "Query.enddate", ""); len = strlen(p); if(!len) return 0; if(len != 10) return 0; if(!count_slash(p)) return 0; parse_end_date(p, e_year, e_month, e_day); return 1; } void init_tm_struct(struct tm *mytime, int year, int month, int today, int hour, int min) { mytime->tm_year = year -1900; mytime->tm_mon = month -1; mytime->tm_mday = today; mytime->tm_hour = hour; mytime->tm_min = min; mytime->tm_sec =0; mytime->tm_isdst = -1; } int check_end_date( int s_year, int s_month, int s_day) { int e_year, e_month, e_day; int s; struct tm s_tm, e_tm; time_t s_time, e_time; s = get_end_date( &e_year, &e_month, &e_day); if(s ==0) return 0; if( e_month <= 0 || e_month > 12) return 0; if(e_day > get_month_days(e_month, e_year)) return 0; init_tm_struct(&e_tm, e_year, e_month, e_day, 10, 10); e_time = mktime(&e_tm); init_tm_struct(&s_tm, s_year, s_month, s_day, 10, 10); s_time = mktime(&s_tm); if(e_time <= s_time) return 0; return 1; } int get_end_time( int *e_hour, int *e_min) { int e_am; *e_hour = hdf_get_int_value(global_cgi->hdf, "Query.e_hour", 0); *e_min = hdf_get_int_value(global_cgi->hdf, "Query.e_min", 0); e_am = hdf_get_int_value(global_cgi->hdf, "Query.e_am", 0); if(*e_hour ==0 || *e_min == -1 || e_am ==0) return 0; if(e_am ==2) /* pm */ { if(*e_hour != 12) *e_hour += 12; } else { if(*e_hour == 12) *e_hour =0; } return 1; } int check_end_time( int year, int month, int today) { int s_hour, s_min, e_hour, e_min; struct tm s_tm, e_tm; time_t s_time, e_time; s_hour = hdf_get_int_value(global_cgi->hdf, "Query.hour", 0); s_min = hdf_get_int_value(global_cgi->hdf, "Query.min", 0); if(!get_end_time( &e_hour, &e_min)) return 0; init_tm_struct(&s_tm, year, month, today, s_hour, s_min); s_time = mktime(&s_tm); init_tm_struct(&e_tm, year, month, today, e_hour, e_min); e_time = mktime(&e_tm); if(e_time <= s_time) return 0; return 1; } int get_week_day_int(int year, int month, int day) { struct tm mytime; mytime.tm_mon = month -1; mytime.tm_mday = day; mytime.tm_year = year -1900; mytime.tm_hour = 8; mytime.tm_min = 30; mytime.tm_sec = 0; mytime.tm_wday = 0; mytime.tm_isdst = -1; mktime(&mytime); return mytime.tm_wday; } int verify_event_on_MWF() { int wday = -1; int year, month, day; year = hdf_get_int_value(global_cgi->hdf, "Query.year", 0); month = hdf_get_int_value(global_cgi->hdf, "Query.month", 0); day = hdf_get_int_value(global_cgi->hdf, "Query.today", 0); wday = get_week_day_int(year, month, day); if(wday == 1 || wday == 3 || wday == 5) return 1; return 0; } int verify_event_on_TTh() { int wday = -1; int year, month, day; year = hdf_get_int_value(global_cgi->hdf, "Query.year", 0); month = hdf_get_int_value(global_cgi->hdf, "Query.month", 0); day = hdf_get_int_value(global_cgi->hdf, "Query.today", 0); wday = get_week_day_int(year, month, day); if(wday == 2 || wday == 4) return 1; return 0; } int verify_MWF_TTh( int frequency) { if(frequency == 4) return verify_event_on_MWF(); else if (frequency == 5) return verify_event_on_TTh(); return 0; } int is_all_right( char **title, char **desc, SESSION *user) { int check, year, month, today; int len; year = hdf_get_int_value (global_cgi->hdf, "Query.year", 0); month = hdf_get_int_value (global_cgi->hdf, "Query.month", 0); today = hdf_get_int_value (global_cgi->hdf, "Query.today", 0); hdf_get_copy (global_cgi->hdf, "Query.title", title, ""); strtrm(*title); cs_set_value( "titlevalue", *title); len = strlen(*title); if(!len || len > MAX_CALENDAR_TITLE) return 0; hdf_get_copy (global_cgi->hdf, "Query.desc", desc, ""); strtrm(*desc); cs_set_value("descvalue", *desc); len = strlen(*desc); if(!len || len > MAX_CALENDAR_DESC) return 0; if(user->group == FACULTY) { check= hdf_get_int_value(global_cgi->hdf, "Query.e_hour", 0); if(check ==0) return 0; check= hdf_get_int_value(global_cgi->hdf, "Query.e_min", -1); if(check == -1) return 0; check= hdf_get_int_value(global_cgi->hdf, "Query.e_am", 0); if(check ==0) return 0; if(!check_end_time( year, month, today)) return 0; check= hdf_get_int_value(global_cgi->hdf, "Query.freq", 0); if(check ==0) return 0; if(check !=1) { if(check == 4 || check == 5) { if(!verify_MWF_TTh( check)) return check; } if(!check_end_date( year, month, today)) return 0; } } check = hdf_get_int_value(global_cgi->hdf, "Query.icon", 0); if(check ==0) return 0; return 1; } void get_shourmin_ehourmin_icon( int *s_hour, int *s_min, int *e_hour, int *e_min, int *icon) { int e_am; *s_hour = hdf_get_int_value(global_cgi->hdf, "Query.hour", 0); *s_min = hdf_get_int_value(global_cgi->hdf, "Query.min", 0); *e_hour = hdf_get_int_value(global_cgi->hdf, "Query.e_hour", 0); *e_min = hdf_get_int_value(global_cgi->hdf, "Query.e_min", 0); e_am = hdf_get_int_value(global_cgi->hdf, "Query.e_am", 0); if(e_am == 2) { if(*e_hour != 12) *e_hour += 12; } else if(e_am ==1) { if(*e_hour == 12) *e_hour = 0; } *icon = hdf_get_int_value(global_cgi->hdf, "Query.icon", 0); } void get_year_month_today( int *year, int *month, int *day) { *year = hdf_get_int_value(global_cgi->hdf, "Query.year", 0); *month = hdf_get_int_value(global_cgi->hdf, "Query.month", 0); *day = hdf_get_int_value(global_cgi->hdf, "Query.today", 0); } EVENT_NODE * initialize_event_node(int s_hour, int s_min, int e_hour, int e_min, int icon, int new_day, int new_month, int new_year, SESSION *user, const char *title, const char *desc) { EVENT_NODE *ptr; ptr = (EVENT_NODE *)malloc(sizeof(EVENT_NODE)); strncpy(ptr->event.username, user->username, MAX_USERNAME +1); strncpy(ptr->event.realname, user->realname, MAX_NAME +1 ); strncpy(ptr->event.title, title, MAX_CALENDAR_TITLE +1 ); strncpy(ptr->event.desc, desc, MAX_CALENDAR_DESC +1); ptr->event.year = new_year; ptr->event.month =new_month; ptr->event.day = new_day; ptr->event.s_hour = s_hour; ptr->event.s_min = s_min; if(user->group == FACULTY) { ptr->event.e_hour = e_hour; ptr->event.e_min = e_min; } else { ptr->event.e_hour = s_hour; ptr->event.e_min = s_min; } ptr->event.icon = icon; if(user->group == FACULTY) strncpy(ptr->event.category,CATEGORY_1, MAX_CATEGORY +1); else strncpy(ptr->event.category,CATEGORY_2, MAX_CATEGORY +1); ptr->next =0; return ptr; } int get_new_day(int *start_day, int *start_month, int *start_year, int days, int end_year, int end_month, int end_day, int add_days) { *start_day += add_days; if(*start_day > days) { *start_month += 1; *start_day = *start_day - days; if(*start_month > 12) { *start_year += 1; *start_month = 1; } } if(*start_year < end_year) return 1; else if(*start_month < end_month && *start_year == end_year) return 1; else if(*start_month == end_month && *start_year == end_year) { if(*start_day <= end_day) return 1; } return 0; } EVENT_NODE * event_weekly_expend( SESSION *user, const char *title, const char *desc) { int start_day; int start_month; int start_year; int s_hour, s_min, e_hour, e_min, icon; int e_year, e_month, e_day; int days; EVENT_NODE *head =0, *current=0, *ptr; days = get_month_days(start_month, start_year); get_year_month_today( &start_year, &start_month, &start_day); days = get_month_days(start_month, start_year); get_shourmin_ehourmin_icon( &s_hour, &s_min, &e_hour, &e_min, &icon); get_end_date( &e_year, &e_month, &e_day); head = initialize_event_node(s_hour, s_min, e_hour, e_min, icon, start_day, start_month, start_year, user, title, desc); current = head; while(get_new_day(&start_day, &start_month, &start_year, days, e_year, e_month, e_day, 7)) { ptr = initialize_event_node(s_hour, s_min, e_hour, e_min, icon, start_day, start_month, start_year, user, title, desc); current->next = ptr; current = ptr; days = get_month_days(start_month, start_year); } return head; } int get_new_month(int *start_day, int *start_month, int *start_year, int end_year, int end_month, int end_day) { int days; *start_month = *start_month +1; if(*start_month > 12) { *start_month = 1; *start_year += 1; } days = get_month_days(*start_month, *start_year); if(*start_day > days) *start_day = days; if(*start_year < end_year) return 1; else if(*start_month < end_month && *start_year == end_year) return 1; else if(*start_month == end_month && *start_year == end_year) { if(*start_day <= end_day) return 1; } return 0; } EVENT_NODE * event_monthly_expend( SESSION *user, const char *title, const char *desc) { int start_day; int start_month; int start_year; int s_hour, s_min, e_hour, e_min, icon; int e_year, e_month, e_day; EVENT_NODE *head =0, *current=0, *ptr; get_year_month_today( &start_year, &start_month, &start_day); get_shourmin_ehourmin_icon( &s_hour, &s_min, &e_hour, &e_min, &icon); get_end_date( &e_year, &e_month, &e_day); head = initialize_event_node(s_hour, s_min, e_hour, e_min, icon, start_day, start_month, start_year, user, title, desc); current = head; while(get_new_month(&start_day, &start_month, &start_year, e_year, e_month, e_day)) { ptr = initialize_event_node(s_hour, s_min, e_hour, e_min, icon, start_day, start_month, start_year, user, title, desc); current->next = ptr; current = ptr; } return head; } EVENT_NODE * event_MWF_expend( SESSION *user, const char *title, const char *desc) { int wday = -1; int start_day; int start_month; int start_year; int days; int s_hour, s_min, e_hour, e_min, icon; int e_year, e_month, e_day; EVENT_NODE *head =0, *current=0, *ptr; get_year_month_today( &start_year, &start_month, &start_day); get_shourmin_ehourmin_icon( &s_hour, &s_min, &e_hour, &e_min, &icon); get_end_date( &e_year, &e_month, &e_day); days = get_month_days(start_month, start_year); head = initialize_event_node(s_hour, s_min, e_hour, e_min, icon, start_day, start_month, start_year, user, title, desc); current = head; wday = get_week_day_int(start_year, start_month, start_day); while(get_new_day(&start_day, &start_month, &start_year, days, e_year, e_month, e_day, wday == 5 ? 3 :2)) { ptr = initialize_event_node(s_hour, s_min, e_hour, e_min, icon, start_day, start_month, start_year, user, title, desc); current->next = ptr; current = ptr; days = get_month_days(start_month, start_year); wday = get_week_day_int(start_year, start_month, start_day); } return head; } EVENT_NODE * event_TTh_expend( SESSION *user, const char *title, const char *desc) { int wday = -1; int start_day; int start_month; int start_year; int days; int s_hour, s_min, e_hour, e_min, icon; int e_year, e_month, e_day; EVENT_NODE *head =0, *current=0, *ptr; get_year_month_today( &start_year, &start_month, &start_day); get_shourmin_ehourmin_icon( &s_hour, &s_min, &e_hour, &e_min, &icon); get_end_date( &e_year, &e_month, &e_day); days = get_month_days(start_month, start_year); head = initialize_event_node(s_hour, s_min, e_hour, e_min, icon, start_day, start_month, start_year, user, title, desc); current = head; wday = get_week_day_int(start_year, start_month, start_day); while(get_new_day(&start_day, &start_month, &start_year, days, e_year, e_month, e_day, wday == 2 ? 2 :5)) { ptr = initialize_event_node(s_hour, s_min, e_hour, e_min, icon, start_day, start_month, start_year, user, title, desc); current->next = ptr; current = ptr; days = get_month_days(start_month, start_year); wday = get_week_day_int(start_year, start_month, start_day); } return head; } EVENT_NODE * event_once( SESSION *user, const char *title, const char *desc) { EVENT_NODE *ptr; int s_hour, s_min, e_hour, e_min, icon, year, month, day; get_shourmin_ehourmin_icon( &s_hour, &s_min, &e_hour, &e_min, &icon); get_year_month_today( &year, &month, &day); ptr = initialize_event_node(s_hour, s_min, e_hour, e_min, icon, day, month, year, user, title, desc); return ptr; } EVENT_NODE * expend_event( SESSION *user, const char *title, const char *desc) { EVENT_NODE *head =0; int freq; if(user->group == FACULTY) freq = hdf_get_int_value(global_cgi->hdf, "Query.freq", 0); else freq =1; if(freq == 1) head = event_once( user, title, desc); else if(freq == 2) head = event_weekly_expend( user, title, desc); else if(freq == 3) head = event_monthly_expend( user, title, desc); else if(freq == 4) head = event_MWF_expend( user, title, desc); else if(freq ==5) head = event_TTh_expend( user, title, desc); return head; } void create_db(const char *path, int id) { int ret; char *stmt; sqlite3 *db; ret = sqlite3_open(path, &db); if(ret) cs_critical_error(ERR_DB_OPEN, "create_db"); ret = sqlite3_exec(db, "create table event( Id integer primary key, username varchar(20), realname varchar(35), title varchar(30), desc text, year integer, month integer, day integer, shour integer, smin integer, ehour integer, emin integer, icon integer, category varchar(25) )", 0,0,0); if(ret) { sqlite3_close(db); cs_critical_error(ERR_DB_CREATE_ERROR, "create_db"); } stmt = sqlite3_mprintf( "insert into event values(%d, '%q', '%q', '%q', '%q', 0,0,0,0,0,0,0,0, '%q')", id, "dummy", "dummy", "dummy", "dummy", "dummy"); if(stmt) { ret = sqlite3_exec(db, stmt, 0,0,0); sqlite3_free(stmt); } if(ret) { sqlite3_close(db); cs_critical_error(ERR_DB_INSERT, "create_db"); } sqlite3_close(db); } int get_event_id() { int seed, i; seed = time(NULL); srand(seed); i = 1000+(int)(9000.0 *rand()/(RAND_MAX + 1000.0)); return i; } void insert_db(CONFIG_STRUCT *conf, EVENT_NODE *head) { EVENT_NODE *ptr; char path[MAX_PATH +1]; sqlite3 *db; int ret; char *stmt; snprintf(path, MAX_PATH +1, "%s%s/%s", conf->course_path, CALENDAR_DIR, EVENT_DB); ret = sqlite3_open(path, &db); if(ret) cs_critical_error(ERR_DB_OPEN, "insert_db"); ret = sqlite3_exec(db, "Begin transaction insert_calendar_event", 0,0,0); for(ptr = head; ptr; ptr = ptr->next) { stmt = sqlite3_mprintf( "insert into event values(NULL, '%q', '%q', '%q', '%q',%d,%d,%d,%d,%d,%d,%d,%d, '%q')", ptr->event.username, ptr->event.realname, ptr->event.title, ptr->event.desc, ptr->event.year, ptr->event.month, ptr->event.day, ptr->event.s_hour, ptr->event.s_min, ptr->event.e_hour, ptr->event.e_min, ptr->event.icon, ptr->event.category); if(stmt) { ret =sqlite3_exec(db, stmt, 0,0,0); sqlite3_free(stmt); if(ret) { ret = sqlite3_exec(db, "rollback transaction insert_calendar_event", 0,0,0); sqlite3_close(db); cs_critical_error(ERR_DB_INSERT, "insert_db"); } } } ret = sqlite3_exec(db,"commit transaction insert_calendar_event", 0,0,0); ret = sqlite3_exec(db,"end transaction insert_calendar_event", 0,0,0); sqlite3_close(db); } void save_data_to_db(CONFIG_STRUCT *conf, SESSION *user, const char *title, const char *desc) { char path[MAX_PATH +1]; EVENT_NODE *head; int id; snprintf(path, MAX_PATH +1, "%s%s", conf->course_path, CALENDAR_DIR); if(!file_exists(path)) { mkdir(path, 0770); snprintf(path, MAX_PATH +1, "%s%s/%s", conf->course_path, CALENDAR_DIR, EVENT_DB); id = get_event_id(); create_db(path, id); } head = expend_event( user, title, desc); insert_db(conf, head); free_event_list(head); } int main () { char course[MAX_PATH +1]; /* from crs=? command line */ char key[MAX_KEY +1]; /* from id=? command line */ char *title; char *desc; int action_flag; SESSION user; CONFIG_STRUCT conf; *course = '\0'; cs_cgi_init(); read_parameters ( course, key); if(!strlen(course)) { validate_super_key(key, &user); /* shared_super_util.c */ mk_conf(&user, &conf); /* shared_calendar.c */ } else { read_configuration_file (course, &conf); /* shared_util.c */ validate_key (key, &user, &conf); /* shared_util.c */ } if(user.group == STUDENT) cs_critical_error(ERR_REQUEST_DENIED, ""); cs_set_course_info( &conf); action_flag = is_all_right( &title, &desc, &user); if(action_flag == 4 || action_flag ==5) { if(action_flag == 4) cs_set_value( "freq_error_code", "MWF"); else cs_set_value( "freq_error_code", "TTh"); cs_cgi_display ("calendar_event_edit", 1); } else if(action_flag ==0) { set_para( &conf, course, key, title, desc, &user); cs_cgi_display ("calendar_event_edit", 1); } else { save_data_to_db(&conf, &user, title, desc); cs_set_int_value("print_success", 1); cs_cgi_display ("calendar_event_edit", 1); } cs_cgi_destroy(); if(title) free(title); if(desc) free(desc); return 0; /* exit successfully */ }