| #include "rar.hpp" |
| |
| static int RarErrorToDll(RAR_EXIT ErrCode); |
| |
| struct DataSet |
| { |
| CommandData Cmd; |
| Archive Arc; |
| CmdExtract Extract; |
| int OpenMode; |
| int HeaderSize; |
| |
| DataSet():Arc(&Cmd),Extract(&Cmd) {}; |
| }; |
| |
| |
| HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *r) |
| { |
| RAROpenArchiveDataEx rx; |
| memset(&rx,0,sizeof(rx)); |
| rx.ArcName=r->ArcName; |
| rx.OpenMode=r->OpenMode; |
| rx.CmtBuf=r->CmtBuf; |
| rx.CmtBufSize=r->CmtBufSize; |
| HANDLE hArc=RAROpenArchiveEx(&rx); |
| r->OpenResult=rx.OpenResult; |
| r->CmtSize=rx.CmtSize; |
| r->CmtState=rx.CmtState; |
| return hArc; |
| } |
| |
| |
| HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r) |
| { |
| DataSet *Data=NULL; |
| try |
| { |
| r->OpenResult=0; |
| Data=new DataSet; |
| Data->Cmd.DllError=0; |
| Data->OpenMode=r->OpenMode; |
| Data->Cmd.FileArgs.AddString(L"*"); |
| |
| char AnsiArcName[NM]; |
| *AnsiArcName=0; |
| if (r->ArcName!=NULL) |
| { |
| strncpyz(AnsiArcName,r->ArcName,ASIZE(AnsiArcName)); |
| #ifdef _WIN_ALL |
| if (!AreFileApisANSI()) |
| { |
| OemToCharBuffA(r->ArcName,AnsiArcName,ASIZE(AnsiArcName)); |
| AnsiArcName[ASIZE(AnsiArcName)-1]=0; |
| } |
| #endif |
| } |
| |
| wchar ArcName[NM]; |
| GetWideName(AnsiArcName,r->ArcNameW,ArcName,ASIZE(ArcName)); |
| |
| Data->Cmd.AddArcName(ArcName); |
| Data->Cmd.Overwrite=OVERWRITE_ALL; |
| Data->Cmd.VersionControl=1; |
| |
| Data->Cmd.Callback=r->Callback; |
| Data->Cmd.UserData=r->UserData; |
| |
| // Open shared mode is added by request of dll users, who need to |
| // browse and unpack archives while downloading. |
| Data->Cmd.OpenShared = true; |
| if (!Data->Arc.Open(ArcName,FMF_OPENSHARED)) |
| { |
| r->OpenResult=ERAR_EOPEN; |
| delete Data; |
| return NULL; |
| } |
| if (!Data->Arc.IsArchive(true)) |
| { |
| if (Data->Cmd.DllError!=0) |
| r->OpenResult=Data->Cmd.DllError; |
| else |
| { |
| RAR_EXIT ErrCode=ErrHandler.GetErrorCode(); |
| if (ErrCode!=RARX_SUCCESS && ErrCode!=RARX_WARNING) |
| r->OpenResult=RarErrorToDll(ErrCode); |
| else |
| r->OpenResult=ERAR_BAD_ARCHIVE; |
| } |
| delete Data; |
| return NULL; |
| } |
| r->Flags=0; |
| |
| if (Data->Arc.Volume) |
| r->Flags|=0x01; |
| if (Data->Arc.Locked) |
| r->Flags|=0x04; |
| if (Data->Arc.Solid) |
| r->Flags|=0x08; |
| if (Data->Arc.NewNumbering) |
| r->Flags|=0x10; |
| if (Data->Arc.Signed) |
| r->Flags|=0x20; |
| if (Data->Arc.Protected) |
| r->Flags|=0x40; |
| if (Data->Arc.Encrypted) |
| r->Flags|=0x80; |
| if (Data->Arc.FirstVolume) |
| r->Flags|=0x100; |
| |
| Array<wchar> CmtDataW; |
| if (r->CmtBufSize!=0 && Data->Arc.GetComment(&CmtDataW)) |
| { |
| Array<char> CmtData(CmtDataW.Size()*4+1); |
| memset(&CmtData[0],0,CmtData.Size()); |
| WideToChar(&CmtDataW[0],&CmtData[0],CmtData.Size()-1); |
| size_t Size=strlen(&CmtData[0])+1; |
| |
| r->Flags|=2; |
| r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1; |
| r->CmtSize=(uint)Min(Size,r->CmtBufSize); |
| memcpy(r->CmtBuf,&CmtData[0],r->CmtSize-1); |
| if (Size<=r->CmtBufSize) |
| r->CmtBuf[r->CmtSize-1]=0; |
| } |
| else |
| r->CmtState=r->CmtSize=0; |
| Data->Extract.ExtractArchiveInit(Data->Arc); |
| return (HANDLE)Data; |
| } |
| catch (RAR_EXIT ErrCode) |
| { |
| if (Data!=NULL && Data->Cmd.DllError!=0) |
| r->OpenResult=Data->Cmd.DllError; |
| else |
| r->OpenResult=RarErrorToDll(ErrCode); |
| if (Data != NULL) |
| delete Data; |
| return NULL; |
| } |
| catch (std::bad_alloc&) // Catch 'new' exception. |
| { |
| r->OpenResult=ERAR_NO_MEMORY; |
| if (Data != NULL) |
| delete Data; |
| } |
| return NULL; // To make compilers happy. |
| } |
| |
| |
| int PASCAL RARCloseArchive(HANDLE hArcData) |
| { |
| DataSet *Data=(DataSet *)hArcData; |
| try |
| { |
| bool Success=Data==NULL ? false:Data->Arc.Close(); |
| delete Data; |
| return Success ? ERAR_SUCCESS : ERAR_ECLOSE; |
| } |
| catch (RAR_EXIT ErrCode) |
| { |
| return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode); |
| } |
| } |
| |
| |
| int PASCAL RARReadHeader(HANDLE hArcData,struct RARHeaderData *D) |
| { |
| struct RARHeaderDataEx X; |
| memset(&X,0,sizeof(X)); |
| |
| int Code=RARReadHeaderEx(hArcData,&X); |
| |
| strncpyz(D->ArcName,X.ArcName,ASIZE(D->ArcName)); |
| strncpyz(D->FileName,X.FileName,ASIZE(D->FileName)); |
| D->Flags=X.Flags; |
| D->PackSize=X.PackSize; |
| D->UnpSize=X.UnpSize; |
| D->HostOS=X.HostOS; |
| D->FileCRC=X.FileCRC; |
| D->FileTime=X.FileTime; |
| D->UnpVer=X.UnpVer; |
| D->Method=X.Method; |
| D->FileAttr=X.FileAttr; |
| D->CmtSize=0; |
| D->CmtState=0; |
| |
| return Code; |
| } |
| |
| |
| int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *D) |
| { |
| DataSet *Data=(DataSet *)hArcData; |
| try |
| { |
| if ((Data->HeaderSize=(int)Data->Arc.SearchBlock(HEAD_FILE))<=0) |
| { |
| if (Data->Arc.Volume && Data->Arc.GetHeaderType()==HEAD_ENDARC && |
| Data->Arc.EndArcHead.NextVolume) |
| if (MergeArchive(Data->Arc,NULL,false,'L')) |
| { |
| Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET); |
| return RARReadHeaderEx(hArcData,D); |
| } |
| else |
| return ERAR_EOPEN; |
| |
| if (Data->Arc.BrokenHeader) |
| return ERAR_BAD_DATA; |
| |
| // Might be necessary if RARSetPassword is still called instead of |
| // open callback for RAR5 archives and if password is invalid. |
| if (Data->Arc.FailedHeaderDecryption) |
| return ERAR_BAD_PASSWORD; |
| |
| return ERAR_END_ARCHIVE; |
| } |
| FileHeader *hd=&Data->Arc.FileHead; |
| if (Data->OpenMode==RAR_OM_LIST && hd->SplitBefore) |
| { |
| int Code=RARProcessFile(hArcData,RAR_SKIP,NULL,NULL); |
| if (Code==0) |
| return RARReadHeaderEx(hArcData,D); |
| else |
| return Code; |
| } |
| wcsncpy(D->ArcNameW,Data->Arc.FileName,ASIZE(D->ArcNameW)); |
| WideToChar(D->ArcNameW,D->ArcName,ASIZE(D->ArcName)); |
| |
| wcsncpy(D->FileNameW,hd->FileName,ASIZE(D->FileNameW)); |
| WideToChar(D->FileNameW,D->FileName,ASIZE(D->FileName)); |
| #ifdef _WIN_ALL |
| CharToOemA(D->FileName,D->FileName); |
| #endif |
| |
| D->Flags=0; |
| if (hd->SplitBefore) |
| D->Flags|=RHDF_SPLITBEFORE; |
| if (hd->SplitAfter) |
| D->Flags|=RHDF_SPLITAFTER; |
| if (hd->Encrypted) |
| D->Flags|=RHDF_ENCRYPTED; |
| if (hd->Solid) |
| D->Flags|=RHDF_SOLID; |
| if (hd->Dir) |
| D->Flags|=RHDF_DIRECTORY; |
| |
| D->PackSize=uint(hd->PackSize & 0xffffffff); |
| D->PackSizeHigh=uint(hd->PackSize>>32); |
| D->UnpSize=uint(hd->UnpSize & 0xffffffff); |
| D->UnpSizeHigh=uint(hd->UnpSize>>32); |
| D->HostOS=hd->HSType==HSYS_WINDOWS ? HOST_WIN32:HOST_UNIX; |
| if (Data->Arc.Format==RARFMT50) |
| D->UnpVer=Data->Arc.FileHead.UnpVer==0 ? 50 : 200; // If it is not 0, just set it to something big. |
| else |
| D->UnpVer=Data->Arc.FileHead.UnpVer; |
| D->FileCRC=hd->FileHash.CRC32; |
| D->FileTime=hd->mtime.GetDos(); |
| |
| uint64 MRaw=hd->mtime.GetWin(); |
| D->MtimeLow=(uint)MRaw; |
| D->MtimeHigh=(uint)(MRaw>>32); |
| uint64 CRaw=hd->ctime.GetWin(); |
| D->CtimeLow=(uint)CRaw; |
| D->CtimeHigh=(uint)(CRaw>>32); |
| uint64 ARaw=hd->atime.GetWin(); |
| D->AtimeLow=(uint)ARaw; |
| D->AtimeHigh=(uint)(ARaw>>32); |
| |
| D->Method=hd->Method+0x30; |
| D->FileAttr=hd->FileAttr; |
| D->CmtSize=0; |
| D->CmtState=0; |
| |
| D->DictSize=uint(hd->WinSize/1024); |
| |
| switch (hd->FileHash.Type) |
| { |
| case HASH_RAR14: |
| case HASH_CRC32: |
| D->HashType=RAR_HASH_CRC32; |
| break; |
| case HASH_BLAKE2: |
| D->HashType=RAR_HASH_BLAKE2; |
| memcpy(D->Hash,hd->FileHash.Digest,BLAKE2_DIGEST_SIZE); |
| break; |
| default: |
| D->HashType=RAR_HASH_NONE; |
| break; |
| } |
| |
| D->RedirType=hd->RedirType; |
| // RedirNameSize sanity check is useful in case some developer |
| // did not initialize Reserved area with 0 as required in docs. |
| // We have taken 'Redir*' fields from Reserved area. We may remove |
| // this RedirNameSize check sometimes later. |
| if (hd->RedirType!=FSREDIR_NONE && D->RedirName!=NULL && |
| D->RedirNameSize>0 && D->RedirNameSize<100000) |
| wcsncpyz(D->RedirName,hd->RedirName,D->RedirNameSize); |
| D->DirTarget=hd->DirTarget; |
| } |
| catch (RAR_EXIT ErrCode) |
| { |
| return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode); |
| } |
| return ERAR_SUCCESS; |
| } |
| |
| |
| int PASCAL ProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName,wchar *DestPathW,wchar *DestNameW) |
| { |
| DataSet *Data=(DataSet *)hArcData; |
| try |
| { |
| Data->Cmd.DllError=0; |
| if (Data->OpenMode==RAR_OM_LIST || Data->OpenMode==RAR_OM_LIST_INCSPLIT || |
| Operation==RAR_SKIP && !Data->Arc.Solid) |
| { |
| if (Data->Arc.Volume && Data->Arc.GetHeaderType()==HEAD_FILE && |
| Data->Arc.FileHead.SplitAfter) |
| if (MergeArchive(Data->Arc,NULL,false,'L')) |
| { |
| Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET); |
| return ERAR_SUCCESS; |
| } |
| else |
| return ERAR_EOPEN; |
| Data->Arc.SeekToNext(); |
| } |
| else |
| { |
| Data->Cmd.DllOpMode=Operation; |
| |
| *Data->Cmd.ExtrPath=0; |
| *Data->Cmd.DllDestName=0; |
| |
| if (DestPath!=NULL) |
| { |
| char ExtrPathA[NM]; |
| strncpyz(ExtrPathA,DestPath,ASIZE(ExtrPathA)-2); |
| #ifdef _WIN_ALL |
| // We must not apply OemToCharBuffA directly to DestPath, |
| // because we do not know DestPath length and OemToCharBuffA |
| // does not stop at 0. |
| OemToCharA(ExtrPathA,ExtrPathA); |
| #endif |
| CharToWide(ExtrPathA,Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath)); |
| AddEndSlash(Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath)); |
| } |
| if (DestName!=NULL) |
| { |
| char DestNameA[NM]; |
| strncpyz(DestNameA,DestName,ASIZE(DestNameA)-2); |
| #ifdef _WIN_ALL |
| // We must not apply OemToCharBuffA directly to DestName, |
| // because we do not know DestName length and OemToCharBuffA |
| // does not stop at 0. |
| OemToCharA(DestNameA,DestNameA); |
| #endif |
| CharToWide(DestNameA,Data->Cmd.DllDestName,ASIZE(Data->Cmd.DllDestName)); |
| } |
| |
| if (DestPathW!=NULL) |
| { |
| wcsncpy(Data->Cmd.ExtrPath,DestPathW,ASIZE(Data->Cmd.ExtrPath)); |
| AddEndSlash(Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath)); |
| } |
| |
| if (DestNameW!=NULL) |
| wcsncpyz(Data->Cmd.DllDestName,DestNameW,ASIZE(Data->Cmd.DllDestName)); |
| |
| wcscpy(Data->Cmd.Command,Operation==RAR_EXTRACT ? L"X":L"T"); |
| Data->Cmd.Test=Operation!=RAR_EXTRACT; |
| bool Repeat=false; |
| Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat); |
| |
| // Now we process extra file information if any. |
| // |
| // Archive can be closed if we process volumes, next volume is missing |
| // and current one is already removed or deleted. So we need to check |
| // if archive is still open to avoid calling file operations on |
| // the invalid file handle. Some of our file operations like Seek() |
| // process such invalid handle correctly, some not. |
| while (Data->Arc.IsOpened() && Data->Arc.ReadHeader()!=0 && |
| Data->Arc.GetHeaderType()==HEAD_SERVICE) |
| { |
| Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat); |
| Data->Arc.SeekToNext(); |
| } |
| Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET); |
| } |
| } |
| catch (std::bad_alloc&) |
| { |
| return ERAR_NO_MEMORY; |
| } |
| catch (RAR_EXIT ErrCode) |
| { |
| return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode); |
| } |
| return Data->Cmd.DllError; |
| } |
| |
| |
| int PASCAL RARProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestName) |
| { |
| return ProcessFile(hArcData,Operation,DestPath,DestName,NULL,NULL); |
| } |
| |
| |
| int PASCAL RARProcessFileW(HANDLE hArcData,int Operation,wchar *DestPath,wchar *DestName) |
| { |
| return ProcessFile(hArcData,Operation,NULL,NULL,DestPath,DestName); |
| } |
| |
| |
| void PASCAL RARSetChangeVolProc(HANDLE hArcData,CHANGEVOLPROC ChangeVolProc) |
| { |
| DataSet *Data=(DataSet *)hArcData; |
| Data->Cmd.ChangeVolProc=ChangeVolProc; |
| } |
| |
| |
| void PASCAL RARSetCallback(HANDLE hArcData,UNRARCALLBACK Callback,LPARAM UserData) |
| { |
| DataSet *Data=(DataSet *)hArcData; |
| Data->Cmd.Callback=Callback; |
| Data->Cmd.UserData=UserData; |
| } |
| |
| |
| void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataProc) |
| { |
| DataSet *Data=(DataSet *)hArcData; |
| Data->Cmd.ProcessDataProc=ProcessDataProc; |
| } |
| |
| |
| #ifndef RAR_NOCRYPT |
| void PASCAL RARSetPassword(HANDLE hArcData,char *Password) |
| { |
| DataSet *Data=(DataSet *)hArcData; |
| wchar PasswordW[MAXPASSWORD]; |
| GetWideName(Password,NULL,PasswordW,ASIZE(PasswordW)); |
| Data->Cmd.Password.Set(PasswordW); |
| cleandata(PasswordW,sizeof(PasswordW)); |
| } |
| #endif |
| |
| |
| int PASCAL RARGetDllVersion() |
| { |
| return RAR_DLL_VERSION; |
| } |
| |
| |
| static int RarErrorToDll(RAR_EXIT ErrCode) |
| { |
| switch(ErrCode) |
| { |
| case RARX_FATAL: |
| return ERAR_EREAD; |
| case RARX_CRC: |
| return ERAR_BAD_DATA; |
| case RARX_WRITE: |
| return ERAR_EWRITE; |
| case RARX_OPEN: |
| return ERAR_EOPEN; |
| case RARX_CREATE: |
| return ERAR_ECREATE; |
| case RARX_MEMORY: |
| return ERAR_NO_MEMORY; |
| case RARX_BADPWD: |
| return ERAR_BAD_PASSWORD; |
| case RARX_SUCCESS: |
| return ERAR_SUCCESS; // 0. |
| default: |
| return ERAR_UNKNOWN; |
| } |
| } |