From ce3f27ff5467a27ccd2ed86e5377d5daf647d4c1 Mon Sep 17 00:00:00 2001 From: Frank Morgner Date: Mon, 19 Jun 2017 11:57:18 +0200 Subject: [PATCH] opensc-notify: notify for card events ... manually or in daemon mode fixes compilation in MinGW/Travis CI --- .travis.yml | 2 +- MacOSX/OpenSC_Notify.applescript | 2 + MacOSX/build-package.in | 2 + MacOSX/opensc-uninstall | 1 + configure.ac | 5 +- m4/ax_check_compile_flag.m4 | 74 ++ src/tools/Makefile.am | 35 +- src/tools/Makefile.mak | 10 +- src/tools/opensc-notify-cmdline.c | 1246 +++++++++++++++++++++++++++++ src/tools/opensc-notify-cmdline.h | 196 +++++ src/tools/opensc-notify.c | 201 ++++- src/tools/opensc-notify.ggo.in | 45 ++ src/ui/char_str_from_wchar.h | 46 ++ src/ui/invisible_window.h | 52 ++ src/ui/notify.c | 102 +-- win32/OpenSC.wxs.in | 4 + 16 files changed, 1940 insertions(+), 83 deletions(-) create mode 100644 MacOSX/OpenSC_Notify.applescript create mode 100644 m4/ax_check_compile_flag.m4 create mode 100644 src/tools/opensc-notify-cmdline.c create mode 100644 src/tools/opensc-notify-cmdline.h create mode 100644 src/tools/opensc-notify.ggo.in create mode 100644 src/ui/char_str_from_wchar.h create mode 100644 src/ui/invisible_window.h diff --git a/.travis.yml b/.travis.yml index 28734919..e958beda 100644 --- a/.travis.yml +++ b/.travis.yml @@ -63,7 +63,7 @@ before_script: fi - ./bootstrap - if [ -z "$HOST" ]; then - CFLAGS="-Werror" ./configure $ENABLE_DOC --enable-dnie-ui; + CFLAGS="-Werror" ./configure $ENABLE_DOC --enable-dnie-ui --disable-notify; else if [ ! -f "$(winepath 'C:/Program Files (x86)/Inno Setup 5/ISCC.exe')" ]; then /sbin/start-stop-daemon --start --quiet --pidfile /tmp/custom_xvfb_99.pid --make-pidfile --background --exec /usr/bin/Xvfb -- :99 -ac -screen 0 1280x1024x16; diff --git a/MacOSX/OpenSC_Notify.applescript b/MacOSX/OpenSC_Notify.applescript new file mode 100644 index 00000000..d2910c22 --- /dev/null +++ b/MacOSX/OpenSC_Notify.applescript @@ -0,0 +1,2 @@ +do shell script "killall opensc-notify || true" +do shell script "nohup /Library/OpenSC/bin/opensc-notify > /dev/null 2>&1 &" diff --git a/MacOSX/build-package.in b/MacOSX/build-package.in index 6ec5f6ba..5724f9bf 100755 --- a/MacOSX/build-package.in +++ b/MacOSX/build-package.in @@ -108,6 +108,8 @@ if ! test -e NotificationProxy; then git clone http://github.com/frankmorgner/NotificationProxy.git fi xcodebuild -target NotificationProxy -configuration Release -project NotificationProxy/NotificationProxy.xcodeproj install DSTROOT=$BUILDPATH/target/Library/Security/tokend/OpenSC.tokend/Contents/Resources/ +mkdir -p "$BUILDPATH/target/Applications" +osacompile -o "$BUILDPATH/target/Applications/OpenSC Notify.app" "MacOSX/OpenSC_Notify.applescript" # Prepare target root # The "UnInstaller" diff --git a/MacOSX/opensc-uninstall b/MacOSX/opensc-uninstall index 2dcfcec9..d4331a9a 100755 --- a/MacOSX/opensc-uninstall +++ b/MacOSX/opensc-uninstall @@ -16,6 +16,7 @@ rm -f /usr/local/lib/opensc-pkcs11.so rm -f /usr/local/lib/onepin-opensc-pkcs11.so # Remove installed files +rm -rf "/Applications/OpenSC Notify.app" rm -rf /Library/OpenSC rm -rf /Library/Security/tokend/OpenSC.tokend rm -rf /System/Library/Security/tokend/OpenSC.tokend diff --git a/configure.ac b/configure.ac index 06107417..9ebbb066 100644 --- a/configure.ac +++ b/configure.ac @@ -133,6 +133,9 @@ case "${host}" in ;; esac +AX_CHECK_COMPILE_FLAG(-Wno-error=unused-but-set-variable, [have_unused_but_set_variable="yes"], [have_unused_but_set_variable="no"], [-Werror -Wunknown-warning-option]) +AM_CONDITIONAL([HAVE_UNUSED_BUT_SET_VARIABLE], [test "${have_unused_but_set_variable}" = "yes"]) + AC_ARG_ENABLE( [strict], [AS_HELP_STRING([--disable-strict],[disable strict compile mode @<:@disabled@:>@])], @@ -384,7 +387,7 @@ AC_FUNC_VPRINTF AC_CHECK_FUNCS([ \ getpass gettimeofday getline memset mkdir \ strdup strerror getopt_long getopt_long_only \ - strlcpy strlcat strnlen + strlcpy strlcat strnlen sigaction ]) AC_CHECK_SIZEOF(void *) if test "${ac_cv_sizeof_void_p}" = 8; then diff --git a/m4/ax_check_compile_flag.m4 b/m4/ax_check_compile_flag.m4 new file mode 100644 index 00000000..dcabb92a --- /dev/null +++ b/m4/ax_check_compile_flag.m4 @@ -0,0 +1,74 @@ +# =========================================================================== +# https://www.gnu.org/software/autoconf-archive/ax_check_compile_flag.html +# =========================================================================== +# +# SYNOPSIS +# +# AX_CHECK_COMPILE_FLAG(FLAG, [ACTION-SUCCESS], [ACTION-FAILURE], [EXTRA-FLAGS], [INPUT]) +# +# DESCRIPTION +# +# Check whether the given FLAG works with the current language's compiler +# or gives an error. (Warnings, however, are ignored) +# +# ACTION-SUCCESS/ACTION-FAILURE are shell commands to execute on +# success/failure. +# +# If EXTRA-FLAGS is defined, it is added to the current language's default +# flags (e.g. CFLAGS) when the check is done. The check is thus made with +# the flags: "CFLAGS EXTRA-FLAGS FLAG". This can for example be used to +# force the compiler to issue an error when a bad flag is given. +# +# INPUT gives an alternative input source to AC_COMPILE_IFELSE. +# +# NOTE: Implementation based on AX_CFLAGS_GCC_OPTION. Please keep this +# macro in sync with AX_CHECK_{PREPROC,LINK}_FLAG. +# +# LICENSE +# +# Copyright (c) 2008 Guido U. Draheim +# Copyright (c) 2011 Maarten Bosmans +# +# This program is free software: you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by the +# Free Software Foundation, either version 3 of the License, or (at your +# option) any later version. +# +# This program is distributed in the hope that it will be useful, but +# WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General +# Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with this program. If not, see . +# +# As a special exception, the respective Autoconf Macro's copyright owner +# gives unlimited permission to copy, distribute and modify the configure +# scripts that are the output of Autoconf when processing the Macro. You +# need not follow the terms of the GNU General Public License when using +# or distributing such scripts, even though portions of the text of the +# Macro appear in them. The GNU General Public License (GPL) does govern +# all other use of the material that constitutes the Autoconf Macro. +# +# This special exception to the GPL applies to versions of the Autoconf +# Macro released by the Autoconf Archive. When you make and distribute a +# modified version of the Autoconf Macro, you may extend this special +# exception to the GPL to apply to your modified version as well. + +#serial 5 + +AC_DEFUN([AX_CHECK_COMPILE_FLAG], +[AC_PREREQ(2.64)dnl for _AC_LANG_PREFIX and AS_VAR_IF +AS_VAR_PUSHDEF([CACHEVAR],[ax_cv_check_[]_AC_LANG_ABBREV[]flags_$4_$1])dnl +AC_CACHE_CHECK([whether _AC_LANG compiler accepts $1], CACHEVAR, [ + ax_check_save_flags=$[]_AC_LANG_PREFIX[]FLAGS + _AC_LANG_PREFIX[]FLAGS="$[]_AC_LANG_PREFIX[]FLAGS $4 $1" + AC_COMPILE_IFELSE([m4_default([$5],[AC_LANG_PROGRAM()])], + [AS_VAR_SET(CACHEVAR,[yes])], + [AS_VAR_SET(CACHEVAR,[no])]) + _AC_LANG_PREFIX[]FLAGS=$ax_check_save_flags]) +AS_VAR_IF(CACHEVAR,yes, + [m4_default([$2], :)], + [m4_default([$3], :)]) +AS_VAR_POPDEF([CACHEVAR])dnl +])dnl AX_CHECK_COMPILE_FLAGS diff --git a/src/tools/Makefile.am b/src/tools/Makefile.am index 9eb827b1..3011e168 100644 --- a/src/tools/Makefile.am +++ b/src/tools/Makefile.am @@ -12,14 +12,16 @@ do_subst = $(SED) \ -e 's,[@]X509DIR[@],$(X509DIR),g' NPA_TOOL_BUILT_SOURCES = npa-tool-cmdline.h npa-tool-cmdline.c +OPENSC_NOTIFY_BUILT_SOURCES = opensc-notify-cmdline.h opensc-notify-cmdline.c MAINTAINERCLEANFILES = $(srcdir)/Makefile.in $(srcdir)/versioninfo-tools.rc -EXTRA_DIST = Makefile.mak versioninfo-tools.rc.in npa-tool.ggo.in npa-tool.1 +EXTRA_DIST = Makefile.mak versioninfo-tools.rc.in npa-tool.ggo.in npa-tool.1 opensc-notify.ggo.in noinst_HEADERS = util.h fread_to_eof.h noinst_PROGRAMS = sceac-example -bin_PROGRAMS = opensc-tool opensc-explorer pkcs15-tool pkcs15-crypt \ - pkcs11-tool cardos-tool eidenv openpgp-tool iasecc-tool opensc-notify +bin_PROGRAMS = opensc-tool opensc-explorer opensc-notify \ + pkcs15-tool pkcs15-crypt pkcs11-tool \ + cardos-tool eidenv openpgp-tool iasecc-tool if ENABLE_OPENSSL bin_PROGRAMS += cryptoflex-tool pkcs15-init netkey-tool piv-tool \ westcos-tool sc-hsm-tool dnie-tool gids-tool npa-tool @@ -46,7 +48,6 @@ piv_tool_SOURCES = piv-tool.c util.c piv_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) opensc_explorer_SOURCES = opensc-explorer.c util.c opensc_explorer_LDADD = $(OPTIONAL_READLINE_LIBS) -opensc_notify_SOURCES = opensc-notify.c pkcs15_tool_SOURCES = pkcs15-tool.c util.c pkcs15_tool_LDADD = $(OPTIONAL_OPENSSL_LIBS) pkcs11_tool_SOURCES = pkcs11-tool.c util.c @@ -101,6 +102,30 @@ npa-tool.1: --source='$(PACKAGE_STRING)' \ $(builddir)/npa-tool$(EXEEXT) +opensc_notify_SOURCES = opensc-notify.c $(OPENSC_NOTIFY_BUILT_SOURCES) +opensc_notify_LDADD = $(top_builddir)/src/libopensc/libopensc.la $(OPTIONAL_NOTIFY_LIBS) +opensc_notify_CFLAGS = -I$(top_srcdir)/src $(OPTIONAL_NOTIFY_CFLAGS) +opensc_notify_CFLAGS += -Wno-unused-but-set-variable -Wno-unknown-warning-option + +opensc-notify.c: $(abs_builddir)/opensc-notify.ggo $(OPENSC_NOTIFY_BUILT_SOURCES) + +# We only want *cmdline* to be generated when they have explicitly been removed. +$(OPENSC_NOTIFY_BUILT_SOURCES): + $(MAKE) $(abs_builddir)/opensc-notify.ggo + $(GENGETOPT) --include-getopt --file-name=opensc-notify-cmdline --output-dir=$(builddir) < $(abs_builddir)/opensc-notify.ggo + +$(abs_builddir)/opensc-notify.ggo: opensc-notify.ggo.in + $(do_subst) < $(abs_srcdir)/opensc-notify.ggo.in > $@ + +# We only want opensc-notify.1 to be generated when it has explicitly been removed. +opensc-notify.1: + $(MAKE) opensc-notify$(EXEEXT) + $(HELP2MAN) \ + --output=$@ \ + --no-info \ + --source='$(PACKAGE_STRING)' \ + $(builddir)/opensc-notify$(EXEEXT) + if WIN32 opensc_tool_SOURCES += versioninfo-tools.rc piv_tool_SOURCES += versioninfo-tools.rc @@ -121,4 +146,4 @@ gids_tool_SOURCES += versioninfo-tools.rc endif clean-local: - rm -f $(abs_builddir)/npa-tool.ggo + rm -f $(abs_builddir)/npa-tool.ggo $(abs_builddir)/opensc-notify.ggo diff --git a/src/tools/Makefile.mak b/src/tools/Makefile.mak index c3ef65c5..8e3be233 100644 --- a/src/tools/Makefile.mak +++ b/src/tools/Makefile.mak @@ -10,6 +10,10 @@ TARGETS = opensc-tool.exe opensc-explorer.exe pkcs15-tool.exe pkcs15-crypt.exe \ $(PROGRAMS_OPENSSL) OBJECTS = util.obj npa-tool-cmdline.obj fread_to_eof.obj versioninfo-tools.res + +TARGET2 = opensc-notify.exe +OBJECT2 = opensc-notify-cmdline.obj versioninfo-tools.res + LIBS = $(TOPDIR)\src\common\common.lib \ $(TOPDIR)\src\scconf\scconf.lib \ $(TOPDIR)\src\libopensc\opensc.lib \ @@ -17,11 +21,13 @@ LIBS = $(TOPDIR)\src\common\common.lib \ $(TOPDIR)\src\common\libpkcs11.lib \ $(TOPDIR)\src\common\libscdl.lib -all: $(TARGETS) +all: $(TARGETS) $(TARGET2) $(TARGETS): $(OBJECTS) $(LIBS) +$(TARGET2): $(OBJECT2) $(LIBS) + .c.exe: cl $(COPTS) /c $< - link $(LINKFLAGS) /pdb:$*.pdb /out:$@ $*.obj $(OBJECTS) $(LIBS) $(OPENPACE_LIB) $(OPENSSL_LIB) gdi32.lib shell32.lib ws2_32.lib + link $(LINKFLAGS) /pdb:$*.pdb /out:$@ $*.obj $(OBJECTS) $(LIBS) $(OPENPACE_LIB) $(OPENSSL_LIB) gdi32.lib shell32.lib User32.lib ws2_32.lib mt -manifest exe.manifest -outputresource:$@;1 diff --git a/src/tools/opensc-notify-cmdline.c b/src/tools/opensc-notify-cmdline.c new file mode 100644 index 00000000..bfdb3dd7 --- /dev/null +++ b/src/tools/opensc-notify-cmdline.c @@ -0,0 +1,1246 @@ +/* + File autogenerated by gengetopt version 2.22.6 + generated with the following command: + /usr/bin/gengetopt --include-getopt --file-name=opensc-notify-cmdline --output-dir=. + + The developers of gengetopt consider the fixed text that goes in all + gengetopt output files to be in the public domain: + we make no copyright claims on it. +*/ + +/* If we use autoconf. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include +#include +#include + +#ifndef FIX_UNUSED +#define FIX_UNUSED(X) (void) (X) /* avoid warnings for unused params */ +#endif + + +#include "opensc-notify-cmdline.h" + +const char *gengetopt_args_info_purpose = ""; + +const char *gengetopt_args_info_usage = "Usage: opensc-notify [OPTIONS]..."; + +const char *gengetopt_args_info_versiontext = ""; + +const char *gengetopt_args_info_description = "If no arguments are given, monitor smart card events and send the appropriate\nnotification."; + +const char *gengetopt_args_info_help[] = { + " -h, --help Print help and exit", + " -V, --version Print version and exit", + "\n Mode: customized\n Send customized notifications.", + " -t, --title[=STRING] Title of the notification", + " -m, --message[=STRING] Main text of the notification", + "\n Mode: standard\n Manually send standard notifications.", + " -I, --notify-card-inserted See notify_card_inserted in opensc.conf\n (default=off)", + " -R, --notify-card-removed See notify_card_inserted in opensc.conf\n (default=off)", + " -G, --notify-pin-good See notify_pin_good in opensc.conf (default=off)", + " -B, --notify-pin-bad See notify_pin_bad in opensc.conf (default=off)", + "\nReport bugs to opensc-devel@lists.sourceforge.net\n\nWritten by Frank Morgner ", + 0 +}; + +typedef enum {ARG_NO + , ARG_FLAG + , ARG_STRING +} cmdline_parser_arg_type; + +static +void clear_given (struct gengetopt_args_info *args_info); +static +void clear_args (struct gengetopt_args_info *args_info); + +static int +cmdline_parser_internal (int argc, char **argv, struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params, const char *additional_error); + + +static char * +gengetopt_strdup (const char *s); + +static +void clear_given (struct gengetopt_args_info *args_info) +{ + args_info->help_given = 0 ; + args_info->version_given = 0 ; + args_info->title_given = 0 ; + args_info->message_given = 0 ; + args_info->notify_card_inserted_given = 0 ; + args_info->notify_card_removed_given = 0 ; + args_info->notify_pin_good_given = 0 ; + args_info->notify_pin_bad_given = 0 ; + args_info->customized_mode_counter = 0 ; + args_info->daemon_mode_counter = 0 ; + args_info->standard_mode_counter = 0 ; +} + +static +void clear_args (struct gengetopt_args_info *args_info) +{ + FIX_UNUSED (args_info); + args_info->title_arg = NULL; + args_info->title_orig = NULL; + args_info->message_arg = NULL; + args_info->message_orig = NULL; + args_info->notify_card_inserted_flag = 0; + args_info->notify_card_removed_flag = 0; + args_info->notify_pin_good_flag = 0; + args_info->notify_pin_bad_flag = 0; + +} + +static +void init_args_info(struct gengetopt_args_info *args_info) +{ + + + args_info->help_help = gengetopt_args_info_help[0] ; + args_info->version_help = gengetopt_args_info_help[1] ; + args_info->title_help = gengetopt_args_info_help[3] ; + args_info->message_help = gengetopt_args_info_help[4] ; + args_info->notify_card_inserted_help = gengetopt_args_info_help[6] ; + args_info->notify_card_removed_help = gengetopt_args_info_help[7] ; + args_info->notify_pin_good_help = gengetopt_args_info_help[8] ; + args_info->notify_pin_bad_help = gengetopt_args_info_help[9] ; + +} + +void +cmdline_parser_print_version (void) +{ + printf ("%s %s\n", + (strlen(CMDLINE_PARSER_PACKAGE_NAME) ? CMDLINE_PARSER_PACKAGE_NAME : CMDLINE_PARSER_PACKAGE), + CMDLINE_PARSER_VERSION); + + if (strlen(gengetopt_args_info_versiontext) > 0) + printf("\n%s\n", gengetopt_args_info_versiontext); +} + +static void print_help_common(void) { + cmdline_parser_print_version (); + + if (strlen(gengetopt_args_info_purpose) > 0) + printf("\n%s\n", gengetopt_args_info_purpose); + + if (strlen(gengetopt_args_info_usage) > 0) + printf("\n%s\n", gengetopt_args_info_usage); + + printf("\n"); + + if (strlen(gengetopt_args_info_description) > 0) + printf("%s\n\n", gengetopt_args_info_description); +} + +void +cmdline_parser_print_help (void) +{ + int i = 0; + print_help_common(); + while (gengetopt_args_info_help[i]) + printf("%s\n", gengetopt_args_info_help[i++]); +} + +void +cmdline_parser_init (struct gengetopt_args_info *args_info) +{ + clear_given (args_info); + clear_args (args_info); + init_args_info (args_info); +} + +void +cmdline_parser_params_init(struct cmdline_parser_params *params) +{ + if (params) + { + params->override = 0; + params->initialize = 1; + params->check_required = 1; + params->check_ambiguity = 0; + params->print_errors = 1; + } +} + +struct cmdline_parser_params * +cmdline_parser_params_create(void) +{ + struct cmdline_parser_params *params = + (struct cmdline_parser_params *)malloc(sizeof(struct cmdline_parser_params)); + cmdline_parser_params_init(params); + return params; +} + +static void +free_string_field (char **s) +{ + if (*s) + { + free (*s); + *s = 0; + } +} + + +static void +cmdline_parser_release (struct gengetopt_args_info *args_info) +{ + + free_string_field (&(args_info->title_arg)); + free_string_field (&(args_info->title_orig)); + free_string_field (&(args_info->message_arg)); + free_string_field (&(args_info->message_orig)); + + + + clear_given (args_info); +} + + +static void +write_into_file(FILE *outfile, const char *opt, const char *arg, const char *values[]) +{ + FIX_UNUSED (values); + if (arg) { + fprintf(outfile, "%s=\"%s\"\n", opt, arg); + } else { + fprintf(outfile, "%s\n", opt); + } +} + + +int +cmdline_parser_dump(FILE *outfile, struct gengetopt_args_info *args_info) +{ + int i = 0; + + if (!outfile) + { + fprintf (stderr, "%s: cannot dump options to stream\n", CMDLINE_PARSER_PACKAGE); + return EXIT_FAILURE; + } + + if (args_info->help_given) + write_into_file(outfile, "help", 0, 0 ); + if (args_info->version_given) + write_into_file(outfile, "version", 0, 0 ); + if (args_info->title_given) + write_into_file(outfile, "title", args_info->title_orig, 0); + if (args_info->message_given) + write_into_file(outfile, "message", args_info->message_orig, 0); + if (args_info->notify_card_inserted_given) + write_into_file(outfile, "notify-card-inserted", 0, 0 ); + if (args_info->notify_card_removed_given) + write_into_file(outfile, "notify-card-removed", 0, 0 ); + if (args_info->notify_pin_good_given) + write_into_file(outfile, "notify-pin-good", 0, 0 ); + if (args_info->notify_pin_bad_given) + write_into_file(outfile, "notify-pin-bad", 0, 0 ); + + + i = EXIT_SUCCESS; + return i; +} + +int +cmdline_parser_file_save(const char *filename, struct gengetopt_args_info *args_info) +{ + FILE *outfile; + int i = 0; + + outfile = fopen(filename, "w"); + + if (!outfile) + { + fprintf (stderr, "%s: cannot open file for writing: %s\n", CMDLINE_PARSER_PACKAGE, filename); + return EXIT_FAILURE; + } + + i = cmdline_parser_dump(outfile, args_info); + fclose (outfile); + + return i; +} + +void +cmdline_parser_free (struct gengetopt_args_info *args_info) +{ + cmdline_parser_release (args_info); +} + +/** @brief replacement of strdup, which is not standard */ +char * +gengetopt_strdup (const char *s) +{ + char *result = 0; + if (!s) + return result; + + result = (char*)malloc(strlen(s) + 1); + if (result == (char*)0) + return (char*)0; + strcpy(result, s); + return result; +} + +int +cmdline_parser (int argc, char **argv, struct gengetopt_args_info *args_info) +{ + return cmdline_parser2 (argc, argv, args_info, 0, 1, 1); +} + +int +cmdline_parser_ext (int argc, char **argv, struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params) +{ + int result; + result = cmdline_parser_internal (argc, argv, args_info, params, 0); + + if (result == EXIT_FAILURE) + { + cmdline_parser_free (args_info); + exit (EXIT_FAILURE); + } + + return result; +} + +int +cmdline_parser2 (int argc, char **argv, struct gengetopt_args_info *args_info, int override, int initialize, int check_required) +{ + int result; + struct cmdline_parser_params params; + + params.override = override; + params.initialize = initialize; + params.check_required = check_required; + params.check_ambiguity = 0; + params.print_errors = 1; + + result = cmdline_parser_internal (argc, argv, args_info, ¶ms, 0); + + if (result == EXIT_FAILURE) + { + cmdline_parser_free (args_info); + exit (EXIT_FAILURE); + } + + return result; +} + +int +cmdline_parser_required (struct gengetopt_args_info *args_info, const char *prog_name) +{ + FIX_UNUSED (args_info); + FIX_UNUSED (prog_name); + return EXIT_SUCCESS; +} + +/* + * Extracted from the glibc source tree, version 2.3.6 + * + * Licensed under the GPL as per the whole glibc source tree. + * + * This file was modified so that getopt_long can be called + * many times without risking previous memory to be spoiled. + * + * Modified by Andre Noll and Lorenzo Bettini for use in + * GNU gengetopt generated files. + * + */ + +/* + * we must include anything we need since this file is not thought to be + * inserted in a file already using getopt.h + * + * Lorenzo + */ + +struct option +{ + const char *name; + /* has_arg can't be an enum because some compilers complain about + type mismatches in all the code that assumes it is an int. */ + int has_arg; + int *flag; + int val; +}; + +/* This version of `getopt' appears to the caller like standard Unix `getopt' + but it behaves differently for the user, since it allows the user + to intersperse the options with the other arguments. + + As `getopt' works, it permutes the elements of ARGV so that, + when it is done, all the options precede everything else. Thus + all application programs are extended to handle flexible argument order. +*/ +/* + If the field `flag' is not NULL, it points to a variable that is set + to the value given in the field `val' when the option is found, but + left unchanged if the option is not found. + + To have a long-named option do something other than set an `int' to + a compiled-in constant, such as set a value from `custom_optarg', set the + option's `flag' field to zero and its `val' field to a nonzero + value (the equivalent single-letter option character, if there is + one). For long options that have a zero `flag' field, `getopt' + returns the contents of the `val' field. */ + +/* Names for the values of the `has_arg' field of `struct option'. */ +#ifndef no_argument +#define no_argument 0 +#endif + +#ifndef required_argument +#define required_argument 1 +#endif + +#ifndef optional_argument +#define optional_argument 2 +#endif + +struct custom_getopt_data { + /* + * These have exactly the same meaning as the corresponding global variables, + * except that they are used for the reentrant versions of getopt. + */ + int custom_optind; + int custom_opterr; + int custom_optopt; + char *custom_optarg; + + /* True if the internal members have been initialized. */ + int initialized; + + /* + * The next char to be scanned in the option-element in which the last option + * character we returned was found. This allows us to pick up the scan where + * we left off. If this is zero, or a null string, it means resume the scan by + * advancing to the next ARGV-element. + */ + char *nextchar; + + /* + * Describe the part of ARGV that contains non-options that have been skipped. + * `first_nonopt' is the index in ARGV of the first of them; `last_nonopt' is + * the index after the last of them. + */ + int first_nonopt; + int last_nonopt; +}; + +/* + * the variables optarg, optind, opterr and optopt are renamed with + * the custom_ prefix so that they don't interfere with getopt ones. + * + * Moreover they're static so they are visible only from within the + * file where this very file will be included. + */ + +/* + * For communication from `custom_getopt' to the caller. When `custom_getopt' finds an + * option that takes an argument, the argument value is returned here. + */ +static char *custom_optarg; + +/* + * Index in ARGV of the next element to be scanned. This is used for + * communication to and from the caller and for communication between + * successive calls to `custom_getopt'. + * + * On entry to `custom_getopt', 1 means this is the first call; initialize. + * + * When `custom_getopt' returns -1, this is the index of the first of the non-option + * elements that the caller should itself scan. + * + * Otherwise, `custom_optind' communicates from one call to the next how much of ARGV + * has been scanned so far. + * + * 1003.2 says this must be 1 before any call. + */ +static int custom_optind = 1; + +/* + * Callers store zero here to inhibit the error message for unrecognized + * options. + */ +static int custom_opterr = 1; + +/* + * Set to an option character which was unrecognized. This must be initialized + * on some systems to avoid linking in the system's own getopt implementation. + */ +static int custom_optopt = '?'; + +/* + * Exchange two adjacent subsequences of ARGV. One subsequence is elements + * [first_nonopt,last_nonopt) which contains all the non-options that have been + * skipped so far. The other is elements [last_nonopt,custom_optind), which contains + * all the options processed since those non-options were skipped. + * `first_nonopt' and `last_nonopt' are relocated so that they describe the new + * indices of the non-options in ARGV after they are moved. + */ +static void exchange(char **argv, struct custom_getopt_data *d) +{ + int bottom = d->first_nonopt; + int middle = d->last_nonopt; + int top = d->custom_optind; + char *tem; + + /* + * Exchange the shorter segment with the far end of the longer segment. + * That puts the shorter segment into the right place. It leaves the + * longer segment in the right place overall, but it consists of two + * parts that need to be swapped next. + */ + while (top > middle && middle > bottom) { + if (top - middle > middle - bottom) { + /* Bottom segment is the short one. */ + int len = middle - bottom; + int i; + + /* Swap it with the top part of the top segment. */ + for (i = 0; i < len; i++) { + tem = argv[bottom + i]; + argv[bottom + i] = + argv[top - (middle - bottom) + i]; + argv[top - (middle - bottom) + i] = tem; + } + /* Exclude the moved bottom segment from further swapping. */ + top -= len; + } else { + /* Top segment is the short one. */ + int len = top - middle; + int i; + + /* Swap it with the bottom part of the bottom segment. */ + for (i = 0; i < len; i++) { + tem = argv[bottom + i]; + argv[bottom + i] = argv[middle + i]; + argv[middle + i] = tem; + } + /* Exclude the moved top segment from further swapping. */ + bottom += len; + } + } + /* Update records for the slots the non-options now occupy. */ + d->first_nonopt += (d->custom_optind - d->last_nonopt); + d->last_nonopt = d->custom_optind; +} + +/* Initialize the internal data when the first call is made. */ +static void custom_getopt_initialize(struct custom_getopt_data *d) +{ + /* + * Start processing options with ARGV-element 1 (since ARGV-element 0 + * is the program name); the sequence of previously skipped non-option + * ARGV-elements is empty. + */ + d->first_nonopt = d->last_nonopt = d->custom_optind; + d->nextchar = NULL; + d->initialized = 1; +} + +#define NONOPTION_P (argv[d->custom_optind][0] != '-' || argv[d->custom_optind][1] == '\0') + +/* return: zero: continue, nonzero: return given value to user */ +static int shuffle_argv(int argc, char *const *argv,const struct option *longopts, + struct custom_getopt_data *d) +{ + /* + * Give FIRST_NONOPT & LAST_NONOPT rational values if CUSTOM_OPTIND has been + * moved back by the user (who may also have changed the arguments). + */ + if (d->last_nonopt > d->custom_optind) + d->last_nonopt = d->custom_optind; + if (d->first_nonopt > d->custom_optind) + d->first_nonopt = d->custom_optind; + /* + * If we have just processed some options following some + * non-options, exchange them so that the options come first. + */ + if (d->first_nonopt != d->last_nonopt && + d->last_nonopt != d->custom_optind) + exchange((char **) argv, d); + else if (d->last_nonopt != d->custom_optind) + d->first_nonopt = d->custom_optind; + /* + * Skip any additional non-options and extend the range of + * non-options previously skipped. + */ + while (d->custom_optind < argc && NONOPTION_P) + d->custom_optind++; + d->last_nonopt = d->custom_optind; + /* + * The special ARGV-element `--' means premature end of options. Skip + * it like a null option, then exchange with previous non-options as if + * it were an option, then skip everything else like a non-option. + */ + if (d->custom_optind != argc && !strcmp(argv[d->custom_optind], "--")) { + d->custom_optind++; + if (d->first_nonopt != d->last_nonopt + && d->last_nonopt != d->custom_optind) + exchange((char **) argv, d); + else if (d->first_nonopt == d->last_nonopt) + d->first_nonopt = d->custom_optind; + d->last_nonopt = argc; + d->custom_optind = argc; + } + /* + * If we have done all the ARGV-elements, stop the scan and back over + * any non-options that we skipped and permuted. + */ + if (d->custom_optind == argc) { + /* + * Set the next-arg-index to point at the non-options that we + * previously skipped, so the caller will digest them. + */ + if (d->first_nonopt != d->last_nonopt) + d->custom_optind = d->first_nonopt; + return -1; + } + /* + * If we have come to a non-option and did not permute it, either stop + * the scan or describe it to the caller and pass it by. + */ + if (NONOPTION_P) { + d->custom_optarg = argv[d->custom_optind++]; + return 1; + } + /* + * We have found another option-ARGV-element. Skip the initial + * punctuation. + */ + d->nextchar = (argv[d->custom_optind] + 1 + (longopts != NULL && argv[d->custom_optind][1] == '-')); + return 0; +} + +/* + * Check whether the ARGV-element is a long option. + * + * If there's a long option "fubar" and the ARGV-element is "-fu", consider + * that an abbreviation of the long option, just like "--fu", and not "-f" with + * arg "u". + * + * This distinction seems to be the most useful approach. + * + */ +static int check_long_opt(int argc, char *const *argv, const char *optstring, + const struct option *longopts, int *longind, + int print_errors, struct custom_getopt_data *d) +{ + char *nameend; + const struct option *p; + const struct option *pfound = NULL; + int exact = 0; + int ambig = 0; + int indfound = -1; + int option_index; + + for (nameend = d->nextchar; *nameend && *nameend != '='; nameend++) + /* Do nothing. */ ; + + /* Test all long options for either exact match or abbreviated matches */ + for (p = longopts, option_index = 0; p->name; p++, option_index++) + if (!strncmp(p->name, d->nextchar, nameend - d->nextchar)) { + if ((unsigned int) (nameend - d->nextchar) + == (unsigned int) strlen(p->name)) { + /* Exact match found. */ + pfound = p; + indfound = option_index; + exact = 1; + break; + } else if (pfound == NULL) { + /* First nonexact match found. */ + pfound = p; + indfound = option_index; + } else if (pfound->has_arg != p->has_arg + || pfound->flag != p->flag + || pfound->val != p->val) + /* Second or later nonexact match found. */ + ambig = 1; + } + if (ambig && !exact) { + if (print_errors) { + fprintf(stderr, + "%s: option `%s' is ambiguous\n", + argv[0], argv[d->custom_optind]); + } + d->nextchar += strlen(d->nextchar); + d->custom_optind++; + d->custom_optopt = 0; + return '?'; + } + if (pfound) { + option_index = indfound; + d->custom_optind++; + if (*nameend) { + if (pfound->has_arg != no_argument) + d->custom_optarg = nameend + 1; + else { + if (print_errors) { + if (argv[d->custom_optind - 1][1] == '-') { + /* --option */ + fprintf(stderr, "%s: option `--%s' doesn't allow an argument\n", + argv[0], pfound->name); + } else { + /* +option or -option */ + fprintf(stderr, "%s: option `%c%s' doesn't allow an argument\n", + argv[0], argv[d->custom_optind - 1][0], pfound->name); + } + + } + d->nextchar += strlen(d->nextchar); + d->custom_optopt = pfound->val; + return '?'; + } + } else if (pfound->has_arg == required_argument) { + if (d->custom_optind < argc) + d->custom_optarg = argv[d->custom_optind++]; + else { + if (print_errors) { + fprintf(stderr, + "%s: option `%s' requires an argument\n", + argv[0], + argv[d->custom_optind - 1]); + } + d->nextchar += strlen(d->nextchar); + d->custom_optopt = pfound->val; + return optstring[0] == ':' ? ':' : '?'; + } + } + d->nextchar += strlen(d->nextchar); + if (longind != NULL) + *longind = option_index; + if (pfound->flag) { + *(pfound->flag) = pfound->val; + return 0; + } + return pfound->val; + } + /* + * Can't find it as a long option. If this is not getopt_long_only, or + * the option starts with '--' or is not a valid short option, then + * it's an error. Otherwise interpret it as a short option. + */ + if (print_errors) { + if (argv[d->custom_optind][1] == '-') { + /* --option */ + fprintf(stderr, + "%s: unrecognized option `--%s'\n", + argv[0], d->nextchar); + } else { + /* +option or -option */ + fprintf(stderr, + "%s: unrecognized option `%c%s'\n", + argv[0], argv[d->custom_optind][0], + d->nextchar); + } + } + d->nextchar = (char *) ""; + d->custom_optind++; + d->custom_optopt = 0; + return '?'; +} + +static int check_short_opt(int argc, char *const *argv, const char *optstring, + int print_errors, struct custom_getopt_data *d) +{ + char c = *d->nextchar++; + const char *temp = strchr(optstring, c); + + /* Increment `custom_optind' when we start to process its last character. */ + if (*d->nextchar == '\0') + ++d->custom_optind; + if (!temp || c == ':') { + if (print_errors) + fprintf(stderr, "%s: invalid option -- %c\n", argv[0], c); + + d->custom_optopt = c; + return '?'; + } + if (temp[1] == ':') { + if (temp[2] == ':') { + /* This is an option that accepts an argument optionally. */ + if (*d->nextchar != '\0') { + d->custom_optarg = d->nextchar; + d->custom_optind++; + } else + d->custom_optarg = NULL; + d->nextchar = NULL; + } else { + /* This is an option that requires an argument. */ + if (*d->nextchar != '\0') { + d->custom_optarg = d->nextchar; + /* + * If we end this ARGV-element by taking the + * rest as an arg, we must advance to the next + * element now. + */ + d->custom_optind++; + } else if (d->custom_optind == argc) { + if (print_errors) { + fprintf(stderr, + "%s: option requires an argument -- %c\n", + argv[0], c); + } + d->custom_optopt = c; + if (optstring[0] == ':') + c = ':'; + else + c = '?'; + } else + /* + * We already incremented `custom_optind' once; + * increment it again when taking next ARGV-elt + * as argument. + */ + d->custom_optarg = argv[d->custom_optind++]; + d->nextchar = NULL; + } + } + return c; +} + +/* + * Scan elements of ARGV for option characters given in OPTSTRING. + * + * If an element of ARGV starts with '-', and is not exactly "-" or "--", + * then it is an option element. The characters of this element + * (aside from the initial '-') are option characters. If `getopt' + * is called repeatedly, it returns successively each of the option characters + * from each of the option elements. + * + * If `getopt' finds another option character, it returns that character, + * updating `custom_optind' and `nextchar' so that the next call to `getopt' can + * resume the scan with the following option character or ARGV-element. + * + * If there are no more option characters, `getopt' returns -1. + * Then `custom_optind' is the index in ARGV of the first ARGV-element + * that is not an option. (The ARGV-elements have been permuted + * so that those that are not options now come last.) + * + * OPTSTRING is a string containing the legitimate option characters. + * If an option character is seen that is not listed in OPTSTRING, + * return '?' after printing an error message. If you set `custom_opterr' to + * zero, the error message is suppressed but we still return '?'. + * + * If a char in OPTSTRING is followed by a colon, that means it wants an arg, + * so the following text in the same ARGV-element, or the text of the following + * ARGV-element, is returned in `custom_optarg'. Two colons mean an option that + * wants an optional arg; if there is text in the current ARGV-element, + * it is returned in `custom_optarg', otherwise `custom_optarg' is set to zero. + * + * If OPTSTRING starts with `-' or `+', it requests different methods of + * handling the non-option ARGV-elements. + * See the comments about RETURN_IN_ORDER and REQUIRE_ORDER, above. + * + * Long-named options begin with `--' instead of `-'. + * Their names may be abbreviated as long as the abbreviation is unique + * or is an exact match for some defined option. If they have an + * argument, it follows the option name in the same ARGV-element, separated + * from the option name by a `=', or else the in next ARGV-element. + * When `getopt' finds a long-named option, it returns 0 if that option's + * `flag' field is nonzero, the value of the option's `val' field + * if the `flag' field is zero. + * + * The elements of ARGV aren't really const, because we permute them. + * But we pretend they're const in the prototype to be compatible + * with other systems. + * + * LONGOPTS is a vector of `struct option' terminated by an + * element containing a name which is zero. + * + * LONGIND returns the index in LONGOPT of the long-named option found. + * It is only valid when a long-named option has been found by the most + * recent call. + * + * Return the option character from OPTS just read. Return -1 when there are + * no more options. For unrecognized options, or options missing arguments, + * `custom_optopt' is set to the option letter, and '?' is returned. + * + * The OPTS string is a list of characters which are recognized option letters, + * optionally followed by colons, specifying that that letter takes an + * argument, to be placed in `custom_optarg'. + * + * If a letter in OPTS is followed by two colons, its argument is optional. + * This behavior is specific to the GNU `getopt'. + * + * The argument `--' causes premature termination of argument scanning, + * explicitly telling `getopt' that there are no more options. If OPTS begins + * with `--', then non-option arguments are treated as arguments to the option + * '\0'. This behavior is specific to the GNU `getopt'. + */ + +static int getopt_internal_r(int argc, char *const *argv, const char *optstring, + const struct option *longopts, int *longind, + struct custom_getopt_data *d) +{ + int ret, print_errors = d->custom_opterr; + + if (optstring[0] == ':') + print_errors = 0; + if (argc < 1) + return -1; + d->custom_optarg = NULL; + + /* + * This is a big difference with GNU getopt, since optind == 0 + * means initialization while here 1 means first call. + */ + if (d->custom_optind == 0 || !d->initialized) { + if (d->custom_optind == 0) + d->custom_optind = 1; /* Don't scan ARGV[0], the program name. */ + custom_getopt_initialize(d); + } + if (d->nextchar == NULL || *d->nextchar == '\0') { + ret = shuffle_argv(argc, argv, longopts, d); + if (ret) + return ret; + } + if (longopts && (argv[d->custom_optind][1] == '-' )) + return check_long_opt(argc, argv, optstring, longopts, + longind, print_errors, d); + return check_short_opt(argc, argv, optstring, print_errors, d); +} + +static int custom_getopt_internal(int argc, char *const *argv, const char *optstring, + const struct option *longopts, int *longind) +{ + int result; + /* Keep a global copy of all internal members of d */ + static struct custom_getopt_data d; + + d.custom_optind = custom_optind; + d.custom_opterr = custom_opterr; + result = getopt_internal_r(argc, argv, optstring, longopts, + longind, &d); + custom_optind = d.custom_optind; + custom_optarg = d.custom_optarg; + custom_optopt = d.custom_optopt; + return result; +} + +static int custom_getopt_long (int argc, char *const *argv, const char *options, + const struct option *long_options, int *opt_index) +{ + return custom_getopt_internal(argc, argv, options, long_options, + opt_index); +} + + +static char *package_name = 0; + +/** + * @brief updates an option + * @param field the generic pointer to the field to update + * @param orig_field the pointer to the orig field + * @param field_given the pointer to the number of occurrence of this option + * @param prev_given the pointer to the number of occurrence already seen + * @param value the argument for this option (if null no arg was specified) + * @param possible_values the possible values for this option (if specified) + * @param default_value the default value (in case the option only accepts fixed values) + * @param arg_type the type of this option + * @param check_ambiguity @see cmdline_parser_params.check_ambiguity + * @param override @see cmdline_parser_params.override + * @param no_free whether to free a possible previous value + * @param multiple_option whether this is a multiple option + * @param long_opt the corresponding long option + * @param short_opt the corresponding short option (or '-' if none) + * @param additional_error possible further error specification + */ +static +int update_arg(void *field, char **orig_field, + unsigned int *field_given, unsigned int *prev_given, + char *value, const char *possible_values[], + const char *default_value, + cmdline_parser_arg_type arg_type, + int check_ambiguity, int override, + int no_free, int multiple_option, + const char *long_opt, char short_opt, + const char *additional_error) +{ + char *stop_char = 0; + const char *val = value; + int found; + char **string_field; + FIX_UNUSED (field); + + stop_char = 0; + found = 0; + + if (!multiple_option && prev_given && (*prev_given || (check_ambiguity && *field_given))) + { + if (short_opt != '-') + fprintf (stderr, "%s: `--%s' (`-%c') option given more than once%s\n", + package_name, long_opt, short_opt, + (additional_error ? additional_error : "")); + else + fprintf (stderr, "%s: `--%s' option given more than once%s\n", + package_name, long_opt, + (additional_error ? additional_error : "")); + return 1; /* failure */ + } + + FIX_UNUSED (default_value); + + if (field_given && *field_given && ! override) + return 0; + if (prev_given) + (*prev_given)++; + if (field_given) + (*field_given)++; + if (possible_values) + val = possible_values[found]; + + switch(arg_type) { + case ARG_FLAG: + *((int *)field) = !*((int *)field); + break; + case ARG_STRING: + if (val) { + string_field = (char **)field; + if (!no_free && *string_field) + free (*string_field); /* free previous string */ + *string_field = gengetopt_strdup (val); + } + break; + default: + break; + }; + + + /* store the original value */ + switch(arg_type) { + case ARG_NO: + case ARG_FLAG: + break; + default: + if (value && orig_field) { + if (no_free) { + *orig_field = value; + } else { + if (*orig_field) + free (*orig_field); /* free previous string */ + *orig_field = gengetopt_strdup (value); + } + } + }; + + return 0; /* OK */ +} + + +static int check_modes( + int given1[], const char *options1[], + int given2[], const char *options2[]) +{ + int i = 0, j = 0, errors = 0; + + while (given1[i] >= 0) { + if (given1[i]) { + while (given2[j] >= 0) { + if (given2[j]) { + ++errors; + fprintf(stderr, "%s: option %s conflicts with option %s\n", + package_name, options1[i], options2[j]); + } + ++j; + } + } + ++i; + } + + return errors; +} + +int +cmdline_parser_internal ( + int argc, char **argv, struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params, const char *additional_error) +{ + int c; /* Character of the parsed option. */ + + int error_occurred = 0; + struct gengetopt_args_info local_args_info; + + int override; + int initialize; + int check_required; + int check_ambiguity; + + char *optarg; + int optind; + int opterr; + int optopt; + + package_name = argv[0]; + + override = params->override; + initialize = params->initialize; + check_required = params->check_required; + check_ambiguity = params->check_ambiguity; + + if (initialize) + cmdline_parser_init (args_info); + + cmdline_parser_init (&local_args_info); + + optarg = 0; + optind = 0; + opterr = params->print_errors; + optopt = '?'; + + while (1) + { + int option_index = 0; + + static struct option long_options[] = { + { "help", 0, NULL, 'h' }, + { "version", 0, NULL, 'V' }, + { "title", 2, NULL, 't' }, + { "message", 2, NULL, 'm' }, + { "notify-card-inserted", 0, NULL, 'I' }, + { "notify-card-removed", 0, NULL, 'R' }, + { "notify-pin-good", 0, NULL, 'G' }, + { "notify-pin-bad", 0, NULL, 'B' }, + { 0, 0, 0, 0 } + }; + + custom_optarg = optarg; + custom_optind = optind; + custom_opterr = opterr; + custom_optopt = optopt; + + c = custom_getopt_long (argc, argv, "hVt::m::IRGB", long_options, &option_index); + + optarg = custom_optarg; + optind = custom_optind; + opterr = custom_opterr; + optopt = custom_optopt; + + if (c == -1) break; /* Exit from `while (1)' loop. */ + + switch (c) + { + case 'h': /* Print help and exit. */ + cmdline_parser_print_help (); + cmdline_parser_free (&local_args_info); + exit (EXIT_SUCCESS); + + case 'V': /* Print version and exit. */ + cmdline_parser_print_version (); + cmdline_parser_free (&local_args_info); + exit (EXIT_SUCCESS); + + case 't': /* Title of the notification. */ + args_info->customized_mode_counter += 1; + + + if (update_arg( (void *)&(args_info->title_arg), + &(args_info->title_orig), &(args_info->title_given), + &(local_args_info.title_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "title", 't', + additional_error)) + goto failure; + + break; + case 'm': /* Main text of the notification. */ + args_info->customized_mode_counter += 1; + + + if (update_arg( (void *)&(args_info->message_arg), + &(args_info->message_orig), &(args_info->message_given), + &(local_args_info.message_given), optarg, 0, 0, ARG_STRING, + check_ambiguity, override, 0, 0, + "message", 'm', + additional_error)) + goto failure; + + break; + case 'I': /* See notify_card_inserted in opensc.conf. */ + args_info->standard_mode_counter += 1; + + + if (update_arg((void *)&(args_info->notify_card_inserted_flag), 0, &(args_info->notify_card_inserted_given), + &(local_args_info.notify_card_inserted_given), optarg, 0, 0, ARG_FLAG, + check_ambiguity, override, 1, 0, "notify-card-inserted", 'I', + additional_error)) + goto failure; + + break; + case 'R': /* See notify_card_inserted in opensc.conf. */ + args_info->standard_mode_counter += 1; + + + if (update_arg((void *)&(args_info->notify_card_removed_flag), 0, &(args_info->notify_card_removed_given), + &(local_args_info.notify_card_removed_given), optarg, 0, 0, ARG_FLAG, + check_ambiguity, override, 1, 0, "notify-card-removed", 'R', + additional_error)) + goto failure; + + break; + case 'G': /* See notify_pin_good in opensc.conf. */ + args_info->standard_mode_counter += 1; + + + if (update_arg((void *)&(args_info->notify_pin_good_flag), 0, &(args_info->notify_pin_good_given), + &(local_args_info.notify_pin_good_given), optarg, 0, 0, ARG_FLAG, + check_ambiguity, override, 1, 0, "notify-pin-good", 'G', + additional_error)) + goto failure; + + break; + case 'B': /* See notify_pin_bad in opensc.conf. */ + args_info->standard_mode_counter += 1; + + + if (update_arg((void *)&(args_info->notify_pin_bad_flag), 0, &(args_info->notify_pin_bad_given), + &(local_args_info.notify_pin_bad_given), optarg, 0, 0, ARG_FLAG, + check_ambiguity, override, 1, 0, "notify-pin-bad", 'B', + additional_error)) + goto failure; + + break; + + case 0: /* Long option with no short option */ + case '?': /* Invalid option. */ + /* `getopt_long' already printed an error message. */ + goto failure; + + default: /* bug: option not considered. */ + fprintf (stderr, "%s: option unknown: %c%s\n", CMDLINE_PARSER_PACKAGE, c, (additional_error ? additional_error : "")); + abort (); + } /* switch */ + } /* while */ + + + + if (args_info->customized_mode_counter && args_info->standard_mode_counter) { + int customized_given[] = {args_info->title_given, args_info->message_given, -1}; + const char *customized_desc[] = {"--title", "--message", 0}; + int standard_given[] = {args_info->notify_card_inserted_given, args_info->notify_card_removed_given, args_info->notify_pin_good_given, args_info->notify_pin_bad_given, -1}; + const char *standard_desc[] = {"--notify-card-inserted", "--notify-card-removed", "--notify-pin-good", "--notify-pin-bad", 0}; + error_occurred += check_modes(customized_given, customized_desc, standard_given, standard_desc); + } + + + cmdline_parser_release (&local_args_info); + + if ( error_occurred ) + return (EXIT_FAILURE); + + return 0; + +failure: + + cmdline_parser_release (&local_args_info); + return (EXIT_FAILURE); +} diff --git a/src/tools/opensc-notify-cmdline.h b/src/tools/opensc-notify-cmdline.h new file mode 100644 index 00000000..f0783d62 --- /dev/null +++ b/src/tools/opensc-notify-cmdline.h @@ -0,0 +1,196 @@ +/** @file opensc-notify-cmdline.h + * @brief The header file for the command line option parser + * generated by GNU Gengetopt version 2.22.6 + * http://www.gnu.org/software/gengetopt. + * DO NOT modify this file, since it can be overwritten + * @author GNU Gengetopt by Lorenzo Bettini */ + +#ifndef OPENSC_NOTIFY_CMDLINE_H +#define OPENSC_NOTIFY_CMDLINE_H + +/* If we use autoconf. */ +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include /* for FILE */ + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +#ifndef CMDLINE_PARSER_PACKAGE +/** @brief the program name (used for printing errors) */ +#define CMDLINE_PARSER_PACKAGE "opensc-notify" +#endif + +#ifndef CMDLINE_PARSER_PACKAGE_NAME +/** @brief the complete program name (used for help and version) */ +#define CMDLINE_PARSER_PACKAGE_NAME "opensc-notify" +#endif + +#ifndef CMDLINE_PARSER_VERSION +/** @brief the program version */ +#define CMDLINE_PARSER_VERSION VERSION +#endif + +/** @brief Where the command line options are stored */ +struct gengetopt_args_info +{ + const char *help_help; /**< @brief Print help and exit help description. */ + const char *version_help; /**< @brief Print version and exit help description. */ + char * title_arg; /**< @brief Title of the notification. */ + char * title_orig; /**< @brief Title of the notification original value given at command line. */ + const char *title_help; /**< @brief Title of the notification help description. */ + char * message_arg; /**< @brief Main text of the notification. */ + char * message_orig; /**< @brief Main text of the notification original value given at command line. */ + const char *message_help; /**< @brief Main text of the notification help description. */ + int notify_card_inserted_flag; /**< @brief See notify_card_inserted in opensc.conf (default=off). */ + const char *notify_card_inserted_help; /**< @brief See notify_card_inserted in opensc.conf help description. */ + int notify_card_removed_flag; /**< @brief See notify_card_inserted in opensc.conf (default=off). */ + const char *notify_card_removed_help; /**< @brief See notify_card_inserted in opensc.conf help description. */ + int notify_pin_good_flag; /**< @brief See notify_pin_good in opensc.conf (default=off). */ + const char *notify_pin_good_help; /**< @brief See notify_pin_good in opensc.conf help description. */ + int notify_pin_bad_flag; /**< @brief See notify_pin_bad in opensc.conf (default=off). */ + const char *notify_pin_bad_help; /**< @brief See notify_pin_bad in opensc.conf help description. */ + + unsigned int help_given ; /**< @brief Whether help was given. */ + unsigned int version_given ; /**< @brief Whether version was given. */ + unsigned int title_given ; /**< @brief Whether title was given. */ + unsigned int message_given ; /**< @brief Whether message was given. */ + unsigned int notify_card_inserted_given ; /**< @brief Whether notify-card-inserted was given. */ + unsigned int notify_card_removed_given ; /**< @brief Whether notify-card-removed was given. */ + unsigned int notify_pin_good_given ; /**< @brief Whether notify-pin-good was given. */ + unsigned int notify_pin_bad_given ; /**< @brief Whether notify-pin-bad was given. */ + + int customized_mode_counter; /**< @brief Counter for mode customized */ + int daemon_mode_counter; /**< @brief Counter for mode daemon */ + int standard_mode_counter; /**< @brief Counter for mode standard */ +} ; + +/** @brief The additional parameters to pass to parser functions */ +struct cmdline_parser_params +{ + int override; /**< @brief whether to override possibly already present options (default 0) */ + int initialize; /**< @brief whether to initialize the option structure gengetopt_args_info (default 1) */ + int check_required; /**< @brief whether to check that all required options were provided (default 1) */ + int check_ambiguity; /**< @brief whether to check for options already specified in the option structure gengetopt_args_info (default 0) */ + int print_errors; /**< @brief whether getopt_long should print an error message for a bad option (default 1) */ +} ; + +/** @brief the purpose string of the program */ +extern const char *gengetopt_args_info_purpose; +/** @brief the usage string of the program */ +extern const char *gengetopt_args_info_usage; +/** @brief the description string of the program */ +extern const char *gengetopt_args_info_description; +/** @brief all the lines making the help output */ +extern const char *gengetopt_args_info_help[]; + +/** + * The command line parser + * @param argc the number of command line options + * @param argv the command line options + * @param args_info the structure where option information will be stored + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser (int argc, char **argv, + struct gengetopt_args_info *args_info); + +/** + * The command line parser (version with additional parameters - deprecated) + * @param argc the number of command line options + * @param argv the command line options + * @param args_info the structure where option information will be stored + * @param override whether to override possibly already present options + * @param initialize whether to initialize the option structure my_args_info + * @param check_required whether to check that all required options were provided + * @return 0 if everything went fine, NON 0 if an error took place + * @deprecated use cmdline_parser_ext() instead + */ +int cmdline_parser2 (int argc, char **argv, + struct gengetopt_args_info *args_info, + int override, int initialize, int check_required); + +/** + * The command line parser (version with additional parameters) + * @param argc the number of command line options + * @param argv the command line options + * @param args_info the structure where option information will be stored + * @param params additional parameters for the parser + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser_ext (int argc, char **argv, + struct gengetopt_args_info *args_info, + struct cmdline_parser_params *params); + +/** + * Save the contents of the option struct into an already open FILE stream. + * @param outfile the stream where to dump options + * @param args_info the option struct to dump + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser_dump(FILE *outfile, + struct gengetopt_args_info *args_info); + +/** + * Save the contents of the option struct into a (text) file. + * This file can be read by the config file parser (if generated by gengetopt) + * @param filename the file where to save + * @param args_info the option struct to save + * @return 0 if everything went fine, NON 0 if an error took place + */ +int cmdline_parser_file_save(const char *filename, + struct gengetopt_args_info *args_info); + +/** + * Print the help + */ +void cmdline_parser_print_help(void); +/** + * Print the version + */ +void cmdline_parser_print_version(void); + +/** + * Initializes all the fields a cmdline_parser_params structure + * to their default values + * @param params the structure to initialize + */ +void cmdline_parser_params_init(struct cmdline_parser_params *params); + +/** + * Allocates dynamically a cmdline_parser_params structure and initializes + * all its fields to their default values + * @return the created and initialized cmdline_parser_params structure + */ +struct cmdline_parser_params *cmdline_parser_params_create(void); + +/** + * Initializes the passed gengetopt_args_info structure's fields + * (also set default values for options that have a default) + * @param args_info the structure to initialize + */ +void cmdline_parser_init (struct gengetopt_args_info *args_info); +/** + * Deallocates the string fields of the gengetopt_args_info structure + * (but does not deallocate the structure itself) + * @param args_info the structure to deallocate + */ +void cmdline_parser_free (struct gengetopt_args_info *args_info); + +/** + * Checks that all the required options were specified + * @param args_info the structure to check + * @param prog_name the name of the program that will be used to print + * possible errors + * @return + */ +int cmdline_parser_required (struct gengetopt_args_info *args_info, + const char *prog_name); + + +#ifdef __cplusplus +} +#endif /* __cplusplus */ +#endif /* OPENSC_NOTIFY_CMDLINE_H */ diff --git a/src/tools/opensc-notify.c b/src/tools/opensc-notify.c index 18a9ce35..91f9b912 100644 --- a/src/tools/opensc-notify.c +++ b/src/tools/opensc-notify.c @@ -21,8 +21,24 @@ #include "config.h" #endif +#include "libopensc/log.h" +#include "opensc-notify-cmdline.h" #include "ui/notify.h" +#include #include +#include +#include + +static int run_daemon = 0; +static struct sc_context *ctx = NULL; + +void stop_daemon(int signo) +{ +#ifdef PCSCLITE_GOOD + sc_cancel(ctx); +#endif + run_daemon = 0; +} #ifndef _WIN32 #include @@ -41,30 +57,183 @@ void Sleep(unsigned int Milliseconds) nanosleep(&req , &rem); } +#else +#include "ui/invisible_window.h" +static HINSTANCE g_hInstance = NULL; + +LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) +{ + if (message == WM_CLOSE) { + sc_cancel(ctx); + run_daemon = 0; + return TRUE; + } + + return DefWindowProc(hwnd, message, wParam, lParam); +} #endif +void notify_daemon(void) +{ + int r; + const unsigned int event_mask = SC_EVENT_CARD_EVENTS; + unsigned int event; + struct sc_reader *event_reader = NULL; + size_t error_count = 0; + /* timeout adjusted to the maximum response time for WM_CLOSE in case + * canceling doesn't work */ + const int timeout = 20000; + struct sc_atr old_atr; + void *reader_states = NULL; +#ifdef _WIN32 + LPCTSTR lpszClassName = "OPENSC_NOTIFY_CLASS"; + HWND hwnd = create_invisible_window(lpszClassName, WndProc, g_hInstance); +#elif HAVE_SIGACTION + struct sigaction new_sig, old_sig; + + /* Register signal handlers */ + new_sig.sa_handler = stop_daemon; + sigemptyset(&new_sig.sa_mask); + new_sig.sa_flags = SA_RESTART; + if ((sigaction(SIGINT, &new_sig, &old_sig) < 0) + || (sigaction(SIGTERM, &new_sig, &old_sig) < 0)) { + fprintf(stderr, "Failed to create signal handler: %s", strerror(errno)); + return; + } +#endif + + r = sc_establish_context(&ctx, "opensc-notify"); + if (r < 0 || !ctx) { + fprintf(stderr, "Failed to create initial context: %s", sc_strerror(r)); + return; + } + + while (run_daemon) { + if (0 > sc_wait_for_event(ctx, event_mask, &event_reader, + &event, timeout, &reader_states)) { + error_count++; + continue; + } + + error_count = 0; + + if (event & SC_EVENT_CARD_REMOVED) { + sc_notify_id(ctx, &old_atr, NULL, NOTIFY_CARD_REMOVED); + } + if (event & SC_EVENT_CARD_INSERTED) { + if (event_reader) { + /* FIXME `pcsc_wait_for_event` has all the information that's + * requested again with `pcsc_detect_card_presence`, but it + * doesn't use the ATR, for example, to refresh the reader's + * attributes. To get the ATR we need to call + * sc_detect_card_presence. Eventually this should be fixed. */ + sc_detect_card_presence(event_reader); + memcpy(old_atr.value, event_reader->atr.value, + event_reader->atr.len); + old_atr.len = event_reader->atr.len; + } else { + old_atr.len = 0; + } + sc_notify_id(ctx, old_atr.len ? &old_atr : NULL, NULL, + NOTIFY_CARD_INSERTED); + } + } + + if (ctx) { + /* free `reader_states` */ + sc_wait_for_event(ctx, 0, NULL, NULL, 0, &reader_states); + reader_states = NULL; + sc_release_context(ctx); + ctx = NULL; + } +#ifdef _WIN32 + delete_invisible_window(hwnd, lpszClassName, g_hInstance); +#endif +} + +#ifdef _WIN32 +#include "ui/char_str_from_wchar.h" +#include + +/* This application shall be executable without a console. Therefor we're + * creating a windows application that requires `WinMain()` rather than + * `main()` as entry point. As benefit, we can properly handle `WM_CLOSE`. */ +int WINAPI +WinMain(HINSTANCE hInstance, HINSTANCE prevInstance, LPSTR lpCmdLine, int nShowCmd) +{ + LPWSTR *wargv = NULL; + char **argv = NULL; + int argc = 0, i; + struct gengetopt_args_info cmdline; + + wargv = CommandLineToArgvW(GetCommandLineW(), &argc); + if (wargv == NULL) { + return 1; + } + + argv = LocalAlloc(0, (sizeof *argv) * argc); + if (argv == NULL) { + goto err; + } + for (i = 0; i < argc; i++) { + argv[i] = char_str_from_wchar(wargv[i]); + } + + g_hInstance = hInstance; + +#else int main (int argc, char **argv) { - const char *title = NULL, *text = NULL; - switch (argc) { - case 3: - text = argv[2]; - /* fall through */ - case 2: - title = argv[1]; - /* fall through */ - case 1: - break; + struct gengetopt_args_info cmdline; +#endif + + if (cmdline_parser(argc, argv, &cmdline) != 0) + goto err; - default: - fprintf(stderr, "Usage: opensc-notify [title [text]]"); - return 1; - } sc_notify_init(); - sc_notify(title, text); - Sleep(250); + + if (cmdline.customized_mode_counter) { + sc_notify(cmdline.title_arg, cmdline.message_arg); + } + + if (cmdline.standard_mode_counter) { + if (cmdline.notify_card_inserted_flag) { + sc_notify_id(NULL, NULL, NULL, NOTIFY_CARD_INSERTED); + } + if (cmdline.notify_card_removed_flag) { + sc_notify_id(NULL, NULL, NULL, NOTIFY_CARD_REMOVED); + } + if (cmdline.notify_pin_good_flag) { + sc_notify_id(NULL, NULL, NULL, NOTIFY_PIN_GOOD); + } + if (cmdline.notify_pin_bad_flag) { + sc_notify_id(NULL, NULL, NULL, NOTIFY_PIN_BAD); + } + } + + if ((!cmdline.customized_mode_counter && !cmdline.standard_mode_counter) + || cmdline.daemon_mode_counter) { + run_daemon = 1; + notify_daemon(); + } else { + /* give the notification process some time to spawn */ + Sleep(100); + } + sc_notify_close(); + cmdline_parser_free (&cmdline); +err: +#ifdef _WIN32 + if (argv) { + for (i = 0; i <= argc; i++) { + LocalFree(argv[i]); + } + LocalFree(argv); + } + LocalFree(wargv); +#endif + return 0; } diff --git a/src/tools/opensc-notify.ggo.in b/src/tools/opensc-notify.ggo.in new file mode 100644 index 00000000..f419835a --- /dev/null +++ b/src/tools/opensc-notify.ggo.in @@ -0,0 +1,45 @@ +package "opensc-notify" +purpose "@PACKAGE_SUMMARY@" +description "If no arguments are given, monitor smart card events and send the appropriate notification." + +defmode "daemon" + modedesc="Monitor smart card events to send notifications." +defmode "standard" + modedesc="Manually send standard notifications." +defmode "customized" + modedesc="Send customized notifications." + +modeoption "title" t + "Title of the notification" + string + mode="customized" + argoptional + optional +modeoption "message" m + "Main text of the notification" + string + mode="customized" + argoptional + optional + +modeoption "notify-card-inserted" I + "See notify_card_inserted in opensc.conf" + flag off + mode="standard" +modeoption "notify-card-removed" R + "See notify_card_inserted in opensc.conf" + flag off + mode="standard" +modeoption "notify-pin-good" G + "See notify_pin_good in opensc.conf" + flag off + mode="standard" +modeoption "notify-pin-bad" B + "See notify_pin_bad in opensc.conf" + flag off + mode="standard" + +text " +Report bugs to @PACKAGE_BUGREPORT@ + +Written by Frank Morgner " diff --git a/src/ui/char_str_from_wchar.h b/src/ui/char_str_from_wchar.h new file mode 100644 index 00000000..270251c0 --- /dev/null +++ b/src/ui/char_str_from_wchar.h @@ -0,0 +1,46 @@ +/* + * char_str_from_wchar.h: Conversion from wide string to string + * + * Copyright (C) 2017 Frank Morgner + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it 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 this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +static char *char_str_from_wchar(const WCHAR *in) +{ + char *out; + int out_len; + + if (!in) + return NULL; + + out_len = WideCharToMultiByte(CP_UTF8, 0, in, -1, NULL, 0, NULL, NULL); + if (0 >= out_len) + return NULL; + + out = LocalAlloc(0, (sizeof *out) * out_len); + if (!out) + return NULL; + + out_len = WideCharToMultiByte(CP_UTF8, 0, in, -1, out, out_len, NULL, NULL); + if (out_len == 0xFFFD || 0 >= out_len) { + LocalFree(out); + return NULL; + } + + return out; +} diff --git a/src/ui/invisible_window.h b/src/ui/invisible_window.h new file mode 100644 index 00000000..f75056c2 --- /dev/null +++ b/src/ui/invisible_window.h @@ -0,0 +1,52 @@ +/* + * invisible_window.h: Create invisible Window + * + * Copyright (C) 2017 Frank Morgner + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it 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 this library; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include + +HWND create_invisible_window(LPCTSTR lpszClassName, + LRESULT (CALLBACK* WndProc)(HWND, UINT, WPARAM, LPARAM), + HINSTANCE hInstance) +{ + HWND hWnd = NULL; + WNDCLASSEX wx = {0}; + + //Register Window class + wx.cbSize = sizeof(WNDCLASSEX); + wx.lpfnWndProc = WndProc; + wx.hInstance = hInstance; + wx.lpszClassName = lpszClassName; + if (RegisterClassEx(&wx)) { + /* create window */ + hWnd = CreateWindowEx(0, lpszClassName, lpszClassName, 0, 0, 0, 0, 0, + HWND_MESSAGE, NULL, NULL, NULL ); + } + + return hWnd; +} + +static BOOL delete_invisible_window(HWND hWnd, LPCTSTR lpszClassName, + HINSTANCE hInstance) +{ + BOOL r; + r = DestroyWindow(hWnd); + r &= UnregisterClass(lpszClassName, hInstance); + + return r; +} diff --git a/src/ui/notify.c b/src/ui/notify.c index faad0af8..fff0f6b5 100644 --- a/src/ui/notify.c +++ b/src/ui/notify.c @@ -65,6 +65,7 @@ void sc_notify_close(void) #if defined(ENABLE_NOTIFY) && defined(_WIN32) +#include "invisible_window.h" #include "wchar_from_char_str.h" #include @@ -72,13 +73,14 @@ static const GUID myGUID = {0x23977b55, 0x10e0, 0x4041, {0xb8, 0x62, 0xb1, 0x95, 0x41, 0x96, 0x36, 0x69}}; HINSTANCE sc_notify_instance = NULL; HWND hwndNotification = NULL; +BOOL delete_icon = TRUE; #define IDI_SMARTCARD 102 #define IDI_UNLOCKED 103 #define IDI_LOCKED 104 #define IDI_READER_EMPTY 105 #define IDI_CARD_INSERTED 106 UINT const WMAPP_NOTIFYCALLBACK = WM_APP + 1; -BOOL RestoreTooltip(void); +static BOOL RestoreTooltip(); // we need commctrl v6 for LoadIconMetric() #include @@ -100,54 +102,16 @@ LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam) return DefWindowProc(hwnd, message, wParam, lParam); } -static const char* class_name = "DUMMY_CLASS"; -static const char* window_name = "DUMMY_WINDOW"; - -static BOOL create_invisible_window(void) -{ - if (!hwndNotification) { - //Register Window class - WNDCLASSEX wx = {0}; - wx.cbSize = sizeof(WNDCLASSEX); - wx.lpfnWndProc = WndProc; // function which will handle messages - wx.hInstance = sc_notify_instance; - wx.lpszClassName = class_name; - if (!RegisterClassEx(&wx)) { - return FALSE; - } - /* create window */ - hwndNotification = CreateWindowEx(0, class_name, window_name, - 0, 0, 0, 0, 0, HWND_MESSAGE, NULL, NULL, NULL ); - } - - if (hwndNotification) { - return TRUE; - } - - return FALSE; -} - -static BOOL delete_invisible_window(void) -{ - BOOL r; - r = DestroyWindow(hwndNotification); - r &= UnregisterClass(class_name, sc_notify_instance); - - hwndNotification = NULL; - - return r; -} +static const char* lpszClassName = "NOTIFY_CLASS"; static BOOL AddNotificationIcon(void) { - NOTIFYICONDATA nid = {sizeof(nid)}; + NOTIFYICONDATA nid; TCHAR path[MAX_PATH]={0}; BOOL r; - if (!create_invisible_window()) { - return FALSE; - } - + memset(&nid, 0, sizeof nid); + nid.cbSize = sizeof nid; nid.hWnd = hwndNotification; // add the icon, setting the icon, tooltip, and callback message. // the icon will be identified with the GUID @@ -156,7 +120,18 @@ static BOOL AddNotificationIcon(void) nid.uCallbackMessage = WMAPP_NOTIFYCALLBACK; LoadIconMetric(sc_notify_instance, MAKEINTRESOURCEW(IDI_SMARTCARD), LIM_SMALL, &nid.hIcon); if (GetModuleFileName(NULL, path, ARRAYSIZE(path))) { - strcpy_s(nid.szTip, ARRAYSIZE(nid.szTip), path); + const char *basename = strrchr(path, '\\'); + if (basename) { + basename++; + if (0 != strcmp(basename, "opensc-notify.exe")) { + /* Allow creation of sytem tray icon only for + * "opensc-notify.exe" to avoid creation of the same icon by + * multiple processes. */ + delete_icon = FALSE; + return FALSE; + } + } + strlcpy(nid.szTip, path, ARRAYSIZE(nid.szTip)); } else { strcpy(nid.szTip, PACKAGE_NAME); } @@ -166,19 +141,32 @@ static BOOL AddNotificationIcon(void) nid.uVersion = NOTIFYICON_VERSION_4; r &= Shell_NotifyIcon(NIM_SETVERSION, &nid); + hwndNotification = create_invisible_window(lpszClassName, WndProc, sc_notify_instance); + if (!hwndNotification) { + r = FALSE; + } + return r; } static BOOL DeleteNotificationIcon(void) { BOOL r; - NOTIFYICONDATA nid = {sizeof(nid)}; + NOTIFYICONDATA nid; + if (!delete_icon) { + return FALSE; + } + + memset(&nid, 0, sizeof nid); + nid.cbSize = sizeof nid; nid.uFlags = NIF_GUID; nid.guidItem = myGUID; r = Shell_NotifyIcon(NIM_DELETE, &nid); - r &= delete_invisible_window(); + + r &= delete_invisible_window(hwndNotification, lpszClassName, + sc_notify_instance); return r; } @@ -186,8 +174,10 @@ static BOOL DeleteNotificationIcon(void) static BOOL RestoreTooltip() { // After the balloon is dismissed, restore the tooltip. - NOTIFYICONDATA nid = {sizeof(nid)}; + NOTIFYICONDATA nid; + memset(&nid, 0, sizeof nid); + nid.cbSize = sizeof nid; nid.uFlags = NIF_SHOWTIP | NIF_GUID; nid.guidItem = myGUID; @@ -197,17 +187,19 @@ static BOOL RestoreTooltip() static void notify_shell(struct sc_context *ctx, const char *title, const char *text, WORD icon) { - NOTIFYICONDATA nid = {sizeof(nid)}; + NOTIFYICONDATA nid; + memset(&nid, 0, sizeof nid); + nid.cbSize = sizeof nid; nid.uFlags = NIF_GUID; nid.guidItem = myGUID; if (title) { - strcpy_s(nid.szInfoTitle, ARRAYSIZE(nid.szInfoTitle), title); + strlcpy(nid.szInfoTitle, title, ARRAYSIZE(nid.szInfoTitle)); } if (text) { nid.uFlags |= NIF_INFO; - strcpy_s(nid.szInfo, ARRAYSIZE(nid.szInfo), text); + strlcpy(nid.szInfo, text, ARRAYSIZE(nid.szInfo)); } if (icon) { nid.dwInfoFlags = NIIF_USER | NIIF_LARGE_ICON; @@ -240,17 +232,11 @@ void sc_notify(const char *title, const char *text) 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, *group; + const char *title, *text; WORD icon; 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 = IDI_CARD_INSERTED; @@ -533,7 +519,7 @@ void sc_notify_id(struct sc_context *ctx, struct sc_atr *atr, #endif -#if defined(ENABLE_NOTIFY) && (defined(ENABLE_GIO2) || defined(GDBUS)) +#if defined(ENABLE_NOTIFY) && (defined(ENABLE_GIO2) || defined(GDBUS) && !defined(_WIN32)) void sc_notify(const char *title, const char *text) { notify_gio(NULL, title, text, NULL, NULL); diff --git a/win32/OpenSC.wxs.in b/win32/OpenSC.wxs.in index e34477e9..9c17ebce 100644 --- a/win32/OpenSC.wxs.in +++ b/win32/OpenSC.wxs.in @@ -138,6 +138,9 @@ + + + @@ -340,6 +343,7 @@ +