/* $Id: e2p_crypt.c 907 2008-06-04 04:02:03Z tpgww $ Copyright (C) 2007-2009 tooar This file is part of emelFM2. emelFM2 is free software; for the most part 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, or (at your option) any later version. emelFM2 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 emelFM2; see the file GPL. If not, see http://www.gnu.org/licenses. */ /** @file plugins/optional/e2p_cryptv1.c @brief plugin for encrypting or decrypting selected items The encryption code is derived from tinycrypt v.0.4. It’s based on ARC4 (see http://en.wikipedia.org/wiki/RC4) with some tweaks by the author of tinycrypt: – reduced nonce value to about 20 bytes (“160 bits should be enough”) – reduced discarded bytes to 512 (“there’s no evidence on the net that more are needed”) – simplified mixing of the password and nonce values with the key, since throwing away the first 512 bytes mixes them up anyway For tinycrypt-mode, any [de]compression is peformed using minilzo, the mini subset of the fast LZO real-time data compression library, v.2.03. Refer to http://www.oberhumer.com/opensource/lzo/#minilzo */ /*TODO en/de-compression single handle counter in new custom name ?m[un]map (void *address, size_t length, int protect, int flags, int filedes, off_t offset) buffered I/O if not processing whole file at once, can’t be done with compression ? pthread_testcancel (); //swap threads, cancel if instructed WHERE RELEVANT library access for [de]compression check – links ok ? LZO supports in-place decompression, so sniff or reallocate loaded buffer NO – fails in-place decompressing instead of duplicate buffers seems to NOT work max size of lzo buffer lzo_unt = size_t? = ? LZO streaming? LZMA/7-zip compression supports streaming ? UI error handling – warnings entry latency sometimes crashes sometimes during dialog setup (maybe more to do with de-cryption?) entry text scrolled out of entry window sometimes some dialog-button choices have a BGL problem, if hide dialog after user-choice ? */ #include “emelfm2.h” #include #include #include #include #include #include “e2_plugins.h” #include “e2_password_dialog.h” #include “e2_task.h” #include “e2_filelist.h” //uncomment to create files compatible with tinycrypt NOT MUCH TESTED #define TC_COMPATIBLE #ifdef TC_COMPATIBLE # define E2_MINICRYPT //TC 0.4 always uses mini-LZO, later versions allow no compression, we don’t do that # define csize_t guint32 //TC code uses gulong’s, but store/retrieve operations use 4 bytes # define SIZESHIFT 24 //(sizeof (csize_t) – 1) * 8, for handling endian-independent stored csize_t # define NONCE_LENGTH 20 # define HEADER_LENGTH1 20 //just the nonce, no flags, in TC file header #elif defined(_LARGEFILE_SOURCE)//if we cater for “large” files # define csize_t guint64 # define SIZESHIFT 56 # define NONCE_LENGTH 24 //HEADER_LENGTH1 – csize_t (>=20) # define HEADER_LENGTH1 32 //multiple of 8 >= (csize_t + 20) #else //if we cater for files up to 2 GB # define csize_t guint32 # define SIZESHIFT 24 # define NONCE_LENGTH 20 # define HEADER_LENGTH1 24 //NONCE_LENGTH + csize_t for flags #endif #ifndef TC_COMPATIBLE //enable internal mini-LZO for en/de-cryption //#define E2_MINICRYPT #endif #define KEY_LENGTH 256 //ARC4 hash table size KEY_LENGTH MUST be 256 #ifdef TC_COMPATIBLE # define DISCARD_BYTES 768 // KEY_LENGTH * 3 #else //”conservative” approach would use much greater size e.g. KEY_LENGTH * 10 or 12 # define DISCARD_BYTES 512 // KEY_LENGTH * 2 this is recommended by TC author #endif #define SWAP(a,b) temp=a;a=b;b=temp; //bitflags in csize_t stored in after nonce in encrypted file //(don’t change or remove any existing flag!) enum { E2_CFLAGNONE = 0, E2_CFLAGCOMPRESS = 1, //file is compressed (and one of the type-flags is set) E2_CFLAGSIZE = 1 << 4, //original file size stored as csize_t E2_CFLAGNAME = 1 << 5, //original file name stored as text E2_CFLAGINFO = 1 << 6, //original file data stored in FileInfo //bits 10-12 reserved for interface version number, 0..7 E2_CFLAGINTLZ = 1 << 16, //file compressed with internal mini lzo E2_CFLAGLZO = 1 << 17, //file compressed with external liblzo E2_CFLAGZ = 1 << 18, //file compressed with external libz E2_CFLAGBZ2 = 1 << 19, //file compressed with external libbz2 E2_CFLAGLZMA = 1 << 20 //file compressed with external liblzma NOT WORKING }; //for [de]compression library checking #define E2_CFLAGLIBMASK 0x1f0000 //for interface version #define E2_GETFLAGVERSION(s) ((s>>10)&7) #define E2_SETFLAGVERSION(s,n) (s|((n&7)<<10)) #ifndef E2_MINICRYPT //FOR LZO COMPRESSION # define LZO1X_1_MEM_COMPRESS 16384*sizeof(guchar *) //for dlopen() usage # define LZO_COMPRESSFUNC "lzo1x_1_compress" # define LZO_DECOMPRESSFUNC "lzo1x_decompress_safe" # define GZIP_COMPRESSFUNC "compress2" # define GZIP_DECOMPRESSFUNC "uncompress" # define BZIP2_COMPRESSFUNC "BZ2_bzBuffToBuffCompress" # define BZIP2_DECOMPRESSFUNC "BZ2_bzBuffToBuffDecompress" //# define LZMA_COMPRESSFUNC "LzmaCompressFIXME" //# define LZMA_DECOMPRESSFUNC "LzmaUnCompressFIXME" //the preferred order for automatically choosing a compression library, if present //NOTE config data now used instead of auto choice #define COMPRESS_CHOICE1 E2_CFLAGLZO #define COMPRESS_CHOICE2 E2_CFLAGZ #define COMPRESS_CHOICE3 E2_CFLAGBZ2 //#define COMPRESS_CHOICE4 E2_CFLAGLZMA #endif //FOR LIBZ [DE]COMPRESSION //compression levels enum { Z_DEFAULT_COMPRESSION = -1, Z_NO_COMPRESSION, Z_BEST_SPEED, Z_BEST_COMPRESSION = 9 }; //FOR LIBBZIP2 [DE]COMPRESSION //FOR LIBLZMA [DE]COMPRESSION //#define SZ_OK ? typedef struct _E2P_CryptOpts { #ifndef TC_COMPATIBLE gboolean en_name_same; //encrypted file name = same as onencrypted name gboolean en_name_suffix;//encrypted file name has user-specified suffix (if any) gboolean en_name_custom;//encrypted file name = user-specified gboolean en_name_embed; //store filename in encrypted file CHECKME used ? gboolean en_properties_embed; //store filename and statbuf in encrypted file gboolean de_name_same; //decrypted file name = same as encrypted name gboolean de_name_stored; //decrypted file name = embedded original name gboolean de_name_suffix; //decrypted file name omits user-specified suffix (if any) gboolean de_name_custom; //decrypted file name = user-specified gboolean de_props_stored; //decrypted file other properties = original file gboolean compress; //compress file before encryption #endif gboolean backup; //preserve any file with same name as specified for the [de]crypted file gboolean preserve; //preserve the file to be [de]crypted, with alternate name if appropriate gboolean recurse; //recursively process all files in any selected dir gboolean walklinks; //process link targets gboolean decryptmode;//TRUE = decrypt, FALSE = encrypt gboolean permission;//for transferring whether it's ok to make the change gboolean multisrc; //TRUE when processing > 1 item in loop gboolean owrite; //don’t ask to confirm overwrites #ifndef TC_COMPATIBLE gboolean ignore_suffix; //don’t worry about incorrect suffix when decompressing gchar *en_suffix; //user-specified suffix, freeable utf-8 gchar *en_name; //user-specified name, freeable utf-8 gchar *de_suffix; //user-specified suffix, freeable utf-8 gchar *de_name; //user-specified name, freeable utf-8 #endif gchar *plain_pw; //store for plaintext password ptr utf-8 const gchar *localpath; //copy of dialog-function arg, or substitute during treewalk #ifndef E2_MINICRYPT csize_t compresslibflags; gpointer libhandle; //result from latest dlopen(), need to dlcose() when done #endif struct stat *statptr; #ifdef E2_VFS PlaceInfo *spacedata; #endif GList *dirdata; //list of E2_Dirent’s for dirs yet to be processed } E2P_CryptOpts; typedef struct _E2P_CryptDlgRuntime { GtkWidget *dialog; //main dialog widget E2P_CryptOpts *opts; E2_PWDataRuntime *pwrt; //data struct for password-related widgets gboolean dlgopen; //the following widgets exist GtkWidget *mode_btn; //radio button for en/de-crypt mode #ifndef TC_COMPATIBLE GtkWidget *encryptbox; //vbox with encryption-specific widgets GtkWidget *en_name_btn_same; GtkWidget *en_name_btn_suffix; GtkWidget *en_name_btn_custom; GtkWidget *en_name_suffix_entry; GtkWidget *en_name_custom_entry; // GtkWidget *en_name_embed_btn; CHECKME use this ? #endif GtkWidget *confirmbox; #ifndef TC_COMPATIBLE GtkWidget *en_properties_embed_btn; GtkWidget *compress_btn; GtkWidget *decryptbox; //vbox with decryption-specific widgets GtkWidget *de_name_btn_same; GtkWidget *de_name_btn_stored; GtkWidget *de_name_btn_suffix; GtkWidget *de_name_btn_custom; GtkWidget *de_name_suffix_entry; GtkWidget *de_name_custom_entry; #endif GtkWidget *recurse_btn; GtkWidget *backup_btn; GtkWidget *preserve_btn; GtkWidget *linktarget_btn; #ifndef TC_COMPATIBLE GtkWidget *properties_btn; #endif // GtkWidget *all_btn; //for [de]sensitisizing apply-to-all choice GtkWidget *ok_btn; // DialogButtons result; gboolean result; } E2P_CryptDlgRuntime; //session-static parameters static E2P_CryptOpts session_opts = { #ifndef TC_COMPATIBLE FALSE, //encrypted file name = same as onencrypted name TRUE, //encrypted file name has user-specified suffix (if any) FALSE, //encrypted file name = user-specified FALSE, //store filename in encrypted file CHECKME relevant ? FALSE, //store filename and statbuf in encrypted file FALSE, //decrypted file name = same as encrypted name FALSE, //decrypted file name = embedded original name TRUE, //decrypted file name omits user-specified suffix (if any) FALSE, //decrypted file name = user-specified FALSE, //reinstate properties of original file other than its name TRUE, //compress file before encryption #endif TRUE, //preserve any file with same name as specified for the [de]crypted file TRUE, //preserve the file to be [de]crypted, with alternate name if appropriate FALSE, //recursively process all files in any selected dir TRUE, //process link targets /*rest are all default values, FALSE or NULL FALSE, //decrypt mode FALSE, //permission FALSE, //>1 item FALSE, //overwrite FALSE, //ignore decomp suffix NULL, //encryption suffix, initialised to “.enc” NULL, //custom name for encryption NULL, //decryption suffix, initialised to “.enc” NULL, //custom name for decryption NULL, //password NULL, //item path #ifndef E2_MINICRYPT 0, //compresslibflags; NULL, //libhandle #endif NULL //stat buffer ptr */ }; //CHECKME are these ok to be static for all usage in session ?? //gboolean lzo_init_done = FALSE; //only init lzo (mini or lib) once?? OR EACH USE ? //gboolean lzma_init_done = FALSE; //ditto for lzma SHOULD BE DONE ONCE ONLY, NOT EACH USAGE #ifndef E2_MINICRYPT //comression-library names used for config labels and error messages. Do not translate static const gchar *libnames [] = { “LZO”, //config option 0 “GZIP”, //1 “BZIP2”,//2 // “LZMA”, //3 NULL }; #endif csize_t compresslib = E2_CFLAGNONE; #ifdef E2_MINICRYPT # if E2_DEBUG_LEVEL > 2 # define LZO_DEBUG # endif /* get mini-lzo headers and code For convenience, this file merges all relevant files */ # include “e2p_crypt_lzo.source” /* If the merge is not available, minilzo.c can be included instead. The files lzodefs.h lzoconf.h minilzo.h will also need to be available, and if built as is, it wlll result in a bogus plugin minilzo.so */ //# include “minilzo.c” #endif static gboolean _e2p_task_docryptQ (E2_ActionTaskData *qed); static csize_t _e2pcr_compress_buffer (gpointer filebuffer, /*ssize_t*/ gulong filebuffersize, gpointer *compressedbuffer); //CHECKME gulong size ? static gboolean _e2pcr_write_buffer (VPATH *localpath, gint descriptor, gpointer filebuffer, /*ssize_t*/ gulong filebuffersize); //CHECKME gulong size ? static gboolean _e2pcr_read_file (VPATH *localpath, gpointer *filebuffer, /*ssize_t*/ gulong filebuffersize); static csize_t _e2pcr_decompress_buffer (gpointer filebuffer, /*ssize_t*/ gulong filebuffersize, csize_t originalfilesize, #ifndef E2_MINICRYPT csize_t libflags, #endif gpointer *decompressedbuffer #ifndef E2_MINICRYPT , csize_t *lastlib, gpointer *libhandle #endif ); static gboolean _e2pcr_wipe_buffer (gpointer buffer, size_t buffersize); static gboolean _e2pcr_flush_file (VPATH *localpath, guint8 hashes[]); /***************/ /** utilities **/ /***************/ /** @brief get a random value in range 0 .. 255 @param retval pointer to store for value @return TRUE if the value was filled */ static gboolean _e2pcr_getrandom (guint8 *retval) { E2_FILE *randFile = fopen //no vfs needed here #if defined(__linux__) || defined(__solaris__) || defined(darwin) || defined(__OpenBSD__) || defined(AIX) //CHECKME which other OS’s ? (“/dev/urandom”, “r”); #else (“/dev/random”, “r”); #endif if (randFile == NULL) { printd (DEBUG, “cannot open random number source”); //FIXME handle error *retval = 0; return FALSE; } *retval = getc (randFile); fclose (randFile); return TRUE; } /** @brief construct a temporary itemname by adding a suffix to @a localpath @param localpath absolute path of item to be processed, localised string @param custom string to append to @a localpath, localised string @return newly-allocated, localised, path string comprising the temp name */ static gchar *_e2pcr_get_tempname (VPATH *localpath, gchar *custom) { gchar *temppath, *s; guint i = 0; E2_ERR_DECLARE #ifdef E2_VFS VPATH tdata; tdata.spacedata = localpath->spacedata; #endif while (TRUE) { temppath = g_strdup_printf (“%s%s~%d”, VPSTR(localpath), custom, i); if (i == 0) { //first try without any “~N” suffix s = strrchr (temppath, ‘~’); *s = ‘’; } #ifdef E2_VFS tdata.path = temppath; if (e2_fs_access2 (&tdata E2_ERR_PTR()) && E2_ERR_IS (ENOENT)) #else if (e2_fs_access2 (temppath E2_ERR_PTR()) && E2_ERR_IS (ENOENT)) #endif { E2_ERR_CLEAR break; } E2_ERR_CLEAR g_free (temppath); i++; } return temppath; } /** @brief in an endian-independant fashion, store unencrypted data corresponding to @a value starting from @a datastart @param datastart pointer to first of a series of unencrypted bytes to store @return */ static void _e2pcr_store (csize_t value, guchar *datastart) { guint i; //stored byte-order is low … high for (i = 0; i < sizeof (csize_t); i++) { *datastart++ = (guchar) value; value >>= 8; } } /** @brief in an endian-independant fashion, construct a csize_t from data stored starting from @a datastart @param datastart pointer to first of a series of decrypted stored bytes to process @return the stored value */ static csize_t _e2pcr_read_store (guchar *datastart) { csize_t value = 0; csize_t grabber; //needs to be bigger than SIZESHIFT to prevent warning guint i; //retrieved byte-order is low … high for (i = 0; i < sizeof (csize_t); i++) { grabber = *datastart++; value = (value >> 8) | (grabber << SIZESHIFT); } return value; } /** @brief check whether the user wlll allow an existing item to be over-written This is not done in main task loop, as we need to handle custom names set in the dialog or stored in a file being decompressed This expects BGL to be open/off @param localpath absolute path of item to check, localised string @param multisrc TRUE if the current item is part of a multi-item selection @return OK if there is no conflict, or code corresponding to the user's choice in the o/w dialog */ static DialogButtons _e2pcr_ow_check (VPATH *localpath, gboolean multisrc) { return (e2_fs_access2 (localpath E2_ERR_NONE()) == 0) ? e2_dialog_ow_check (NULL, localpath, (multisrc) ? BOTHALL : NONE): OK; } #ifndef TC_COMPATIBLE /** @brief run warning dialog for confirmation @param prompt prompt string @param multi this is part of a multi-file task @return button code returned by the dialog */ static DialogButtons _e2pcr_dialog_warning (gchar *prompt, gboolean multi) { DialogButtons retval; GtkWidget *dialog = e2_dialog_create (GTK_STOCK_DIALOG_WARNING, prompt, _("confirm"), e2_dialog_response_decode_cb, &retval); //set default button to 'no' E2_BUTTON_NO.showflags |= E2_BTN_DEFAULT; gdk_threads_enter (); if (multi) e2_dialog_show (dialog, NULL, //parent stays sensitive 0, &E2_BUTTON_NOTOALL, &E2_BUTTON_YESTOALL, &E2_BUTTON_YES, &E2_BUTTON_NO, NULL); else e2_dialog_show (dialog, NULL, //parent stays sensitive 0, &E2_BUTTON_YES, &E2_BUTTON_NO, NULL); gtk_main (); //because the decoder has gtk_main_quit(); gtk_widget_destroy (dialog); gdk_threads_leave (); return retval; } #endif /****************************/ /* en/de-cryption functions */ /****************************/ #ifndef E2_MINICRYPT /** @brief setup things for decompression, according to @a flags A pointer to library function for compressing or decompressing a buffer will be set to relevant function from liblzo, libz or libbz2, according to which lib is wanted and if it's available If a library requires initialisation before use, that's done here @param flags the flags recorded in a file being decompressed, in particular, the type of compression library @param lastlib store for lib-in-use flag (may have been used for alternate operation) @param getcompress TRUE to get compress function, FALSE for decompress @param libhandle store for handle to dlopen'd library @param libfunc store for desired function pointer @return TRUE if no error happens */ static gboolean _e2pcr_check_lib (csize_t flags, csize_t *lastlib, gboolean getcompress, gpointer *libhandle, gpointer *libfunc) { printd (DEBUG, "masked file lib-flags %0x", flags & E2_CFLAGLIBMASK); gboolean retval; gint (*init_func) (); gpointer thishandle; retval = FALSE; if (flags & E2_CFLAGLZO) //want to use LZO { if (*lastlib & E2_CFLAGLZO) //lib is already interrogated { thishandle = *libhandle; printd (DEBUG, "LZO decompression library used before"); } else thishandle = dlopen ("liblzo2.so.2", RTLD_LAZY | RTLD_GLOBAL); if (thishandle != NULL) { printd (DEBUG, "LZO decompression library opened"); init_func = dlsym (thishandle, "__lzo_init_v2"); //a #define in lzoconf.h if (init_func != NULL) { // if (!lzo_init_done)//CHECKME needed every time ? // { //this hack is not much better than no init at all ! gint result = init_func (2, sizeof(gshort), sizeof(gint), sizeof(glong), sizeof(guint32), sizeof(guint), sizeof(guchar *), //dict_t sizeof(gchar *), sizeof(gpointer), -1 //sizeof(lzo_callback_t)); ); // if (result == 0) // lzo_init_done = TRUE; // else if (result != 0) printd (DEBUG, "Cannot init compression using LZO lib"); // } // if (lzo_init_done) if (result == 0) { *libfunc = dlsym (thishandle, (getcompress) ? LZO_COMPRESSFUNC : LZO_DECOMPRESSFUNC); if (*libfunc != NULL) { *lastlib &= ~E2_CFLAGLIBMASK; *lastlib |= E2_CFLAGLZO; if (*libhandle != NULL && *libhandle != thishandle) dlclose (*libhandle); *libhandle = thishandle; retval = TRUE; printd (DEBUG, "using LZO lib"); } } if (!(*lastlib & E2_CFLAGLZO)) { // lzo_init_done = FALSE; dlclose (thishandle); if (*libhandle == thishandle) *libhandle = NULL; } } } #ifdef DEBUG_MESSAGES else //thishandle == NULL printd (DEBUG, "LZO decompression library missing"); #endif } else if (flags & E2_CFLAGZ) { if (*lastlib & E2_CFLAGZ) //lib is already interrogated thishandle = *libhandle; else thishandle = dlopen ("libz.so.1", RTLD_LAZY | RTLD_GLOBAL); if (thishandle != NULL) { *libfunc = dlsym (thishandle, (getcompress) ? GZIP_COMPRESSFUNC : GZIP_DECOMPRESSFUNC); if (*libfunc != NULL) { *lastlib &= ~E2_CFLAGLIBMASK; *lastlib |= E2_CFLAGZ; if (*libhandle != NULL && *libhandle != thishandle) dlclose (*libhandle); *libhandle = thishandle; retval = TRUE; printd (DEBUG, "using ZLIB lib"); } else { dlclose (thishandle); if (*libhandle == thishandle) *libhandle = NULL; } } #ifdef DEBUG_MESSAGES else //thishandle == NULL printd (DEBUG, "ZLIB decompression library gone missing"); #endif } else if (flags & E2_CFLAGBZ2) { if (*lastlib & E2_CFLAGBZ2) //lib is already interrogated thishandle = *libhandle; else thishandle = dlopen ("libbz2.so.1", RTLD_LAZY | RTLD_GLOBAL); if (thishandle != NULL) { *libfunc = dlsym (thishandle, (getcompress) ? BZIP2_COMPRESSFUNC : BZIP2_DECOMPRESSFUNC); if (*libfunc != NULL) { *lastlib &= ~E2_CFLAGLIBMASK; *lastlib |= E2_CFLAGBZ2; if (*libhandle != NULL && *libhandle != thishandle) dlclose (*libhandle); *libhandle = thishandle; retval = TRUE; printd (DEBUG, "using BZ2 lib"); } else { dlclose (thishandle); if (*libhandle == thishandle) *libhandle = NULL; } } #ifdef DEBUG_MESSAGES else //thishandle == NULL printd (DEBUG, "BZ2LIB decompression library gone missing"); #endif } /*NOT WORKING else if (flags & E2_CFLAGLZMA) { if (*lastlib & E2_CFLAGLZMA) //lib is already interrogated thishandle = *libhandle; else thishandle = dlopen ("liblzma.so.0", RTLD_LAZY | RTLD_GLOBAL); if (thishandle != NULL) { if (!lzma_init_done) { init_func = dlsym (thishandle, "lzma_init"); //a #define in lzma/init.h if (init_func != NULL) { lzma_init_done = init_func (); } } if (lzma_init_done) { *libfunc = dlsym (thishandle, (getcompress) ? LZMA_COMPRESSFUNC : LZMA_DECOMPRESSFUNC); if (*libfunc != NULL) { *lastlib &= ~E2_CFLAGLIBMASK; *lastlib |= E2_CFLAGLZMA; if (*libhandle != NULL && *libhandle != thishandle) dlclose (*libhandle); *libhandle = thishandle; printd (DEBUG, "using LZMA lib"); } else { dlclose (thishandle); if (*libhandle == thishandle) *libhandle = NULL; } } } #ifdef DEBUG_MESSAGES else //thishandle == NULL printd (DEBUG, "LZMA decompression library missing"); #endif } */ return retval; } #endif /** @brief initialize hash key Unlike with tinycrypt, the "discard bytes" process starts at hashes[0], not hashes[1], and at the end of that process tha i-index is 255, not 0 @param hashes array of hash-bytes @param password the en/de-cryption password string (UTF-8) @param nonce pointer to start of "nonce" string @return the "seed" (j-index) of the hashkey to use for the next operation */ static guint8 _e2pcr_init_key (guint8 hashes[], const gchar *password, const guchar *nonce) { guint indx; guint8 i, j, temp; const gchar *p, *pe; //initialize the key; for (indx = 0; indx < KEY_LENGTH; indx++) hashes[indx] = (guint8)indx; //mangle the key from the password, as done in ARC4 i = j = 0; p = password; pe = password + strlen (password); //use end-marker in case password is empty for (indx = 0; indx < KEY_LENGTH; indx++) { if (p >= pe) p = password; j += hashes[(guint8)indx] + *p++; SWAP(hashes[(guint8)indx], hashes[j]); } //mangle the key from the “public” random bytes in the nonce //CHECKME why not KEY_LENGTH repetitions ? for (indx = 0; indx < NONCE_LENGTH; indx++) { j += hashes[i] + *nonce++; SWAP(hashes[i], hashes[j]); i++; //CHECKME want this before j update ? } //"discard bytes" = mangle some more to increase chance of all array-members //being swapped #ifdef TC_COMPATIBLE i = 0; //do hashes[1] first, hashes[0] last #else i = 255; //do hashes[0] first #endif j = 0; for (indx = 0; indx < DISCARD_BYTES; indx++) //DISCARD_BYTES is a multiple of KEY_LENGTH { i++; j += hashes[i]; SWAP(hashes[i], hashes[j]); } return j; } /** @brief setup nonce to use for encryption (and later, decryption) @param noncebuffer array of chars in which to store the nonce, must be sized >= NONCE_LENGTH @return FALSE on error */ static gboolean _e2pcr_init_nonce (guchar noncebuffer[]) { //fill nonce with random bytes E2_FILE *randFile = fopen #if defined(__linux__) || defined(__solaris__) || defined(darwin) || defined(__OpenBSD__) || defined(AIX) //CHECKME which other OS’s ? (“/dev/urandom”, “r”); #else (“/dev/random”, “r”); #endif if (randFile == NULL) { printd (DEBUG, “cannot open random number source”); return FALSE; } guchar *bufferp = noncebuffer; guint i; for (i = 0; i < NONCE_LENGTH; i++) *bufferp++ = getc (randFile); fclose (randFile); return TRUE; } /** @brief encrypt or decrypt @a filebuffer Buffer contents are XOR-ed with the user's munged key @param hashes array of key bytes @param iseed pointer to store for i-index for manipulating @a hashes @param jseed pointer to store for j-index for manipulating @a hashes @param filebuffer the buffer to be processed @param filebuffersize byte-size of @a filebuffer @return */ static void _e2pcr_crypt_buffer (guint8 hashes[], guint8 *iseed, guint8 *jseed, gpointer filebuffer, csize_t filebuffersize) { guchar *filebufferp, *filebufferend; guint8 i, j, temp; filebufferend = filebuffer + filebuffersize; i = *iseed; j = *jseed; for (filebufferp = filebuffer; filebufferp < filebufferend; filebufferp++) { #ifdef TC_COMPATIBLE //careful, just about _any_ change here seems to break compatibility i++; j += hashes[i]; SWAP(hashes[i], hashes[j]); (*filebufferp) ^= hashes[(guint8)(hashes[i] + hashes[j])]; #else guint8 k; k = hashes[i]; j += k; (*filebufferp) ^= hashes[(guint8)(k + hashes[j])]; //originally, had bare *filebufferp SWAP(hashes[i], hashes[j]); i++; #endif } *iseed = i; *jseed = j; } /** @brief finalise naming of en/de-crypted file This is same for en- and de-cryption @param localpath localised path of the item that's been processed @param temppath localised path of interim temp file holding the processed results @param newpath localised path of final name that's different from @a localpath @param same_name TRUE when the processed file ends up as @a localpath @param options pointer to operation data struct @return TRUE if all was done as required */ static gboolean _e2pcr_finalise_item (VPATH *localpath, VPATH *temppath, VPATH *newpath, gboolean same_name, E2P_CryptOpts *options) { #ifdef E2_VFS VPATH otherpath; otherpath.spacedata = localpath->spacedata; #endif gboolean success; gchar *tmp; if (same_name) { if (options->backup) { //no need to check for permission – that’s done in the dialog tmp = _e2pcr_get_tempname (localpath, “-original”); //ascii, & don’t bother with translation #ifdef E2_VFS otherpath.path = tmp; success = e2_task_backend_rename (localpath, &otherpath); #else success = e2_task_backend_rename (localpath, tmp); #endif g_free (tmp); if (!success) return FALSE; } if (!e2_task_backend_rename (temppath, localpath)) return FALSE; } else //final name != original name { if (!e2_fs_access (newpath, F_OK E2_ERR_NONE())) { if (options->backup) { if (!e2_fs_access (newpath, W_OK E2_ERR_NONE())) { tmp = _e2pcr_get_tempname (newpath, “-original”); //ascii, & don’t bother to translate #ifdef E2_VFS otherpath.path = tmp; success = e2_task_backend_rename (newpath, &otherpath); #else success = e2_task_backend_rename (newpath, tmp); #endif g_free (tmp); if (!success) { //can’t change original file //CHECKME get & use a tempname for localpath? return FALSE; } } } else { if (e2_option_bool_get (“confirm-overwrite”)) { DialogButtons choice = _e2pcr_ow_check (newpath, FALSE); if (choice != OK) return FALSE; } e2_task_backend_delete (newpath); } } if (!e2_task_backend_rename (temppath, newpath)) return FALSE; } return TRUE; } /** @brief encrypt file @a localpath Because a glib function is used for allocating buffer memory, its size is limited to G_MAXUINT32 on a 32-bit platform Any error message expects BGL open @a newname is freed, if non-NULL @param localpath localised path of item being processed, also reflected in @a dir and @a oldname @param dir directory in which the item is located, absolute localised path @param oldname name of item being processed, localised string @param newname new name of item after processing, NULL if @a use_same_name is TRUE @param use_same_name TRUE to give encrypted file same name as original @param check whether to confirm overwrites when renaming @param options pointer to crypt options data @return code indicating choice or success */ static DialogButtons _e2pcr_encrypt1 (VPATH *localpath, const gchar *dir, const gchar *oldname, gchar *newname, gboolean use_same_name, gboolean check, E2P_CryptOpts *options) { guint8 iseed; guint8 jseed; guint8 hashes[KEY_LENGTH]; gint fdesc; gsize filebuffersize, compressedbuffersize; //limited to 2^32 on 32-bit platform gboolean onwards; gpointer filebuffer, compressedbuffer; gchar *temppath; E2_ERR_DECLARE struct stat sb; if (e2_fs_stat (localpath, &sb E2_ERR_NONE())) { printd (DEBUG, “cannpt stat the file”); bad_open: #ifdef E2_VFS e2_fs_set_error_from_errno (&E2_ERR_NAME); #endif e2_fs_error_local (_(“Cannot read ‘%s'”), localpath E2_ERR_MSGL()); E2_ERR_CLEAR return NO; } if (sb.st_size == 0) { //error or empty file, probably not error as file was stat’d ok return CANCEL; } //read local files uses glib gsize (size_t) for buffer-allocation byte-size if (sb.st_size > G_MAXUINT32 && sizeof (gsize) <= 4) { //FIXME more-informative warning for this goto bad_open; } filebuffersize = (gsize) sb.st_size; //CHECKME mmap() file if possible, but then need separate buffer to crypt it if (!_e2pcr_read_file (localpath, &filebuffer, filebuffersize)) { goto bad_open; } #ifndef TC_COMPATIBLE if (options->compress) { #endif printd (DEBUG, “go to compress file buffer”); compressedbuffersize = _e2pcr_compress_buffer ( filebuffer, filebuffersize, &compressedbuffer //, localpath #ifndef E2_MINICRYPT , &options->compresslibflags, &options->libhandle #endif ); if (compressedbuffersize == 0) { #ifdef E2_VFS e2_fs_set_error_from_errno (&E2_ERR_NAME); #endif e2_fs_error_local (_(“Compression failed for file %s”), localpath E2_ERR_MSGL()); E2_ERR_CLEAR //NEEDED? _e2pcr_wipe_buffer (filebuffer, filebuffersize); g_free (filebuffer); return NO; //or CANCEL ? } #ifndef TC_COMPATIBLE } else //warnings prevention only { compressedbuffersize = 0; compressedbuffer = NULL; } #endif temppath = e2_utils_get_tempname (VPSTR (localpath)); #ifdef E2_VFS VPATH tdata = { temppath, localpath->spacedata }; #endif //descriptor for blockwize writing encryped file fdesc = e2_fs_safeopen (temppath, O_CREAT | O_WRONLY, S_IWUSR | S_IRUSR); if (fdesc < 0) { #ifdef E2_VFS e2_fs_set_error_from_errno (&E2_ERR_NAME); #endif e2_fs_error_local (_("Cannot open '%s' for writing"), #ifdef E2_VFS &tdata E2_ERR_MSGL()); #else temppath E2_ERR_MSGL()); #endif E2_ERR_CLEAR //NEEDED?_e2pcr_wipe_buffer (filebuffer, filebuffersize); g_free (filebuffer); g_free (temppath); if (compressedbuffer != NULL) { //NEEDED? _e2pcr_wipe_buffer (compressedbuffer, compressedbuffersize); free (compressedbuffer); //not g_free } return NO; } #ifdef TC_COMPATIBLE /* TC does this: get original size of file; compress original file; store nonce then store encypted size (4 bytes) then store encypted compressed file then store encryped 0 (4 bytes) */ guchar headerbuffer[NONCE_LENGTH]; #else csize_t flags = (options->compress) ? # ifdef E2_MINICRYPT E2_CFLAGCOMPRESS | E2_CFLAGINTLZ : E2_CFLAGNONE; # else E2_CFLAGCOMPRESS | (options->compresslibflags & E2_CFLAGLIBMASK) : E2_CFLAGNONE; # endif printd (DEBUG, “masked file lib-flags %0x”, flags & E2_CFLAGLIBMASK); guint len = HEADER_LENGTH1; //NONCE_LENGTH + bitflags /* if (options->en_name_embed) { flags |= E2_CFLAGNAME; len += (NAME_MAX+1); } else */ if (options->en_properties_embed) { flags |= E2_CFLAGINFO; len += sizeof (FileInfo); } else { //CHECKME needed for tinycrypt ? flags |= E2_CFLAGSIZE; len += sizeof (csize_t); } guint8 headerbuffer[len]; memset (headerbuffer, 0, len); //CHECKME any of this needed ? #endif //def TC_COMPATIBLE //if this func fails, the nonce will contain whatever stackspace memory has _e2pcr_init_nonce (headerbuffer); //nonce at start of header iseed = 0; jseed = _e2pcr_init_key (hashes, options->plain_pw, headerbuffer); #ifdef TC_COMPATIBLE //encrypt original size, stored after the nonce guint8 fsize [sizeof (csize_t)]; _e2pcr_store (filebuffersize, fsize); _e2pcr_crypt_buffer (hashes, &iseed, &jseed, fsize, sizeof (csize_t)); //write header (nonce and size) onwards = _e2pcr_write_buffer ( # ifdef E2_VFS &tdata, # else temppath, # endif fdesc, headerbuffer, HEADER_LENGTH1) && _e2pcr_write_buffer ( # ifdef E2_VFS &tdata, # else temppath, # endif fdesc, fsize, sizeof (csize_t)); #else // *((csize_t *) (headerbuffer + NONCE_LENGTH)) = flags; _e2pcr_store (flags, headerbuffer + NONCE_LENGTH); /* if (options->en_name_embed) { g_strlcpy (headerbuffer + HEADER_LENGTH1, oldname, NAME_MAX+1); FIXME need sixe too } else */ if (options->en_properties_embed) { FileInfo *info = (FileInfo *) (headerbuffer + HEADER_LENGTH1); g_strlcpy (info->filename, oldname, sizeof (info->filename)); //freshen the times to approx. this access if (e2_fs_lstat (localpath, &info->statbuf E2_ERR_NONE())) //or else just set old times memcpy (&info->statbuf, options->statptr, sizeof (struct stat)); } else { //CHECKME needed for tinycrypt ? // sptr = (csize_t *) (headerBuffer + HEADER_LENGTH1); // *sptr = filebuffersize; _e2pcr_store (filebuffersize, headerbuffer + HEADER_LENGTH1); } //encrypt header after the nonce _e2pcr_crypt_buffer (hashes, &iseed, &jseed, headerbuffer + NONCE_LENGTH, len – NONCE_LENGTH); //write header # ifdef E2_VFS onwards = _e2pcr_write_buffer (&tdata, fdesc, headerbuffer, len); # else onwards = _e2pcr_write_buffer (temppath, fdesc, headerbuffer, len); # endif #endif if (onwards) { //encrypt and write the main data #ifndef TC_COMPATIBLE if (flags & E2_CFLAGCOMPRESS) { #endif _e2pcr_crypt_buffer (hashes, &iseed, &jseed, compressedbuffer, compressedbuffersize); #ifdef E2_VFS onwards = _e2pcr_write_buffer (&tdata, #else onwards = _e2pcr_write_buffer (temppath, #endif fdesc, compressedbuffer, compressedbuffersize); #ifndef TC_COMPATIBLE } else { _e2pcr_crypt_buffer (hashes, &iseed, &jseed, filebuffer, filebuffersize); # ifdef E2_VFS onwards = _e2pcr_write_buffer (&tdata, # else onwards = _e2pcr_write_buffer (temppath, # endif fdesc, filebuffer, filebuffersize); } #endif } if (onwards) { //create trailer for signature-check when de-compressing //because it’s 0, endian-ness doesn’t matter csize_t *sptr = (csize_t *) headerbuffer; *sptr = 0; _e2pcr_crypt_buffer (hashes, &iseed, &jseed, headerbuffer, sizeof (csize_t)); #ifdef E2_VFS onwards = _e2pcr_write_buffer (&tdata, #else onwards = _e2pcr_write_buffer (temppath, #endif fdesc, headerbuffer, sizeof (csize_t)); } //fs rename operation may be not reliably atomic (ext4) if (onwards && e2_fs_writeflush (fdesc) != 0) onwards = FALSE; e2_fs_safeclose (fdesc); if (onwards) { gchar *newpath = (newname == NULL) ? NULL : g_build_filename (dir, newname, NULL); #ifdef E2_VFS VPATH ddata = { newpath, localpath->spacedata }; #endif onwards = _e2pcr_finalise_item (localpath, #ifdef E2_VFS &tdata, &ddata, #else temppath, newpath, #endif use_same_name, options); if (newpath != NULL) g_free (newpath); } _e2pcr_wipe_buffer (filebuffer, filebuffersize); g_free (filebuffer); if (compressedbuffer != NULL) { //CHECKME maybe compressed, encytped, maybe paged, should buffer be wiped ? //_e2pcr_wipe_buffer (compressedbuffer, compressedbuffersize); free (compressedbuffer); //not g_free } g_free (temppath); if (newname != NULL) g_free (newname); if (onwards && !options->preserve) { //wipe & delete unencrypted original file onwards = _e2pcr_flush_file (localpath, hashes); } return ((onwards) ? OK : NO); } /** @brief decrypt file @a localpath Any error message expects BGL open @param localpath localised path of item being processed, also reflected in @a dir and @a oldname @param dir directory in which the item is located, absolute localised path @param oldname name of item being processed, localised string @param newname new name of item after processing, NULL if @a use_same_name is TRUE @param use_same_name TRUE to give decrypted file same name as original @param check whether to confirm overwrites when renaming @param options pointer to crypt options data @return code indicating choice or NO for error */ static DialogButtons _e2pcr_decrypt1 (VPATH *localpath, const gchar *dir, const gchar *oldname, gchar *newname, gboolean use_same_name, gboolean check, E2P_CryptOpts *options) { guint8 iseed; guint8 jseed; guint8 hashes[KEY_LENGTH]; gboolean onwards; #ifndef TC_COMPATIBLE FileInfo *info; #endif // FileInfo stored_info; //for remembering a streamed info E2_ERR_DECLARE struct stat sb; if (e2_fs_stat (localpath, &sb E2_ERR_NONE())) { printd (DEBUG, “cannot stat the file”); bad_open: #ifdef E2_VFS e2_fs_set_error_from_errno (&E2_ERR_NAME); #endif e2_fs_error_local (_(“Cannot read ‘%s'”), localpath E2_ERR_MSGL()); E2_ERR_CLEAR return NO; } if (sb.st_size == 0) { //error or empty file, probably not error as file was stat’d ok return CANCEL; } //read local files uses glib gsize (size_t) for buffer-allocation byte-size if (sb.st_size > G_MAXUINT32 && sizeof (gsize) <= 4) { //FIXME more-informative warning for this goto bad_open; } //file reading is size-limited to gulong (was ssize_t) gsize filebuffersize = (gsize) sb.st_size; //* for debugging ... /* NO MERIT IN 2-STAGE PROCESS, UNLESS SOME HEADER-TESTING IS DONE //sniff the file //sniff size must be >= NONCE_LENGTH + sizeof(c_size_t)(for flags) + sizeof(FileInfo) guchar headerbuffer[512]; memset (headerbuffer, 0, ); ssize_t headersize = MIN (sizeof (headerbuffer), filebuffersize); gpointer headerbuffer; gulong headersize = filebuffersize; if (!_e2pcr_read_file (localpath, &headerbuffer, headersize)) */ gpointer filebuffer; if (!_e2pcr_read_file (localpath, &filebuffer, filebuffersize)) { goto bad_open; } //setup hashes using password and nonce at start of file iseed = 0; jseed = _e2pcr_init_key (hashes, options->plain_pw, filebuffer); //decrypt rest of file _e2pcr_crypt_buffer (hashes, &iseed, &jseed, filebuffer + NONCE_LENGTH, filebuffersize – NONCE_LENGTH); /* when encrypting, TC does this: get original size of file; compress original file; store nonce then store encypted size (4 bytes) then store encypted compressed file then store encryped 0 (4 bytes) */ csize_t flags; //if reading the whole file, check the trailing signature //since it’s supposed to be 0, endian-ness doesn’t matter flags = *((csize_t *) (filebuffer + filebuffersize – sizeof (csize_t))); if (flags != 0) { g_free (filebuffer); //original is encrypted, no need to wipe printd (DEBUG, “bad password”); e2_fs_error_simple (_(“Wrong password for %s”), localpath); return NO; } #ifdef E2_VFS VPATH ddata; ddata.spacedata = localpath->spacedata; #endif guint skiplen; csize_t originalFileLength; #ifdef TC_COMPATIBLE skiplen = NONCE_LENGTH + sizeof (csize_t); originalFileLength = _e2pcr_read_store (filebuffer + NONCE_LENGTH); #endif #ifndef TC_COMPATIBLE // flags = *((csize_t *) (filebuffer + NONCE_LENGTH)); flags = _e2pcr_read_store (filebuffer + NONCE_LENGTH); gboolean compressed = (flags & E2_CFLAGLIBMASK); // gboolean name = ; gboolean properties = flags & E2_CFLAGINFO; gboolean use_stored_props = properties; gboolean use_stored_name; if (properties) { info = (FileInfo *)(filebuffer + HEADER_LENGTH1); if (options->de_name_stored) { use_stored_name = strcmp (oldname, info->filename); if (use_stored_name) { newname = g_strdup (info->filename); if (check) //no point in warning about re-use of same name { gchar *checkpath = g_build_filename (dir, newname, NULL); #ifdef E2_VFS ddata.path = checkpath; DialogButtons choice = _e2pcr_ow_check (&ddata, options->multisrc); #else DialogButtons choice = _e2pcr_ow_check (checkpath, options->multisrc); #endif g_free (checkpath); if (choice == YES_TO_ALL) options->owrite = FALSE; else if (choice == CANCEL || choice == NO_TO_ALL) { g_free (newname); g_free (filebuffer); return choice; } } use_same_name = FALSE; } } else use_stored_name = FALSE; skiplen = HEADER_LENGTH1 + sizeof (FileInfo); originalFileLength = (csize_t) info->statbuf.st_size; if (options->de_props_stored) use_stored_props = TRUE; //signal for later attention } else { info = NULL; //warning prevention only if (options->de_name_stored || newname == NULL) { //user wants to use stored name but none is available gchar *s = strstr (oldname, “.enc”); if (s != NULL && *(s+4) == ‘’) newname = g_strndup (oldname, s-oldname); else newname = g_strconcat (oldname, “-decrypted”, NULL); use_stored_name = TRUE; //signal that cleanup is wanted, tho’ not stored } else use_stored_name = FALSE; skiplen = HEADER_LENGTH1 + sizeof (csize_t); // originalFileLength = *((csize_t *) (headerBuffer + HEADER_LENGTH1)); originalFileLength = _e2pcr_read_store (filebuffer + HEADER_LENGTH1); } #endif /* if (filebuffersize > headersize) { //decrypt rest of file filebuffer = malloc (filebuffersize); //not g_try_malloc, that’s limited to gulong CHECKALLOCATEDWARNT (filebuffer, ) //FIXME memcpy (filebuffer, headerbuffer, headersize); if (!_e2pcr_read_file ((gchar *)localpath, in_desc, filebuffer + headersize, filebuffersize – headersize)) { e2_fs_safeclose (in_desc); retval = NO; goto cleanup; } _e2pcr_crypt_buffer (hashes, &iseed, &jseed, filebuffer + headersize, filebuffersize – headersize); } //check the trailing signature //since it’s supposed to be 0, endian-ness doesn’t matter flags = *((csize_t *) (filebuffer + filebuffersize – sizeof (csize_t))); if (flags != 0) { printd (DEBUG, “bad password”); e2_fs_error_simple (_(“Wrong password for %s”), (gchar *)localpath); e2_fs_safeclose (in_desc); retval = CANCEL; goto cleanup; } */ gpointer uncompressedbuffer; csize_t uncompressedbuffersize; #ifndef TC_COMPATIBLE if (compressed) { #endif uncompressedbuffersize = _e2pcr_decompress_buffer (filebuffer + skiplen, filebuffersize – skiplen – sizeof (csize_t), originalFileLength, #ifndef E2_MINICRYPT flags & E2_CFLAGLIBMASK, #endif &uncompressedbuffer #ifndef E2_MINICRYPT , &options->compresslibflags, &options->libhandle #endif ); if (uncompressedbuffersize == 0) { printd (DEBUG, “decompression failed”); e2_fs_error_simple (_(“Error decompressing file %s”), localpath); #ifndef TC_COMPATIBLE if (use_stored_name) g_free (newname); #endif g_free (filebuffer); //FIXME other cleanups return CANCEL; } #ifndef TC_COMPATIBLE } #endif gchar *temppath = e2_utils_get_tempname (VPSTR (localpath)); #ifdef E2_VFS VPATH tdata = { temppath, localpath->spacedata }; #endif #ifndef TC_COMPATIBLE if (compressed) { #endif onwards = e2_fs_set_file_contents ( #ifdef E2_VFS &tdata, #else temppath, #endif uncompressedbuffer, uncompressedbuffersize, S_IWUSR | S_IRUSR E2_ERR_PTR()); //NEEDED? _e2pcr_wipe_buffer (uncompressedbuffer, uncompressedbuffersize); free (uncompressedbuffer); #ifndef TC_COMPATIBLE } else { onwards = e2_fs_set_file_contents ( # ifdef E2_VFS &tdata, # else temppath, # endif filebuffer + skiplen, filebuffersize – skiplen – sizeof (csize_t), S_IWUSR | S_IRUSR E2_ERR_PTR()); } #endif gchar *newpath = (newname == NULL) ? NULL : g_build_filename (dir, newname, NULL); if (onwards) { #ifdef E2_VFS ddata.path = newpath; #endif onwards = _e2pcr_finalise_item (localpath, #ifdef E2_VFS &tdata, &ddata, #else temppath, newpath, #endif use_same_name, options); } #ifndef TC_COMPATIBLE if (onwards && use_stored_props) { //decrypting with stored data #ifndef E2_VFS const gchar *p; #endif if (use_same_name) { #ifdef E2_VFS ddata.path = localpath->localpath; #else p = VPCSTR (localpath); #endif } else { // ddata.path = newpath already #ifndef E2_VFS p = newpath; #endif } if (onwards && #ifdef E2_VFS !e2_task_backend_chown (&ddata, info->statbuf.st_uid, info->statbuf.st_gid, FALSE)) #else !e2_task_backend_chown (p, info->statbuf.st_uid, info->statbuf.st_gid, FALSE)) #endif onwards = FALSE; if (onwards && #ifdef E2_VFS e2_fs_chmod (&ddata, info->statbuf.st_mode & ALLPERMS E2_ERR_NONE())) #else e2_fs_chmod (p, info->statbuf.st_mode & ALLPERMS E2_ERR_NONE())) #endif onwards = FALSE; if (onwards) { struct utimbuf tb; tb.modtime = info->statbuf.st_mtime; tb.actime = info->statbuf.st_atime; #ifdef E2_VFS if (e2_fs_utime (&ddata, &tb E2_ERR_NONE())) #else if (e2_fs_utime (p, &tb E2_ERR_NONE())) #endif onwards = FALSE; } } #endif //ndef TC_COMPATIBLE if (onwards && !options->preserve) { //delete encrypted original file onwards = e2_task_backend_delete (localpath); } g_free (filebuffer); g_free (temppath); if (newpath != NULL) g_free (newpath); if (newname != NULL) g_free (newname); //CAREFUL maybe in caller too return ((onwards) ? OK : NO); } /*****************************/ /** buffer & file functions **/ /*****************************/ /** @brief compress @a filebuffer using using mini-lzo or some other supported library Any error message expects BGL open Size of the buffer is implicitly limited to ULONG_MAX. After use, the compressed buffer should be free’d not g_free’d @param filebuffer pointer to buffer to compress @param filebuffersize no. of bytes to compress in @a filebuffer @param compressedbuffer store for pointer newly-allocated buffer holding compressed bytes @param localpath pointer to path data for any error message #ifndef E2_MINICRYPT @param usedlib store for flags indicating which compression library is used @param libhandle store for handle to dlopen’d lib, for repeated use if any #endif @return size of compressed buffer, 0 on error */ static csize_t _e2pcr_compress_buffer (gpointer filebuffer, /*size_t*/ gulong filebuffersize, gpointer *compressedbuffer #ifndef E2_MINICRYPT , csize_t *usedlib, gpointer *libhandle #endif ) { #ifdef E2_MINICRYPT // if (!lzo_init_done) // { if (lzo_init () != LZO_E_OK) { // msg = _(“Cannot initialize compression process”); // gdk_threads_enter (); // e2_output_print_error (msg, FALSE); // gdk_threads_leave (); printd (WARN, “Cannot initialize compression process”); return 0; } // lzo_init_done = TRUE; // } csize_t compressedbuffersize = filebuffersize + (filebuffersize >> 6) + 19; *compressedbuffer = malloc (compressedbuffersize); //not g_try_malloc, that’s limited to gulong #if (CHECKALLOCATEDWARNT) CHECKALLOCATEDWARNT (*compressedbuffer, return 0;) #else if (*compressedbuffer == NULL) { //FIXME handle error return 0; } #endif /* Work-memory needed for compression. CHECKME there may be some alignment issue with this */ gpointer workmem = g_try_malloc (LZO1X_1_MEM_COMPRESS); #if (CHECKALLOCATEDWARNT) CHECKALLOCATEDWARNT (workmem, free (*compressedbuffer);return 0;) #else if (workmem == NULL) { //FIXME handle error free (*compressedbuffer); return 0; } #endif lzo_uint newbuffersize; gint result = lzo1x_1_compress (filebuffer, filebuffersize, *compressedbuffer, &newbuffersize, workmem); g_free (workmem); if (result == LZO_E_OK) { workmem = realloc (*compressedbuffer, newbuffersize); if (workmem != NULL) *compressedbuffer = workmem; return (csize_t) newbuffersize; } #else printd (DEBUG, “compress file buffer”); csize_t libflags; csize_t compressedbuffersize; gint (*compress_buf) (); gchar *msg; //decide preferred compression library from those available gint libnum = e2_option_sel_get (“compress-library”); switch (libnum) { case 0: libflags = E2_CFLAGLZO; break; case 1: libflags = E2_CFLAGZ; break; case 2: libflags = E2_CFLAGBZ2; break; /*LZMA not working yet case 3: libflags = E2_CFLAGLZMA; break; */ default: libflags = 0; //trigger abort break; } if (!(libflags & compresslib) || !_e2pcr_check_lib (libflags, usedlib, TRUE, libhandle, (gpointer *)&compress_buf)) { msg = g_strdup_printf ( _(“Requested %s compression library not found or not compatible”), libnames[libnum]); gdk_threads_enter (); e2_output_print_error (msg, TRUE); gdk_threads_leave (); return 0; } /* AUTOMATIC SELECTION gint libnum = 0; while (TRUE) { switch (libnum) { case 0: libflags = COMPRESS_CHOICE1; break; case 1: libflags = COMPRESS_CHOICE2; break; case 2: libflags = COMPRESS_CHOICE3; break; / *LZMA not working yet case 3: libflags = COMPRESS_CHOICE4; break; * / default: msg = _(“No supported compression library”); gdk_threads_enter (); e2_output_print_error (msg, FALSE); gdk_threads_leave (); return 0; } if ((libflags & compresslib) && _e2pcr_check_lib (libflags, usedlib, TRUE, libhandle, (gpointer *)&compress_buf)) break; else { #ifdef DEBUG_MESSAGES switch (libflags & E2_CFLAGLIBMASK) { case E2_CFLAGLZO: msg = libnames[0]; break; case E2_CFLAGZ: msg = libnames[1]; break; case E2_CFLAGBZ2: msg = libnames[2]; break; // case E2_CFLAGLZMA: // msg = libnames[3]; // break; default: //should never happen msg = “”; break; } printd (DEBUG, “No usable %s compression-library”, msg); #endif libnum++; } } */ if (libflags & E2_CFLAGLZO) compressedbuffersize = filebuffersize + (filebuffersize >> 6) + 19; else if (libflags & E2_CFLAGZ) compressedbuffersize = (filebuffersize * 1.001 + 20 + 8) / 8 * 8; else //if (libflags & E2_CFLAGBZ2) compressedbuffersize = (filebuffersize * 1.01 + 600 + 8) / 8 * 8; // else //if (libflags & E2_CFLAGLZMA) // compressedbuffersize = (filebuffersize * 1); //FIXME; *compressedbuffer = malloc (compressedbuffersize); //not g_try_malloc, that’s limited to gulong CHECKALLOCATEDWARNT (*compressedbuffer, return 0;) if (libflags & E2_CFLAGLZO) { printd (DEBUG, “compress using LZO”); gpointer workmem = g_try_malloc (LZO1X_1_MEM_COMPRESS); CHECKALLOCATEDWARNT (workmem, ) if (workmem != NULL) { guint compressedlen; gint res = compress_buf (filebuffer, (guint) filebuffersize, *compressedbuffer, &compressedlen, workmem); g_free (workmem); if (res == 0) { workmem = realloc (*compressedbuffer, compressedlen); if (workmem != NULL) *compressedbuffer = workmem; return (csize_t) compressedlen; } } } else if (libflags & E2_CFLAGZ) { printd (DEBUG, “compress using ZLIB”); gulong compressedlen = (gulong) compressedbuffersize; if (compress_buf (*compressedbuffer, &compressedlen, filebuffer, (gulong) filebuffersize, Z_BEST_SPEED) == 0) { gpointer workmem = realloc (*compressedbuffer, compressedlen); if (workmem != NULL) *compressedbuffer = workmem; return (csize_t) compressedlen; } } else //if (libflags & E2_CFLAGBZ2) { printd (DEBUG, “compress using BZ2”); guint compressedlen = (guint) compressedbuffersize; if (compress_buf (*compressedbuffer, &compressedlen, filebuffer, (guint) filebuffersize, 2, 0, 30) == 0) { gpointer workmem = realloc (*compressedbuffer, compressedlen); if (workmem != NULL) *compressedbuffer = workmem; return (csize_t) compressedlen; } } /*NOT WORKING else //if (libflags & E2_CFLAGLZMA) { printd (DEBUG, “compress using LZMA”); guint compressedlen = (guint) compressedbuffersize; size_t outPropsSize = 5; guint8 outProps[5]; //CHECKME last 4 of these for dictionary size, may need to be 8 for 64-bits if (compress_buf (*compressedbuffer, &compressedlen, filebuffer, filebuffersize, &outProps, &outPropsSize, // *outPropsSize must be = 5 -1, //gint level, 0 <= level <= 9, default = 5 0, //unsigned dictSize, default = (1 << 24) -1, //gint lc, 0 <= lc <= 8, default = 3 -1, //gint lp, 0 <= lp <= 4, default = 0 -1, //gint pb, 0 <= pb <= 4, default = 2 -1, //gint fb, 5 <= fb <= 273, default = 32 -1, //gint numThreads 1 or 2, default = 2 ) == SZ_OK) { gpointer workmem = realloc (*compressedbuffer, compressedlen); if (workmem != NULL) *compressedbuffer = workmem; return (csize_t) compressedlen; } } */ #endif printd (WARN, "Compression failed"); free (*compressedbuffer); *compressedbuffer = NULL; return 0; } /** @brief decompress buffer using mini-lzo or some other supported library Size of the de-compress buffer is implicitly limited to ULONG_MAX After use, the compressed buffer should be free'd not g_free'd @param filebuffer pointer to buffer to decompress @param filebuffersize no. of bytes in @a filebuffer @param originalfilesize expected length of decompressed buffer #ifndef E2_MINICRYPT @param csize_t libflags which decompression mode to use #endif @param decompressedbuffer store for pointer to newly-allocated buffer holding @a originalFileLength decompressed bytes @param localpath pointer to path data for any error message #ifndef E2_MINICRYPT @param usedlib store for flags indicating which decompression library is used @param libhandle store for handle to dlopen'd lib, for repeated use if any #endif @return size of uncompressed buffer, 0 on error */ static csize_t _e2pcr_decompress_buffer (gpointer filebuffer, /*size_t*/ gulong filebuffersize, csize_t originalfilesize, #ifndef E2_MINICRYPT //FIXME make altdecompress_buf a function arg, non-static csize_t libflags, //gint (*altdecompress_buf) (), #endif gpointer *decompressedbuffer //, VPATH *localpath #ifndef E2_MINICRYPT , csize_t *usedlib, gpointer *libhandle #endif ) { #ifdef E2_MINICRYPT // if (!lzo_init_done) // { if (lzo_init () != LZO_E_OK) { //FIXME handle error // msg = _("Cannot initialize decompression process"); // gdk_threads_enter (); // e2_output_print_error (msg, FALSE); // gdk_threads_leave (); printd (WARN, "Cannot initialize decompression process"); return 0; } // lzo_init_done = TRUE; // } #else gchar *msg; if ((libflags & E2_CFLAGLIBMASK) == 0) { msg = _("Unknown compression library"); gdk_threads_enter (); e2_output_print_error (msg, FALSE); gdk_threads_leave (); return 0; } gint (*decompress_buf) (); if (!_e2pcr_check_lib (libflags, usedlib, FALSE, libhandle, (gpointer *)&decompress_buf)) { const gchar *arg; switch (libflags & E2_CFLAGLIBMASK) { case E2_CFLAGLZO: arg = libnames[0]; break; case E2_CFLAGZ: arg = libnames[1]; break; case E2_CFLAGBZ2: arg = libnames[2]; break; // case E2_CFLAGLZMA: // arg = libnames[3]; // break; default: //should never happen arg = ""; break; } msg = (*arg != '') ? g_strdup_printf (_("No %s compression library"), arg) : _("Unknown compression library"); gdk_threads_enter (); e2_output_print_error (msg, (*arg != '')); gdk_threads_leave (); return 0; } #endif *decompressedbuffer = malloc (originalfilesize); //not g_try_malloc #if (CHECKALLOCATEDWARNT) CHECKALLOCATEDWARNT (*decompressedbuffer, return 0;) #else if (*decompressedbuffer == NULL) return 0; #endif #ifdef E2_MINICRYPT lzo_uint decompressedlen; gint result = lzo1x_decompress_safe (filebuffer, filebuffersize, *decompressedbuffer, &decompressedlen, NULL); if (result == LZO_E_OK && decompressedlen == originalfilesize) return (csize_t) decompressedlen; printd (DEBUG, "minilzo de-compression error %d", result); #else if (libflags & E2_CFLAGLZO) //(E2_CFLAGLZO | E2_CFLAGINTLZ)); { printd (DEBUG, "de-compressing using LZO lib"); guint decompressedlen = (guint) originalfilesize; if (decompress_buf (filebuffer, (guint) filebuffersize, *decompressedbuffer, &decompressedlen, NULL //gpointer wrkmem NOT USED ) == 0 && (csize_t) decompressedlen == originalfilesize) { return (csize_t) decompressedlen; } printd (DEBUG, "but that failed - original size %d decompressed to %d", originalfilesize, decompressedlen); } else if (libflags & E2_CFLAGZ) { printd (DEBUG, "de-compressing using ZLIB lib"); gulong decompressedlen = (gulong) originalfilesize; if (decompress_buf (*decompressedbuffer, &decompressedlen, filebuffer, (gulong) filebuffersize) == 0 && (csize_t) decompressedlen == originalfilesize) { return (csize_t) decompressedlen; } printd (DEBUG, "but that failed - original size %d decompressed to %d", originalfilesize, decompressedlen); } else if (libflags & E2_CFLAGBZ2) { printd (DEBUG, "de-compressing using BZ2 lib"); guint decompressedlen = (guint) originalfilesize; if (decompress_buf (*decompressedbuffer, &decompressedlen, filebuffer, (guint) filebuffersize, 0, 0) == 0 && (csize_t) decompressedlen == originalfilesize) { return (csize_t) decompressedlen; } printd (DEBUG, "but that failed - original size %d decompressed to %d", originalfilesize, decompressedlen); } /* else if (libflags & E2_CFLAGLZMA) { printd (DEBUG, "de-compressing using LZMA lib"); gint result = decompress_buf (*decompressedbuffer, &decompressedlen, filebuffer, &filebuffersize, const unsigned char *props, size_t propsSize); if (result == SZ_OK && (csize_t) decompressedlen == originalfilesize) { return (csize_t) decompressedlen; } printd (DEBUG, "but that failed - original size %d decompressed to %d", originalfilesize, decompressedlen); } */ #endif //FIXME handle error free (*decompressedbuffer); *decompressedbuffer = NULL; return 0; } /** @brief fill @a buffer with the contents of some file from $PATH This is an alternative to storing some sequence of data that is readily recognisable as over-written data Expects BGL to be open on arrival here @param buffer pointer to buffer to be overwritten @param buffersize size of @a buffer @return TRUE if the process was completed */ static gboolean _e2pcr_wipe_buffer (gpointer buffer, size_t buffersize) { gboolean retval = FALSE; gchar *sep; gchar *execpath = (gchar *)g_getenv ("PATH"); if (execpath == NULL) { sep = NULL; execpath = "/bin"; } else { sep = strchr (execpath, ':'); //ascii scan ok if (sep != NULL) execpath = g_strndup (execpath, sep-execpath); //FIXME preserve execpath so that later members can be used } #ifdef E2_VFS VPATH ddata = { execpath, NULL }; //files in $PATH must be local GList *entries = (GList *)e2_fs_dir_foreach (&ddata, #else GList *entries = (GList *)e2_fs_dir_foreach (execpath, #endif E2_DIRWATCH_NO, //local = fast read NULL, NULL, NULL E2_ERR_NONE()); if (E2DREAD_FAILED (entries)) { //FIXME try another dir in PATH, or ... //FIXME warn user // e2_fs_error_simple ( // _("You do not have authority to read %s"), execpath); if (sep != NULL) g_free (execpath); return FALSE; } guint count = g_list_length (entries); guint8 c; restart: if (!_e2pcr_getrandom (&c)) { //CHECKME recover ? or ... //FIXME warn user // e2_fs_error_simple ( // _("You do not have authority to modify %s"), (*iterator)->filename); goto cleanup; } guint first = count * c / 256; guint i = 0; gchar *filename, *filepath = NULL; GList *member; reloop: for (member = g_list_nth (entries, first); member != NULL; member = member->next) { filename = (gchar *)member->data; if (strcmp (filename, “..”)) { filepath = g_build_filename (execpath, filename, NULL); #ifdef E2_VFS ddata.path = filepath; if (!e2_fs_access (&ddata, R_OK E2_ERR_NONE())) #else if (!e2_fs_access (filepath, R_OK E2_ERR_NONE())) #endif break; g_free (filepath); } filepath = NULL; if (++i == count); { //try with next dir from PATH or … printd (DEBUG, “cannot find a file for data source”); //FIXME warn user // e2_fs_error_simple ( // _(“You do not have authority to read anything in %s”), execpath); goto cleanup; } } if (member == NULL && i < count) { //reached end of list, cycle back to start first = 0; goto reloop; } if (filepath == NULL) goto cleanup; E2_ERR_DECLARE gint fdesc = e2_fs_safeopen (filepath, O_RDONLY, 0); if (fdesc < 0) { printd (DEBUG, "cannot open data source file"); goto restart; //try with another file from list /* or ... #ifdef E2_VFS e2_fs_set_error_from_errno (E2_ERR_NAME); #endif e2_fs_error_local (_("Cannot open '%s' for reading"), filepath E2_ERR_MSGL()); E2_ERR_CLEAR goto cleanup; */ } struct stat sb; #ifdef E2_VFS e2_fs_stat (&ddata, &sb E2_ERR_NONE()); #else e2_fs_stat (filepath, &sb E2_ERR_NONE()); #endif csize_t masksize = (csize_t) sb.st_size; ssize_t n_read; if (masksize >= buffersize) { n_read = e2_fs_read (fdesc, buffer, buffersize E2_ERR_PTR()); if (n_read < buffersize) { //#ifdef E2_VFS // e2_fs_set_error_from_errno (&E2_ERR_NAME); //#endif // e2_fs_error_local (_("Error reading file %s"), localpath E2_ERR_MSGL()); E2_ERR_CLEAR //FIXME handle shortfall } } else { //mask-file is smaller than the buffer, read repeatedly until buffer is full csize_t readsofar = 0; guchar *readPtr = buffer; while (readsofar < buffersize) { n_read = e2_fs_read (fdesc, readPtr, masksize E2_ERR_PTR()); if (n_read < masksize) { //#ifdef E2_VFS // e2_fs_set_error_from_errno (&E2_ERR_NAME); //#endif // e2_fs_error_local (_("Error reading file %s"), localpath E2_ERR_MSGL()); E2_ERR_CLEAR //FIXME handle shortfall } lseek (fdesc, 0, SEEK_SET); //FIXME vfs readsofar += masksize; readPtr += masksize; if (readsofar > (buffersize – masksize)) masksize = buffersize – readsofar; } } //FIXME page buffer to disk, to mask any swap storage e2_fs_safeclose (fdesc); retval = TRUE; cleanup: if (sep != NULL) g_free (execpath); e2_list_free_with_data (&entries); return retval; } /** @brief read some or all of a file into @a filebuffer This allows full or partial writing of a file, to support streaming and otherwise-segemented input Any error message expects BGL to be open @param localpath localised name of item being read, used only for error messages @param filebuffer pointer to store for address of allocated buffer @param filebuffersize no. of bytes to read into @a filebuffer @return TRUE if requested no. of bytes were read */ static gboolean _e2pcr_read_file (VPATH *localpath, gpointer *filebuffer, /*size_t*/ gulong filebuffersize) { if (filebuffersize > 0) { E2_ERR_DECLARE /*ssize_t*/ gulong nread; if (!e2_fs_get_file_contents (localpath, filebuffer, &nread, FALSE E2_ERR_PTR()) || nread < filebuffersize) { printd (DEBUG, "cannot read whole file"); //FIXME handle error #ifdef E2_VFS e2_fs_set_error_from_errno (&E2_ERR_NAME); #endif e2_fs_error_local (_("Error reading file %s"), localpath E2_ERR_MSGL()); E2_ERR_CLEAR return FALSE; } } return TRUE; } /** @brief write @a buffer out to storage This allows full or partial writing of a file, to support streaming and otherwise-segemented output Any error message expects BGL to be open @param localpath localised name of item being read, used only for error messages @param descriptor file descriptor @param buffer store for pointer to buffer holding data to write @param buffersize size of @a filebuffer, > 0 @return TRUE if the write was completed */ static gboolean _e2pcr_write_buffer (VPATH *localpath, gint descriptor, gpointer buffer, /*size_t*/ gulong buffersize) { if (buffersize > 0) { E2_ERR_DECLARE ssize_t bytes_written = e2_fs_write (descriptor, buffer, buffersize E2_ERR_PTR()); if ((gulong)bytes_written < buffersize) { #ifdef E2_VFS e2_fs_set_error_from_errno (&E2_ERR_NAME); #endif e2_fs_error_local (_("Error writing file %s"), localpath E2_ERR_MSGL()); E2_ERR_CLEAR return FALSE; } } return TRUE; } /** @brief overwrite and delete @a localpath with the contents of some file from /bin Any error message here expects BGL to be open @param localpath absolute path of item to be processed, localised string @param hashes the hash array used for en/de-cryption @return TRUE if the process was completed */ static gboolean _e2pcr_flush_file (VPATH *localpath, guint8 hashes[]) { E2_ERR_DECLARE struct stat sb; if (e2_fs_stat (localpath, &sb E2_ERR_PTR())) { e2_fs_error_local (_("Cannot get current data for %s"), localpath E2_ERR_MSGL()); E2_ERR_CLEAR return FALSE; } if (sb.st_size == 0) return TRUE; guint8 randomval = 112; _e2pcr_getrandom (&randomval);//fudge the size csize_t wipesize = (csize_t) sb.st_size + (csize_t) randomval; //find a buffer up to 64 times file's block-size csize_t buffersize = sb.st_blksize * 64; while (buffersize > wipesize) buffersize /= 2; if (buffersize < wipesize && buffersize < sb.st_blksize) buffersize = wipesize; gpointer buffer; while ((buffer = malloc (buffersize)) == NULL) { if (buffersize < sb.st_blksize) { gdk_threads_enter (); e2_utils_show_memory_message (); gdk_threads_leave (); return FALSE; } buffersize /= 2; } //open file for writing without truncation gint fdesc = e2_fs_safeopen (VPCSTR (localpath), O_RDWR | O_NONBLOCK, 0); if (fdesc < 0) { g_free (buffer); #ifdef E2_VFS e2_fs_set_error_from_errno (&E2_ERR_NAME); #endif e2_fs_error_local (_("Cannot open '%s' for writing"), localpath E2_ERR_MSGL()); E2_ERR_CLEAR return FALSE; } gboolean retval = FALSE; // flockfile (outputFile); if (buffersize == wipesize) { if (!_e2pcr_wipe_buffer (buffer, buffersize) || !_e2pcr_write_buffer (localpath, fdesc, buffer, buffersize)) { //FIXME error message // e2_fs_error_simple ( // _("You do not have authority to modify %s"), (*iterator)->filename); goto cleanup; } } else { csize_t writesofar = 0; csize_t bsize = buffersize; while (writesofar < wipesize) { if (_e2pcr_wipe_buffer (buffer, bsize) && _e2pcr_write_buffer (localpath, fdesc, buffer, bsize)) { writesofar += bsize; if (writesofar > (wipesize – buffersize)) bsize = wipesize – writesofar; } else { //FIXME error message // e2_fs_error_simple ( // _(“You do not have authority to modify %s”), (*iterator)->filename); goto cleanup; } } } e2_fs_writeflush (fdesc); //should never fail retval = TRUE; cleanup: g_free (buffer); // funlockfile (outputFile); e2_fs_safeclose (fdesc); if (retval) { //rename it (which changes ctime to now) gchar *s = _e2pcr_get_tempname (localpath, “ABCDE”); gchar *t = strrchr (s, G_DIR_SEPARATOR); t += sizeof(gchar); guint8 iseed = randomval; guint8 jseed = (guint8) randomval * 2; _e2pcr_crypt_buffer (hashes, &iseed, &jseed, t, (csize_t) strlen (t)); guchar *p = (guchar *) t; while (*p != ‘’) { if (*p < '0') *p += '0'; else { while (*p > 0x7e) *p -= 0x10; } p += sizeof(gchar); } #ifdef E2_VFS VPATH ddata = { s, localpath->spacedata }; e2_task_backend_move (localpath, &ddata); #else e2_task_backend_move (localpath, s); #endif //mask file m, atimes – random dates in past year time_t now = time (NULL); struct utimbuf tb; tb.modtime = now – 365 * 24 * 3600 * (time_t) randomval / 256; _e2pcr_getrandom (&randomval); tb.actime = now – 365 * 24 * 3600 * (time_t) randomval / 256; while (tb.actime < tb.modtime) tb.actime += 3600; #ifdef E2_VFS e2_fs_utime (&ddata, &tb E2_ERR_NONE()); #else e2_fs_utime (s, &tb E2_ERR_NONE()); #endif //delete #ifdef E2_VFS e2_task_backend_delete (&ddata); #else e2_task_backend_delete (s); #endif g_free (s); } return retval; } /** @brief encrypt or decrypt file @a localpath @a localpath may be target of a link, whose path is given in options Any error message here expects BGL to be open @param localpath absolute path of item to process, localised string @param options pointer to process-parameters data @return OK or YES_TO_ALL if successful, CANCEL or NO_TO_ALL if user chooses those, NO after error */ static DialogButtons _e2pcr_crypt1 (VPATH *localpath, E2P_CryptOpts *options) { gchar *s, *newname = NULL; //warning prevention gchar *dir = g_path_get_dirname (VPSTR (localpath)); gchar *oldname = g_path_get_basename (VPSTR (localpath)); DialogButtons retval; gboolean use_same_name = FALSE; gboolean check = (options->backup || options->owrite) ? FALSE : e2_option_bool_get (“confirm-overwrite”); #ifdef E2_VFS VPATH ddata; ddata.spacedata = localpath->spacedata; #endif if (options->decryptmode) //doing decryption { #ifndef TC_COMPATIBLE //check naming arrangements specified by dialog flags & widgets gboolean use_stored_name = FALSE; if (options->de_name_same) //decrypted file name = same as encrypted name use_same_name = TRUE; else { if (options->de_name_suffix) { newname = F_FILENAME_TO_LOCALE (options->de_suffix); if (*newname != ‘’ && g_str_has_suffix (oldname, newname)) { use_same_name = FALSE; gint len = strlen (newname); F_FREE (newname, options->de_suffix); newname = g_strdup (oldname); *(newname + strlen (newname) – len) = ‘’; } else { if (*newname != ‘’ && !options->ignore_suffix) { //ask user what to do gchar *utf = F_FILENAME_FROM_LOCALE (VPSTR (localpath)); s = g_strdup_printf ( _(“%s does not end with “%s”.nProcess this file anyway?”), utf, options->de_suffix); retval = _e2pcr_dialog_warning (s, options->multisrc); F_FREE (utf, VPSTR (localpath)); g_free (s); switch (retval) { case YES_TO_ALL: options->ignore_suffix = TRUE; case OK: break; //case NO_TO_ALL: //CHECKME consider a flag for stopping, instead of the current spaghetti to provoke a stop //case CANCEL: default: F_FREE (newname, options->de_suffix); g_free (dir); g_free (oldname); return retval; } } use_same_name = TRUE; F_FREE (newname, options->de_suffix); } } else if (options->de_name_custom) { if (*options->de_name != ‘’) { newname = D_FILENAME_TO_LOCALE (options->de_name); use_same_name = !strcmp (oldname, newname); if (use_same_name) g_free (newname); } else use_same_name = TRUE; } else //last choice is stored name (which may, later, turn out to be N/A or same) { use_stored_name = TRUE; // use_same_name = TRUE; //nothing to cleanup afterwards } //newname now clear or g_freeable } #else s = strstr (oldname, “.enc”); if (s == NULL) { //FIXME warn user g_free (dir); g_free (oldname); return NO; } newname = g_strndup (oldname, s – oldname); gboolean use_stored_name = FALSE; #endif //ndef TC_COMPATIBLE if (check && !use_same_name //no point in warning about re-use of same name && !use_stored_name //we already know the name of the processed item ) { s = g_build_filename (dir, newname, NULL); #ifdef E2_VFS ddata.path = s; retval = _e2pcr_ow_check (&ddata, options->multisrc); #else retval = _e2pcr_ow_check (s, options->multisrc); #endif g_free (s); if (retval == YES_TO_ALL) options->owrite = FALSE; else if (retval == CANCEL || retval == NO_TO_ALL) { g_free (dir); g_free (oldname); // if (!use_same_name) g_free (newname); return retval; } } retval = _e2pcr_decrypt1 (localpath, dir, oldname, newname, use_same_name, check, options); } else //doing encryption { #ifndef TC_COMPATIBLE //check naming arrangements specified by dialog flags & widgets if (options->en_name_same) //encrypted file name = same as original name use_same_name = TRUE; else { if (options->en_name_suffix) { if (*options->en_suffix != ‘’) { use_same_name = FALSE; s = F_FILENAME_TO_LOCALE (options->en_suffix); newname = e2_utils_strcat (oldname, s); F_FREE (s, options->en_suffix); } else use_same_name = TRUE; } else if (options->en_name_custom) { if (*options->en_name != ‘’) { newname = D_FILENAME_TO_LOCALE (options->en_name); use_same_name = !strcmp (oldname, newname); if (use_same_name) g_free (newname); } else use_same_name = TRUE; } else use_same_name = TRUE; //should never get here } #else newname = g_strconcat (oldname, “.enc”, NULL); #endif //ndef TC_COMPATIBLE if (check && !use_same_name) //no point in warning about re-use of same name { s = g_build_filename (dir, newname, NULL); #ifdef E2_VFS ddata.path = s; retval = _e2pcr_ow_check (&ddata, options->multisrc); #else retval = _e2pcr_ow_check (s, options->multisrc); #endif g_free (s); if (retval == YES_TO_ALL) options->owrite = FALSE; else if (retval == CANCEL || retval == NO_TO_ALL) { g_free (dir); g_free (oldname); // if (!use_same_name) g_free (newname); return retval; } } retval = _e2pcr_encrypt1 (localpath, dir, oldname, newname, use_same_name, check, options); } //end of encryption-specific section g_free (dir); g_free (oldname); // if (!use_same_name) cleared downstream // g_free (newname); return retval; } /** @brief callback function for recursive directory processing This is called for each non-directory item in the directory to be processed. Treewalk is breadth-first, not physical @param localpath absolute path of item to copy, localised string @param statptr pointer to struct stat with info about @a localpath @param status code from the walker, indicating what type of report it is @param user_data pointer to user-specified data @return E2TW_CONTINUE on success, others as appropriate */ static E2_TwResult _e2pcr_task_twcb_crypt (VPATH *localpath, const struct stat *statptr, E2_TwStatus status, E2P_CryptOpts *user_data) { E2_TwResult retval = E2TW_CONTINUE; switch (status) { DialogButtons cryptresult; mode_t mode; E2_DirEnt *dirfix; GList *member; #ifdef E2_VFS VPATH ddata; #endif case E2TW_DP: //dir completed //revert any altered dir permissions #ifdef E2_VFS ddata.spacedata = localpath->spacedata; #endif mode = statptr->st_mode & ALLPERMS; for (member = g_list_last (user_data->dirdata); member != NULL; member = member->prev) { dirfix = member->data; if (dirfix != NULL) { if (!strcmp (dirfix->path, VPSTR (localpath))) { #ifdef E2_VFS ddata.path = dirfix->path; #endif if ((mode & ALLPERMS) != dirfix->mode && #ifdef E2_VFS e2_fs_chmod (&ddata, dirfix->mode E2_ERR_NONE())) #else e2_fs_chmod (localpath, dirfix->mode E2_ERR_NONE())) #endif retval = E2TW_STOP; //CHECKME – want cleanup of copied file to continue g_free (dirfix->path); DEALLOCATE (E2_DirEnt, dirfix); user_data->dirdata = g_list_delete_link (user_data->dirdata, member); break; } } // else //should never happen CHECKME ok when walking list ? // user_data->dirdata = g_list_delete_link (user_data->dirdata, member); } break; case E2TW_DM: //dir, not opened due to different file system (reported upstream) case E2TW_DL: //dir, not opened due to depth limit (reported upstream) case E2TW_DNR: //unreadable dir (for which, error is reported upstream) //eventual chmod for this will probably fail, but try anyhow //CHECKME report continue after problem // retval = E2TW_FIXME; break; case E2TW_DRR: //dir now readable case E2TW_D: //ensure dir is writable, if we can if (e2_fs_tw_adjust_dirmode (localpath, statptr, (W_OK | X_OK)) == 0) //failed to set missing W and/or X perm retval = E2TW_SKIPSUB; //can’t process any item in the dir //CHECKME does DP report arrive ? else { //add this dir to list of items to revert afterwards //CHECKME what if newmode == oldmode ? (omit == scan full list in DP cb) dirfix = ALLOCATE (E2_DirEnt); CHECKALLOCATEDWARNT (dirfix, result = E2TW_STOP; break;) dirfix->path = g_strdup (VPSTR (localpath)); dirfix->mode = statptr->st_mode & ALLPERMS; user_data->dirdata = g_list_append (user_data->dirdata, dirfix); } break; case E2TW_F: //not directory or link if (S_ISREG (statptr->st_mode)) { struct stat sb; user_data->localpath = VPSTR (localpath); //ok to throw away the original sb = *statptr; //get a non-const statbuf user_data->statptr = &sb; cryptresult = _e2pcr_crypt1 (localpath, user_data); if (cryptresult == NO_TO_ALL || cryptresult == NO) //NO == error retval = E2TW_STOP; } break; case E2TW_SL: //symbolic link if (user_data->walklinks) { //get ultimate target of link gchar *target = g_strdup (VPSTR (localpath)); if (e2_fs_walk_link (&target E2_ERR_NONE())) { struct stat sb; #ifdef E2_VFS ddata.path = target; ddata.spacedata = localpath->spacedata; if (!e2_fs_stat (&ddata, &sb E2_ERR_NONE())) #else if (!e2_fs_stat (target, &sb E2_ERR_NONE())) #endif { user_data->localpath = VPSTR (localpath); //ok to throw away the original user_data->statptr = &sb; #ifdef E2_VFS cryptresult = _e2pcr_crypt1 (&ddata, user_data); #else cryptresult = _e2pcr_crypt1 (target, user_data); #endif } else cryptresult = NO; } else cryptresult = NO; g_free (target); if (cryptresult == NO_TO_ALL || cryptresult == NO) //NO == error retval = E2TW_STOP; } // case E2TW_SLN: //symbolic link targeting non-existent item // error message ?? // retval = E2TW_STOP; default: break; } return retval; } /** @brief apply the user’s choice to @a localpath Error messages here and downstream expect BGL open @param options pointer to process parameters data @return TRUE if successful */ static DialogButtons _e2pcr_apply (E2P_CryptOpts *options) { #ifdef E2_VFS VPATH ddata; ddata.spacedata = options->spacedata; #endif if (S_ISDIR (options->statptr->st_mode)) { if (!options->recurse) return CANCEL; #ifndef TC_COMPATIBLE if ((options->decryptmode && options->de_name_same) || (!options->decryptmode && options->en_name_same)) return CANCEL; //CHECKME warning ? ok if only 1 file/link in dir ? #endif //the path may be changed in the treewalk const gchar *savepath = options->localpath; //recursively process dir contents //walk-flags for: fix-DNR, no thru-links if appropriate E2_TwFlags exec_flags = E2TW_FIXDIR; if (!options->walklinks) exec_flags |= E2TW_PHYS; E2_ERR_DECLARE; //CHECKME allow continue after error? #ifdef E2_VFS ddata.path = options->localpath; gboolean retval = e2_fs_tw (&ddata, #else gboolean retval = e2_fs_tw ((gchar *)options->localpath, #endif _e2pcr_task_twcb_crypt, options, -1, exec_flags E2_ERR_PTR()); //normally no leftover dir data, but ensure cleanup … GList *member; for (member = g_list_last (options->dirdata); member != NULL; member = member->prev) { E2_DirEnt *dirfix = member->data; if (dirfix != NULL) { #ifdef E2_VFS ddata.path = dirfix->path; if (e2_fs_chmod (&ddata, dirfix->mode E2_ERR_PTR())) #else if (e2_fs_chmod (dirfix->path, dirfix->mode E2_ERR_PTR())) #endif retval = FALSE; g_free (dirfix->path); DEALLOCATE (E2_DirEnt, dirfix); } } //handle errors if (!retval && E2_ERR_ISNOT(0)) //might be a user-abort, not an error { #ifdef E2_VFS ddata.path = savepath; #endif e2_fs_error_local (_(“Cannot process all of %s”), #ifdef E2_VFS &ddata E2_ERR_MSGL()); #else (gchar *) savepath E2_ERR_MSGL()); #endif E2_ERR_CLEAR } return (retval) ? OK : NO_TO_ALL; //no need for a simple CANCEL } else //not a dir if (S_ISLNK (options->statptr->st_mode)) { //handle links separately, to manage ‘look-through’ if (!options->walklinks) return FALSE; //get ultimate target and remove any relativity DialogButtons cryptresult; gchar *target = g_strdup (options->localpath); if (e2_fs_walk_link (&target E2_ERR_NONE())) { struct stat sb; #ifdef E2_VFS ddata.path = target; if (!e2_fs_stat (&ddata, &sb E2_ERR_NONE())) #else if (!e2_fs_stat (target, &sb E2_ERR_NONE())) #endif { options->localpath = target; //ok to throw away the original options->statptr = &sb; #ifdef E2_VFS cryptresult = _e2pcr_crypt1 (&ddata, options); #else cryptresult = _e2pcr_crypt1 (target, options); #endif } else cryptresult = NO; } else cryptresult = NO; g_free (target); return cryptresult; // return (_e2pcr_crypt1 (target, options) == OK); } else //not dir or link #ifdef E2_VFS { ddata.path = options->localpath; return (_e2pcr_crypt1 (&ddata, options)); } #else return (_e2pcr_crypt1 (options->localpath, options)); #endif } /** @brief determine whether intended change is permitted @param rt pointer to dialog runtime data struct @return TRUE if change is permitted, or if current item is a dir with W,X permissions */ static gboolean _e2pcr_check_permission (E2P_CryptDlgRuntime *rt) { gchar target[PATH_MAX]; #ifndef TC_COMPATIBLE gchar *newpath, *localname; #endif gchar *dir; const gchar *utfname, *localpath; struct stat *sp; #ifdef E2_VFS VPATH ddata; ddata.spacedata = rt->opts->spacedata; #endif localpath = rt->opts->localpath; sp = rt->opts->statptr; restart: #ifdef E2_VFS ddata.path = localpath; if (e2_fs_lstat (&ddata, sp E2_ERR_NONE())) #else if (e2_fs_lstat (localpath, sp E2_ERR_NONE())) #endif { rt->opts->permission = FALSE; return FALSE; } if (S_ISLNK (sp->st_mode)) { if ((rt->dlgopen && !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rt->linktarget_btn))) || !rt->opts->walklinks) { rt->opts->permission = FALSE; return FALSE; } gchar *checktarget = g_strdup (localpath); if (!e2_fs_walk_link (&checktarget E2_ERR_NONE())) { g_free (checktarget); rt->opts->permission = FALSE; return FALSE; } g_strlcpy (target, checktarget, sizeof (target)); g_free (checktarget); localpath = target; goto restart; } if (S_ISDIR (sp->st_mode)) { #ifdef E2_VFS rt->opts->permission = !e2_fs_access (&ddata, X_OK | W_OK E2_ERR_NONE()); #else rt->opts->permission = !e2_fs_access (localpath, X_OK | W_OK E2_ERR_NONE()); #endif return rt->opts->permission; } dir = g_path_get_dirname (localpath); //access _always_ traverses links #ifdef E2_VFS ddata.path = dir; if (!e2_fs_access (&ddata, X_OK | W_OK E2_ERR_NONE())) #else if (!e2_fs_access (dir, X_OK | W_OK E2_ERR_NONE())) #endif { if (rt->opts->decryptmode) { #ifndef TC_COMPATIBLE if (rt->opts->de_name_same) //decrypted file name = same as encrypted name # ifdef E2_VFS { ddata.path = localpath; rt->opts->permission = !e2_fs_access (&ddata, W_OK E2_ERR_NONE()); } # else rt->opts->permission = !e2_fs_access (localpath, W_OK E2_ERR_NONE()); # endif else { if (rt->opts->de_name_suffix) { utfname = (rt->dlgopen) ? gtk_entry_get_text (GTK_ENTRY (rt->de_name_suffix_entry)): rt->opts->de_suffix; localname = F_FILENAME_TO_LOCALE (utfname); newpath = g_strdup (localpath); if (*localname != ‘’ && g_str_has_suffix (newpath, localname)) *(newpath + strlen (newpath) – strlen (localname)) = ‘’; #ifdef E2_VFS ddata.path = newpath; rt->opts->permission = e2_fs_access (&ddata, F_OK E2_ERR_NONE()) || !e2_fs_access (&ddata, W_OK E2_ERR_NONE()); #else rt->opts->permission = e2_fs_access (newpath, F_OK E2_ERR_NONE()) || !e2_fs_access (newpath, W_OK E2_ERR_NONE()); #endif F_FREE (localname, utfname); g_free (newpath); } else if (rt->opts->de_name_custom) { utfname = (rt->dlgopen) ? gtk_entry_get_text (GTK_ENTRY (rt->de_name_custom_entry)): rt->opts->de_name; localname = F_FILENAME_TO_LOCALE (utfname); newpath = g_build_filename (dir, localname, NULL); #ifdef E2_VFS ddata.path = newpath; rt->opts->permission = e2_fs_access (&ddata, F_OK E2_ERR_NONE()) || !e2_fs_access (&ddata, W_OK E2_ERR_NONE()); #else rt->opts->permission = e2_fs_access (newpath, F_OK E2_ERR_NONE()) || !e2_fs_access (newpath, W_OK E2_ERR_NONE()); #endif F_FREE (localname, utfname); g_free (newpath); } else rt->opts->permission = TRUE; } #else //FIXME rt->opts->permission = TRUE; #endif } else //encrypting { #ifndef TC_COMPATIBLE if (rt->opts->en_name_same) //encrypted file name = same as original #ifdef E2_VFS { ddata.path = localpath; rt->opts->permission = !e2_fs_access (&ddata, W_OK E2_ERR_NONE()); } #else rt->opts->permission = !e2_fs_access (localpath, W_OK E2_ERR_NONE()); #endif else { if (rt->opts->en_name_suffix) { utfname = (rt->dlgopen) ? gtk_entry_get_text (GTK_ENTRY (rt->en_name_suffix_entry)): rt->opts->en_suffix; localname = F_FILENAME_TO_LOCALE (utfname); newpath = g_build_filename (localpath, localname, NULL); #ifdef E2_VFS ddata.path = newpath; rt->opts->permission = e2_fs_access (&ddata, F_OK E2_ERR_NONE()) || !e2_fs_access (&ddata, W_OK E2_ERR_NONE()); #else rt->opts->permission = e2_fs_access (newpath, F_OK E2_ERR_NONE()) || !e2_fs_access (newpath, W_OK E2_ERR_NONE()); #endif F_FREE (localname, utfname); g_free (newpath); } else if (rt->opts->en_name_custom) { utfname = (rt->dlgopen) ? gtk_entry_get_text (GTK_ENTRY (rt->en_name_custom_entry)): rt->opts->en_name; localname = F_FILENAME_TO_LOCALE (utfname); newpath = g_build_filename (dir, localname, NULL); #ifdef E2_VFS ddata.path = newpath; rt->opts->permission = e2_fs_access (&ddata, F_OK E2_ERR_NONE()) || !e2_fs_access (&ddata, W_OK E2_ERR_NONE()); #else rt->opts->permission = e2_fs_access (newpath, F_OK E2_ERR_NONE()) || !e2_fs_access (newpath, W_OK E2_ERR_NONE()); #endif F_FREE (localname, utfname); g_free (newpath); } else rt->opts->permission = TRUE; } #else //FIXME rt->opts->permission = TRUE; #endif } } else rt->opts->permission = FALSE; g_free (dir); return rt->opts->permission; } /** @brief determine change-permission and set dialog button-sensitivities accordingly @param rt pointer to dialog data struct @return */ static void _e2pcr_set_buttons (E2P_CryptDlgRuntime *rt) { gboolean ok; #ifndef TC_COMPATIBLE gboolean custom, encmode; #endif ok = _e2pcr_check_permission (rt); //enable or disable button(s) if (rt->opts->multisrc) { #ifndef TC_COMPATIBLE if (ok) { //disable yes-to-all button (if any) when a custom name is to be used encmode = //(rt->dlgopen) ? gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rt->mode_btn));// : !rt->opts->decryptmode; if (encmode) custom = //(rt->dlgopen) ? gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rt->en_name_btn_custom));// : rt->opts->en_name_custom ; else custom =// (rt->dlgopen) ? gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rt->de_name_btn_custom));// : rt->opts->de_name_custom ; } else custom = FALSE; //warning prevention gtk_dialog_set_response_sensitive (GTK_DIALOG (rt->dialog), E2_RESPONSE_APPLYTOALL, ok & !custom); #else gtk_dialog_set_response_sensitive (GTK_DIALOG (rt->dialog), E2_RESPONSE_APPLYTOALL, ok); #endif } gtk_dialog_set_response_sensitive (GTK_DIALOG (rt->dialog), GTK_RESPONSE_APPLY, ok); #ifndef TC_COMPATIBLE ok = !((!rt->opts->decryptmode && rt->opts->en_name_custom) || (rt->opts->decryptmode && rt->opts->de_name_custom)); gtk_widget_set_sensitive (rt->recurse_btn, ok); #endif } #ifndef TC_COMPATIBLE /** @brief key-release callback @param entry UNUSED the entry widget where the key was released @param event pointer to event data struct @param rt pointer to data struct for the search @return FALSE always */ static gboolean _e2pcr_keyrel_cb (GtkWidget *entry, GdkEventKey *event, E2P_CryptDlgRuntime *rt) { _e2pcr_set_buttons (rt); return FALSE; } #endif /** @brief callback for toggle encryption (mode) button @param widget toggled encrypt button @param rt pointer to dialog data struct @return */ static void _e2pcr_toggle_mode_cb (GtkWidget *widget, E2P_CryptDlgRuntime *rt) { gboolean state = gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)); if (state) { #ifndef TC_COMPATIBLE gtk_widget_hide (rt->decryptbox); gtk_widget_show (rt->encryptbox); #endif gtk_widget_show (rt->confirmbox); #ifndef TC_COMPATIBLE gtk_widget_show (rt->en_properties_embed_btn); gtk_widget_show (rt->compress_btn); gtk_widget_hide (rt->properties_btn); gtk_widget_set_sensitive (rt->recurse_btn, !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rt->en_name_btn_custom))); #endif e2_button_set_label (rt->ok_btn, _(“_Encrypt”)); } else { #ifndef TC_COMPATIBLE gtk_widget_hide (rt->encryptbox); gtk_widget_show (rt->decryptbox); #endif gtk_widget_hide (rt->confirmbox); #ifndef TC_COMPATIBLE gtk_widget_hide (rt->en_properties_embed_btn); gtk_widget_hide (rt->compress_btn); gtk_widget_show (rt->properties_btn); gtk_widget_set_sensitive (rt->recurse_btn, !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (rt->de_name_btn_custom))); #endif e2_button_set_label (rt->ok_btn, _(“_Decrypt”)); } // gtk_widget_set_sensitive (rt->pwrt->pwentry2, state); rt->opts->decryptmode = !state; rt->pwrt->confirm = state; //make the password dialog check/not for matches //determine and handle permission _e2pcr_set_buttons (rt); } #ifndef TC_COMPATIBLE /** @brief callback for encryped name toggle buttons @param widget clicked button @param rt pointer to dialog data struct @return */ static void _e2pcr_toggle_encname_cb (GtkWidget *widget, E2P_CryptDlgRuntime *rt) { if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) { if (widget == rt->en_name_btn_suffix) { gtk_widget_set_sensitive (rt->en_name_suffix_entry, TRUE); gtk_widget_set_sensitive (rt->en_name_custom_entry, FALSE); } else if (widget == rt->en_name_btn_custom) { gtk_widget_set_sensitive (rt->en_name_custom_entry, TRUE); gtk_widget_set_sensitive (rt->en_name_suffix_entry, FALSE); } else //same name { gtk_widget_set_sensitive (rt->en_name_suffix_entry, FALSE); gtk_widget_set_sensitive (rt->en_name_custom_entry, FALSE); } gtk_widget_set_sensitive (rt->recurse_btn, widget != rt->en_name_btn_custom); //determine and handle permission _e2pcr_set_buttons (rt); } } /** @brief callback for decryped name toggle buttons Assumes BGL is off/open @param widget clicked button @param rt pointer to dialog data struct @return */ static void _e2pcr_toggle_decname_cb (GtkWidget *widget, E2P_CryptDlgRuntime *rt) { if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget))) { if (widget == rt->de_name_btn_suffix) { gtk_widget_set_sensitive (rt->de_name_suffix_entry, TRUE); gtk_widget_set_sensitive (rt->de_name_custom_entry, FALSE); } else if (widget == rt->de_name_btn_custom) { gtk_widget_set_sensitive (rt->de_name_custom_entry, TRUE); gtk_widget_set_sensitive (rt->de_name_suffix_entry, FALSE); } else //same name { gtk_widget_set_sensitive (rt->de_name_suffix_entry, FALSE); gtk_widget_set_sensitive (rt->de_name_custom_entry, FALSE); } gtk_widget_set_sensitive (rt->recurse_btn, widget != rt->de_name_btn_custom); //determine and handle permission _e2pcr_set_buttons (rt); } } #endif /** @brief handle button click, window-close etc for crypt dialog This is the callback for “response” signals emitted from @a dialog @param dialog UNUSED the dialog where the response was generated @param response the response returned from the dialog @param rt pointer to data struct for the dialog @return */ static void _e2pcr_response_cb (GtkDialog *dialog, gint response, E2P_CryptDlgRuntime *rt) { gboolean finished; switch (response) { case E2_RESPONSE_APPLYTOALL: case GTK_RESPONSE_APPLY: //only end if the password(s) are ok finished = e2_password_dialog_confirm (rt->pwrt); break; default: finished = TRUE; break; } rt->result = finished; //confirm or refute that the returned enum is valid if (finished) e2_password_dialog_backup (rt->pwrt); //backup static stuff //do not cleanup widgets here – done in main code } /** @brief create and run an encryption-change dialog @param options pointer to options data struct @return enumerator corresponding to user’s choice of action */ static DialogButtons _e2pcr_crypt_dialog_run (E2P_CryptOpts *options) { const gchar *localpath = options->localpath; struct stat *sp = options->statptr; #ifdef E2_VFS VPATH ddata = { localpath, options->spacedata }; if (e2_fs_lstat (&ddata, sp E2_ERR_NONE())) #else if (e2_fs_lstat (localpath, sp E2_ERR_NONE())) #endif return CANCEL; if (!(S_ISREG (sp->st_mode) || S_ISDIR (sp->st_mode) || S_ISLNK (sp->st_mode))) return CANCEL; printd (DEBUG, “create crypt dialog”); E2P_CryptDlgRuntime crt; gchar *pw = NULL; //create a temporary container from which we can later plunder some widgets GtkWidget *tempbox = gtk_vbox_new (FALSE, 0); //create P/W widgets, with both entries, we’ll hide one as appropriate //CHECKME make it tabular ? // gdk_threads_enter (); //CHECKME seems to reduce UI lags when entering text crt.pwrt = e2_password_dialog_setup (tempbox, TRUE, NULL, &pw); // gdk_threads_leave (); if (crt.pwrt == NULL) { printd (ERROR, “Not enough memory for password widgets”); //FIXME warn user about memory error gtk_widget_destroy (tempbox); return NO_TO_ALL; } crt.opts = options; crt.dialog = e2_dialog_create (NULL, NULL, _(“en/decrypt file”), _e2pcr_response_cb, &crt); GtkWidget *dialog_vbox = GTK_DIALOG (crt.dialog)->vbox; //some things that go before the password entries e2_widget_add_mid_label (dialog_vbox, #ifdef TC_COMPATIBLE _(“Operating in tinycrypt-compatible mode”), #else _(“Using deprecated ARC4 algorithm”), #endif 0.5, FALSE, E2_PADDING); //L, R padding GString *label_text = g_string_sized_new (NAME_MAX+20); gchar *type; switch (sp->st_mode & S_IFMT) { case S_IFDIR: type = _(“Directory”); break; case S_IFLNK: type = _(“Symbolic link”); break; // case S_IFREG: default: type = _(“Filename”); break; } gchar *name = g_path_get_basename (localpath); gchar *utf = F_FILENAME_FROM_LOCALE (name); //needed for naming g_string_printf (label_text, “%s: %s“, type, utf); if (S_ISLNK (sp->st_mode)) { gchar *target = g_strdup (localpath); if (e2_fs_walk_link (&target E2_ERR_NONE())) { gchar *utfname = F_DISPLAYNAME_FROM_LOCALE (target); g_string_append_printf (label_text, _(” to %s”), utfname); F_FREE (utfname, target); } g_free (target); } e2_widget_add_mid_label (dialog_vbox, label_text->str, 0, TRUE, E2_PADDING); //L, R padding GtkWidget *hbox = e2_widget_add_box (dialog_vbox, FALSE, 0, FALSE, TRUE, 0); //FIXME set initial mode according to extension of item crt.mode_btn = e2_button_add_radio (hbox, _(“_encrypt”), NULL, !options->decryptmode, FALSE, 0, NULL, NULL); //set callback later, after widgets created GSList *group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (crt.mode_btn)); e2_button_add_radio (hbox, _(“_decrypt”), group, options->decryptmode, FALSE, 0, NULL, NULL); hbox = e2_widget_add_box (dialog_vbox, FALSE, 0, FALSE, FALSE, 0); //the existing label in tempbox is centred, might as well set a new one GtkWidget *wid = e2_widget_add_mid_label (hbox, _(“Enter password:”), 0, FALSE, E2_PADDING_SMALL); //L, R padding GtkSizeGroup *same = gtk_size_group_new (GTK_SIZE_GROUP_HORIZONTAL); gtk_size_group_add_widget (same, wid); //supply a previously-entered password (but not the confirmation one when encrypting?) if (options->plain_pw != NULL) { gtk_entry_set_text (GTK_ENTRY (crt.pwrt->pwentry1), options->plain_pw); gtk_editable_select_region (GTK_EDITABLE (crt.pwrt->pwentry1), 0, -1); } wid = e2_widget_add_box (hbox, TRUE, E2_PADDING, FALSE, FALSE, 0); //#ifndef TC_COMPATIBLE // gtk_widget_set_size_request (wid, 200, -1); //#endif gtk_widget_reparent (crt.pwrt->pwentry1, wid); crt.confirmbox = e2_widget_add_box (dialog_vbox, FALSE, 0, FALSE, FALSE, 0); wid = e2_widget_add_mid_label (crt.confirmbox, _(“Confirm password:”), 0, FALSE, E2_PADDING_SMALL); //L, R padding gtk_size_group_add_widget (same, wid); g_object_unref (G_OBJECT (same)); if (options->plain_pw != NULL) { gtk_entry_set_text (GTK_ENTRY (crt.pwrt->pwentry2), options->plain_pw); // gtk_editable_select_region (GTK_EDITABLE (rt->pwentry2), 0, -1); } wid = e2_widget_add_box (crt.confirmbox, TRUE, E2_PADDING, FALSE, FALSE, 0); gtk_widget_reparent (crt.pwrt->pwentry2, wid); gtk_widget_destroy (tempbox); //finished with that now #ifndef TC_COMPATIBLE crt.encryptbox = gtk_vbox_new (FALSE, 0); crt.en_name_btn_same = e2_button_add_radio (crt.encryptbox, _(“encrypted file will have _same name”), NULL, options->en_name_same, FALSE, 0, NULL, NULL); //set callback later, after widgets created; hbox = e2_widget_add_box (crt.encryptbox, TRUE, 0, FALSE, FALSE, 0); group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (crt.en_name_btn_same)); crt.en_name_btn_suffix = e2_button_add_radio (hbox, _(“_append this to encrypted file name”), group, options->en_name_suffix, FALSE, 0, NULL, NULL); //setup all entries in 2 steps to avoid selecting entry text crt.en_name_suffix_entry = e2_widget_add_entry (hbox, NULL, TRUE, FALSE); gtk_entry_set_text (GTK_ENTRY (crt.en_name_suffix_entry), options->en_suffix); gtk_widget_set_size_request (crt.en_name_suffix_entry, 80, -1); gtk_widget_set_sensitive (crt.en_name_suffix_entry, options->en_name_suffix); //enable permissions change after any change g_signal_connect_after (G_OBJECT (crt.en_name_suffix_entry), “key-release-event”, G_CALLBACK (_e2pcr_keyrel_cb), &crt); hbox = e2_widget_add_box (crt.encryptbox, TRUE, 0, FALSE, TRUE, 0); group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (crt.en_name_btn_same)); crt.en_name_btn_custom = e2_button_add_radio (hbox, _(“encrypted file _name will be”), group, options->en_name_custom, FALSE, 0, NULL, NULL); crt.en_name_custom_entry = e2_widget_add_entry (hbox, NULL, TRUE, FALSE); gtk_entry_set_text (GTK_ENTRY (crt.en_name_custom_entry), utf); // gtk_widget_set_size_request (crt.en_name_custom_entry, 120, -1); gtk_widget_set_sensitive (crt.en_name_custom_entry, options->en_name_custom); #ifdef E2_ASSISTED e2_widget_set_label_relations (crt.en_name_btn_custom, crt.en_name_custom_entry); #endif g_signal_connect_after (G_OBJECT (crt.en_name_custom_entry), “key-release-event”, G_CALLBACK (_e2pcr_keyrel_cb), &crt); gtk_box_pack_start (GTK_BOX (dialog_vbox), crt.encryptbox, TRUE, TRUE, 0); //top, bottom padding crt.decryptbox = gtk_vbox_new (FALSE, 0); crt.de_name_btn_same = e2_button_add_radio (crt.decryptbox, _(“decrypted file will have _same name”), NULL, options->de_name_same, FALSE, 0, NULL, NULL); //set callback later, after widgets created; //#ifndef TC_COMPATIBLE group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (crt.de_name_btn_same)); crt.de_name_btn_stored = e2_button_add_radio (crt.decryptbox, _(“decrypted file will have e_mbedded name”), group, options->de_name_stored, FALSE, 0, NULL, NULL); //#endif hbox = e2_widget_add_box (crt.decryptbox, TRUE, 0, FALSE, FALSE, 0); group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (crt.de_name_btn_same)); crt.de_name_btn_suffix = e2_button_add_radio (hbox, _(“strip this _from end of decrypted file name”), group, options->de_name_suffix, FALSE, 0, NULL, NULL); crt.de_name_suffix_entry = e2_widget_add_entry (hbox, NULL, TRUE, FALSE); gtk_entry_set_text (GTK_ENTRY (crt.de_name_suffix_entry), options->de_suffix); gtk_widget_set_size_request (crt.de_name_suffix_entry, 80, -1); gtk_widget_set_sensitive (crt.de_name_suffix_entry, options->de_name_suffix); #ifdef E2_ASSISTED e2_widget_set_label_relations (crt.de_name_btn_suffix, crt.de_name_suffix_entry); #endif g_signal_connect_after (G_OBJECT (crt.de_name_suffix_entry), “key-release-event”, G_CALLBACK (_e2pcr_keyrel_cb), &crt); hbox = e2_widget_add_box (crt.decryptbox, TRUE, 0, FALSE, TRUE, 0); group = gtk_radio_button_get_group (GTK_RADIO_BUTTON (crt.de_name_btn_same)); crt.de_name_btn_custom = e2_button_add_radio (hbox, _(“decrypted file _name will be”), group, options->de_name_custom, FALSE, 0, NULL, NULL); crt.de_name_custom_entry = e2_widget_add_entry (hbox, NULL, TRUE, FALSE); gtk_entry_set_text (GTK_ENTRY (crt.de_name_custom_entry), utf); // gtk_widget_set_size_request (crt.de_name_custom_entry, 120, -1); gtk_widget_set_sensitive (crt.de_name_custom_entry, options->de_name_custom); #ifdef E2_ASSISTED e2_widget_set_label_relations (crt.de_name_btn_custom, crt.de_name_custom_entry); #endif g_signal_connect_after (G_OBJECT (crt.de_name_custom_entry), “key-release-event”, G_CALLBACK (_e2pcr_keyrel_cb), &crt); gtk_box_pack_start (GTK_BOX (dialog_vbox), crt.decryptbox, FALSE, FALSE, E2_PADDING); //top, bottom padding crt.properties_btn = e2_button_add_toggle (dialog_vbox, TRUE, options->de_props_stored, _(“restore _properties”), _(“decrypted file will have stored owners, permissions and dates”), FALSE, 0, NULL, NULL); hbox = e2_widget_add_box (dialog_vbox, FALSE, 0, FALSE, TRUE, E2_PADDING_SMALL); # ifdef E2_MINICRYPT crt.compress_btn = e2_button_add_toggle (hbox, TRUE, options->compress, _(“_compress”), _(“compress file before encryption”), FALSE, 0, NULL, NULL); # else crt.compress_btn = e2_button_add_toggle (hbox, TRUE, (options->compress && (compresslib & E2_CFLAGLIBMASK)), _(“_compress”), _(“compress file before encryption”), FALSE, 0, NULL, NULL); if (!(compresslib & E2_CFLAGLIBMASK)) { options->compress = FALSE; gtk_widget_set_sensitive (crt.compress_btn, FALSE); } # endif crt.en_properties_embed_btn = e2_button_add_toggle (hbox, TRUE, options->en_properties_embed, _(“store _properties”), _(“store current name, permissions etc in the encrypted file”), FALSE, 0, NULL, NULL); #endif hbox = e2_widget_add_box (dialog_vbox, FALSE, 0, FALSE, TRUE, E2_PADDING_SMALL); crt.backup_btn = e2_button_add_toggle (hbox, TRUE, options->backup, _(“_backup”), _(“backup an existing file with the same name as the processed file”), FALSE, 0, NULL, NULL); crt.preserve_btn = e2_button_add_toggle (hbox, TRUE, options->preserve, _(“_keep original”), _(“do not remove the original file, after processing it”), FALSE, 0, NULL, NULL); hbox = e2_widget_add_box (dialog_vbox, FALSE, 0, FALSE, TRUE, E2_PADDING_SMALL); crt.linktarget_btn = e2_button_add_toggle (hbox, TRUE, options->walklinks, _(“through _links”), _(“if file is a symlink, process its target”), FALSE, 0, NULL, NULL); crt.recurse_btn = e2_button_add_toggle (hbox, TRUE, options->recurse, _(“_recurse directories”), NULL, FALSE, 0, NULL, NULL); //now it’s safe to connect toggle button callbacks g_signal_connect (G_OBJECT (crt.mode_btn), “toggled”, G_CALLBACK (_e2pcr_toggle_mode_cb), &crt); #ifndef TC_COMPATIBLE g_signal_connect (G_OBJECT (crt.en_name_btn_same), “toggled”, G_CALLBACK (_e2pcr_toggle_encname_cb), &crt); g_signal_connect (G_OBJECT (crt.en_name_btn_suffix), “toggled”, G_CALLBACK (_e2pcr_toggle_encname_cb), &crt); g_signal_connect (G_OBJECT (crt.en_name_btn_custom), “toggled”, G_CALLBACK (_e2pcr_toggle_encname_cb), &crt); g_signal_connect (G_OBJECT (crt.de_name_btn_same), “toggled”, G_CALLBACK (_e2pcr_toggle_decname_cb), &crt); g_signal_connect (G_OBJECT (crt.de_name_btn_suffix), “toggled”, G_CALLBACK (_e2pcr_toggle_decname_cb), &crt); g_signal_connect (G_OBJECT (crt.de_name_btn_custom), “toggled”, G_CALLBACK (_e2pcr_toggle_decname_cb), &crt); g_signal_connect (G_OBJECT (crt.de_name_btn_stored), “toggled”, G_CALLBACK (_e2pcr_toggle_decname_cb), &crt); #endif if (options->decryptmode) { #ifndef TC_COMPATIBLE gtk_widget_show (crt.decryptbox); #endif gtk_widget_hide (crt.confirmbox); #ifndef TC_COMPATIBLE gtk_widget_hide (crt.compress_btn); gtk_widget_hide (crt.en_properties_embed_btn); #endif } #ifndef TC_COMPATIBLE else { gtk_widget_show (crt.encryptbox); gtk_widget_hide (crt.properties_btn); } #endif E2_Button no_btn; if (options->multisrc) { e2_dialog_set_negative_response (crt.dialog, E2_RESPONSE_NOTOALL); e2_dialog_add_defined_button (crt.dialog, &E2_BUTTON_CANCEL); e2_dialog_add_defined_button (crt.dialog, &E2_BUTTON_APPLYTOALL); e2_button_derive (&no_btn, &E2_BUTTON_NO, BTN_NO_SKIP); } else { e2_dialog_set_negative_response (crt.dialog, GTK_RESPONSE_NO); e2_button_derive (&no_btn, &E2_BUTTON_NO, BTN_NO_CANCEL); } // no_btn.showflags |= E2_BTN_DEFAULT; e2_dialog_add_defined_button (crt.dialog, &no_btn); E2_Button yes_btn; yes_btn = E2_BUTTON_YES; yes_btn.label = (options->decryptmode) ? _(“_Decrypt”) : _(“_Encrypt”); yes_btn.showflags &= ~E2_BTN_DEFAULT; crt.ok_btn = e2_dialog_add_defined_button (crt.dialog, &yes_btn); //set where to focus when confirmation entry is activated crt.pwrt->focus = crt.ok_btn; _e2pcr_set_buttons (&crt); crt.pwrt->confirm = !options->decryptmode; //make the password dialog check/not for matches crt.dlgopen = TRUE; crt.result = NO_TO_ALL; //default in case of abort gtk_widget_show_all (dialog_vbox); _e2pcr_toggle_mode_cb (crt.mode_btn, &crt); //gboolean QQQQcheckmutexok2; // gdk_threads_leave(); // pthread_mutex_lock (&pw_mutex); /* if (window_width == -1) window_width = MIN(400, app.window.panes_paned->allocation.width*2/3); gtk_window_resize (GTK_WINDOW(rt->dialog), window_width, -1); */ // g_static_rec_mutex_unlock (&pw_mutex); // pthread_mutex_unlock (&pw_mutex); gdk_threads_enter (); e2_dialog_setup (crt.dialog, app.main_window); e2_dialog_run (crt.dialog, NULL, E2_DIALOG_DONT_SHOW_ALL); gtk_widget_grab_focus (crt.pwrt->pwentry1); DialogButtons retval; crt.result = FALSE; while !crt.result) retval = e2_dialog_wait (crt.dialog, TRUE, FALSE, options->multisrc, TRUE); //CHECKME TRUE maincontext gdk_threads_leave (); //remember most of the current dialog data options->decryptmode = !gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (crt.mode_btn)); #ifndef TC_COMPATIBLE options->en_name_same = //encrypted file name = same as onencrypted name gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (crt.en_name_btn_same)); options->en_name_suffix = //encrypted file name has user-specified suffix (if any) gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (crt.en_name_btn_suffix)); options->en_name_custom = //encrypted file name = user-specified gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (crt.en_name_btn_custom)); // options->en_name_embed = //store filenama in encrypted file // gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (crt.en_name_embed_btn)); options->en_properties_embed = //store filenama and statbuf in encrypted file gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (crt.en_properties_embed_btn)); options->de_name_same = //decrypted file name = same as encrypted name gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (crt.de_name_btn_same)); options->de_name_stored = //decrypted file name = embedded original name gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (crt.de_name_btn_stored)); options->de_name_suffix = //decrypted file name omits user-specified suffix (if any) gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (crt.de_name_btn_suffix)); options->de_name_custom = //decrypted file name = user-specified gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (crt.de_name_btn_custom)); options->de_props_stored = //reinstate other properties of original file gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (crt.properties_btn)); options->compress = //compress file before encryption gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (crt.compress_btn)); #endif options->backup = //preserve any file with same name as specified for the [de]crypted file gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (crt.backup_btn)); options->preserve = //preserve the file to be [de]crypted, with alternate name if appropriate gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (crt.preserve_btn)); options->recurse = //recursively process all files in any selected dir gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (crt.recurse_btn)); options->walklinks =//process link targets gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (crt.linktarget_btn)); #ifndef TC_COMPATIBLE if (options->en_name != NULL) g_free (options->en_name); options->en_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (crt.en_name_custom_entry))); //user-specified suffix, freeable utf-8 if (options->en_suffix != NULL) g_free (options->en_suffix); options->en_suffix = g_strdup (gtk_entry_get_text (GTK_ENTRY (crt.en_name_suffix_entry))); //user-specified suffix, freeable utf-8 if (options->de_name != NULL) g_free (options->de_name); options->de_name = g_strdup (gtk_entry_get_text (GTK_ENTRY (crt.de_name_custom_entry))); //user-specified suffix, freeable utf-8 if (options->de_suffix != NULL) g_free (options->de_suffix); options->de_suffix = g_strdup (gtk_entry_get_text (GTK_ENTRY (crt.de_name_suffix_entry))); //user-specified suffix, freeable utf-8 #endif //remember password only if a single-item is to be processed, //and permission is ok and password is not empty if (retval == OK || retval == YES_TO_ALL || (retval == CANCEL && options->multisrc)) { if (pw == NULL || *pw == ‘’) retval = CANCEL; else { if (retval != CANCEL) { _e2pcr_check_permission (&crt); if (!options->permission) retval = CANCEL; } if (options->plain_pw != NULL) g_free (options->plain_pw); options->plain_pw = pw; } } if (!(retval == OK || retval == YES_TO_ALL || (retval == CANCEL && options->multisrc))) { if (options->plain_pw != NULL) { g_free (options->plain_pw); options->plain_pw = NULL; } } g_free (name); F_FREE (utf, name); if (GTK_IS_DIALOG(crt.dialog)) { //not explicitly closed by the user gdk_threads_enter (); gtk_widget_destroy (crt.dialog); gdk_threads_leave (); } //do not free p/w string – it’s saved at options->plain_pw DEALLOCATE (E2_PWDataRuntime, crt.pwrt); return retval; } /** @brief encryt or decrypt selected item(s) in active pane If > 1 item is selected, a dialog is created for each such item in turn (or until the user chooses stop or apply-to-all) @param from the button, menu item etc which was activated @param art action runtime data @return TRUE if action completed successfully, else FALSE */ static gboolean _e2p_task_docrypt (gpointer from, E2_ActionRuntime *art) { return (e2_task_enqueue_task (E2_TASK_CRYPT, art, from, _e2p_task_docryptQ, e2_task_refresh_lists)); } static gboolean _e2p_task_docryptQ (E2_ActionTaskData *qed) { //printd (DEBUG, “task: crypt”); #ifdef E2_VFSTMP if (qed->currspace != NULL) //not a local space return FALSE; #endif struct stat sb; //statbuf for general use E2P_CryptOpts options = session_opts; options.permission = FALSE; #ifndef TC_COMPATIBLE options.ignore_suffix = FALSE; #endif options.owrite = FALSE; #ifndef TC_COMPATIBLE options.en_suffix = g_strdup (session_opts.en_suffix); options.de_suffix = g_strdup (session_opts.de_suffix); #endif options.plain_pw = NULL; //probably redundant options.statptr = &sb; options.dirdata = NULL; #ifdef E2_VFS options.spacedata = qed->currspace; #endif GPtrArray *names = qed->names; options.multisrc = names->len > 1; gchar *curr_local = qed->currdir; guint count; gboolean all = FALSE; E2_SelectedItemInfo **iterator = (E2_SelectedItemInfo **) names->pdata; //guess the initial mode //too bad if 1st item is a dir with the expected suffix gchar *utf = F_FILENAME_TO_LOCALE ((*iterator)->filename); options.decryptmode = #ifdef TC_COMPATIBLE g_str_has_suffix (utf, “.enc”); #else (*session_opts.de_suffix != ‘’ && g_str_has_suffix (utf, session_opts.de_suffix)); #endif F_FREE (utf, (*iterator)->filename); GString *path = g_string_sized_new (PATH_MAX); #ifdef E2_REFRESH_DEBUG printd (DEBUG, “disable refresh, crypt task”); #endif e2_filelist_disable_refresh (); e2_task_advise (); #ifdef E2_VFS VPATH ddata; ddata.spacedata = qed->currspace; #endif for (count = 0; count < names->len; count++, iterator++) { DialogButtons choice; //”..” entries filtered when names compiled //FIXME for single-setup: instead of the following, adjust file details in dialog, reset default button // gchar *itempath = e2_utils_dircat (curr_view, (*iterator)->filename, TRUE); g_string_printf (path, “%s%s”, curr_local, (*iterator)->filename); //separator comes with dir options.path = path->str; if (all) { //check if we have permission to change this item E2P_CryptDlgRuntime crt; crt.opts = &options; crt.dlgopen = FALSE; if (_e2pcr_check_permission (&crt)) choice = OK; else { #ifdef E2_VFS ddata.path = (*iterator)->filename; #endif e2_fs_error_simple ( _(“You do not have authority to modify %s”), #ifdef E2_VFS &ddata); #else (*iterator)->filename); #endif choice = CANCEL; } } else { #ifdef E2_REFRESH_DEBUG printd (DEBUG, “enable refresh, encrypt dialog”); #endif e2_filelist_enable_refresh (); //allow updates while we wait *qed->status = E2_TASK_PAUSED; choice = _e2pcr_crypt_dialog_run (&options); *qed->status = E2_TASK_RUNNING; #ifdef E2_REFRESH_DEBUG printd (DEBUG, “disable refresh, encrypt dialog”); #endif e2_filelist_disable_refresh (); } switch (choice) { case YES_TO_ALL: all = TRUE; // myuid = getuid (); //do this once, for speed choice = OK; case OK: if (options.permission) // && (axs_changes != NULL || def_changes != NULL)) { #ifdef E2_INCLIST if ((choice = _e2pcr_apply (&options)) == OK) { //FIXME update line in treeview } #else # ifdef E2_FAM choice = _e2pcr_apply (&options); # else if ((choice = _e2pcr_apply (&options)) == OK) # ifdef E2_VFS { if (ddata.spacedata == NULL) { ddata.path = qed->currdir; e2_fs_touchnow (&ddata E2_ERR_NONE()); } } # else //make the file-list refresher notice successful change e2_fs_touchnow (qed->currdir E2_ERR_NONE()); # endif # endif #endif /* if (!all) { CLEANUPS } */ } case CANCEL: break; default: choice = NO_TO_ALL; // break flag; break; } if (choice == NO_TO_ALL) break; } //backup relevant last-used option data #ifndef TC_COMPATIBLE g_free (session_opts.en_suffix); g_free (session_opts.de_suffix); if (options.en_name != NULL) { g_free (options.en_name); options.en_name = NULL; } if (options.de_name != NULL) { g_free (options.de_name); options.de_name = NULL; } #endif if (options.plain_pw != NULL) { g_free (options.plain_pw); options.plain_pw = NULL; } session_opts = options; g_string_free (path, TRUE); e2_window_clear_status_message (); #ifdef E2_REFRESH_DEBUG printd (DEBUG, “enable refresh, acl task”); #endif e2_filelist_enable_refresh (); #ifndef E2_MINICRYPT if (options.libhandle != NULL) //disconnect from the compression library { dlclose (options.libhandle); session_opts.libhandle = NULL; session_opts.compresslibflags = 0; // lzo_init_done = FALSE; //re-initialize lzo lib if used again // lzma_init_done = FALSE; //ditto for lzma } #endif return TRUE; } //aname must be confined to this module static const gchar *aname; /** @brief plugin initialization function, called by main program @param p ptr to plugin data struct @return TRUE if the initialization succeeds, else FALSE */ gboolean init_plugin (Plugin *p) { #define ANAME “crypt1” //distinct from current version aname = _(“crypt1”); p->signature = ANAME VERSION; p->menu_name = _(“_En/decrypt..”); p->description = _(“Encrypt or decrypt selected items”); p->icon = “plugin_”ANAME”_”E2IP”.png”; //use icon file pathname if appropriate if (p->action == NULL) { //no need to free name here E2_Action plugact = {g_strconcat (_A(6),”.”,aname,NULL),_e2p_task_docrypt,FALSE,E2_ACTION_TYPE_ITEM,0,NULL,NULL}; p->action = e2_plugins_action_register (&plugact); #ifndef TC_COMPATIBLE session_opts.en_suffix = g_strdup (“.enc”); //no translation session_opts.de_suffix = g_strdup (“.enc”); //no translation //detect available compression libs gpointer libhandle; gint (*initfunc) (); gint deflib = -1; //first try, fastest performer, lzo if ((libhandle = dlopen (“liblzo2.so.2”, RTLD_LAZY #ifdef RTLD_DEEPBIND | RTLD_DEEPBIND #endif )) != NULL) { if (dlsym (libhandle, LZO_COMPRESSFUNC) != NULL) { if (dlsym (libhandle, LZO_DECOMPRESSFUNC) != NULL) { initfunc = dlsym (libhandle, “__lzo_init_v2”); //a #define in lzoconf.h if (initfunc != NULL) { //this hack is not much better than no init at all ! gint result = initfunc (2, sizeof(gshort), sizeof(gint), sizeof(glong), sizeof(guint32), sizeof(guint), sizeof(guchar *), //dict_t sizeof(gchar *), sizeof(gpointer), -1 //sizeof(lzo_callback_t)); ); if (result == 0) { //actual usage in another thread, re-init there lzo_init_done = TRUE; compresslib |= E2_CFLAGLZO; //we have this one deflib = 0; } } } } dlclose (libhandle); } #ifdef DEBUG_MESSAGES if (!(compresslib & E2_CFLAGLZO)) printd (DEBUG, “Cannot [de]compress using LZO lib”); #endif //second, intermediate speed, zlib if ((libhandle = dlopen (“libz.so.1”, RTLD_LAZY #ifdef RTLD_DEEPBIND | RTLD_DEEPBIND #endif )) != NULL) { if (dlsym (libhandle, GZIP_COMPRESSFUNC) != NULL) { if (dlsym (libhandle, GZIP_DECOMPRESSFUNC) != NULL) { compresslib |= E2_CFLAGZ; //and we have this one if (deflib == -1) deflib = 1; } } dlclose (libhandle); } #ifdef DEBUG_MESSAGES if (!(compresslib & E2_CFLAGZ)) printd (DEBUG, “Cannot [de]compress using libz”); #endif //next, libbz2 if ((libhandle = dlopen (“libbz2.so.1”, RTLD_LAZY #ifdef RTLD_DEEPBIND | RTLD_DEEPBIND #endif )) != NULL) { if (dlsym (libhandle, BZIP2_COMPRESSFUNC) != NULL) { if (dlsym (libhandle, BZIP2_DECOMPRESSFUNC) != NULL) { compresslib |= E2_CFLAGBZ2; //and we have this one if (deflib == -1) deflib = 2; } } dlclose (libhandle); } #ifdef DEBUG_MESSAGES if (!(compresslib & E2_CFLAGBZ2)) printd (DEBUG, “Cannot [de]compress using libbzip2”); #endif /* //lzma (7-zip) compression supports streaming, slower compression than bzip2, compression ratio and decryption speed between gzip and bzip2 if ((libhandle = dlopen (“liblzma.so.0”, RTLD_LAZY #ifdef RTLD_DEEPBIND | RTLD_DEEPBIND #endif )) != NULL) { if (dlsym (libhandle, LZMA_COMPRESSFUNC) != NULL) { if (dlsym (libhandle, LZMA_DECOMPRESSFUNC) != NULL) { compresslib |= E2_CFLAGLZMA; //and we have this one if (deflib == -1) deflib = 3; } } dlclose (libhandle); } #ifdef DEBUG_MESSAGES if (!(compresslib & E2_CFLAGLZMA)) printd (DEBUG, “Cannot [de]compress using liblzma”); #endif */ //no flags set in compresslib if nothing found printd (DEBUG, “masked lib-flags %0x”, compresslib & E2_CFLAGLIBMASK); if (deflib == -1) deflib = 0; //nothing valid found, just pick the first one gchar *group = g_strconcat(_C(34),”.”,_C(27),”:”,aname,NULL); //_(“plugins.options:crypt” E2_OptionSetupExtra ex; memset (&ex, 0, sizeof (E2_OptionSetupExtra)); ex.exsel.def = deflib; ex.exsel.values = libnames; E2_OptionSet *set = e2_plugins_option_register (E2_OPTION_TYPE_SEL, “compress-library”, group, _(“compression type”), _(“Use this form of compression before encryption”), NULL, &ex, E2_OPTION_FLAG_FREEGROUP | E2_OPTION_FLAG_ADVANCED); FIXME deflib, libnames, //because plugins are loaded after config data, config options need to //get any data from unknown-options data e2_option_transient_value_get (set); #endif //ndef TC_COMPATIBLE return TRUE; } return FALSE; } /** @brief cleanup transient things for this plugin @param p pointer to data struct for the plugin @return TRUE if all cleanups were completed */ gboolean clean_plugin (Plugin *p) { gchar *action_name = g_strconcat (_A(6),”.”,aname,NULL); gboolean ret = e2_plugins_action_unregister (action_name); g_free (action_name); #ifndef TC_COMPATIBLE if (ret) { g_free (session_opts.en_suffix); g_free (session_opts.de_suffix); ret = ret && e2_plugins_option_unregister (“compress-library”); } #endif return ret; }