From 0ae519ad54e1c9b8c8d2455207b83568613c703d Mon Sep 17 00:00:00 2001 From: Raul Metsma Date: Tue, 23 Jan 2024 17:37:15 +0200 Subject: [PATCH] Merge extensions repository (#1226) IB-7385 Signed-off-by: Raul Metsma --- .github/workflows/build.yml | 1 + .gitmodules | 3 - CMakeLists.txt | 2 +- extensions | 1 - extensions/DigiDocQL/CMakeLists.txt | 24 ++ extensions/DigiDocQL/GeneratePreviewForURL.mm | 201 +++++++++++ .../DigiDocQL/GenerateThumbnailForURL.c | 30 ++ extensions/DigiDocQL/Info.plist | 62 ++++ .../DigiDocQL/en.lproj/InfoPlist.strings | 2 + extensions/DigiDocQL/main.c | 220 ++++++++++++ extensions/kde/CMakeLists.txt | 2 + extensions/kde/qdigidoc-signer.desktop | 17 + extensions/nautilus/CMakeLists.txt | 6 + extensions/nautilus/nautilus-qdigidoc.py | 117 +++++++ extensions/nautilus/po/et.po | 36 ++ extensions/nautilus/po/nautilus-qdigidoc.pot | 37 ++ extensions/nautilus/po/ru.po | 40 +++ extensions/nautilus/po/update-po.sh | 30 ++ extensions/windows/CMakeLists.txt | 84 +++++ extensions/windows/EsteidShellExtension.cpp | 59 ++++ extensions/windows/EsteidShellExtension.def | 10 + extensions/windows/EsteidShellExtension.idl | 40 +++ extensions/windows/EsteidShellExtension.rc | 155 +++++++++ extensions/windows/EsteidShellExtension.rgs | 11 + extensions/windows/EsteidShellExtension.wxs | 57 +++ extensions/windows/EsteidShlExt.cpp | 324 ++++++++++++++++++ extensions/windows/EsteidShlExt.h | 54 +++ extensions/windows/EsteidShlExt_x64.rgs | 23 ++ extensions/windows/EsteidShlExt_x86.rgs | 23 ++ extensions/windows/dllmain.cpp | 15 + extensions/windows/dllmain.h | 10 + extensions/windows/resource.h | 19 + extensions/windows/resources/digidoc.ico | Bin 0 -> 152170 bytes extensions/windows/stdafx.cpp | 5 + extensions/windows/stdafx.h | 26 ++ extensions/windows/targetver.h | 23 ++ 36 files changed, 1764 insertions(+), 5 deletions(-) delete mode 160000 extensions create mode 100644 extensions/DigiDocQL/CMakeLists.txt create mode 100644 extensions/DigiDocQL/GeneratePreviewForURL.mm create mode 100644 extensions/DigiDocQL/GenerateThumbnailForURL.c create mode 100644 extensions/DigiDocQL/Info.plist create mode 100644 extensions/DigiDocQL/en.lproj/InfoPlist.strings create mode 100644 extensions/DigiDocQL/main.c create mode 100644 extensions/kde/CMakeLists.txt create mode 100644 extensions/kde/qdigidoc-signer.desktop create mode 100644 extensions/nautilus/CMakeLists.txt create mode 100644 extensions/nautilus/nautilus-qdigidoc.py create mode 100644 extensions/nautilus/po/et.po create mode 100644 extensions/nautilus/po/nautilus-qdigidoc.pot create mode 100644 extensions/nautilus/po/ru.po create mode 100644 extensions/nautilus/po/update-po.sh create mode 100644 extensions/windows/CMakeLists.txt create mode 100644 extensions/windows/EsteidShellExtension.cpp create mode 100644 extensions/windows/EsteidShellExtension.def create mode 100644 extensions/windows/EsteidShellExtension.idl create mode 100644 extensions/windows/EsteidShellExtension.rc create mode 100644 extensions/windows/EsteidShellExtension.rgs create mode 100644 extensions/windows/EsteidShellExtension.wxs create mode 100644 extensions/windows/EsteidShlExt.cpp create mode 100644 extensions/windows/EsteidShlExt.h create mode 100644 extensions/windows/EsteidShlExt_x64.rgs create mode 100644 extensions/windows/EsteidShlExt_x86.rgs create mode 100644 extensions/windows/dllmain.cpp create mode 100644 extensions/windows/dllmain.h create mode 100644 extensions/windows/resource.h create mode 100644 extensions/windows/resources/digidoc.ico create mode 100644 extensions/windows/stdafx.cpp create mode 100644 extensions/windows/stdafx.h create mode 100644 extensions/windows/targetver.h diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index e489bd067..9d1b923e7 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -190,6 +190,7 @@ jobs: -DCMAKE_TOOLCHAIN_FILE=${{ env.RUNVCPKG_VCPKG_ROOT }}/scripts/buildsystems/vcpkg.cmake ` "-DLibDigiDocpp_ROOT=libs/libdigidocpp" cmake --build build --target msi + cmake --build build --target msishellext cmake --build build --target appx - name: Archive artifacts uses: actions/upload-artifact@v3 diff --git a/.gitmodules b/.gitmodules index 7cbda08be..fff60d64e 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,6 +4,3 @@ [submodule "common"] path = common url = ../qt-common -[submodule "extensions"] - path = extensions - url = ../digidoc-extensions diff --git a/CMakeLists.txt b/CMakeLists.txt index e29adabd3..7e037671e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -39,7 +39,7 @@ include(CPack) if(APPLE) add_subdirectory(extensions/DigiDocQL) elseif(WIN32) - add_subdirectory(extensions/windows EXCLUDE_FROM_ALL) + add_subdirectory(extensions/windows) elseif(UNIX) option(ENABLE_KDE "Install KDE service menu (default: TRUE)" TRUE) option(ENABLE_NAUTILUS_EXTENSION "Build Nautilus extension (default: TRUE)" TRUE) diff --git a/extensions b/extensions deleted file mode 160000 index d428a88c8..000000000 --- a/extensions +++ /dev/null @@ -1 +0,0 @@ -Subproject commit d428a88c8acdd2f89dbefd4c4c5691761518160a diff --git a/extensions/DigiDocQL/CMakeLists.txt b/extensions/DigiDocQL/CMakeLists.txt new file mode 100644 index 000000000..8856ad0d3 --- /dev/null +++ b/extensions/DigiDocQL/CMakeLists.txt @@ -0,0 +1,24 @@ +set( RESOURCES ../../client/mac/Resources/asic.icns ) +add_library( DigiDocQL MODULE + ${RESOURCES} + Info.plist + main.c + GenerateThumbnailForURL.c + GeneratePreviewForURL.mm +) +set_source_files_properties( GeneratePreviewForURL.mm PROPERTIES COMPILE_FLAGS "-fobjc-arc" ) +set_source_files_properties( Info.plist PROPERTIES MACOSX_PACKAGE_LOCATION . ) +set_target_properties( DigiDocQL PROPERTIES + BUNDLE YES + BUNDLE_EXTENSION qlgenerator + RESOURCE "${RESOURCES}" + XCODE_ATTRIBUTE_WRAPPER_EXTENSION qlgenerator + MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist + AUTOMOC OFF + INCLUDE_DIRECTORIES "${LIBDIGIDOCPP_INCLUDE_DIR}" + COMPILE_FLAGS "-Wno-unused-parameter" + LINK_LIBRARIES "-framework QuickLook;-framework digidocpp;-framework Cocoa" + LINK_FLAGS "-F/Library/Frameworks -fobjc-arc" + BUILD_WITH_INSTALL_RPATH YES + INSTALL_RPATH "@loader_path/../../../../../Frameworks" +) diff --git a/extensions/DigiDocQL/GeneratePreviewForURL.mm b/extensions/DigiDocQL/GeneratePreviewForURL.mm new file mode 100644 index 000000000..c1b07476b --- /dev/null +++ b/extensions/DigiDocQL/GeneratePreviewForURL.mm @@ -0,0 +1,201 @@ +/* + * QEstEidClient + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +// https://developer.apple.com/library/archive/documentation/UserExperience/Conceptual/Quicklook_Programming_Guide/Introduction/Introduction.html + +#include +#include +#include +#include +#include + +#include +#include + +using namespace digidoc; + +QL_EXTERN_C_BEGIN +OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, + CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options); +void CancelPreviewGeneration(void * /*thisInterface*/, QLPreviewRequestRef /*preview*/) {} +QL_EXTERN_C_END + +@interface NSString (Digidoc) ++ (NSString*)stdstring:(const std::string&)str; ++ (NSString*)fileSize:(unsigned long)bytes; ++ (void)parseException:(const Exception&)e result:(NSMutableArray *)result; +@end + +@implementation NSString (Digidoc) ++ (NSString*)stdstring:(const std::string&)str +{ + return str.empty() ? [NSString string] : [NSString stringWithUTF8String:str.c_str()]; +} + ++ (NSString*)htmlEntityEncode:(NSString*)str +{ + str = [str stringByReplacingOccurrencesOfString:@"&" withString:@"&"]; + str = [str stringByReplacingOccurrencesOfString:@"\"" withString:@"""]; + str = [str stringByReplacingOccurrencesOfString:@"'" withString:@"'"]; + str = [str stringByReplacingOccurrencesOfString:@"<" withString:@"<"]; + str = [str stringByReplacingOccurrencesOfString:@">" withString:@">"]; + return str; +} + ++ (NSString*)fileSize:(unsigned long)bytes +{ + enum { + kb = 1UL << 1, + mb = 1UL << 2, + gb = 1UL << 3 + }; + if (bytes >= gb) + return [NSString stringWithFormat:@"%1.2f GB", double(bytes) / gb]; + if (bytes >= mb) + return [NSString stringWithFormat:@"%1.2f MB", double(bytes) / mb]; + if (bytes >= kb) + return [NSString stringWithFormat:@"%1.1f KB", double(bytes) / kb]; + return [NSString stringWithFormat:@"%lu bytes", bytes]; +} + ++ (void)parseException:(const Exception&)e result:(NSMutableArray *)result +{ + [result addObject:[self stdstring:e.msg()]]; + for (const Exception &i : e.causes()) { + [self parseException:i result:result]; + } +} +@end + +class DigidocConf: public digidoc::XmlConfCurrent +{ +public: + bool TSLAutoUpdate() const final { return false; } + bool TSLOnlineDigest() const final { return false; } + std::string TSLCache() const final + { + std::string home = "~"; + if(char *var = getenv("HOME")) + home = var; + return home + "/Library/Containers/ee.ria.qdigidoc4/Data/Library/Application Support/RIA/qdigidoc4/"; + } +}; + +OSStatus GeneratePreviewForURL(void */*thisInterface*/, QLPreviewRequestRef preview, + CFURLRef url, CFStringRef /*contentTypeUTI*/, CFDictionaryRef /*options*/) +{ + @autoreleasepool { + NSMutableString *h = [NSMutableString string]; + [h appendString:@""]; + [h appendFormat:@"

%@

", [NSString htmlEntityEncode:[(__bridge NSURL*)url lastPathComponent]]]; + try + { + digidoc::Conf::init( new DigidocConf ); + digidoc::initialize(); + std::unique_ptr d(Container::openPtr([(__bridge NSURL*)url path].UTF8String)); + + [h appendString:@"Files
    "]; + for (const DataFile *doc : d->dataFiles()) { + [h appendFormat:@"
  1. %@
  2. ", [NSString htmlEntityEncode:[NSString stdstring:doc->fileName()]]]; + } + [h appendString:@"
"]; + + [h appendString:@"Signatures"]; + for (const Signature *s : d->signatures()) { + [h appendFormat:@"
Signer
%@
", [NSString htmlEntityEncode:[NSString stdstring:s->signedBy()]]]; + + NSString *date = [NSString stdstring:s->trustedSigningTime()]; + [date stringByReplacingOccurrencesOfString:@"Z" withString:@"-0000"]; + NSDateFormatter *df = [[NSDateFormatter alloc] init]; + [df setDateFormat:@"yyyy-MM-dd'T'HH:mm:ssZ"]; + NSDate *formateddate = [df dateFromString:date]; + [df setTimeZone: [NSTimeZone defaultTimeZone]]; + [df setDateFormat:@"YYYY-MM-dd HH:mm:ss z"]; + [h appendFormat:@"
Time
%@
", [df stringFromDate:formateddate]]; + + Signature::Validator v(s); + NSString *status = @"not valid"; + switch(v.status()) + { + case Signature::Validator::Valid: status = @"valid"; break; + case Signature::Validator::Warning: status = @"valid with warnings"; break; + case Signature::Validator::NonQSCD: status = @"valid with limitations"; break; + case Signature::Validator::Test: status = @"valid test signature"; break; + case Signature::Validator::Invalid: status = @"invalid"; break; + case Signature::Validator::Unknown: status = @"unknown"; break; + } + [h appendFormat:@"
Validity
Signature is %@
", status]; + + NSMutableArray *roles = [NSMutableArray array]; + for (const std::string &role : s->signerRoles()) { + if( !role.empty() ) { + [roles addObject:[NSString htmlEntityEncode:[NSString stdstring:role]]]; + } + } + if( [roles count] > 0 ) { + [h appendFormat:@"
Role
%@ 
", [NSString htmlEntityEncode:[roles componentsJoinedByString:@" / "]]]; + } + if (!s->countryName().empty()) { + [h appendFormat:@"
Country
%@ 
", [NSString htmlEntityEncode:[NSString stdstring:s->countryName()]]]; + } + if (!s->city().empty()) { + [h appendFormat:@"
City
%@ 
", [NSString htmlEntityEncode:[NSString stdstring:s->city()]]]; + } + if (!s->stateOrProvince().empty()) { + [h appendFormat:@"
State
%@ 
", [NSString htmlEntityEncode:[NSString stdstring:s->stateOrProvince()]]]; + } + if (!s->postalCode().empty()) { + [h appendFormat:@"
Postal code
%@ 
", [NSString htmlEntityEncode:[NSString stdstring:s->postalCode()]]]; + } + [h appendString:@"
"]; + } + digidoc::terminate(); + } catch (const Exception &e) { + NSMutableArray *err = [NSMutableArray array]; + [NSString parseException:e result:err]; + [h appendFormat:@"Failed to load document:
%@", [err componentsJoinedByString:@"
"]]; + } + [h appendString:@""]; + + NSBundle *bundle = [NSBundle bundleWithIdentifier:@"ee.ria.DigiDocQL"]; + NSData *image = [NSData dataWithContentsOfFile:[bundle pathForResource:@"asic" ofType:@"icns"]]; + NSDictionary *props = @{ + (__bridge id)kQLPreviewPropertyTextEncodingNameKey : @"UTF-8", + (__bridge id)kQLPreviewPropertyMIMETypeKey : @"text/html", + (__bridge id)kQLPreviewPropertyWidthKey : [[bundle infoDictionary] valueForKey:@"QLPreviewWidth"], + (__bridge id)kQLPreviewPropertyHeightKey : [[bundle infoDictionary] valueForKey:@"QLPreviewHeight"], + (__bridge id)kQLPreviewPropertyAttachmentsKey : @{ + @"asic.icns" : @{ + (__bridge id)kQLPreviewPropertyMIMETypeKey : @"image/icns", + (__bridge id)kQLPreviewPropertyAttachmentDataKey : image + } + } + }; + QLPreviewRequestSetDataRepresentation(preview, + (__bridge CFDataRef)[h dataUsingEncoding:NSUTF8StringEncoding], kUTTypeHTML, (__bridge CFDictionaryRef)props); + } + return noErr; +} diff --git a/extensions/DigiDocQL/GenerateThumbnailForURL.c b/extensions/DigiDocQL/GenerateThumbnailForURL.c new file mode 100644 index 000000000..6f93ad2cd --- /dev/null +++ b/extensions/DigiDocQL/GenerateThumbnailForURL.c @@ -0,0 +1,30 @@ +/* + * QEstEidClient + * + * 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + * + */ + +#include + +OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thumbnail, + CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options, CGSize maxSize) +{ + return noErr; +} + +void CancelThumbnailGeneration(void *thisInterface, QLThumbnailRequestRef thumbnail) +{ +} diff --git a/extensions/DigiDocQL/Info.plist b/extensions/DigiDocQL/Info.plist new file mode 100644 index 000000000..1789ee05d --- /dev/null +++ b/extensions/DigiDocQL/Info.plist @@ -0,0 +1,62 @@ + + + + + CFBundleDevelopmentRegion + English + CFBundleDocumentTypes + + + CFBundleTypeRole + QLGenerator + LSItemContentTypes + + ee.ria.bdoc + ee.ria.asics + + + + CFBundleExecutable + DigiDocQL + CFBundleIdentifier + ee.ria.DigiDocQL + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + DigiDocQL + CFBundleShortVersionString + 1 + CFBundleVersion + 1.0 + CFPlugInDynamicRegisterFunction + + CFPlugInDynamicRegistration + NO + CFPlugInFactories + + 60213CB1-91F9-43A6-BEF5-9DE58A2B6C52 + QuickLookGeneratorPluginFactory + + CFPlugInTypes + + 5E2D9680-5022-40FA-B806-43349622E5B9 + + 60213CB1-91F9-43A6-BEF5-9DE58A2B6C52 + + + CFPlugInUnloadFunction + + NSHumanReadableCopyright + Copyright © 2012-2023 Estonian Information System's Authority. All rights reserved. + QLNeedsToBeRunInMainThread + + QLPreviewHeight + 600 + QLPreviewWidth + 500 + QLSupportsConcurrentRequests + + QLThumbnailMinimumSize + 17 + + diff --git a/extensions/DigiDocQL/en.lproj/InfoPlist.strings b/extensions/DigiDocQL/en.lproj/InfoPlist.strings new file mode 100644 index 000000000..477b28ff8 --- /dev/null +++ b/extensions/DigiDocQL/en.lproj/InfoPlist.strings @@ -0,0 +1,2 @@ +/* Localized versions of Info.plist keys */ + diff --git a/extensions/DigiDocQL/main.c b/extensions/DigiDocQL/main.c new file mode 100644 index 000000000..4288f17d2 --- /dev/null +++ b/extensions/DigiDocQL/main.c @@ -0,0 +1,220 @@ +//============================================================================== +// +// DO NO MODIFY THE CONTENT OF THIS FILE +// +// This file contains the generic CFPlug-in code necessary for your generator +// To complete your generator implement the function in GenerateThumbnailForURL/GeneratePreviewForURL.c +// +//============================================================================== + + + + + + +#include +#include +#include +#include + +#define EXP __attribute__ ((visibility("default"))) + +// ----------------------------------------------------------------------------- +// constants +// ----------------------------------------------------------------------------- + +// Don't modify this line +#define PLUGIN_ID "60213CB1-91F9-43A6-BEF5-9DE58A2B6C52" + +// +// Below is the generic glue code for all plug-ins. +// +// You should not have to modify this code aside from changing +// names if you decide to change the names defined in the Info.plist +// + + +// ----------------------------------------------------------------------------- +// typedefs +// ----------------------------------------------------------------------------- + +// The thumbnail generation function to be implemented in GenerateThumbnailForURL.c +EXP OSStatus GenerateThumbnailForURL(void *thisInterface, QLThumbnailRequestRef thumbnail, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options, CGSize maxSize); +EXP void CancelThumbnailGeneration(void* thisInterface, QLThumbnailRequestRef thumbnail); + +// The preview generation function to be implemented in GeneratePreviewForURL.c +EXP OSStatus GeneratePreviewForURL(void *thisInterface, QLPreviewRequestRef preview, CFURLRef url, CFStringRef contentTypeUTI, CFDictionaryRef options); +EXP void CancelPreviewGeneration(void *thisInterface, QLPreviewRequestRef preview); + +// The layout for an instance of QuickLookGeneratorPlugIn +typedef struct __QuickLookGeneratorPluginType +{ + void *conduitInterface; + CFUUIDRef factoryID; + UInt32 refCount; +} QuickLookGeneratorPluginType; + +// ----------------------------------------------------------------------------- +// prototypes +// ----------------------------------------------------------------------------- +// Forward declaration for the IUnknown implementation. +// + +EXP QuickLookGeneratorPluginType *AllocQuickLookGeneratorPluginType(CFUUIDRef inFactoryID); +EXP void DeallocQuickLookGeneratorPluginType(QuickLookGeneratorPluginType *thisInstance); +EXP HRESULT QuickLookGeneratorQueryInterface(void *thisInstance,REFIID iid,LPVOID *ppv); +EXP void *QuickLookGeneratorPluginFactory(CFAllocatorRef allocator,CFUUIDRef typeID); +EXP ULONG QuickLookGeneratorPluginAddRef(void *thisInstance); +EXP ULONG QuickLookGeneratorPluginRelease(void *thisInstance); + +// ----------------------------------------------------------------------------- +// myInterfaceFtbl definition +// ----------------------------------------------------------------------------- +// The QLGeneratorInterfaceStruct function table. +// +static QLGeneratorInterfaceStruct myInterfaceFtbl = { + NULL, + QuickLookGeneratorQueryInterface, + QuickLookGeneratorPluginAddRef, + QuickLookGeneratorPluginRelease, + NULL, + NULL, + NULL, + NULL +}; + + +// ----------------------------------------------------------------------------- +// AllocQuickLookGeneratorPluginType +// ----------------------------------------------------------------------------- +// Utility function that allocates a new instance. +// You can do some initial setup for the generator here if you wish +// like allocating globals etc... +// +QuickLookGeneratorPluginType *AllocQuickLookGeneratorPluginType(CFUUIDRef inFactoryID) +{ + QuickLookGeneratorPluginType *theNewInstance; + + theNewInstance = (QuickLookGeneratorPluginType *)malloc(sizeof(QuickLookGeneratorPluginType)); + memset(theNewInstance,0,sizeof(QuickLookGeneratorPluginType)); + + /* Point to the function table Malloc enough to store the stuff and copy the filler from myInterfaceFtbl over */ + theNewInstance->conduitInterface = malloc(sizeof(QLGeneratorInterfaceStruct)); + memcpy(theNewInstance->conduitInterface,&myInterfaceFtbl,sizeof(QLGeneratorInterfaceStruct)); + + /* Retain and keep an open instance refcount for each factory. */ + theNewInstance->factoryID = CFRetain(inFactoryID); + CFPlugInAddInstanceForFactory(inFactoryID); + + /* This function returns the IUnknown interface so set the refCount to one. */ + theNewInstance->refCount = 1; + return theNewInstance; +} + +// ----------------------------------------------------------------------------- +// DeallocQuickLookGeneratorPluginType +// ----------------------------------------------------------------------------- +// Utility function that deallocates the instance when +// the refCount goes to zero. +// In the current implementation generator interfaces are never deallocated +// but implement this as this might change in the future +// +void DeallocQuickLookGeneratorPluginType(QuickLookGeneratorPluginType *thisInstance) +{ + CFUUIDRef theFactoryID; + + theFactoryID = thisInstance->factoryID; + /* Free the conduitInterface table up */ + free(thisInstance->conduitInterface); + + /* Free the instance structure */ + free(thisInstance); + if (theFactoryID){ + CFPlugInRemoveInstanceForFactory(theFactoryID); + CFRelease(theFactoryID); + } +} + +// ----------------------------------------------------------------------------- +// QuickLookGeneratorQueryInterface +// ----------------------------------------------------------------------------- +// Implementation of the IUnknown QueryInterface function. +// +HRESULT QuickLookGeneratorQueryInterface(void *thisInstance,REFIID iid,LPVOID *ppv) +{ + CFUUIDRef interfaceID; + + interfaceID = CFUUIDCreateFromUUIDBytes(kCFAllocatorDefault,iid); + + if (CFEqual(interfaceID,kQLGeneratorCallbacksInterfaceID)){ + /* If the Right interface was requested, bump the ref count, + * set the ppv parameter equal to the instance, and + * return good status. + */ + ((QLGeneratorInterfaceStruct *)((QuickLookGeneratorPluginType *)thisInstance)->conduitInterface)->GenerateThumbnailForURL = GenerateThumbnailForURL; + ((QLGeneratorInterfaceStruct *)((QuickLookGeneratorPluginType *)thisInstance)->conduitInterface)->CancelThumbnailGeneration = CancelThumbnailGeneration; + ((QLGeneratorInterfaceStruct *)((QuickLookGeneratorPluginType *)thisInstance)->conduitInterface)->GeneratePreviewForURL = GeneratePreviewForURL; + ((QLGeneratorInterfaceStruct *)((QuickLookGeneratorPluginType *)thisInstance)->conduitInterface)->CancelPreviewGeneration = CancelPreviewGeneration; + ((QLGeneratorInterfaceStruct *)((QuickLookGeneratorPluginType*)thisInstance)->conduitInterface)->AddRef(thisInstance); + *ppv = thisInstance; + CFRelease(interfaceID); + return S_OK; + }else{ + /* Requested interface unknown, bail with error. */ + *ppv = NULL; + CFRelease(interfaceID); + return E_NOINTERFACE; + } +} + +// ----------------------------------------------------------------------------- +// QuickLookGeneratorPluginAddRef +// ----------------------------------------------------------------------------- +// Implementation of reference counting for this type. Whenever an interface +// is requested, bump the refCount for the instance. NOTE: returning the +// refcount is a convention but is not required so don't rely on it. +// +ULONG QuickLookGeneratorPluginAddRef(void *thisInstance) +{ + ((QuickLookGeneratorPluginType *)thisInstance )->refCount += 1; + return ((QuickLookGeneratorPluginType*) thisInstance)->refCount; +} + +// ----------------------------------------------------------------------------- +// QuickLookGeneratorPluginRelease +// ----------------------------------------------------------------------------- +// When an interface is released, decrement the refCount. +// If the refCount goes to zero, deallocate the instance. +// +ULONG QuickLookGeneratorPluginRelease(void *thisInstance) +{ + ((QuickLookGeneratorPluginType*)thisInstance)->refCount -= 1; + if (((QuickLookGeneratorPluginType*)thisInstance)->refCount == 0){ + DeallocQuickLookGeneratorPluginType((QuickLookGeneratorPluginType*)thisInstance ); + return 0; + }else{ + return ((QuickLookGeneratorPluginType*) thisInstance )->refCount; + } +} + +// ----------------------------------------------------------------------------- +// QuickLookGeneratorPluginFactory +// ----------------------------------------------------------------------------- +void *QuickLookGeneratorPluginFactory(CFAllocatorRef allocator,CFUUIDRef typeID) +{ + QuickLookGeneratorPluginType *result; + CFUUIDRef uuid; + + /* If correct type is being requested, allocate an + * instance of kQLGeneratorTypeID and return the IUnknown interface. + */ + if (CFEqual(typeID,kQLGeneratorTypeID)){ + uuid = CFUUIDCreateFromString(kCFAllocatorDefault,CFSTR(PLUGIN_ID)); + result = AllocQuickLookGeneratorPluginType(uuid); + CFRelease(uuid); + return result; + } + /* If the requested type is incorrect, return NULL. */ + return NULL; +} + diff --git a/extensions/kde/CMakeLists.txt b/extensions/kde/CMakeLists.txt new file mode 100644 index 000000000..33984ed30 --- /dev/null +++ b/extensions/kde/CMakeLists.txt @@ -0,0 +1,2 @@ +set(SERVICES_INSTALL_DIR "${CMAKE_INSTALL_DATADIR}/kservices5" CACHE PATH "Install dir for KDE service (desktop, protocol, ...) files") +install(FILES qdigidoc-signer.desktop DESTINATION "${SERVICES_INSTALL_DIR}") diff --git a/extensions/kde/qdigidoc-signer.desktop b/extensions/kde/qdigidoc-signer.desktop new file mode 100644 index 000000000..070cc3563 --- /dev/null +++ b/extensions/kde/qdigidoc-signer.desktop @@ -0,0 +1,17 @@ +[Desktop Entry] +X-KDE-ServiceTypes=KonqPopupMenu/Plugin,all/allfiles +Actions=sign; +Encoding=UTF-8 +Type=Service +X-KDE-Priority=TopLevel +X-KDE-PluginInfo-Name=digidoc-signer +X-KDE-PluginInfo-Author=Erkko Kebbinau +X-KDE-PluginInfo-Email=erkko@smartlink.ee +X-KDE-PluginInfo-Version=1.0 + +[Desktop Action sign] +Name=Sign digitally +Name[et]=Allkirjasta digitaalselt +Name[ru]=Подписать дигитально +Icon=qdigidoc4 +Exec=qdigidoc4 -sign %U diff --git a/extensions/nautilus/CMakeLists.txt b/extensions/nautilus/CMakeLists.txt new file mode 100644 index 000000000..b7c0e1ef3 --- /dev/null +++ b/extensions/nautilus/CMakeLists.txt @@ -0,0 +1,6 @@ +find_package(Gettext) + +file(GLOB PO_FILES po/*.po) +gettext_create_translations(po/nautilus-qdigidoc.pot ALL ${PO_FILES}) + +install(FILES nautilus-qdigidoc.py DESTINATION ${CMAKE_INSTALL_DATADIR}/nautilus-python/extensions/) diff --git a/extensions/nautilus/nautilus-qdigidoc.py b/extensions/nautilus/nautilus-qdigidoc.py new file mode 100644 index 000000000..dd510ab68 --- /dev/null +++ b/extensions/nautilus/nautilus-qdigidoc.py @@ -0,0 +1,117 @@ +# +# QDigiDoc Nautilus Extension +# +# Copyright (C) 2010 Erkko Kebbinau +# +# 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA +# +import os +import gettext +import locale +import gi + +try: + # Python 3. + from urllib.parse import unquote +except ImportError: + from urllib import unquote + +from gi.repository import Nautilus, GObject, Gio +if hasattr(Nautilus, "LocationWidgetProvider"): + gi.require_version('Nautilus', '3.0') + +APP = 'nautilus-qdigidoc' + +locale.setlocale(locale.LC_ALL, '') +gettext.bindtextdomain(APP) +gettext.textdomain(APP) + +class OpenDigidocExtension(GObject.GObject, Nautilus.MenuProvider): + def __init__(self): + super().__init__() + + def menu_activate_cb(self, menu, paths): + args = "-sign " + for path in paths: + args += "\"%s\" " % path + cmd = ("qdigidoc4 " + args + "&") + os.system(cmd) + + def valid_file(self, file): + return file.get_file_type() == Gio.FileType.REGULAR and file.get_uri_scheme() == 'file' + + def get_file_items(self, *args): + paths = [] + files = args[-1] + for file in files: + if self.valid_file(file): + path = unquote(file.get_uri()[7:]) + paths.append(path) + + if len(paths) < 1: + return [] + + item = Nautilus.MenuItem( + name="OpenDigidocExtension::DigidocSigner", + label=gettext.gettext('Sign digitally'), + tip=gettext.ngettext('Sign selected file with DigiDoc4 Client', + 'Sign selected files with DigiDoc4 Client', + len(paths)), + icon='qdigidoc4' + ) + item.connect('activate', self.menu_activate_cb, paths) + return [item] + + def get_background_items(self, current_folder): + return [] + +class OpenCryptoExtension(GObject.GObject, Nautilus.MenuProvider): + def __init__(self): + super().__init__() + + def menu_activate_cb(self, menu, paths): + args = "-crypto " + for path in paths: + args += "\"%s\" " % path + cmd = ("qdigidoc4 " + args + "&") + os.system(cmd) + + def valid_file(self, file): + return file.get_file_type() == Gio.FileType.REGULAR and file.get_uri_scheme() == 'file' + + def get_file_items(self, *args): + paths = [] + files = args[-1] + for file in files: + if self.valid_file(file): + path = unquote(file.get_uri()[7:]) + paths.append(path) + + if len(paths) < 1: + return [] + + item = Nautilus.MenuItem( + name="OpenCryptoExtension::DigidocEncrypter", + label=gettext.gettext('Encrypt files'), + tip=gettext.ngettext('Encrypt selected file with DigiDoc4 Client', + 'Encrypt selected files with DigiDoc4 Client', + len(paths)), + icon='qdigidoc4' + ) + item.connect('activate', self.menu_activate_cb, paths) + return [item] + + def get_background_items(self, current_folder): + return [] diff --git a/extensions/nautilus/po/et.po b/extensions/nautilus/po/et.po new file mode 100644 index 000000000..8f9161ced --- /dev/null +++ b/extensions/nautilus/po/et.po @@ -0,0 +1,36 @@ +# Estonian translations for qdigidoc package. +# This file is distributed under the same license as the qdigidoc package. +# Erkko Kebbinau , 2010. +# Sander Lepik , 2014. +# +msgid "" +msgstr "" +"Project-Id-Version: qdigidoc-3.9.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2010-08-25 15:51+0300\n" +"PO-Revision-Date: 2014-11-11 14:23+0200\n" +"Last-Translator: Sander Lepik \n" +"Language-Team: Estonian\n" +"Language: et\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=2; plural=(n != 1);\n" + +msgid "Encrypt files" +msgstr "Krüpteeri failid" + +msgid "Encrypt selected file with DigiDoc4 Client" +msgid_plural "Encrypt selected files with DigiDoc4 Client" +msgstr[0] "Krüpteeri valitud fail DigiDoc4 kliendiga" +msgstr[1] "Krüpteeri valitud failid DigiDoc4 kliendiga" + +#: nautilus-qdigidoc.py:77 +msgid "Sign digitally" +msgstr "Allkirjasta digitaalselt" + +#: nautilus-qdigidoc.py:74 +msgid "Sign selected file with DigiDoc4 Client" +msgid_plural "Sign selected files with DigiDoc4 Client" +msgstr[0] "Allkirjasta valitud fail DigiDoc4 kliendiga" +msgstr[1] "Allkirjasta valitud failid DigiDoc4 kliendiga" diff --git a/extensions/nautilus/po/nautilus-qdigidoc.pot b/extensions/nautilus/po/nautilus-qdigidoc.pot new file mode 100644 index 000000000..bba16a696 --- /dev/null +++ b/extensions/nautilus/po/nautilus-qdigidoc.pot @@ -0,0 +1,37 @@ +# Translation template for qdigidoc package. +# This file is distributed under the same license as the qdigidoc package. +# Erkko Kebbinau , 2010. +# Sander Lepik , 2014. +# +#, fuzzy +msgid "" +msgstr "" +"Project-Id-Version: PACKAGE VERSION\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2010-08-25 15:51+0300\n" +"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" +"Last-Translator: FULL NAME \n" +"Language-Team: LANGUAGE \n" +"Language: \n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n" + +#: nautilus-qdigidoc.py:74 +msgid "Sign selected file with DigiDoc4 Client" +msgid_plural "Sign selected files with DigiDoc4 Client" +msgstr[0] "" +msgstr[1] "" + +#: nautilus-qdigidoc.py:77 +msgid "Sign digitally" +msgstr "" + +msgid "Encrypt selected file with DigiDoc4 Client" +msgid_plural "Encrypt selected files with DigiDoc4 Client" +msgstr[0] "" +msgstr[1] "" + +msgid "Encrypt files" +msgstr "" \ No newline at end of file diff --git a/extensions/nautilus/po/ru.po b/extensions/nautilus/po/ru.po new file mode 100644 index 000000000..4117a19fb --- /dev/null +++ b/extensions/nautilus/po/ru.po @@ -0,0 +1,40 @@ +# Russian translations for qdigidoc package. +# This file is distributed under the same license as the qdigidoc package. +# Erkko Kebbinau , 2010. +# Sander Lepik , 2014. +# Edmund Laugasson , 2020. +# +msgid "" +msgstr "" +"Project-Id-Version: qdigidoc-3.9.1\n" +"Report-Msgid-Bugs-To: \n" +"POT-Creation-Date: 2010-08-25 15:51+0300\n" +"PO-Revision-Date: 2020-08-18 00:14+0300\n" +"Last-Translator: Edmund Laugasson \n" +"Language-Team: Russian\n" +"Language: ru\n" +"MIME-Version: 1.0\n" +"Content-Type: text/plain; charset=UTF-8\n" +"Content-Transfer-Encoding: 8bit\n" +"Plural-Forms: nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n" +"%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);\n" + +msgid "Encrypt files" +msgstr "Зашифруйте файлы" + +msgid "Encrypt selected file with DigiDoc4 Client" +msgid_plural "Encrypt selected files with DigiDoc4 Client" +msgstr[0] "Зашифруйте выбранный файл с клиентом DigiDoc4" +msgstr[1] "Зашифруйте выбранныйые файлы с клиентом DigiDoc4" +msgstr[2] "Зашифруйте выбранныйые файлы с клиентом DigiDoc4" + +#: nautilus-qdigidoc.py:77 +msgid "Sign digitally" +msgstr "Подпишите дигитально" + +#: nautilus-qdigidoc.py:74 +msgid "Sign selected file with DigiDoc4 Client" +msgid_plural "Sign selected files with DigiDoc4 Client" +msgstr[0] "Подпишите выбранный файл с клиентом DigiDoc4" +msgstr[1] "Подпишите выбранныйые файлы с клиентом DigiDoc4" +msgstr[2] "Подпишите выбранныйые файлы с клиентом DigiDoc4" diff --git a/extensions/nautilus/po/update-po.sh b/extensions/nautilus/po/update-po.sh new file mode 100644 index 000000000..8d5c1df3c --- /dev/null +++ b/extensions/nautilus/po/update-po.sh @@ -0,0 +1,30 @@ +#!/bin/sh + +# Copyright (C) 2010 Erkko Kebbinau +# +# 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, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +# Generate .pot file: +pushd .. +xgettext -k_ -kN_ nautilus-qdigidoc.py --output=po/nautilus-qdigidoc.pot +popd + +# Fix up charset +sed -i -e '/Content-Type/ s/CHARSET/UTF-8/' nautilus-qdigidoc.pot + +# Update po files: +for f in *.po ; do + msgmerge -U "$f" nautilus-qdigidoc.pot +done diff --git a/extensions/windows/CMakeLists.txt b/extensions/windows/CMakeLists.txt new file mode 100644 index 000000000..7845191e5 --- /dev/null +++ b/extensions/windows/CMakeLists.txt @@ -0,0 +1,84 @@ +cmake_minimum_required(VERSION 3.16) +project(digidocshellextension VERSION 3.13.9) + +set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake/modules) +set(CMAKE_INCLUDE_CURRENT_DIR ON) + +include( VersionInfo ) + +if(CMAKE_SIZEOF_VOID_P EQUAL 8) + set(MIDL_TARGET "x64") + set(PLATFORM "x64") +else() + set(MIDL_TARGET "win32") + set(PLATFORM "x86") +endif() + +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/EsteidShellExtension_i.h + ${CMAKE_CURRENT_BINARY_DIR}/EsteidShellExtension_i.c + ${CMAKE_CURRENT_BINARY_DIR}/EsteidShellExtension_p.c + COMMAND Midl.Exe ${CMAKE_CURRENT_SOURCE_DIR}/EsteidShellExtension.idl + /nologo /no_robust /char signed /Oicf /env ${MIDL_TARGET} + /I ${CMAKE_CURRENT_SOURCE_DIR} + /tlb EsteidShellExtension.tlb + /h EsteidShellExtension_i.h + /iid EsteidShellExtension_i.c + /proxy EsteidShellExtension_p.c 2> nul + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + VERBATIM +) + +add_library(EsteidShellExtension SHARED + ${CMAKE_CURRENT_BINARY_DIR}/EsteidShellExtension_i.c + dllmain.cpp + EsteidShellExtension.cpp + EsteidShellExtension.def + EsteidShlExt.cpp + stdafx.cpp + EsteidShellExtension.rc + EsteidShellExtension.rgs + EsteidShlExt_x86.rgs + EsteidShlExt_x64.rgs +) +set_target_properties(EsteidShellExtension PROPERTIES + MSVC_RUNTIME_LIBRARY "MultiThreaded$<$:Debug>" + COMPILE_DEFINITIONS "_UNICODE;UNICODE;_MERGE_PROXYSTUB;_WINDLL" + COMPILE_OPTIONS "/guard:cf" + INCLUDE_DIRECTORIES ${CMAKE_CURRENT_BINARY_DIR} + INTERPROCEDURAL_OPTIMIZATION YES + LINK_OPTIONS "/guard:cf" + LINK_LIBRARIES "uxtheme.lib" + SKIP_AUTOMOC ON +) + +add_custom_target(msishellext DEPENDS EsteidShellExtension + COMMAND "$ENV{WIX}bin\\candle.exe" -nologo -arch ${PLATFORM} + -dMSI_VERSION=${VERSION} -dShellExt=$ + ${CMAKE_CURRENT_SOURCE_DIR}/EsteidShellExtension.wxs + ${CMAKE_MODULE_PATH}/WelcomeDlg2.wxs + ${CMAKE_MODULE_PATH}/WixUI_Minimal2.wxs + COMMAND "$ENV{WIX}bin\\light.exe" -nologo -ext WixUIExtension + EsteidShellExtension.wixobj WelcomeDlg2.wixobj WixUI_Minimal2.wixobj + -dWixUIDialogBmp=${CMAKE_MODULE_PATH}/dlgbmp.bmp + -dWixUIBannerBmp=${CMAKE_MODULE_PATH}/banner.bmp + -o "Digidoc_ShellExt-${VERSION}$ENV{VER_SUFFIX}.${PLATFORM}.msi" + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} +) + +if(SIGNCERT) + if(CROSSSIGNCERT) + target_link_options(EsteidShellExtension PRIVATE "/INTEGRITYCHECK") + endif() + add_custom_command(TARGET EsteidShellExtension POST_BUILD + COMMAND signtool.exe sign /a /v /s MY /n "${SIGNCERT}" /fd SHA256 /du http://installer.id.ee + "$<$:/ph;/ac;${CROSSSIGNCERT}>" + /tr http://sha256timestamp.ws.symantec.com/sha256/timestamp /td SHA256 $ + COMMAND_EXPAND_LISTS + ) + add_custom_command(TARGET msishellext POST_BUILD + COMMAND signtool.exe sign /a /v /s MY /n "${SIGNCERT}" /fd SHA256 /du http://installer.id.ee + /tr http://sha256timestamp.ws.symantec.com/sha256/timestamp /td SHA256 + "${CMAKE_BINARY_DIR}/Digidoc_ShellExt-${VERSION}$ENV{VER_SUFFIX}.${PLATFORM}.msi" + ) +endif() diff --git a/extensions/windows/EsteidShellExtension.cpp b/extensions/windows/EsteidShellExtension.cpp new file mode 100644 index 000000000..3de907dd0 --- /dev/null +++ b/extensions/windows/EsteidShellExtension.cpp @@ -0,0 +1,59 @@ +// EsteidShellExtension.cpp : Implementation of DLL Exports. + + +#include "stdafx.h" +#include "EsteidShellExtension_i.h" +#include "dllmain.h" + +// Used to determine whether the DLL can be unloaded by OLE +STDAPI DllCanUnloadNow(void) +{ + return _AtlModule.DllCanUnloadNow(); +} + + +// Returns a class factory to create an object of the requested type +STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv) +{ + return _AtlModule.DllGetClassObject(rclsid, riid, ppv); +} + + +// DllRegisterServer - Adds entries to the system registry +STDAPI DllRegisterServer(void) +{ + // registers object, typelib and all interfaces in typelib + HRESULT hr = _AtlModule.DllRegisterServer(); + return hr; +} + + +// DllUnregisterServer - Removes entries from the system registry +STDAPI DllUnregisterServer(void) +{ + HRESULT hr = _AtlModule.DllUnregisterServer(); + return hr; +} + +// DllInstall - Adds/Removes entries to the system registry per user +// per machine. +STDAPI DllInstall(BOOL bInstall, LPCWSTR pszCmdLine) +{ + HRESULT hr = E_FAIL; + static const wchar_t szUserSwitch[] = _T("user"); + + if (pszCmdLine != NULL) { + if (_wcsnicmp(pszCmdLine, szUserSwitch, _countof(szUserSwitch)) == 0) + AtlSetPerUserRegistration(true); + } + + if (bInstall) { + hr = DllRegisterServer(); + if (FAILED(hr)) + DllUnregisterServer(); + } else { + hr = DllUnregisterServer(); + } + + return hr; +} diff --git a/extensions/windows/EsteidShellExtension.def b/extensions/windows/EsteidShellExtension.def new file mode 100644 index 000000000..d606fda47 --- /dev/null +++ b/extensions/windows/EsteidShellExtension.def @@ -0,0 +1,10 @@ +; EsteidShellExtension.def : Declares the module parameters. + +LIBRARY "EsteidShellExtension.DLL" + +EXPORTS + DllCanUnloadNow PRIVATE + DllGetClassObject PRIVATE + DllRegisterServer PRIVATE + DllUnregisterServer PRIVATE + DllInstall PRIVATE diff --git a/extensions/windows/EsteidShellExtension.idl b/extensions/windows/EsteidShellExtension.idl new file mode 100644 index 000000000..92361f614 --- /dev/null +++ b/extensions/windows/EsteidShellExtension.idl @@ -0,0 +1,40 @@ +// EsteidShellExtension.idl : IDL source for EsteidShellExtension +// + +// This file will be processed by the MIDL tool to +// produce the type library (EsteidShellExtension.tlb) and marshalling code. + +import "oaidl.idl"; +import "ocidl.idl"; + +[ + object, + uuid(8BD7CE13-2DB7-4268-8201-CED0626CB94E), + dual, + nonextensible, + helpstring("IEsteidShlExt Interface"), + pointer_default(unique) +] +interface IEsteidShlExt : IDispatch{ +}; +[ + uuid(F4748FA8-B59E-43FA-9D53-2380EB141AED), + version(1.0), + helpstring("EsteidShellExtension 1.0 Type Library") +] +library EsteidShellExtensionLib +{ + importlib("stdole2.tlb"); + [ +#ifdef _WIN64 + uuid(5606A547-759D-43DA-AEEB-D3BF1D1E816D), +#else + uuid(310AAB39-76FE-401B-8A7F-0F578C5F6AB5), +#endif + helpstring("EsteidShlExt Class") + ] + coclass EsteidShlExt + { + [default] interface IEsteidShlExt; + }; +}; diff --git a/extensions/windows/EsteidShellExtension.rc b/extensions/windows/EsteidShellExtension.rc new file mode 100644 index 000000000..1dc252cb9 --- /dev/null +++ b/extensions/windows/EsteidShellExtension.rc @@ -0,0 +1,155 @@ +// Microsoft Visual C++ generated resource script. +// +#include "resource.h" + +#define APSTUDIO_READONLY_SYMBOLS +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 2 resource. +// +#ifndef APSTUDIO_INVOKED +#include "targetver.h" +#endif +#include "winres.h" + +///////////////////////////////////////////////////////////////////////////// +#undef APSTUDIO_READONLY_SYMBOLS + +///////////////////////////////////////////////////////////////////////////// +// English (United States) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU) +LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US +#pragma code_page(1252) + +#ifdef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// TEXTINCLUDE +// + +1 TEXTINCLUDE +BEGIN + "resource.h\0" +END + +2 TEXTINCLUDE +BEGIN + "#ifndef APSTUDIO_INVOKED\r\n" + "#include ""targetver.h""\r\n" + "#endif\r\n" + "#include ""winres.h""\r\0" +END + +3 TEXTINCLUDE +BEGIN + "\r\0" +END + +#endif // APSTUDIO_INVOKED + + +///////////////////////////////////////////////////////////////////////////// +// +// Version +// + +#define VER_STR_HELPER(x) #x +#define VER_STR(x) VER_STR_HELPER(x) + +VS_VERSION_INFO VERSIONINFO + FILEVERSION MAJOR_VER,MINOR_VER,RELEASE_VER,BUILD_VER + PRODUCTVERSION MAJOR_VER,MINOR_VER,RELEASE_VER,BUILD_VER + FILEFLAGSMASK 0x3fL +#ifdef _DEBUG + FILEFLAGS 0x1L +#else + FILEFLAGS 0x0L +#endif + FILEOS 0x4L + FILETYPE 0x2L + FILESUBTYPE 0x0L +BEGIN + BLOCK "StringFileInfo" + BEGIN + BLOCK "040904e4" + BEGIN + VALUE "CompanyName", "RIA" + VALUE "FileDescription", "Estonian ID Card Software explorer integration" + VALUE "FileVersion", VER_STR(MAJOR_VER.MINOR_VER.RELEASE_VER.BUILD_VER) + VALUE "InternalName", "EsteidShellExtension.dll" + VALUE "LegalCopyright", "(c) RIA. All rights reserved." + VALUE "OriginalFilename", "EsteidShellExtension.dll" + VALUE "ProductName", "Estonian ID Card Software" + VALUE "ProductVersion", VER_STR(MAJOR_VER.MINOR_VER.RELEASE_VER.BUILD_VER) + END + END + BLOCK "VarFileInfo" + BEGIN + VALUE "Translation", 0x409, 1252 + END +END + + +///////////////////////////////////////////////////////////////////////////// +// +// REGISTRY +// + +IDR_ESTEIDEXT REGISTRY "EsteidShellExtension.rgs" + + +///////////////////////////////////////////////////////////////////////////// +// +// String Table +// + +STRINGTABLE +BEGIN + IDS_PROJNAME "EsteidShellExtension" +END + +#endif // English (United States) resources +///////////////////////////////////////////////////////////////////////////// + + +///////////////////////////////////////////////////////////////////////////// +// Estonian (Estonia) resources + +#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ETI) +LANGUAGE LANG_ESTONIAN, SUBLANG_DEFAULT +#pragma code_page(1257) + +///////////////////////////////////////////////////////////////////////////// +// +// REGISTRY +// + +#ifdef _WIN64 +IDR_ESTEIDSHLEXT REGISTRY "EsteidShlExt_x64.rgs" +#else +IDR_ESTEIDSHLEXT REGISTRY "EsteidShlExt_x86.rgs" +#endif + +///////////////////////////////////////////////////////////////////////////// +// +// Bitmap +// + +IDB_DIGIDOCICO ICON "resources\\digidoc.ico" + +#endif // Estonian (Estonia) resources +///////////////////////////////////////////////////////////////////////////// + + + +#ifndef APSTUDIO_INVOKED +///////////////////////////////////////////////////////////////////////////// +// +// Generated from the TEXTINCLUDE 3 resource. +// + + +///////////////////////////////////////////////////////////////////////////// +#endif // not APSTUDIO_INVOKED + diff --git a/extensions/windows/EsteidShellExtension.rgs b/extensions/windows/EsteidShellExtension.rgs new file mode 100644 index 000000000..1da405ca5 --- /dev/null +++ b/extensions/windows/EsteidShellExtension.rgs @@ -0,0 +1,11 @@ +HKCR +{ + NoRemove AppID + { + '%APPID%' = s 'EsteidShellExtension' + 'EsteidShellExtension.DLL' + { + val AppID = s '%APPID%' + } + } +} diff --git a/extensions/windows/EsteidShellExtension.wxs b/extensions/windows/EsteidShellExtension.wxs new file mode 100644 index 000000000..5fbe4068f --- /dev/null +++ b/extensions/windows/EsteidShellExtension.wxs @@ -0,0 +1,57 @@ + + + + + + + + + + + + + + + + + + + + + + = 601)]]> + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/extensions/windows/EsteidShlExt.cpp b/extensions/windows/EsteidShlExt.cpp new file mode 100644 index 000000000..78e9c38e5 --- /dev/null +++ b/extensions/windows/EsteidShlExt.cpp @@ -0,0 +1,324 @@ +// EsteidShlExt.cpp : Implementation of CEsteidShlExt +// http://msdn.microsoft.com/en-us/library/bb757020.aspx + +#include "stdafx.h" +#include "EsteidShlExt.h" + +#include + +typedef DWORD ARGB; + +bool HasAlpha(ARGB *pargb, SIZE &sizeImage, int cxRow) +{ + ULONG cxDelta = cxRow - sizeImage.cx; + for(ULONG y = sizeImage.cy; y; --y) + { + for(ULONG x = sizeImage.cx; x; --x) + { + if(*pargb++ & 0xFF000000) + return true; + } + pargb += cxDelta; + } + return false; +} + +BITMAPINFO InitBitmapInfo(const SIZE &sizeImage) +{ + BITMAPINFO pbmi = {}; + pbmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER); + pbmi.bmiHeader.biPlanes = 1; + pbmi.bmiHeader.biCompression = BI_RGB; + + pbmi.bmiHeader.biWidth = sizeImage.cx; + pbmi.bmiHeader.biHeight = sizeImage.cy; + pbmi.bmiHeader.biBitCount = 32; + return pbmi; +} + +HBITMAP Create32BitHBITMAP(HDC hdc, const SIZE &sizeImage, void **ppvBits) +{ + BITMAPINFO bmi = InitBitmapInfo(sizeImage); + if (HDC hdcUsed = hdc ? hdc : GetDC(nullptr)) + { + HBITMAP phBmp = CreateDIBSection(hdcUsed, &bmi, DIB_RGB_COLORS, ppvBits, nullptr, 0); + if (hdc != hdcUsed) + ReleaseDC(NULL, hdcUsed); + return phBmp; + } + return nullptr; +} + +HRESULT ConvertToPARGB32(HDC hdc, ARGB *pargb, HBITMAP hbmp, SIZE &sizeImage, int cxRow) +{ + BITMAPINFO bmi = InitBitmapInfo(sizeImage); + HRESULT hr = E_OUTOFMEMORY; + HANDLE hHeap = GetProcessHeap(); + if (void *pvBits = HeapAlloc(hHeap, 0, bmi.bmiHeader.biWidth * 4 * bmi.bmiHeader.biHeight)) + { + hr = E_UNEXPECTED; + if (GetDIBits(hdc, hbmp, 0, bmi.bmiHeader.biHeight, pvBits, &bmi, DIB_RGB_COLORS) == bmi.bmiHeader.biHeight) + { + ULONG cxDelta = cxRow - bmi.bmiHeader.biWidth; + ARGB *pargbMask = static_cast(pvBits); + for (ULONG y = bmi.bmiHeader.biHeight; y; --y) + { + for (ULONG x = bmi.bmiHeader.biWidth; x; --x) + { + if (*pargbMask++) // transparent pixel + *pargb++ = 0; + else // opaque pixel + *pargb++ |= 0xFF000000; + } + pargb += cxDelta; + } + hr = S_OK; + } + HeapFree(hHeap, 0, pvBits); + } + return hr; +} + +HRESULT ConvertBufferToPARGB32(HPAINTBUFFER hPaintBuffer, HDC hdc, HICON hicon, SIZE &sizeIcon) +{ + RGBQUAD *prgbQuad; + int cxRow = 0; + HRESULT hr = GetBufferedPaintBits(hPaintBuffer, &prgbQuad, &cxRow); + if (SUCCEEDED(hr)) + { + ARGB *pargb = reinterpret_cast(prgbQuad); + if (!HasAlpha(pargb, sizeIcon, cxRow)) + { + ICONINFO info = {}; + if (GetIconInfo(hicon, &info)) + { + if (info.hbmMask) + hr = ConvertToPARGB32(hdc, pargb, info.hbmMask, sizeIcon, cxRow); + DeleteObject(info.hbmColor); + DeleteObject(info.hbmMask); + } + } + } + return hr; +} + +CEsteidShlExt::CEsteidShlExt() +{ + SIZE sizeIcon = { GetSystemMetrics(SM_CXSMICON), GetSystemMetrics(SM_CYSMICON) }; + if(HICON hIcon = (HICON)LoadImage(_AtlBaseModule.GetModuleInstance(), MAKEINTRESOURCE(IDB_DIGIDOCICO), IMAGE_ICON, sizeIcon.cx, sizeIcon.cy, LR_DEFAULTCOLOR|LR_CREATEDIBSECTION)) + { + if(HDC hdcDest = CreateCompatibleDC(nullptr)) { + if((m_DigidocBmp = Create32BitHBITMAP(hdcDest, sizeIcon, nullptr))) { + if(HBITMAP hbmpOld = (HBITMAP)SelectObject(hdcDest, m_DigidocBmp)) { + RECT rcIcon = { 0, 0, sizeIcon.cx, sizeIcon.cy }; + BLENDFUNCTION bfAlpha = { AC_SRC_OVER, 0, 255, AC_SRC_ALPHA }; + BP_PAINTPARAMS paintParams = { sizeof(paintParams), BPPF_ERASE, nullptr, &bfAlpha }; + HDC hdcBuffer; + if(HPAINTBUFFER hPaintBuffer = BeginBufferedPaint(hdcDest, &rcIcon, BPBF_DIB, &paintParams, &hdcBuffer)) { + if(DrawIconEx(hdcBuffer, 0, 0, hIcon, sizeIcon.cx, sizeIcon.cy, 0, nullptr, DI_NORMAL)) { + // If icon did not have an alpha channel, we need to convert buffer to PARGB. + ConvertBufferToPARGB32(hPaintBuffer, hdcDest, hIcon, sizeIcon); + } + EndBufferedPaint(hPaintBuffer, TRUE); + } + SelectObject(hdcDest, hbmpOld); + } + } + DeleteDC(hdcDest); + } + DestroyIcon(hIcon); + } +} + +CEsteidShlExt::~CEsteidShlExt() +{ + DeleteObject(m_DigidocBmp); +} + +STDMETHODIMP CEsteidShlExt::Initialize( + LPCITEMIDLIST /* pidlFolder */, LPDATAOBJECT pDataObj, HKEY /* hProgID */) +{ + FORMATETC fmt = { CF_HDROP, nullptr, DVASPECT_CONTENT, -1, TYMED_HGLOBAL }; + STGMEDIUM stg = { TYMED_HGLOBAL }; + m_Files.clear(); + + // Look for CF_HDROP data in the data object. + if (FAILED(pDataObj->GetData(&fmt, &stg))) { + // Nope! Return an "invalid argument" error back to Explorer. + return E_INVALIDARG; + } + + // Get a pointer to the actual data. + HDROP hDrop = HDROP(GlobalLock(stg.hGlobal)); + if (!hDrop) { + ReleaseStgMedium(&stg); + return E_INVALIDARG; + } + + // Sanity check - make sure there is at least one filename. + UINT nFiles = DragQueryFile(hDrop, 0xFFFFFFFF, nullptr, 0); + if (nFiles == 0) { + GlobalUnlock(stg.hGlobal); + ReleaseStgMedium(&stg); + return E_INVALIDARG; + } + + for (UINT i = 0; i < nFiles; i++) { + // Get path length in chars + UINT len = DragQueryFile(hDrop, i, nullptr, 0); + if (len == 0 || len >= MAX_PATH) + continue; + + // Get the name of the file + TCHAR szFile[MAX_PATH]; + if (DragQueryFile(hDrop, i, szFile, len+1) == 0) + continue; + + tstring str = tstring(szFile); + if (str.empty()) + continue; + + m_Files.push_back(str); + } + + GlobalUnlock(stg.hGlobal); + ReleaseStgMedium(&stg); + + // Don't show menu if no items were found + return m_Files.empty() ? E_INVALIDARG : S_OK; +} + +STDMETHODIMP CEsteidShlExt::QueryContextMenu( + HMENU hmenu, UINT uMenuIndex, UINT uidFirstCmd, + UINT /* uidLastCmd */, UINT uFlags) +{ + // If the flags include CMF_DEFAULTONLY then we shouldn't do anything. + if (uFlags & CMF_DEFAULTONLY) + return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 0); + + PCTCH sign = _T("Sign digitally"); + PCTCH encrypt = _T("Encrypt"); + switch (PRIMARYLANGID(GetUserDefaultUILanguage())) + { + case LANG_ESTONIAN: + sign = _T("Allkirjasta digitaalselt"); + encrypt = _T("Krüpteeri"); + break; + case LANG_RUSSIAN: + sign = _T("Подписать дигитально"); + encrypt = _T("Зашифровать"); + break; + default: break; + } + + InsertMenu(hmenu, uMenuIndex, MF_STRING | MF_BYPOSITION, uidFirstCmd, sign); + if (m_DigidocBmp) + SetMenuItemBitmaps(hmenu, uMenuIndex, MF_BYPOSITION, m_DigidocBmp, nullptr); + InsertMenu(hmenu, uMenuIndex + MENU_ENCRYPT, MF_STRING | MF_BYPOSITION, uidFirstCmd + MENU_ENCRYPT, encrypt); + if (m_DigidocBmp) + SetMenuItemBitmaps(hmenu, uMenuIndex + MENU_ENCRYPT, MF_BYPOSITION, m_DigidocBmp, nullptr); + + return MAKE_HRESULT(SEVERITY_SUCCESS, FACILITY_NULL, 2); +} + +STDMETHODIMP CEsteidShlExt::GetCommandString( + UINT_PTR idCmd, UINT uFlags, UINT * /* pwReserved */, LPSTR pszName, UINT cchMax) +{ + USES_CONVERSION; + + // Check idCmd, it must be 0 or 1 since we have only two menu items. + if (idCmd > MENU_ENCRYPT) + return E_INVALIDARG; + + // If Explorer is asking for a help string, copy our string into the + // supplied buffer. + if (uFlags & GCS_HELPTEXT) { + LPCTSTR szText = idCmd == MENU_SIGN ? _T("Allkirjasta valitud failid digitaalselt") : _T("Krüpteeri valitud failid"); + + if (uFlags & GCS_UNICODE) { + // We need to cast pszName to a Unicode string, and then use the + // Unicode string copy API. + lstrcpynW(LPWSTR(pszName), T2CW(szText), int(cchMax)); + } else { + // Use the ANSI string copy API to return the help string. + lstrcpynA(pszName, T2CA(szText), int(cchMax)); + } + + return S_OK; + } + + return E_INVALIDARG; +} + +bool WINAPI CEsteidShlExt::FindRegistryInstallPath(tstring* path) +{ + static PCTCH IDCARD_REGKEY = _T("SOFTWARE\\RIA\\Open-EID"); + static PCTCH IDCARD_REGVALUE = _T("Installed"); + HKEY hkey; + DWORD dwSize = MAX_PATH * sizeof(TCHAR); + TCHAR szInstalldir[MAX_PATH]; + LSTATUS dwRet = RegOpenKeyEx(HKEY_LOCAL_MACHINE, IDCARD_REGKEY, 0, KEY_QUERY_VALUE, &hkey); + if (dwRet == ERROR_SUCCESS) { + dwRet = RegQueryValueEx(hkey, IDCARD_REGVALUE, nullptr, nullptr, LPBYTE(szInstalldir), &dwSize); + RegCloseKey(hkey); + *path = tstring(szInstalldir); + return true; + } + dwRet = RegOpenKeyEx(HKEY_CURRENT_USER, IDCARD_REGKEY, 0, KEY_QUERY_VALUE, &hkey); + if (dwRet == ERROR_SUCCESS) { + RegCloseKey(hkey); + *path = tstring(szInstalldir); + return true; + } + return false; +} + +STDMETHODIMP CEsteidShlExt::ExecuteDigidocclient(LPCMINVOKECOMMANDINFO /* pCmdInfo */, bool crypto) +{ + if (m_Files.empty()) + return E_INVALIDARG; + + tstring path(MAX_PATH, 0); + tstring command(MAX_PATH, 0); + + // Read the location of the installation from registry + if (!FindRegistryInstallPath(&path)) { + // .. and fall back to directory where shellext resides if not found from registry + GetModuleFileName(_AtlBaseModule.m_hInst, &path[0], MAX_PATH); + path.resize(path.find_last_of(_T('\\')) + 1); + } + + command = path + _T("qdigidoc4.exe"); + if(PathFileExists(command.c_str()) != 1) { + // Replace "c:\Program Files\" with "c:\Program Files (x86)\" + command.insert(16, _T(" (x86)")); + } + + // Construct command line arguments to pass to qdigidocclient.exe + tstring parameters = crypto ? _T("\"-crypto\" ") : _T("\"-sign\" "); + for (const tstring &file: m_Files) + parameters += _T("\"") + file + _T("\" "); + + SHELLEXECUTEINFO seInfo = { sizeof(SHELLEXECUTEINFO) }; + seInfo.lpFile = command.c_str(); + seInfo.lpParameters = parameters.c_str(); + seInfo.nShow = SW_SHOW; + return ShellExecuteEx(&seInfo) ? S_OK : S_FALSE; +} + +STDMETHODIMP CEsteidShlExt::InvokeCommand(LPCMINVOKECOMMANDINFO pCmdInfo) +{ + // If lpVerb really points to a string, ignore this function call and bail out. + if (HIWORD(pCmdInfo->lpVerb) != 0) + return E_INVALIDARG; + + // Get the command index - the valid ones are 0 and 1. + switch (LOWORD(pCmdInfo->lpVerb)) { + case MENU_SIGN: + return ExecuteDigidocclient(pCmdInfo); + case MENU_ENCRYPT: + return ExecuteDigidocclient(pCmdInfo, true); + default: + return E_INVALIDARG; + } +} diff --git a/extensions/windows/EsteidShlExt.h b/extensions/windows/EsteidShlExt.h new file mode 100644 index 000000000..b4caba6a9 --- /dev/null +++ b/extensions/windows/EsteidShlExt.h @@ -0,0 +1,54 @@ +// EsteidShlExt.h : Declaration of the CEsteidShlExt + +#pragma once +#include "resource.h" // main symbols + +#include "EsteidShellExtension_i.h" + +#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA) +#error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms." +#endif + +class ATL_NO_VTABLE CEsteidShlExt : + public CComObjectRootEx, + public CComCoClass, + public IShellExtInit, + public IContextMenu +{ +public: + CEsteidShlExt(); + ~CEsteidShlExt(); + + DECLARE_REGISTRY_RESOURCEID(IDR_ESTEIDSHLEXT) + DECLARE_NOT_AGGREGATABLE(CEsteidShlExt) + + BEGIN_COM_MAP(CEsteidShlExt) + COM_INTERFACE_ENTRY(IShellExtInit) + COM_INTERFACE_ENTRY(IContextMenu) + END_COM_MAP() + + DECLARE_PROTECT_FINAL_CONSTRUCT() + + // IShellExtInit + STDMETHODIMP Initialize(LPCITEMIDLIST, LPDATAOBJECT, HKEY) override; + + // IContextMenu + STDMETHODIMP QueryContextMenu(HMENU, UINT, UINT, UINT, UINT) override; + STDMETHODIMP InvokeCommand(LPCMINVOKECOMMANDINFO) override; + STDMETHODIMP GetCommandString(UINT_PTR, UINT, UINT*, LPSTR, UINT) override; + +private: + enum { + MENU_SIGN = 0, + MENU_ENCRYPT = 1, + }; + + using tstring = std::basic_string; + bool WINAPI FindRegistryInstallPath(tstring* path); + STDMETHODIMP ExecuteDigidocclient(LPCMINVOKECOMMANDINFO pCmdInfo, bool crypto = false); + + HBITMAP m_DigidocBmp = nullptr; + std::vector m_Files; +}; + +OBJECT_ENTRY_AUTO(__uuidof(EsteidShlExt), CEsteidShlExt) diff --git a/extensions/windows/EsteidShlExt_x64.rgs b/extensions/windows/EsteidShlExt_x64.rgs new file mode 100644 index 000000000..a77b39827 --- /dev/null +++ b/extensions/windows/EsteidShlExt_x64.rgs @@ -0,0 +1,23 @@ +HKCR +{ + NoRemove CLSID + { + ForceRemove {5606A547-759D-43DA-AEEB-D3BF1D1E816D} = s 'EsteidShlExt Class' + { + InprocServer32 = s '%MODULE%' + { + val ThreadingModel = s 'Apartment' + } + } + } + NoRemove * + { + NoRemove ShellEx + { + NoRemove ContextMenuHandlers + { + ForceRemove EsteidShlExt = s '{5606A547-759D-43DA-AEEB-D3BF1D1E816D}' + } + } + } +} diff --git a/extensions/windows/EsteidShlExt_x86.rgs b/extensions/windows/EsteidShlExt_x86.rgs new file mode 100644 index 000000000..21b81d78c --- /dev/null +++ b/extensions/windows/EsteidShlExt_x86.rgs @@ -0,0 +1,23 @@ +HKCR +{ + NoRemove CLSID + { + ForceRemove {310AAB39-76FE-401B-8A7F-0F578C5F6AB5} = s 'EsteidShlExt Class' + { + InprocServer32 = s '%MODULE%' + { + val ThreadingModel = s 'Apartment' + } + } + } + NoRemove * + { + NoRemove ShellEx + { + NoRemove ContextMenuHandlers + { + ForceRemove EsteidShlExt = s '{310AAB39-76FE-401B-8A7F-0F578C5F6AB5}' + } + } + } +} diff --git a/extensions/windows/dllmain.cpp b/extensions/windows/dllmain.cpp new file mode 100644 index 000000000..c1ac50115 --- /dev/null +++ b/extensions/windows/dllmain.cpp @@ -0,0 +1,15 @@ +// dllmain.cpp : Implementation of DllMain. + +#include "stdafx.h" +#include "resource.h" +#include "EsteidShellExtension_i.h" +#include "dllmain.h" + +CEsteidShellExtensionModule _AtlModule; + +// DLL Entry Point +extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved) +{ + hInstance; + return _AtlModule.DllMain(dwReason, lpReserved); +} diff --git a/extensions/windows/dllmain.h b/extensions/windows/dllmain.h new file mode 100644 index 000000000..05f6a30d6 --- /dev/null +++ b/extensions/windows/dllmain.h @@ -0,0 +1,10 @@ +// dllmain.h : Declaration of module class. + +class CEsteidShellExtensionModule : public CAtlDllModuleT< CEsteidShellExtensionModule > +{ +public : + DECLARE_LIBID(LIBID_EsteidShellExtensionLib) + DECLARE_REGISTRY_APPID_RESOURCEID(IDR_ESTEIDEXT, "{08BD50C2-0AFC-4ED0-BC78-78488EDF9E58}") +}; + +extern class CEsteidShellExtensionModule _AtlModule; diff --git a/extensions/windows/resource.h b/extensions/windows/resource.h new file mode 100644 index 000000000..6d15be586 --- /dev/null +++ b/extensions/windows/resource.h @@ -0,0 +1,19 @@ +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by EsteidShellExtension.rc +// +#define IDS_PROJNAME 100 +#define IDR_ESTEIDEXT 101 +#define IDR_ESTEIDSHLEXT 102 +#define IDB_DIGIDOCICO 201 + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 203 +#define _APS_NEXT_COMMAND_VALUE 32768 +#define _APS_NEXT_CONTROL_VALUE 201 +#define _APS_NEXT_SYMED_VALUE 103 +#endif +#endif diff --git a/extensions/windows/resources/digidoc.ico b/extensions/windows/resources/digidoc.ico new file mode 100644 index 0000000000000000000000000000000000000000..658ea2e934d7f7e87e13b008ab8d3238bae6727d GIT binary patch literal 152170 zcmdS=bzD?m^e_sap#=$1KpF`VDM3Pplnw#uZfORjrAI+Qk&teb7`jVxK#=b4p}VE? zp85Ws=lR_GzVCnUUzb0Iv*WDTd+k+w9RPp;xB&VO0x$ql0s!FgU+aI`>Qn$w3--a` z|I>bk2LTAw06;?Gf3+n6;H4h;Cd2=whM0ES^7Xa9o zLV&MtRpbcqsqn!8LIrtgbpXHsA29$t9Pr=qhtW#_oU9=ITJuB7?wn_W=GbEU;hDJa@(E@;T_iE)FSI!)?%OHNu zT_?-SkeZ5$3TbGte&zjs{SW6{y<3Xl=J=0o<&N3tTq==)y0y(FlS19O0%h+lEql)$ zsIQXV=XWzvBTw|y$jAV=iPcP$ID=jsz6@y*c&H|Om3H^N5f%V{Wuf$(i`{A8MtEWTVX(>IdEXz(^q^&^St8`s!X$N* zk%qS3<8W6@AX1*FQ-Ry~rK|Z>oS0K+Kalc%DAUt-dk<{^5P-rqGpYjfh@ggl0=fk_)gl;?G90|BK_$w6Ms{!Bb{nxWG{NlgOXDVJDfw1K?Qekuf_q zm#gmbT)`_L2RNRNHT_}@szW^yMx?`Wf1Z%RD>}rc_E%LYxHK7hnVg#Ao>Q~r=*ZE` z>PubKXm~glySN;ULdA88{=JNi{lO@gbvFnB?s1af+33iGDOSf9yz|NH?)I>;w#^f} zbcq(zQ)j%`&UUL=!TVR_94uu`%!Z$bg_XL~Ag&^MelhhAi9fSIUz+W0Aa`b0^g8%- z&`IvTmRd>mVl6oUn3cYtvQqZBV)8rvtJtTn&FEEh5DN*GTBqgzIq;9znBCm z)*B~l@uM|d%P@Dsmd`Wv{sy?KEcR{AQf_y@QRQhQ011VI0hQ++U*io*waQ$sXvN_I z(x19A#E%pbvtIiBK=|w|;ivi|qQ(|3u9Ev=Q(_;~qfS;eCJJ;s#Wpd4rHOK_Pm41k1WDNAh3 zj(W?&p}JL1TD`UZ+Scm6_#iBx$phIw*Uvg9;x{)=i?l8Cx+sVCc`rVt<^NsJv33cr zXJ<96FRemRF2Y-)Tfr_bI^Q+9VRL)FaedU!Z&NIOAfoOx@!z-;J1nsUMgMkyXjiMu zt^5noX3GLGAg>!|X}b|gm#+)e6BVwEzve9cJzrkwZ?M^^HX-IZFNSn8F^S|o$Zt>j zBCAs&1+^s66VcQ%hMszM%J0JgWIB289mEs`v#CB=vV;48vGC(Jv%K_fes>}aa*)sG3LKEEA`j!Tmi z$g9;Tg?K^h15f#*3lV*R8zP(l0QeLW9h*VsH2)-hpkjaQ(nkX|$cMQ5lwS4eWrx_R z*U9q0c5>BLfL3EQ%vbuNmhx9RCU|6O8Y?A>poUEDXJ7nDXU6rUi2pSmb^aB`fdf4gh^I>B+ywl=gq*Cm*gE^{6Xbj{OzC*`+6g9}+k2dtLR! zMg;qlZT?8^SDWa^?i0cWfXO2JbqrCL5urY4#|(kaE$okp*unL|nZG75fA>yBe5y|u z_I7z_k|2Wc<^up6ytLpJ(at~Ixgt1gt3H|UNI$(=bh^m}V#fG-2`*M{;m;{%2Mj

j^!$l&2h={CZ%A$jb7B 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 q5QrvPOZ9=#{|0&f- +#include +#include +#include +#include +#include + +using namespace ATL; diff --git a/extensions/windows/targetver.h b/extensions/windows/targetver.h new file mode 100644 index 000000000..5f1a19420 --- /dev/null +++ b/extensions/windows/targetver.h @@ -0,0 +1,23 @@ +#pragma once + +// The following macros define the minimum required platform. The minimum required platform +// is the earliest version of Windows, Internet Explorer etc. that has the necessary features to run +// your application. The macros work by enabling all features available on platform versions up to and +// including the version specified. + +#ifndef WINVER +#define WINVER 0x0A00 +#endif + +#ifndef _WIN32_WINNT +#define _WIN32_WINNT 0x0A00 +#endif + +#ifndef _WIN32_WINDOWS +#define _WIN32_WINDOWS 0x0A00 +#endif + +#ifndef _WIN32_IE +#define _WIN32_IE 0x0A00 +#endif +