/* UNRAR plugin for Tux Commander * version 0.4.1, designed for unrar v3.8.2 * Copyright (C) 2007-2009 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 "tuxcmd-vfs.h" #include "vfsutils.h" #include "strutils.h" #include "filelist.h" #include "filelist-vfs-intf.h" #define _UNIX #include "unrar/version.hpp" #include "unrar/dll.hpp" /* 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.4.1" #define BUILD_DATE "2009-11-29" #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_file_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; globs = g_malloc0 (sizeof (struct TVFSGlobs)); globs->block_size = DEFAULT_BLOCK_SIZE; globs->need_password = FALSE; globs->failed_passwd_callback = FALSE; globs->volume_missing_abort = FALSE; globs->password = NULL; globs->op_handle = NULL; globs->callback_data = NULL; globs->callback_ask_question = NULL; globs->callback_ask_password = NULL; globs->callback_progress = NULL; globs->log_func = log_func; if (globs->log_func) globs->log_func("unrar plugin: VFSInit"); 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) { if (globs->log_func) globs->log_func("unrar plugin: VFSDestroy"); g_free (globs); } int VFSVersion () { return cVFSVersion; } struct TVFSInfo * VFSGetInfo () { struct TVFSInfo *module_info; 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", VERSION, BUILD_DATE, RARVER_MAJOR, RARVER_MINOR, RARVER_YEAR, RARVER_MONTH, RARVER_DAY); module_info->Copyright = g_strdup ("Plugin Copyright (C) 2007-2009 Tomáš Bžatek\nUNRAR sources Copyright (C) 2002-2008 Alexander Roshal"); return module_info; } 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; // fprintf(stderr, "(II) unrar_callback called: msg = %d, UserData = %lx, sizeof(UserData) = %ld, P1 = %ld, P2 = %ld\n", msg, UserData, sizeof(UserData), P1, P2); if (! UserData) { fprintf (stderr, "(WW) unrar_callback: UserData == NULL, exiting!\n"); return 0; } globs = (struct TVFSGlobs *) UserData; // >= 0 => Weiter, -1 => Stop switch (msg) { case UCM_CHANGEVOLUME: { if (P2 == RAR_VOL_ASK) { fprintf (stderr, " (II) unrar_callback: UCM_CHANGEVOLUME message, RAR_VOL_ASK, P1 = %ld, (char*)P1 = '%s' \n", P1, (char*)P1); if (P1) { if (globs->callback_ask_question) { static const char *choices[] = { "Abort", /* response = 0 */ "Ignore", /* response = 1 */ NULL }; char *message, *s; int choice = -1; s = g_filename_display_name((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 (access((char*)P1, R_OK) != 0) { fprintf (stderr, "(EE) unrar_callback: UCM_CHANGEVOLUME message, RAR_VOL_ASK: access test failed - missing part? Error = %s \n", strerror (errno)); return -1; } } } } else if (P2 == RAR_VOL_NOTIFY) { fprintf (stderr, " (II) unrar_callback: UCM_CHANGEVOLUME message, RAR_VOL_NOTIFY, P1 = %ld, (char*)P1 = '%s' \n", P1, (char*)P1); } break; } case UCM_PROCESSDATA: { fprintf (stderr, " (II) unrar_callback: UCM_PROCESSDATA message, P1 = %ld, P2 = %ld \n", P1, P2); // printf(" (II) unrar_callback: globs = 0x%lX, UserData = 0x%lX \n", (unsigned long int)globs, UserData); if (globs && globs->callback_progress) { // printf(" (II) unrar_callback: globs->extract_callback_func = 0x%lX, globs->extract_callback_data = 0x%lX \n", (unsigned long int)globs->extract_callback_func, (unsigned long int)globs->extract_callback_data); // long int res = globs->extract_callback_func((u_int64_t)P1, (u_int64_t)((u_int64_t)P1 + (u_int64_t)P2), globs->extract_callback_data); globs->extract_done += P2; gboolean res = globs->callback_progress(globs->extract_done, globs->extract_file_size, globs->callback_data); // fprintf(stderr, " (II) unrar_callback: res = %d \n", res); if (! res ) { globs->extract_cancelled = TRUE; fprintf (stderr, "(WW) unrar_callback: received cancellation result\n"); return -1; // Cancel operation } } break; } case UCM_NEEDPASSWORD: { fprintf (stderr, " (II) unrar_callback: UCM_NEEDPASSWORD message, P1 = %ld, P2 = %ld, (char*)P1 = '%s', maxlen = %ld \n", P1, P2, (char*)P1, P2); char *passwd = NULL; gboolean res = FALSE; globs->failed_passwd_callback = TRUE; if (globs->callback_ask_password) { res = globs->callback_ask_password ("The archive is encrypted and requires 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) { fprintf(stderr, " (II) unrar_callback: setting password to '%s'\n", passwd); if (strlen (passwd) > 0) { strncpy ((char *)P1, passwd, P2); g_free (globs->password); globs->password = g_strdup (passwd); } g_free (passwd); } } if (res) return 0; else { globs->extract_cancelled = TRUE; return -1; } } } return 0; } static int PASCAL unrar_changevol_proc (char *ArcName, int Mode) { fprintf (stderr, "(II) unrar_changevol_proc called: ArcName = '%s', Mode = %d\n", ArcName, Mode); return 0; } static int PASCAL unrar_process_data_proc (unsigned char *Addr, int Size) { fprintf (stderr, "(II) unrar_process_data_proc called: Addr = '%s', Size = %d\n", Addr, Size); return 0; } 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)); } /* ----------------------------------------------------------------------------------------------------------- */ TVFSResult VFSOpenArchive (struct TVFSGlobs *globs, const char *sName) { TVFSResult Result; HANDLE PASCAL handle; struct RAROpenArchiveDataEx *archive_data; guint64 inode_no; Result = cVFS_OK; 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; fprintf (stderr, "(--) VFSOpenArchive: trying to open archive '%s'...\n", globs->archive_path); /* WARNING: do not use g_malloc0() here, leads to memory corruption */ archive_data = malloc (sizeof (struct RAROpenArchiveDataEx)); memset (archive_data, 0, sizeof (struct RAROpenArchiveDataEx)); archive_data->ArcName = globs->archive_path; archive_data->CmtBuf = NULL; archive_data->CmtBufSize = 0; archive_data->OpenMode = RAR_OM_LIST; handle = RAROpenArchiveEx (archive_data); // printf(" handle = %lu \n", (unsigned long int)handle); // printf(" archive_data->OpenResult = %d \n", archive_data->OpenResult); // printf(" archive_data->CmtState = %d \n", archive_data->CmtState); // printf(" archive_data->CmtSize = %d \n", archive_data->CmtSize); if (handle && ! archive_data->OpenResult) { // Set callbacks RARSetCallback (handle, unrar_callback, (LONG)globs); // RARSetChangeVolProc(handle, unrar_changevol_proc); // RARSetProcessDataProc(handle, unrar_process_data_proc); if (globs->password) { fprintf (stderr, "(II) VFSOpenArchive: Setting password... \n"); RARSetPassword (handle, globs->password); } struct RARHeaderDataEx *header; /* WARNING: do not use g_malloc0() here, leads to memory corruption */ header = malloc (sizeof (struct RARHeaderDataEx)); memset (header, 0, sizeof (struct RARHeaderDataEx)); int PASCAL res = 0; while ((res = RARReadHeaderEx(handle, header)) == 0) { // printf(" header->FileName = '%s', Flags = 0x%x\n", header->FileName, header->Flags); inode_no++; /* Create a TVFSItem entry and fill all info */ struct TVFSItem *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); item->inode_no = inode_no; globs->total_size += item->iSize; if ((header->Flags & 0x00e0 /* LHD_WINDOWMASK */ ) == 0x00e0 /* LHD_DIRECTORY */) item->ItemType = vDirectory; else item->ItemType = vRegular; if ((header->Flags & 0x0004) == 0x0004) 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; // g_print (" valid = %d\n", g_utf8_validate (header->FileName, -1, NULL)); char *s; if (g_utf8_validate (header->FileName, -1, NULL)) s = g_strdup (header->FileName); else s = wide_to_utf8 (header->FileNameW); // g_print (" ansi = '%s'\n wide = '%ls'\n utf8 = '%s'\n", header->FileName, header->FileNameW, s); // Add 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); int PASCAL res2 = RARProcessFile (handle, RAR_SKIP, NULL, NULL); if (res2) printf ("RARProcessFile result = %d\n", res2); } // printf("\nRARReadHeader result = %d\n", res); if (res != ERAR_END_ARCHIVE || globs->volume_missing_abort) { fprintf (stderr, "(EE) VFSOpenArchive: RARReadHeader result = %d\n", res); Result = cVFS_Failed; if (res == ERAR_MISSING_PASSWORD || (res == ERAR_BAD_DATA && globs->failed_passwd_callback)) Result = cVFS_BadPassword; } free (header); res = RARCloseArchive(handle); if (res) fprintf (stderr, "(EE) VFSOpenArchive: RARCloseArchive result = %d\n", res); } else { fprintf (stderr, "(EE) VFSOpenArchive: error occured when opening archive: OpenResult = %d\n", archive_data->OpenResult); Result = cVFS_Failed; } free (archive_data); fprintf (stderr, "(II) VFSOpenArchive: done. \n"); if (globs->need_password) printf ("Password present.\n"); printf ("\n\nList of items:\n"); filelist_tree_print (globs->files); return Result; } TVFSResult VFSClose (struct TVFSGlobs *globs) { if (globs) { fprintf (stderr, "(II) VFSClose: Freeing objects...\n"); if (globs->vfs_filelist) vfs_filelist_free(globs->vfs_filelist); if (globs->files) filelist_tree_free(globs->files); g_free (globs->archive_path); g_free (globs->curr_dir); g_free (globs->password); } return cVFS_OK; } char * VFSGetPath (struct TVFSGlobs *globs) { return include_trailing_path_sep (globs->curr_dir); } TVFSResult 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; return cVFS_OK; } /******************************************************************************************************/ TVFSResult VFSChangeDir (struct TVFSGlobs *globs, const char *NewPath) { if (NewPath == NULL) { printf ("(EE) VFSChangeDir: NewPath is NULL!\n"); return cVFS_Failed; } globs->curr_dir = vfs_filelist_change_dir (globs->vfs_filelist, NewPath); if (globs->curr_dir) return cVFS_OK; else return cVFS_Failed; } 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; } } /******************************************************************************************************/ TVFSResult VFSListFirst (struct TVFSGlobs *globs, const char *sDir, struct TVFSItem *Item, gboolean FollowSymlinks, gboolean AddFullPath) { if (sDir == NULL) { printf ("(EE) VFSListFirst: sDir is NULL!\n"); return cVFS_Failed; } printf ("(--) VFSListFirst: Going to list all items in '%s'\n", sDir); return vfs_filelist_list_first (globs->vfs_filelist, sDir, Item, FollowSymlinks, AddFullPath); } TVFSResult VFSListNext (struct TVFSGlobs *globs, struct TVFSItem *Item) { return vfs_filelist_list_next (globs->vfs_filelist, Item); } TVFSResult VFSListClose (struct TVFSGlobs *globs) { return vfs_filelist_list_close (globs->vfs_filelist); } /******************************************************************************************************/ TVFSResult VFSFileInfo (struct TVFSGlobs *globs, const char *AFileName, struct TVFSItem *Item, gboolean FollowSymlinks, gboolean AddFullPath) { printf ("(--) VFSFileInfo: requested info for object '%s'\n", AFileName); if (! globs) return cVFS_Failed; return vfs_filelist_file_info (globs->vfs_filelist, AFileName, Item, FollowSymlinks, AddFullPath); } /******************************************************************************************************/ /** 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) { printf ("(WW) VFSBreakGetDirSize: calling break\n"); if (globs) vfs_filelist_get_dir_size_break (globs->vfs_filelist); } /******************************************************************************************************/ /** Methods modifying the archive */ /************** ****************/ 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; } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////// 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; } 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) { printf ("(WW) VFSIsOnSameFS: Not supported in UNRAR plugin.\n"); return TRUE; } gboolean VFSTwoSameFiles (struct TVFSGlobs *globs, const char *Path1, const char *Path2, gboolean FollowSymlinks) { printf ("(WW) VFSTwoSameFiles: Not supported in RAR archives, comparing by paths.\n"); return compare_two_same_files (Path1, Path2); } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// //////////////////////// TVFSResult VFSStartCopyOperation (struct TVFSGlobs *globs) { TVFSResult Result; struct RAROpenArchiveDataEx *archive_data; HANDLE PASCAL handle; g_return_val_if_fail (globs != NULL, cVFS_Failed); g_return_val_if_fail (globs->op_handle == NULL, cVFS_Failed); printf ("(II) VFSStartCopyOperation: opening archive '%s'\n", globs->archive_path); Result = cVFS_Failed; /* WARNING: do not use g_malloc0() here, leads to memory corruption */ archive_data = malloc (sizeof (struct RAROpenArchiveDataEx)); memset (archive_data, 0, sizeof (struct RAROpenArchiveDataEx)); archive_data->ArcName = globs->archive_path; archive_data->OpenMode = RAR_OM_EXTRACT; handle = RAROpenArchiveEx (archive_data); // printf(" handle = %lu \n", (unsigned long int)handle); // printf(" archive_data->OpenResult = %d \n", archive_data->OpenResult); // printf(" archive_data->CmtState = %d \n", archive_data->CmtState); // printf(" archive_data->CmtSize = %d \n", archive_data->CmtSize); if (handle && ! archive_data->OpenResult) { /* set callbacks */ // printf(" setting callback: globs = 0x%lX, (unsigned long int)globs = 0x%lX, (LONG)globs = 0x%lX\n", globs, (unsigned long int)globs, (LONG)globs); RARSetCallback (handle, unrar_callback, (LONG) globs); // RARSetChangeVolProc(handle, unrar_changevol_proc); // RARSetProcessDataProc(handle, unrar_process_data_proc); /* set password */ if (globs->password) { printf ("(II) VFSStartCopyOperation: Setting password... \n"); RARSetPassword (handle, globs->password); } globs->op_handle = handle; Result = cVFS_OK; } else { fprintf (stderr, "(EE) VFSStartCopyOperation: error occured when opening archive: OpenResult = %d\n", archive_data->OpenResult); Result = cVFS_ReadErr; } free (archive_data); return Result; } TVFSResult VFSStopCopyOperation (struct TVFSGlobs *globs) { TVFSResult Result; int res; g_return_val_if_fail (globs != NULL, cVFS_Failed); g_return_val_if_fail (globs->op_handle != NULL, cVFS_Failed); printf ("(II) VFSStopCopyOperation: closing archive.\n"); Result = cVFS_OK; res = RARCloseArchive (globs->op_handle); if (res) { fprintf (stderr, "(EE) VFSCopyToLocal: RARCloseArchive result = %d\n", res); Result = cVFS_ReadErr; } globs->op_handle = NULL; return Result; } TVFSResult VFSCopyToLocal (struct TVFSGlobs *globs, const char *sSrcName, const char *sDstName, gboolean Append) { struct PathTree *node; TVFSResult Result; gboolean found; char *src; g_return_val_if_fail (globs->op_handle != NULL, cVFS_ReadErr); if (sSrcName == NULL || sDstName == NULL || strlen (sSrcName) < 1 || strlen (sDstName) < 1) { printf ("(EE) VFSCopyToLocal: The value of 'sSrcName' or 'sDstName' is NULL or empty\n"); return cVFS_Failed; } printf ("(II) VFSCopyToLocal: copying file '%s' out to '%s'\n", sSrcName, sDstName); node = filelist_tree_find_node_by_path (globs->files, sSrcName); if (! node) { fprintf (stderr, "(EE) VFSCopyToLocal: cannot find file '%s'\n", sSrcName); return cVFS_ReadErr; } src = node->original_pathstr; if (! src) { fprintf (stderr, "(WW) VFSCopyToLocal: cannot determine original filename\n"); src = (char *) sSrcName; } printf ("(II) VFSCopyToLocal: new src path: '%s'\n", src); struct RARHeaderDataEx *header; /* WARNING: do not use g_malloc0() here, leads to memory corruption */ header = malloc (sizeof (struct RARHeaderDataEx)); memset (header, 0, sizeof (struct RARHeaderDataEx)); int res = 0; found = FALSE; Result = cVFS_OK; while ((res = RARReadHeaderEx(globs->op_handle, header)) == 0) { g_print ("VFSCopyToLocal: found '%s'\n", header->FileName); if (g_strcmp0 (src, header->FileName) == 0) { // fprintf(stderr, "(II) VFSCopyToLocal: extract_file_path(sDstName) = '%s', extract_file_name(sDstName) = '%s' \n", extract_file_path(sDstName), extract_file_name(sDstName)); globs->extract_done = 0; globs->extract_file_size = (guint64)((guint64)(header->UnpSizeHigh * 0x100000000) + (guint64)header->UnpSize); globs->extract_cancelled = FALSE; found = TRUE; int res2 = RARProcessFile (globs->op_handle, RAR_EXTRACT, NULL, (char *)sDstName); if (globs->extract_cancelled) { fprintf (stderr, "(WW) VFSCopyToLocal: cancelled !\n"); Result = cVFS_Cancelled; } else if (res2) { fprintf (stderr, "(EE) VFSCopyToLocal: RARProcessFile result = %d\n", res2); Result = cVFS_ReadErr; } break; } else { int res2 = RARProcessFile (globs->op_handle, RAR_SKIP, NULL, NULL); if (res2) { fprintf (stderr, "(EE) VFSCopyToLocal: RARProcessFile result = %d\n", res2); Result = cVFS_ReadErr; } } } /* TODO: reopen archive on 'Unexpected end of archive' */ if (res != ERAR_END_ARCHIVE && res) { if (globs->extract_cancelled) { fprintf (stderr, "(WW) VFSCopyToLocal: cancelled !\n"); Result = cVFS_Cancelled; } else { fprintf (stderr, "(EE) VFSCopyToLocal: RARReadHeader result = %d\n", res); switch (res) { case ERAR_NO_MEMORY: case ERAR_SMALL_BUF: Result = cVFS_mallocFailed; break; case ERAR_BAD_DATA: case ERAR_BAD_ARCHIVE: case ERAR_UNKNOWN_FORMAT: case ERAR_EOPEN: case ERAR_ECLOSE: case ERAR_EREAD: Result = cVFS_ReadErr; break; case ERAR_ECREATE: case ERAR_EWRITE: Result = cVFS_WriteErr; break; case ERAR_MISSING_PASSWORD: Result = cVFS_BadPassword; g_free (globs->password); globs->password = NULL; break; case ERAR_UNKNOWN: default: Result = cVFS_WriteErr; break; } } } free (header); if (! found && Result == cVFS_OK) { fprintf (stderr, "(EE) VFSCopyToLocal: file not found in archive.\n"); Result = cVFS_ReadErr; } fprintf (stderr, "(II) VFSCopyToLocal: finished. \n"); return Result; } TVFSResult VFSCopyFromLocal (struct TVFSGlobs *globs, const char *sSrcName, const char *sDstName, gboolean Append) { printf ("(WW) VFSCopyFromLocal: Not supported in UNRAR plugin.\n"); return cVFS_Not_Supported; } /********** * 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' * ***/