commit f6e133c2c6b3a2c8139560a5d1978d4541eb1ac6 Author: Stefan Pranger Date: Sun Jun 27 14:03:11 2021 +0200 initial commit with some parsing already working diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..0f323b8 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,19 @@ +cmake_minimum_required(VERSION 2.8 FATAL_ERROR) +project(notification-daemon CXX) +# get all *.cpp files recursively +#file(GLOB_RECURSE SRC_FILES ${PROJECT_SOURCE_DIR}/*.cpp) +#file(GLOB_RECURSE HEADER_FILES ${PROJECT_SOURCE_DIR}/*.h) +include_directories(${PROJECT_SOURCE_DIR}) +#add_executable(notification-daemon ${SRC_FILES} ${HEADER_FILES}) +add_executable(notification-daemon + calendar-daemon + util/calendar_parsing.cpp + util/notifications.h + ical/Alarm.cpp + ical/Event.cpp + ical/IcalObject.h + io/debug.cpp + ) +add_definitions(-DLOG_DEBUG) +target_link_libraries(notification-daemon ical boost_system boost_filesystem) +target_compile_options(notification-daemon PRIVATE -g) diff --git a/calendar-daemon.cpp b/calendar-daemon.cpp new file mode 100644 index 0000000..38fd7ca --- /dev/null +++ b/calendar-daemon.cpp @@ -0,0 +1,108 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +const int MINUTE = 1000000; + +void update_database() { + std::vector objects = util::parse_cal_dir(); +} + +void do_heartbeat() +{ + util::notify("Testing", ""); + update_database(); +} + +// For security purposes, we don't allow any arguments to be passed into the daemon +int main(void) +{ + // Define variables + //pid_t pid, sid; + + //// Fork the current process + //pid = fork(); + //// The parent process continues with a process ID greater than 0 + //if(pid > 0) + //{ + // exit(EXIT_SUCCESS); + //} + //// A process ID lower than 0 indicates a failure in either process + //else if(pid < 0) + //{ + // exit(EXIT_FAILURE); + //} + //// The parent process has now terminated, and the forked child process will continue + //// (the pid of the child process was 0) + + //// Since the child process is a daemon, the umask needs to be set so files and logs can be written + //umask(0); + + //// Open system logs for the child process + //openlog("daemon-named", LOG_NOWAIT | LOG_PID, LOG_USER); + //syslog(LOG_NOTICE, "Successfully started daemon-name"); + + //// Generate a session ID for the child process + //sid = setsid(); + //// Ensure a valid SID for the child process + //if(sid < 0) + //{ + // // Log failure and exit + // syslog(LOG_ERR, "Could not generate session ID for child process"); + + // // If a new session ID could not be generated, we must terminate the child process + // // or it will be orphaned + // exit(EXIT_FAILURE); + //} + + //// Change the current working directory to a directory guaranteed to exist + //if((chdir("/")) < 0) + //{ + // // Log failure and exit + // syslog(LOG_ERR, "Could not change working directory to /"); + + // // If our guaranteed directory does not exist, terminate the child process to ensure + // // the daemon has not been hijacked + // exit(EXIT_FAILURE); + //} + + //// A daemon cannot use the terminal, so close standard file descriptors for security reasons + //close(STDIN_FILENO); + //close(STDOUT_FILENO); + //close(STDERR_FILENO); + + // Daemon-specific intialization should go here + const int SLEEP_INTERVAL = 5 * MINUTE; + + // Enter daemon loop + while(1) + { + // Execute daemon heartbeat, where your recurring activity occurs + do_heartbeat(); + exit(0); + + // Sleep for a period of time + sleep(SLEEP_INTERVAL); + } + + // Close system logs for the child process + //syslog(LOG_NOTICE, "Stopping daemon-name"); + //closelog(); + + // Terminate the child process when the daemon completes + exit(EXIT_SUCCESS); +} diff --git a/ical/Alarm.cpp b/ical/Alarm.cpp new file mode 100644 index 0000000..8640509 --- /dev/null +++ b/ical/Alarm.cpp @@ -0,0 +1,44 @@ +#include +#include + +namespace ical { + Alarm::Alarm(icalcomponent* alarm_component) { + parse(alarm_component); + } + + void Alarm::parse(icalcomponent* alarm_component) { + parse_action(alarm_component); + parse_description(alarm_component); + parse_trigger(alarm_component); + DEBUG << "\n" << print(); + } + + void Alarm::parse_action(icalcomponent* alarm_component) { + DEBUG << icalproperty_get_value_as_string(icalcomponent_get_first_property(alarm_component, ICAL_ACTION_PROPERTY)); + } + + void Alarm::parse_description(icalcomponent* alarm_component) { + // TODO Error Handling + description = icalproperty_get_value_as_string(icalcomponent_get_first_property(alarm_component, ICAL_DESCRIPTION_PROPERTY)); + } + + void Alarm::parse_trigger(icalcomponent* alarm_component) { + icalproperty* trigger_property = icalcomponent_get_first_property(alarm_component, ICAL_TRIGGER_PROPERTY); + struct icaltriggertype tr; + if(trigger_property != 0) { + tr = icalproperty_get_trigger(trigger_property); + if (!icaltime_is_null_time(tr.time)) { + trigger = ical::trigger_type::ABSOLUTE; + } else { + trigger = ical::trigger_type::RELATIVE; + } + } + } + + std::string Alarm::print() { + std::string s = ""; + s += "DESCRIPTION: " + description + "\n"; + s += trigger == ical::trigger_type::ABSOLUTE ? "ABSOLUTE: " : "RELATIVE: "; + return s; + } +} diff --git a/ical/Alarm.h b/ical/Alarm.h new file mode 100644 index 0000000..b61bd64 --- /dev/null +++ b/ical/Alarm.h @@ -0,0 +1,32 @@ +#pragma once + +#include +#include +#include + +namespace ical { + enum class action { + DISPLAY, AUDIO, EMAIL, PROC + }; + + enum class trigger_type { + ABSOLUTE, RELATIVE + }; + + class Alarm { + public: + Alarm(icalcomponent* alarm_component); + + void parse(icalcomponent* alarm_component); + void parse_action(icalcomponent* alarm_component); + void parse_description(icalcomponent* alarm_component); + void parse_trigger(icalcomponent* alarm_component); + + std::string print(); + private: + action a; + trigger_type trigger; + + std::string description; + }; +} diff --git a/ical/Event.cpp b/ical/Event.cpp new file mode 100644 index 0000000..d2fcce9 --- /dev/null +++ b/ical/Event.cpp @@ -0,0 +1,38 @@ +#include +#include + +namespace ical { + Event::Event(ical::ical_object* object, icalcomponent* event_component) { + parse(object, event_component); + } + + void Event::parse(ical::ical_object* object, icalcomponent* event_component) { + dtstart = icalcomponent_get_dtstart(event_component); + dtend = icalcomponent_get_dtstart(event_component); + summary = std::string(icalcomponent_get_summary(event_component)); + parse_alarms(event_component); + DEBUG << "\n" << print(); + for(auto& a : alarms) { + DEBUG << "\n" << a.print(); + } + object->events.push_back(this); + } + + void Event::parse_alarms(icalcomponent* event_component) { + icalcomponent* c; + for(c = icalcomponent_get_first_component(event_component, ICAL_VALARM_COMPONENT); + c != 0; + c = icalcomponent_get_next_component(event_component, ICAL_VALARM_COMPONENT)) + { + alarms.push_back(Alarm(c)); + } + } + + std::string Event::print() { + std::string s = ""; + s += "DTSTART: " + std::string(icaltime_as_ical_string(dtstart)) + "\n"; + s += "DTEND: " + std::string(icaltime_as_ical_string(dtend)) + "\n"; + s += "SUMMARY: " + summary + "\n"; + return s; + } +} diff --git a/ical/Event.h b/ical/Event.h new file mode 100644 index 0000000..11bd164 --- /dev/null +++ b/ical/Event.h @@ -0,0 +1,23 @@ +#pragma once + +#include +#include +#include +#include + +namespace ical { + class Event { + public: + Event(ical::ical_object* object, icalcomponent* event_component); + + void parse(ical::ical_object* object, icalcomponent* event); + void parse_alarms(icalcomponent* event_component); + + std::string print(); + private: + std::string summary; + icaltimetype dtstart; + icaltimetype dtend; + std::vector alarms; + }; +} diff --git a/ical/IcalObject.h b/ical/IcalObject.h new file mode 100644 index 0000000..87e705e --- /dev/null +++ b/ical/IcalObject.h @@ -0,0 +1,15 @@ +#pragma once + +#include +#include + +#include + +namespace ical { + class Alarm; + class Event; + + struct ical_object { + std::vector events; + }; +} diff --git a/io/debug.cpp b/io/debug.cpp new file mode 100644 index 0000000..9de255e --- /dev/null +++ b/io/debug.cpp @@ -0,0 +1,7 @@ +#include + +namespace io { + void debug(std::string msg) { + std::cout << "DEBUG: " << msg << std::endl;; + } +} diff --git a/io/debug.h b/io/debug.h new file mode 100644 index 0000000..9e28fc6 --- /dev/null +++ b/io/debug.h @@ -0,0 +1,30 @@ +#pragma once + +#include +#include + +struct X { +#ifdef LOG_DEBUG + X() { if(false) std::cout.setstate(std::ios_base::failbit); } + ~X() { if(false) { std::cout.clear(); } else { std::cout << std::endl << std::flush; } } +#else + X() { std::cout.setstate(std::ios_base::failbit); } + ~X() { std::cout.clear(); } +#endif +}; + +#define DEBUG (X(), std::cout << "DEBUG[" << __FILE__ << ":" << __LINE__ << " in " << __func__ << "()]: ") +#define WARN (X(), std::cout << "WARN [" << __FILE__ << ":" << __LINE__ << " in " << __func__ << "()]: ") + +#ifdef LOG_DEBUG +#define STEP \ + do { DEBUG << "Stepping through, Hit Enter..."; \ + std::cin.get(); \ + } while (0) +#else +#define STEP (void)(0) +#endif + +namespace io { + void debug(std::string msg); +} diff --git a/util/calendar_parsing.cpp b/util/calendar_parsing.cpp new file mode 100644 index 0000000..b17419e --- /dev/null +++ b/util/calendar_parsing.cpp @@ -0,0 +1,244 @@ +#include + +#include + +namespace util { + std::vector parse_cal_dir() { + std::vector objects; + std::vector components; + for (boost::filesystem::directory_entry& entry : boost::filesystem::directory_iterator(cal_dir)) { + DEBUG << "Parsing: " + entry.path().generic_string(); + ical::ical_object* object = new ical::ical_object(); + parse_main_component(object, parse_ics_file(entry.path().generic_string())); + } + return objects; + } + + icalcomponent* parse_main_component(ical::ical_object* object, icalcomponent* component) { + icalcomponent* c = icalcomponent_get_first_component(component, ICAL_ANY_COMPONENT); + while(c != 0) { + if(c != 0) { + parse_component(object, c); + } + c = parse_main_component(object, c); + if(c == 0) { + c = icalcomponent_get_next_component(component, ICAL_ANY_COMPONENT); + } + } + return c; + } + + void parse_component(ical::ical_object* object, icalcomponent* component) { + switch(icalcomponent_isa(component)) { + case ICAL_VALARM_COMPONENT: break; + case ICAL_NO_COMPONENT: + { + DEBUG << "parsed component: ICAL_NO_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_ANY_COMPONENT: + { + DEBUG << "parsed component: ICAL_ANY_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_XROOT_COMPONENT: + { + DEBUG << "parsed component: ICAL_XROOT_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_XATTACH_COMPONENT: + { + DEBUG << "parsed component: ICAL_XATTACH_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_VEVENT_COMPONENT: + { + DEBUG << "parsed component: ICAL_VEVENT_COMPONENT"; + //DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + ical::Event(object, component); + break; + } + case ICAL_VTODO_COMPONENT: + { + DEBUG << "parsed component: ICAL_VTODO_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_VJOURNAL_COMPONENT: + { + DEBUG << "parsed component: ICAL_VJOURNAL_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_VCALENDAR_COMPONENT: + { + DEBUG << "parsed component: ICAL_VCALENDAR_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_VAGENDA_COMPONENT: + { + DEBUG << "parsed component: ICAL_VAGENDA_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_VFREEBUSY_COMPONENT: + { + DEBUG << "parsed component: ICAL_VFREEBUSY_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_XAUDIOALARM_COMPONENT: + { + DEBUG << "parsed component: ICAL_XAUDIOALARM_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_XDISPLAYALARM_COMPONENT: + { + DEBUG << "parsed component: ICAL_XDISPLAYALARM_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_XEMAILALARM_COMPONENT: + { + DEBUG << "parsed component: ICAL_XEMAILALARM_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_XPROCEDUREALARM_COMPONENT: + { + DEBUG << "parsed component: ICAL_XPROCEDUREALARM_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_VTIMEZONE_COMPONENT: + { + DEBUG << "parsed component: ICAL_VTIMEZONE_COMPONENT"; + break; + } + case ICAL_XSTANDARD_COMPONENT: + { + DEBUG << "parsed component: ICAL_XSTANDARD_COMPONENT"; + break; + } + case ICAL_XDAYLIGHT_COMPONENT: + { + DEBUG << "parsed component: ICAL_XDAYLIGHT_COMPONENT"; + break; + } + case ICAL_X_COMPONENT: + { + DEBUG << "parsed component: ICAL_X_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_VSCHEDULE_COMPONENT: + { + DEBUG << "parsed component: ICAL_VSCHEDULE_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_VQUERY_COMPONENT: + { + DEBUG << "parsed component: ICAL_VQUERY_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_VREPLY_COMPONENT: + { + DEBUG << "parsed component: ICAL_VREPLY_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_VCAR_COMPONENT: + { + DEBUG << "parsed component: ICAL_VCAR_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_VCOMMAND_COMPONENT: + { + DEBUG << "parsed component: ICAL_VCOMMAND_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_XLICINVALID_COMPONENT: + { + DEBUG << "parsed component: ICAL_XLICINVALID_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_XLICMIMEPART_COMPONENT: + { + DEBUG << "parsed component: ICAL_XLICMIMEPART_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_VAVAILABILITY_COMPONENT: + { + DEBUG << "parsed component: ICAL_VAVAILABILITY_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_XAVAILABLE_COMPONENT: + { + DEBUG << "parsed component: ICAL_XAVAILABLE_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_VPOLL_COMPONENT: + { + DEBUG << "parsed component: ICAL_VPOLL_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_VVOTER_COMPONENT: + { + DEBUG << "parsed component: ICAL_VVOTER_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_XVOTE_COMPONENT: + { + DEBUG << "parsed component: ICAL_XVOTE_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_VPATCH_COMPONENT: + { + DEBUG << "parsed component: ICAL_VPATCH_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + case ICAL_XPATCH_COMPONENT: + { + DEBUG << "parsed component: ICAL_XPATCH_COMPONENT"; + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + break; + } + default: + DEBUG << "parse_component: something else found"; + break; + } + } + + std::string slurp(std::ifstream& in) { + std::ostringstream sstr; + sstr << in.rdbuf(); + return sstr.str(); + } + + icalcomponent* parse_ics_file(std::string filename) { + std::ifstream ifs (filename, std::ifstream::in); + return icalparser_parse_string(slurp(ifs).c_str()); + } + + void debug(icalcomponent* component) { + DEBUG << "\n" << icalcomponent_as_ical_string_r(component); + } +} diff --git a/util/calendar_parsing.h b/util/calendar_parsing.h new file mode 100644 index 0000000..bca1aa8 --- /dev/null +++ b/util/calendar_parsing.h @@ -0,0 +1,28 @@ +#pragma once + +#include +#include +#include + +#include +#include +#include +#include + +#include + +#include + +const std::string cal_dir = "/home/stefan/.local/share/khal/calendars/dummy/"; + +namespace util { + std::vector parse_cal_dir(); + icalcomponent* parse_main_component(ical::ical_object* object, icalcomponent* component); + void parse_component(ical::ical_object* object, icalcomponent* component); + + std::string slurp(std::ifstream& in); + icalcomponent* parse_ics_file(std::string filename); + + + void debug(icalcomponent* component); +} diff --git a/util/calendar_parsing.o b/util/calendar_parsing.o new file mode 100644 index 0000000..44b58d3 Binary files /dev/null and b/util/calendar_parsing.o differ diff --git a/util/calendar_parsing.o.d b/util/calendar_parsing.o.d new file mode 100644 index 0000000..6ad59fa --- /dev/null +++ b/util/calendar_parsing.o.d @@ -0,0 +1,2 @@ +util/calendar_parsing.o: util/calendar_parsing.cpp \ + util/calendar_parsing.h io/debug.h diff --git a/util/notifications.h b/util/notifications.h new file mode 100644 index 0000000..0c0121c --- /dev/null +++ b/util/notifications.h @@ -0,0 +1,12 @@ +#pragma once + +//#include +#include + +const std::string exec = "notify-send"; +namespace util { + uint notify(std::string summary, std::string body /* */) { + std::string cmd = exec + " " + summary + " " + body; + return system(cmd.c_str()); + } +}