/* UNRAR plugin for Tux Commander * version 0.5.1, designed for unrar v6.2.12 * Copyright (C) 2007-2024 Tomas Bzatek * Check for updates on tuxcmd.sourceforge.net * * Uses UNRAR sources * UnRAR - free utility for RAR archives * Copyright (C) Alexander L. Roshal * http://www.rarlab.com/ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include #include #include #include #include #include "tuxcmd-vfs.h" #include "vfsutils.h" #include "strutils.h" #include "logutils.h" #include "filelist.h" #include "filelist-vfs-intf.h" #define _UNIX #include #include /* Compatibility types from headers.hpp */ enum HOST_SYSTEM { HOST_MSDOS = 0, HOST_OS2 = 1, HOST_WIN32 = 2, HOST_UNIX = 3, HOST_MACOS = 4, HOST_BEOS = 5, HOST_MAX }; #define VERSION "0.5.1" #define BUILD_DATE "2024-01-19" #define DEFAULT_BLOCK_SIZE 65536 /* Declaration of the global plugin object */ struct TVFSGlobs { TVFSLogFunc log_func; char *curr_dir; char *archive_path; gboolean need_password; gboolean failed_passwd_callback; gboolean volume_missing_abort; char *password; guint32 block_size; struct PathTree *files; struct VfsFilelistData *vfs_filelist; guint64 total_size; guint64 extract_done; gboolean extract_cancelled; TVFSAskQuestionCallback callback_ask_question; TVFSAskPasswordCallback callback_ask_password; TVFSProgressCallback callback_progress; void *callback_data; /* copy operation private */ HANDLE PASCAL op_handle; }; //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////// // Basic initialization functions struct TVFSGlobs * VFSNew (TVFSLogFunc log_func) { struct TVFSGlobs * globs; log_init (); globs = g_malloc0 (sizeof (struct TVFSGlobs)); globs->block_size = DEFAULT_BLOCK_SIZE; globs->log_func = log_func; return globs; } void VFSSetCallbacks (struct TVFSGlobs *globs, TVFSAskQuestionCallback ask_question_callback, TVFSAskPasswordCallback ask_password_callback, TVFSProgressCallback progress_func, void *data) { globs->callback_ask_question = ask_question_callback; globs->callback_ask_password = ask_password_callback; globs->callback_progress = progress_func; globs->callback_data = data; } void VFSFree (struct TVFSGlobs *globs) { g_free (globs->curr_dir); g_free (globs->archive_path); g_free (globs->password); filelist_tree_free (globs->files); vfs_filelist_free (globs->vfs_filelist); g_free (globs); } int VFSVersion () { return cVFSVersion; } struct TVFSInfo * VFSGetInfo () { struct TVFSInfo *module_info; #ifdef MODULE_SHARED const char *shared_module = "dynamically linked"; #else const char *shared_module = "statically linked"; #endif module_info = g_malloc0 (sizeof (struct TVFSInfo)); module_info->ID = g_strdup ("unrar_plugin"); module_info->Name = g_strdup ("UNRAR plugin"); module_info->About = g_strdup_printf ("version %s, build date: %s\nusing unrar sources v%d.%d [%d-%.2d-%.2d]\n%s\n", VERSION, BUILD_DATE, RARVER_MAJOR, RARVER_MINOR, RARVER_YEAR, RARVER_MONTH, RARVER_DAY, shared_module); module_info->Copyright = g_strdup ("Plugin Copyright (C) 2007-2024 Tomáš Bžatek\nUNRAR sources Copyright (C) 2002-2023 Alexander Roshal"); return module_info; } guint32 VFSGetCapabilities () { return VFS_CAP_ARCHIVE_STREAMING; } char * VFSGetArchiveExts () { return g_strdup ("rar;r00;r01;r02;r03;r04;r05;r06;r07;r08;r09;r10;r11;r12;r13;r14;r15;r16;r17;r18;r19;r20;r21;r22;r23;r24;r25;r26;r27;r28;r29;r30;r31;r32;r33;r34;r35;r36;r37;r38;r39;r40;r41;r42;r43;r44;r45;r46;r47;r48;r49;r50;r51;r52;r53;r54;r55;r56;r57;r58;r59;r60;r61;r62;r63;r64;r65;r66;r67;r68;r69;r70;r71;r72;r73;r74;r75;r76;r77;r78;r79;r80;r81;r82;r83;r84;r85;r86;r87;r88;r89;r90;r91;r92;r93;r94;r95;r96;r97;r98;r99"); } /**************************************************************************************************************************************/ /**************************************************************************************************************************************/ static int unrar_callback (UINT msg, LONG UserData, LONG P1, LONG P2) { struct TVFSGlobs *globs = (struct TVFSGlobs *) UserData; if (globs == NULL) { log_warn ("unrar_callback: UserData == NULL, aborting."); return 0; } // >= 0 => Weiter, -1 => Stop switch (msg) { case UCM_CHANGEVOLUME: if (P2 == RAR_VOL_ASK) { log_info (" unrar_callback: UCM_CHANGEVOLUME message, RAR_VOL_ASK, P1='%s'", (char *) P1); if (globs->callback_ask_question) { static const char *choices[] = { "Abort", /* response = 0 */ "Ignore", /* response = 1 */ NULL }; char *message; char *s; int choice = -1; s = g_filename_display_name (P1 ? (char *) P1 : ""); message = g_strdup_printf ("Archive is incomplete, volume missing:\n\n%s", s ? s : (char *) P1); globs->callback_ask_question (message, &choices[0], &choice, 0, globs->callback_data); g_free (message); g_free (s); if (choice != 1) globs->volume_missing_abort = TRUE; return -1; } else { if (P1 && access ((char *) P1, R_OK) == 0) return 0; log_error ("unrar_callback: UCM_CHANGEVOLUME message, RAR_VOL_ASK: access test failed: %s", strerror (errno)); return -1; } return 0; } else if (P2 == RAR_VOL_NOTIFY) { log_info (" unrar_callback: UCM_CHANGEVOLUME message, RAR_VOL_NOTIFY, P1='%s'", (char *) P1); return 0; } else { log_error (" unrar_callback: UCM_CHANGEVOLUME message, P1='%s', unhandled P2/action %ld, aborting.", (char *) P1, P2); return -1; } break; case UCM_PROCESSDATA: log_info (" unrar_callback: UCM_PROCESSDATA message, P1/Addr = %p, P2/Count = %ld", (void *) P1, P2); if (globs->callback_progress) { gboolean res; globs->extract_done += P2; res = globs->callback_progress (globs->extract_done, NULL, globs->callback_data); if (! res) { globs->extract_cancelled = TRUE; log_warn ("unrar_callback: received cancellation result"); return -1; /* cancel the operation */ } } return 0; case UCM_NEEDPASSWORD: char *passwd = NULL; gboolean res = FALSE; log_notice (" unrar_callback: UCM_NEEDPASSWORD message, P1/buf = %p, P2/bufsize = %ld", (void *) P1, P2); globs->failed_passwd_callback = TRUE; if (globs->callback_ask_password) { res = globs->callback_ask_password ("The archive is encrypted and needs a password", NULL, NULL, NULL, VFS_ASK_PASSWORD_NEED_PASSWORD | VFS_ASK_PASSWORD_ARCHIVE_MODE, NULL, &passwd, NULL, NULL, NULL, globs->callback_data); if (res && passwd && strlen (passwd) > 0) { strncpy ((char *) P1, passwd, P2); g_free (globs->password); globs->password = g_strdup (passwd); } g_free (passwd); } if (res) return 0; globs->extract_cancelled = TRUE; return -1; case UCM_NEEDPASSWORDW: case UCM_CHANGEVOLUMEW: /* deliberately ignoring these due to bogus data in P1 */ return 0; default: log_warn ("unrar_callback: unhandled msg code %u, exiting!", msg); return -1; } return -1; } /* ----------------------------------------------------------------------------------------------------------- */ static time_t rar_time_to_unix (unsigned int FileTime) { struct tm t; t.tm_sec = (FileTime & 0x1f) * 2; t.tm_min = (FileTime >> 5) & 0x3f; t.tm_hour = (FileTime >> 11) & 0x1f; t.tm_mday = (FileTime >> 16) & 0x1f; t.tm_mon = ((FileTime >> 21) & 0x0f)-1; t.tm_year = (FileTime >> 25) + 80; t.tm_isdst = -1; // printf("date: %d-%.2d-%.2d %d:%.2d:%.2d \n", t.tm_year, t.tm_mon, t.tm_mday, t.tm_hour, t.tm_min, t.tm_sec); return (mktime(&t)); } /* TODO: find proper verbose explanation for each error. */ static void rar_error_to_gerror (int code, GError **error) { switch (code) { case ERAR_END_ARCHIVE: g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED, "Unexpected end of archive."); break; case ERAR_NO_MEMORY: g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Memory allocation failed."); break; case ERAR_BAD_DATA: g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Bad data."); break; case ERAR_BAD_ARCHIVE: g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Bad archive."); break; case ERAR_UNKNOWN_FORMAT: g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Unknown format."); break; case ERAR_EOPEN: g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Open error."); break; case ERAR_ECREATE: g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED, "Create error."); break; case ERAR_ECLOSE: g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CLOSED, "Close error."); break; case ERAR_EREAD: g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Read error."); break; case ERAR_EWRITE: g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Write error."); break; case ERAR_SMALL_BUF: g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "Small buffer error."); break; case ERAR_UNKNOWN: g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_UNKNOWN, "Unknown error."); break; case ERAR_MISSING_PASSWORD: g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED, "Missing password."); break; case ERAR_EREFERENCE: g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Bad reference."); break; case ERAR_BAD_PASSWORD: g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PERMISSION_DENIED, "Bad password."); break; default: g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "Operation failed."); break; } } /* ----------------------------------------------------------------------------------------------------------- */ gboolean VFSOpenArchive (struct TVFSGlobs *globs, const char *sName, GError **error) { HANDLE PASCAL handle; struct RAROpenArchiveDataEx archive_data = { 0, }; guint64 inode_no; gboolean Result; Result = TRUE; globs->files = filelist_tree_new (); globs->vfs_filelist = vfs_filelist_new (globs->files); globs->curr_dir = NULL; globs->archive_path = g_strdup (sName); globs->total_size = 0; globs->failed_passwd_callback = FALSE; globs->volume_missing_abort = FALSE; inode_no = 0; log_notice ("VFSOpenArchive: opening archive '%s':", globs->archive_path); archive_data.ArcName = globs->archive_path; archive_data.CmtBuf = NULL; archive_data.CmtBufSize = 0; archive_data.OpenMode = RAR_OM_LIST; handle = RAROpenArchiveEx (&archive_data); if (handle && archive_data.OpenResult == 0) { struct RARHeaderDataEx header = { 0, }; int PASCAL res = 0; RARSetCallback (handle, unrar_callback, (LONG) globs); if (globs->password) { log_notice ("VFSOpenArchive: Setting password..."); RARSetPassword (handle, globs->password); } while ((res = RARReadHeaderEx (handle, &header)) == 0) { struct TVFSItem *item; char *s; int PASCAL res2; item = g_malloc0 (sizeof (struct TVFSItem)); item->iSize = (guint64)((guint64)(header.UnpSizeHigh * 0x100000000) + (guint64)header.UnpSize); item->iPackedSize = (guint64)((guint64)(header.PackSizeHigh * 0x100000000) + (guint64)header.PackSize); inode_no++; item->inode_no = inode_no; globs->total_size += item->iSize; if ((header.Flags & RHDF_DIRECTORY) == RHDF_DIRECTORY) item->ItemType = vDirectory; else item->ItemType = vRegular; if ((header.Flags & RHDF_ENCRYPTED) == RHDF_ENCRYPTED) globs->need_password = TRUE; switch (header.HostOS) { case HOST_MSDOS: case HOST_OS2: case HOST_WIN32: if (header.FileAttr & 0x10) header.FileAttr = 0x41ff | header.FileAttr; else if (header.FileAttr & 1) header.FileAttr = 0x8124 | header.FileAttr; else header.FileAttr = 0x81b6 | header.FileAttr; break; } item->iMode = header.FileAttr; item->iUID = geteuid (); item->iGID = getegid (); item->m_time = rar_time_to_unix (header.FileTime); item->c_time = item->m_time; item->a_time = item->m_time; if (g_utf8_validate (header.FileName, -1, NULL)) s = g_strdup (header.FileName); else s = wide_to_utf8 (header.FileNameW); /* Add the item to the global list and continue with next file */ filelist_tree_add_item (globs->files, s, item, header.FileName, inode_no); g_free (s); res2 = RARProcessFile (handle, RAR_SKIP, NULL, NULL); if (res2) log_debug ("RARProcessFile result = %d", res2); } if (res != ERAR_END_ARCHIVE || globs->volume_missing_abort) { log_error ("VFSOpenArchive: RARReadHeader result = %d", res); rar_error_to_gerror (res, error); Result = FALSE; /* TODO: if (res == ERAR_MISSING_PASSWORD || (res == ERAR_BAD_DATA && globs->failed_passwd_callback)) Result = cVFS_BadPassword; */ } res = RARCloseArchive (handle); if (res) log_warn ("VFSOpenArchive: RARCloseArchive result = %d", res); } else { log_error ("VFSOpenArchive: error occured when opening archive: OpenResult = %d", archive_data.OpenResult); rar_error_to_gerror (archive_data.OpenResult, error); Result = FALSE; } log_info ("VFSOpenArchive: done."); if (globs->need_password) log_notice ("VFSOpenArchive: Password present."); filelist_tree_print (globs->files); return Result; } gboolean VFSClose (struct TVFSGlobs *globs, GError **error) { if (globs) { log_debug ("VFSClose: Freeing objects..."); vfs_filelist_free (globs->vfs_filelist); globs->vfs_filelist = NULL; filelist_tree_free (globs->files); globs->files = NULL; g_free (globs->archive_path); globs->archive_path = NULL; g_free (globs->curr_dir); globs->curr_dir = NULL; g_free (globs->password); globs->password = NULL; } return TRUE; } char * VFSGetPath (struct TVFSGlobs *globs) { return include_trailing_path_sep (globs->curr_dir); } void VFSGetFileSystemInfo (struct TVFSGlobs *globs, const char *APath, gint64 *FSSize, gint64 *FSFree, char **FSLabel) { if (FSSize) *FSSize = globs->total_size; if (FSFree) *FSFree = 0; if (FSLabel) *FSLabel = NULL; } /******************************************************************************************************/ gboolean VFSChangeDir (struct TVFSGlobs *globs, const char *NewPath, GError **error) { char *s; s = vfs_filelist_change_dir (globs->vfs_filelist, NewPath, error); if (s) { globs->curr_dir = s; return TRUE; } else return FALSE; } gboolean VFSGetPasswordRequired (struct TVFSGlobs *globs) { if (globs) return globs->need_password; return FALSE; } void VFSResetPassword (struct TVFSGlobs *globs) { if (globs) { g_free (globs->password); globs->password = NULL; } } /******************************************************************************************************/ struct TVFSItem * VFSListFirst (struct TVFSGlobs *globs, const char *sDir, gboolean FollowSymlinks, gboolean AddFullPath, GError **error) { log_debug ("VFSListFirst: Going to list all items in '%s'", sDir); return vfs_filelist_list_first (globs->vfs_filelist, sDir, FollowSymlinks, AddFullPath, error); } struct TVFSItem * VFSListNext (struct TVFSGlobs *globs, GError **error) { return vfs_filelist_list_next (globs->vfs_filelist, error); } gboolean VFSListClose (struct TVFSGlobs *globs, GError **error) { return vfs_filelist_list_close (globs->vfs_filelist, error); } /******************************************************************************************************/ struct TVFSItem * VFSFileInfo (struct TVFSGlobs *globs, const char *AFileName, gboolean FollowSymlinks, gboolean AddFullPath, GError **error) { log_debug ("VFSFileInfo: requested info for object '%s'", AFileName); return vfs_filelist_file_info (globs->vfs_filelist, AFileName, FollowSymlinks, AddFullPath, error); } /******************************************************************************************************/ /** Recursive tree size counting */ /************** ****************/ guint64 VFSGetDirSize (struct TVFSGlobs *globs, const char *APath) { if (! globs) return 0; return vfs_filelist_get_dir_size (globs->vfs_filelist, APath); } void VFSBreakGetDirSize (struct TVFSGlobs *globs) { log_debug ("VFSBreakGetDirSize: calling break"); if (globs) vfs_filelist_get_dir_size_break (globs->vfs_filelist); } /******************************************************************************************************/ /** Methods modifying the archive */ /************** ****************/ #if 0 TVFSResult VFSMkDir (struct TVFSGlobs *globs, const char *sDirName) { printf ("(WW) VFSMkDir: Not supported in UNRAR plugin.\n"); return cVFS_Not_Supported; } TVFSResult VFSRemove (struct TVFSGlobs *globs, const char *APath) { printf ("(WW) VFSRemove: Not supported in UNRAR plugin.\n"); return cVFS_Not_Supported; } TVFSResult VFSRename (struct TVFSGlobs *globs, const char *sSrcName, const char *sDstName) { printf ("(WW) VFSRename: Not supported in UNRAR plugin.\n"); return cVFS_Not_Supported; } TVFSResult VFSMakeSymLink (struct TVFSGlobs *globs, const char *NewFileName, const char *PointTo) { printf ("(WW) VFSMakeSymLink: Not supported in UNRAR plugin.\n"); return cVFS_Not_Supported; } TVFSResult VFSChmod (struct TVFSGlobs *globs, const char *FileName, guint32 Mode) { printf ("(WW) VFSChmod: Not supported in UNRAR plugin.\n"); return cVFS_Not_Supported; } TVFSResult VFSChown (struct TVFSGlobs *globs, const char *FileName, guint32 UID, guint32 GID) { printf ("(WW) VFSChown: Not supported in UNRAR plugin.\n"); return cVFS_Not_Supported; } TVFSResult VFSChangeTimes (struct TVFSGlobs *globs, const char *APath, guint32 mtime, guint32 atime) { printf ("(WW) VFSChangeTimes: Not supported in UNRAR plugin.\n"); return cVFS_Not_Supported; } #endif //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////// #if 0 TVFSFileDes VFSOpenFile(struct TVFSGlobs *globs, const char *APath, int Mode, int *Error) { *Error = cVFS_Not_Supported; return (TVFSFileDes)0; } TVFSResult VFSCloseFile(struct TVFSGlobs *globs, TVFSFileDes FileDescriptor) { return cVFS_Not_Supported; } u_int64_t VFSFileSeek(struct TVFSGlobs *globs, TVFSFileDes FileDescriptor, u_int64_t AbsoluteOffset, int *Error) { *Error = cVFS_Not_Supported; return 0; } int VFSReadFile(struct TVFSGlobs *globs, TVFSFileDes FileDescriptor, void *Buffer, int ABlockSize, int *Error) { *Error = cVFS_Not_Supported; return 0; } int VFSWriteFile(struct TVFSGlobs *globs, TVFSFileDes FileDescriptor, void *Buffer, int BytesCount, int *Error) { *Error = cVFS_Not_Supported; return 0; } #endif void VFSSetBlockSize (struct TVFSGlobs *globs, guint32 Value) { if (globs) globs->block_size = Value; } gboolean VFSIsOnSameFS (struct TVFSGlobs *globs, const char *Path1, const char *Path2, gboolean FollowSymlinks) { log_debug ("VFSIsOnSameFS: Not supported in UNRAR plugin."); return TRUE; } gboolean VFSTwoSameFiles (struct TVFSGlobs *globs, const char *Path1, const char *Path2, gboolean FollowSymlinks) { log_debug ("VFSTwoSameFiles: Not supported in RAR archives, comparing by paths."); return compare_two_same_files (Path1, Path2); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////// gboolean VFSStartCopyOperation (struct TVFSGlobs *globs, GError **error) { struct RAROpenArchiveDataEx archive_data = { 0, }; HANDLE PASCAL handle; gboolean Result = FALSE; if (globs->op_handle != NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_ALREADY_MOUNTED, "globs->op_handle != NULL"); return FALSE; } log_debug ("VFSStartCopyOperation: opening archive '%s'", globs->archive_path); archive_data.ArcName = globs->archive_path; archive_data.OpenMode = RAR_OM_EXTRACT; handle = RAROpenArchiveEx (&archive_data); if (handle && archive_data.OpenResult == 0) { RARSetCallback (handle, unrar_callback, (LONG) globs); /* set password */ if (globs->password) { log_info ("VFSStartCopyOperation: Setting password..."); RARSetPassword (handle, globs->password); } globs->op_handle = handle; Result = TRUE; } else { log_error ("VFSStartCopyOperation: error occured when opening archive: OpenResult = %d", archive_data.OpenResult); rar_error_to_gerror (archive_data.OpenResult, error); Result = FALSE; } return Result; } gboolean VFSStopCopyOperation (struct TVFSGlobs *globs, GError **error) { int res; gboolean Result; if (globs->op_handle == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_ALREADY_MOUNTED, "globs->op_handle == NULL"); return FALSE; } log_debug ("VFSStopCopyOperation: closing archive."); Result = TRUE; res = RARCloseArchive (globs->op_handle); if (res) { log_warn ("VFSCopyToLocal: RARCloseArchive result = %d", res); rar_error_to_gerror (res, error); Result = FALSE; } globs->op_handle = NULL; return Result; } gboolean VFSCopyToLocal (struct TVFSGlobs *globs, const char *sSrcName, const char *sDstName, gboolean Append, GError **error) { struct PathTree *node; struct RARHeaderDataEx header = { 0, }; gboolean found = FALSE; gboolean Result = TRUE; int res = 0; char *src; if (globs->op_handle == NULL) { g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_FAILED, "globs->op_handle == NULL"); return FALSE; } if (sSrcName == NULL || sDstName == NULL || strlen (sSrcName) < 1 || strlen (sDstName) < 1) { log_error ("VFSCopyToLocal: The value of 'sSrcName' or 'sDstName' is NULL or empty"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_INVALID_ARGUMENT, "The value of 'sSrcName' or 'sDstName' is NULL or empty."); return FALSE; } log_notice ("VFSCopyToLocal: copying file '%s' out to '%s'", sSrcName, sDstName); node = filelist_tree_find_node_by_path (globs->files, sSrcName); if (! node) { log_error ("VFSCopyToLocal: cannot find file '%s'", sSrcName); g_set_error (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "cannot find file '%s'", sSrcName); return FALSE; } src = node->original_pathstr; if (! src) { log_warn ("VFSCopyToLocal: cannot determine original filename"); src = (char *) sSrcName; } log_debug ("VFSCopyToLocal: new src path: '%s'", src); while ((res = RARReadHeaderEx (globs->op_handle, &header)) == 0) { int res2; log_debug ("VFSCopyToLocal: found '%s'", header.FileName); if (g_strcmp0 (src, header.FileName) == 0) { globs->extract_done = 0; globs->extract_cancelled = FALSE; found = TRUE; res2 = RARProcessFile (globs->op_handle, RAR_EXTRACT, NULL, (char *)sDstName); if (globs->extract_cancelled) { log_debug ("VFSCopyToLocal: cancelled!"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Operation has been cancelled."); Result = FALSE; } else if (res2) { log_error ("VFSCopyToLocal: RARProcessFile result = %d", res2); rar_error_to_gerror (res2, error); Result = FALSE; } break; } else { res2 = RARProcessFile (globs->op_handle, RAR_SKIP, NULL, NULL); if (res2) { log_error ("VFSCopyToLocal: RARProcessFile result = %d", res2); rar_error_to_gerror (res2, error); Result = FALSE; } } } /* TODO: reopen archive on 'Unexpected end of archive' */ if (res != ERAR_END_ARCHIVE && res) { if (globs->extract_cancelled) { log_debug ("VFSCopyToLocal: cancelled!"); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_CANCELLED, "Operation has been cancelled."); Result = FALSE; } else { log_error ("VFSCopyToLocal: RARReadHeader result = %d", res); rar_error_to_gerror (res, error); } } if (! found && Result) { log_error ("VFSCopyToLocal: file not found in archive."); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_FOUND, "File not found in archive."); Result = FALSE; } log_debug ("VFSCopyToLocal: finished."); return Result; } gboolean VFSCopyFromLocal (struct TVFSGlobs *globs, const char *sSrcName, const char *sDstName, gboolean Append, GError **error) { log_warn ("VFSCopyFromLocal: Not supported in UNRAR plugin."); g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_NOT_SUPPORTED, "Not supported in UNRAR plugin."); return FALSE; } /********** * TODO: * * DONE - UTF-8, FName/FDisplayName and absolute/relative paths revision needed! * - find a reliable way to catch bad password errors and free the cached invalid password * - no error reporting when archive is corrupted -- hopefully fixed by ask_question callback * - archive testing (needs new VFS API) * - when bad password was entered during archive open, loop until succeeded or cancelled * - when copy failed due to wrong password, reset it and do the callback (though we can't do that in loop) * - reopen archive on 'Unexpected end of archive' * ***/