diff --git a/configure.ac b/configure.ac index e0bcf539..06107417 100644 --- a/configure.ac +++ b/configure.ac @@ -463,6 +463,37 @@ case "${host}" in have_notify="yes" ;; *) + AC_PATH_PROG([GDBUS], [gdbus], [not found]) + if test "${GDBUS}" = "not found"; then + have_notify="no" + have_gdbus="no" + else + have_notify="yes" + have_gdbus="yes" + fi + + if test "${have_notify}" = "no"; then + PKG_CHECK_MODULES( [GIO2], [gio-2.0], + [ have_notify="yes" + have_gio2="yes" ], + [ have_notify="no" + have_gio2="no" ]) + saved_CFLAGS="${CFLAGS}" + CFLAGS="${CFLAGS} ${GIO2_CFLAGS}" + AC_CHECK_HEADERS(gio/gio.h, [], + [ AC_MSG_WARN([glib2 headers not found]) + have_notify="no" + have_gio2="no" ]) + CFLAGS="${saved_CFLAGS}" + saved_LIBS="$LIBS" + LIBS="$LIBS ${GIO2_LIBS}" + AC_MSG_CHECKING([for g_application_send_notification]) + AC_TRY_LINK_FUNC(g_application_send_notification, [ AC_MSG_RESULT([yes]) ], + [ AC_MSG_WARN([Cannot link against glib2]) + have_notify="no" + have_gio2="no" ]) + LIBS="$saved_LIBS" + fi ;; esac @@ -482,8 +513,14 @@ esac if test "${enable_notify}" = "yes"; then if test "${have_notify}" = "yes"; then AC_DEFINE([ENABLE_NOTIFY], [1], [Use notification libraries and header files]) - OPTIONAL_NOTIFY_CFLAGS="${NOTIFY_CFLAGS}" - OPTIONAL_NOTIFY_LIBS="${NOTIFY_LIBS}" + if test "${have_gdbus}" = "yes"; then + AC_DEFINE_UNQUOTED([GDBUS], ["${GDBUS}"], [Path to gdbus]) + fi + if test "${have_gio2}" = "yes"; then + AC_DEFINE([ENABLE_GIO2], [1], [Use glib2 libraries and header files]) + OPTIONAL_NOTIFY_CFLAGS="${GIO2_CFLAGS}" + OPTIONAL_NOTIFY_LIBS="${GIO2_LIBS}" + fi else AC_MSG_ERROR([notification linkage required, but no notification provider was found]) fi @@ -1087,8 +1124,9 @@ OPENCT_CFLAGS: ${OPENCT_CFLAGS} OPENCT_LIBS: ${OPENCT_LIBS} PCSC_CFLAGS: ${PCSC_CFLAGS} CRYPTOTOKENKIT_CFLAGS: ${CRYPTOTOKENKIT_CFLAGS} -NOTIFY_CFLAGS: ${NOTIFY_CFLAGS} -NOTIFY_LIBS: ${NOTIFY_LIBS} +GDBUS: ${GDBUS} +GIO2_CFLAGS: ${GIO2_CFLAGS} +GIO2_LIBS: ${GIO2_LIBS} EOF diff --git a/src/libopensc/opensc.h b/src/libopensc/opensc.h index f81f27e3..bd2eb500 100644 --- a/src/libopensc/opensc.h +++ b/src/libopensc/opensc.h @@ -1333,7 +1333,7 @@ scconf_block *sc_match_atr_block(sc_context_t *ctx, struct sc_card_driver *drive * @param value pointer to data used for CRC calculation * @param len length of data used for CRC calculation */ -unsigned sc_crc32(unsigned char *value, size_t len); +unsigned sc_crc32(const unsigned char *value, size_t len); /** * Used to initialize the @c sc_remote_data structure -- diff --git a/src/libopensc/sc.c b/src/libopensc/sc.c index 051fcf69..6c40839e 100644 --- a/src/libopensc/sc.c +++ b/src/libopensc/sc.c @@ -949,7 +949,7 @@ void sc_remote_data_init(struct sc_remote_data *rdata) static unsigned long sc_CRC_tab32[256]; static int sc_CRC_tab32_initialized = 0; -unsigned sc_crc32(unsigned char *value, size_t len) +unsigned sc_crc32(const unsigned char *value, size_t len) { size_t ii, jj; unsigned long crc; diff --git a/src/tools/opensc-notify.c b/src/tools/opensc-notify.c index c5b16c86..18a9ce35 100644 --- a/src/tools/opensc-notify.c +++ b/src/tools/opensc-notify.c @@ -24,6 +24,25 @@ #include "ui/notify.h" #include +#ifndef _WIN32 +#include + +void Sleep(unsigned int Milliseconds) +{ + struct timespec req, rem; + + if (Milliseconds > 999) { + req.tv_sec = Milliseconds / 1000; /* Must be Non-Negative */ + req.tv_nsec = (Milliseconds - (req.tv_sec * 1000)) * 1000000; /* Must be in range of 0 to 999999999 */ + } else { + req.tv_sec = 0; /* Must be Non-Negative */ + req.tv_nsec = Milliseconds * 1000000; /* Must be in range of 0 to 999999999 */ + } + + nanosleep(&req , &rem); +} +#endif + int main (int argc, char **argv) { @@ -33,7 +52,7 @@ main (int argc, char **argv) text = argv[2]; /* fall through */ case 2: - text = argv[1]; + title = argv[1]; /* fall through */ case 1: break; @@ -44,6 +63,7 @@ main (int argc, char **argv) } sc_notify_init(); sc_notify(title, text); + Sleep(250); sc_notify_close(); return 0; diff --git a/src/ui/notify.c b/src/ui/notify.c index b55432dc..1be6706b 100644 --- a/src/ui/notify.c +++ b/src/ui/notify.c @@ -24,7 +24,7 @@ #include "notify.h" -#if defined(ENABLE_NOTIFY) && defined(__APPLE__) +#if defined(ENABLE_NOTIFY) && (defined(__APPLE__) || defined(GDBUS)) #include "libopensc/log.h" #include @@ -61,6 +61,10 @@ void sc_notify_close(void) } } +#endif + +#if defined(ENABLE_NOTIFY) && defined(__APPLE__) + static void notify_proxy(struct sc_context *ctx, const char *title, const char* subtitle, const char *text, const char *icon, const char *sound, @@ -165,6 +169,151 @@ void sc_notify_id(struct sc_context *ctx, struct sc_atr *atr, notify_proxy(ctx, title, NULL, text, icon, NULL, group); } +#elif defined(ENABLE_NOTIFY) && defined(GDBUS) + +#include +/* save the notification's id for replacement with a new one */ +uint32_t message_id = 0; + +static void notify_gio(struct sc_context *ctx, + const char *title, const char *text, const char *icon, + const char *group) +{ + char message_id_str[22]; + int pipefd[2]; + int pass_to_pipe = 1; + snprintf(message_id_str, sizeof message_id_str, "%"PRIu32, message_id); + + if (child > 0) { + int status; + if (0 == waitpid(child, &status, WNOHANG)) { + kill(child, SIGKILL); + usleep(100); + if (0 == waitpid(child, &status, WNOHANG)) { + sc_log(ctx, "Can't kill %ld, skipping current notification", (long) child); + return; + } + } + } + + if (0 == pipe(pipefd)) { + pass_to_pipe = 1; + } + + child = fork(); + switch (child) { + case 0: + /* child process */ + if (pass_to_pipe) { + /* close reading end of the pipe */ + close(pipefd[0]); + /* send stdout to the pipe */ + dup2(pipefd[1], 1); + /* this descriptor is no longer needed */ + close(pipefd[1]); + } + + if (0 > execl(GDBUS, GDBUS, + "call", "--session", + "--dest", "org.freedesktop.Notifications", + "--object-path", "/org/freedesktop/Notifications", + "--method", "org.freedesktop.Notifications.Notify", + "org.opensc-project", + message_id_str, + icon ? icon : "", + title ? title : "", + text ? text : "", + "[]", "{}", "5000", + (char *) NULL)) { + perror("exec failed"); + exit(1); + } + break; + case -1: + sc_log(ctx, "failed to fork for notification"); + break; + default: + /* parent process */ + + if (ctx) { + sc_log(ctx, "Created %ld for notification:", (long) child); + sc_log(ctx, "%s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s", GDBUS, + "call", "--session", + "--dest", "org.freedesktop.Notifications", + "--object-path", "/org/freedesktop/Notifications", + "--method", "org.freedesktop.Notifications.Notify", + "org.opensc-project", + message_id_str, + icon ? icon : "", + title ? title : "", + text ? text : "", + "[]", "{}", "5000"); + } + + if (pass_to_pipe) { + /* close the write end of the pipe */ + close(pipefd[1]); + memset(message_id_str, '\0', sizeof message_id_str); + if (0 < read(pipefd[0], message_id_str, sizeof(message_id_str))) { + message_id_str[(sizeof message_id_str) - 1] = '\0'; + sscanf(message_id_str, "(uint32 %"SCNu32",)", &message_id); + } + /* close the read end of the pipe */ + close(pipefd[0]); + } + break; + } +} + +#elif defined(ENABLE_NOTIFY) && defined(ENABLE_GIO2) + +static GtkApplication *application = NULL; + +#include + +void sc_notify_init(void) +{ + sc_notify_close(); + application = g_application_new("org.opensc-project", G_APPLICATION_FLAGS_NONE); + if (application) { + g_application_register(application, NULL, NULL); + } +} + +void sc_notify_close(void) +{ + if (application) { + g_object_unref(application); + application = NULL; + } +} + +static void notify_gio(struct sc_context *ctx, + const char *title, const char *text, const char *icon, + const char *group) +{ + GIcon *gicon = NULL; + GNotification *notification = g_notification_new (title); + if (!notification) { + return; + } + + g_notification_set_body (notification, text); + if (icon) { + gicon = g_themed_icon_new (icon); + if (gicon) { + g_notification_set_icon (notification, gicon); + } + } + + g_application_send_notification(application, group, notification); + + if (gicon) { + g_object_unref(gicon); + } + g_object_unref(notification); +} + #else void sc_notify_init(void) {} @@ -174,3 +323,45 @@ void sc_notify_id(struct sc_context *ctx, struct sc_atr *atr, struct sc_pkcs15_card *p15card, enum ui_str id) {} #endif + +#if defined(ENABLE_NOTIFY) && (defined(ENABLE_GIO2) || defined(GDBUS)) +void sc_notify(const char *title, const char *text) +{ + notify_gio(NULL, title, text, NULL, NULL); +} + +void sc_notify_id(struct sc_context *ctx, struct sc_atr *atr, + struct sc_pkcs15_card *p15card, enum ui_str id) +{ + const char *title, *text, *icon, *group; + title = ui_get_str(ctx, atr, p15card, id); + text = ui_get_str(ctx, atr, p15card, id+1); + + if (p15card && p15card->card && p15card->card->reader) { + group = p15card->card->reader->name; + } else { + group = ctx ? ctx->app_name : NULL; + } + + switch (id) { + case NOTIFY_CARD_INSERTED: + icon = "dialog-information"; + break; + case NOTIFY_CARD_REMOVED: + icon = "media-removed"; + break; + case NOTIFY_PIN_GOOD: + icon = "changes-allow"; + break; + case NOTIFY_PIN_BAD: + icon = "changes-prevent"; + break; + default: + icon = NULL; + break; + } + + notify_gio(ctx, title, text, icon, group); +} + +#endif