diff --git a/English.lproj/MainMenu.xib b/English.lproj/MainMenu.xib
deleted file mode 100644
index 9d75ca9..0000000
--- a/English.lproj/MainMenu.xib
+++ /dev/null
@@ -1,4116 +0,0 @@
-
-
-
- 1060
- 10C540
- 740
- 1038.25
- 458.00
-
-
-
-
-
- YES
-
- NSApplication
-
-
- FirstResponder
-
-
- NSApplication
-
-
-
- 15
- 2
- {{335, 390}, {480, 360}}
- 1954021376
- QuietUnrar
- NSWindow
-
- {1.79769e+308, 1.79769e+308}
-
-
- 256
- {480, 360}
-
- {{0, 0}, {1920, 1178}}
- {1.79769e+308, 1.79769e+308}
-
-
- QuietUnrarAppDelegate
-
-
- NSFontManager
-
-
-
-
- YES
-
-
- performMiniaturize:
-
-
-
- 37
-
-
-
- arrangeInFront:
-
-
-
- 39
-
-
-
- print:
-
-
-
- 86
-
-
-
- runPageLayout:
-
-
-
- 87
-
-
-
- clearRecentDocuments:
-
-
-
- 127
-
-
-
- orderFrontStandardAboutPanel:
-
-
-
- 142
-
-
-
- performClose:
-
-
-
- 193
-
-
-
- toggleContinuousSpellChecking:
-
-
-
- 222
-
-
-
- undo:
-
-
-
- 223
-
-
-
- copy:
-
-
-
- 224
-
-
-
- checkSpelling:
-
-
-
- 225
-
-
-
- paste:
-
-
-
- 226
-
-
-
- stopSpeaking:
-
-
-
- 227
-
-
-
- cut:
-
-
-
- 228
-
-
-
- showGuessPanel:
-
-
-
- 230
-
-
-
- redo:
-
-
-
- 231
-
-
-
- selectAll:
-
-
-
- 232
-
-
-
- startSpeaking:
-
-
-
- 233
-
-
-
- delete:
-
-
-
- 235
-
-
-
- performZoom:
-
-
-
- 240
-
-
-
- performFindPanelAction:
-
-
-
- 241
-
-
-
- centerSelectionInVisibleArea:
-
-
-
- 245
-
-
-
- toggleGrammarChecking:
-
-
-
- 347
-
-
-
- toggleSmartInsertDelete:
-
-
-
- 355
-
-
-
- toggleAutomaticQuoteSubstitution:
-
-
-
- 356
-
-
-
- toggleAutomaticLinkDetection:
-
-
-
- 357
-
-
-
- saveDocument:
-
-
-
- 362
-
-
-
- saveDocumentAs:
-
-
-
- 363
-
-
-
- revertDocumentToSaved:
-
-
-
- 364
-
-
-
- runToolbarCustomizationPalette:
-
-
-
- 365
-
-
-
- toggleToolbarShown:
-
-
-
- 366
-
-
-
- hide:
-
-
-
- 367
-
-
-
- hideOtherApplications:
-
-
-
- 368
-
-
-
- unhideAllApplications:
-
-
-
- 370
-
-
-
- newDocument:
-
-
-
- 373
-
-
-
- openDocument:
-
-
-
- 374
-
-
-
- addFontTrait:
-
-
-
- 421
-
-
-
- addFontTrait:
-
-
-
- 422
-
-
-
- modifyFont:
-
-
-
- 423
-
-
-
- orderFrontFontPanel:
-
-
-
- 424
-
-
-
- modifyFont:
-
-
-
- 425
-
-
-
- raiseBaseline:
-
-
-
- 426
-
-
-
- lowerBaseline:
-
-
-
- 427
-
-
-
- copyFont:
-
-
-
- 428
-
-
-
- subscript:
-
-
-
- 429
-
-
-
- superscript:
-
-
-
- 430
-
-
-
- tightenKerning:
-
-
-
- 431
-
-
-
- underline:
-
-
-
- 432
-
-
-
- orderFrontColorPanel:
-
-
-
- 433
-
-
-
- useAllLigatures:
-
-
-
- 434
-
-
-
- loosenKerning:
-
-
-
- 435
-
-
-
- pasteFont:
-
-
-
- 436
-
-
-
- unscript:
-
-
-
- 437
-
-
-
- useStandardKerning:
-
-
-
- 438
-
-
-
- useStandardLigatures:
-
-
-
- 439
-
-
-
- turnOffLigatures:
-
-
-
- 440
-
-
-
- turnOffKerning:
-
-
-
- 441
-
-
-
- terminate:
-
-
-
- 449
-
-
-
- toggleAutomaticSpellingCorrection:
-
-
-
- 456
-
-
-
- orderFrontSubstitutionsPanel:
-
-
-
- 458
-
-
-
- toggleAutomaticDashSubstitution:
-
-
-
- 461
-
-
-
- toggleAutomaticTextReplacement:
-
-
-
- 463
-
-
-
- uppercaseWord:
-
-
-
- 464
-
-
-
- capitalizeWord:
-
-
-
- 467
-
-
-
- lowercaseWord:
-
-
-
- 468
-
-
-
- pasteAsPlainText:
-
-
-
- 486
-
-
-
- performFindPanelAction:
-
-
-
- 487
-
-
-
- performFindPanelAction:
-
-
-
- 488
-
-
-
- performFindPanelAction:
-
-
-
- 489
-
-
-
- showHelp:
-
-
-
- 493
-
-
-
- delegate
-
-
-
- 495
-
-
-
- alignCenter:
-
-
-
- 518
-
-
-
- pasteRuler:
-
-
-
- 519
-
-
-
- toggleRuler:
-
-
-
- 520
-
-
-
- alignRight:
-
-
-
- 521
-
-
-
- copyRuler:
-
-
-
- 522
-
-
-
- alignJustified:
-
-
-
- 523
-
-
-
- alignLeft:
-
-
-
- 524
-
-
-
- makeBaseWritingDirectionNatural:
-
-
-
- 525
-
-
-
- makeBaseWritingDirectionLeftToRight:
-
-
-
- 526
-
-
-
- makeBaseWritingDirectionRightToLeft:
-
-
-
- 527
-
-
-
- makeTextWritingDirectionNatural:
-
-
-
- 528
-
-
-
- makeTextWritingDirectionLeftToRight:
-
-
-
- 529
-
-
-
- makeTextWritingDirectionRightToLeft:
-
-
-
- 530
-
-
-
- window
-
-
-
- 532
-
-
-
-
- YES
-
- 0
-
-
-
-
-
- -2
-
-
- File's Owner
-
-
- -1
-
-
- First Responder
-
-
- -3
-
-
- Application
-
-
- 29
-
-
- YES
-
-
-
-
-
-
-
-
-
-
-
- 19
-
-
- YES
-
-
-
-
-
- 56
-
-
- YES
-
-
-
-
-
- 217
-
-
- YES
-
-
-
-
-
- 83
-
-
- YES
-
-
-
-
-
- 81
-
-
- YES
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 75
-
-
-
-
- 80
-
-
-
-
- 78
-
-
-
-
- 72
-
-
-
-
- 82
-
-
-
-
- 124
-
-
- YES
-
-
-
-
-
- 77
-
-
-
-
- 73
-
-
-
-
- 79
-
-
-
-
- 112
-
-
-
-
- 74
-
-
-
-
- 125
-
-
- YES
-
-
-
-
-
- 126
-
-
-
-
- 205
-
-
- YES
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 202
-
-
-
-
- 198
-
-
-
-
- 207
-
-
-
-
- 214
-
-
-
-
- 199
-
-
-
-
- 203
-
-
-
-
- 197
-
-
-
-
- 206
-
-
-
-
- 215
-
-
-
-
- 218
-
-
- YES
-
-
-
-
-
- 216
-
-
- YES
-
-
-
-
-
- 200
-
-
- YES
-
-
-
-
-
-
-
-
-
-
- 219
-
-
-
-
- 201
-
-
-
-
- 204
-
-
-
-
- 220
-
-
- YES
-
-
-
-
-
-
-
-
-
- 213
-
-
-
-
- 210
-
-
-
-
- 221
-
-
-
-
- 208
-
-
-
-
- 209
-
-
-
-
- 57
-
-
- YES
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 58
-
-
-
-
- 134
-
-
-
-
- 150
-
-
-
-
- 136
-
-
-
-
- 144
-
-
-
-
- 129
-
-
-
-
- 143
-
-
-
-
- 236
-
-
-
-
- 131
-
-
- YES
-
-
-
-
-
- 149
-
-
-
-
- 145
-
-
-
-
- 130
-
-
-
-
- 24
-
-
- YES
-
-
-
-
-
-
-
-
- 92
-
-
-
-
- 5
-
-
-
-
- 239
-
-
-
-
- 23
-
-
-
-
- 295
-
-
- YES
-
-
-
-
-
- 296
-
-
- YES
-
-
-
-
-
-
- 297
-
-
-
-
- 298
-
-
-
-
- 211
-
-
- YES
-
-
-
-
-
- 212
-
-
- YES
-
-
-
-
-
-
- 195
-
-
-
-
- 196
-
-
-
-
- 346
-
-
-
-
- 348
-
-
- YES
-
-
-
-
-
- 349
-
-
- YES
-
-
-
-
-
-
-
-
-
-
-
- 350
-
-
-
-
- 351
-
-
-
-
- 354
-
-
-
-
- 371
-
-
- YES
-
-
-
-
-
- 372
-
-
-
-
- 375
-
-
- YES
-
-
-
-
-
- 376
-
-
- YES
-
-
-
-
-
-
- 377
-
-
- YES
-
-
-
-
-
- 388
-
-
- YES
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 389
-
-
-
-
- 390
-
-
-
-
- 391
-
-
-
-
- 392
-
-
-
-
- 393
-
-
-
-
- 394
-
-
-
-
- 395
-
-
-
-
- 396
-
-
-
-
- 397
-
-
- YES
-
-
-
-
-
- 398
-
-
- YES
-
-
-
-
-
- 399
-
-
- YES
-
-
-
-
-
- 400
-
-
-
-
- 401
-
-
-
-
- 402
-
-
-
-
- 403
-
-
-
-
- 404
-
-
-
-
- 405
-
-
- YES
-
-
-
-
-
-
-
-
-
- 406
-
-
-
-
- 407
-
-
-
-
- 408
-
-
-
-
- 409
-
-
-
-
- 410
-
-
-
-
- 411
-
-
- YES
-
-
-
-
-
-
-
- 412
-
-
-
-
- 413
-
-
-
-
- 414
-
-
-
-
- 415
-
-
- YES
-
-
-
-
-
-
-
-
- 416
-
-
-
-
- 417
-
-
-
-
- 418
-
-
-
-
- 419
-
-
-
-
- 420
-
-
-
-
- 450
-
-
- YES
-
-
-
-
-
- 451
-
-
- YES
-
-
-
-
-
-
-
- 452
-
-
-
-
- 453
-
-
-
-
- 454
-
-
-
-
- 457
-
-
-
-
- 459
-
-
-
-
- 460
-
-
-
-
- 462
-
-
-
-
- 465
-
-
-
-
- 466
-
-
-
-
- 485
-
-
-
-
- 490
-
-
- YES
-
-
-
-
-
- 491
-
-
- YES
-
-
-
-
-
- 492
-
-
-
-
- 494
-
-
-
-
- 496
-
-
- YES
-
-
-
-
-
- 497
-
-
- YES
-
-
-
-
-
-
-
-
-
-
-
-
-
-
- 498
-
-
-
-
- 499
-
-
-
-
- 500
-
-
-
-
- 501
-
-
-
-
- 502
-
-
-
-
- 503
-
-
- YES
-
-
-
-
-
- 504
-
-
-
-
- 505
-
-
-
-
- 506
-
-
-
-
- 507
-
-
-
-
- 508
-
-
- YES
-
-
-
-
-
-
-
-
-
-
-
-
-
- 509
-
-
-
-
- 510
-
-
-
-
- 511
-
-
-
-
- 512
-
-
-
-
- 513
-
-
-
-
- 514
-
-
-
-
- 515
-
-
-
-
- 516
-
-
-
-
- 517
-
-
-
-
-
-
- YES
-
- YES
- -3.IBPluginDependency
- 112.IBPluginDependency
- 112.ImportedFromIB2
- 124.IBPluginDependency
- 124.ImportedFromIB2
- 125.IBPluginDependency
- 125.ImportedFromIB2
- 125.editorWindowContentRectSynchronizationRect
- 126.IBPluginDependency
- 126.ImportedFromIB2
- 129.IBPluginDependency
- 129.ImportedFromIB2
- 130.IBPluginDependency
- 130.ImportedFromIB2
- 130.editorWindowContentRectSynchronizationRect
- 131.IBPluginDependency
- 131.ImportedFromIB2
- 134.IBPluginDependency
- 134.ImportedFromIB2
- 136.IBPluginDependency
- 136.ImportedFromIB2
- 143.IBPluginDependency
- 143.ImportedFromIB2
- 144.IBPluginDependency
- 144.ImportedFromIB2
- 145.IBPluginDependency
- 145.ImportedFromIB2
- 149.IBPluginDependency
- 149.ImportedFromIB2
- 150.IBPluginDependency
- 150.ImportedFromIB2
- 19.IBPluginDependency
- 19.ImportedFromIB2
- 195.IBPluginDependency
- 195.ImportedFromIB2
- 196.IBPluginDependency
- 196.ImportedFromIB2
- 197.IBPluginDependency
- 197.ImportedFromIB2
- 198.IBPluginDependency
- 198.ImportedFromIB2
- 199.IBPluginDependency
- 199.ImportedFromIB2
- 200.IBEditorWindowLastContentRect
- 200.IBPluginDependency
- 200.ImportedFromIB2
- 200.editorWindowContentRectSynchronizationRect
- 201.IBPluginDependency
- 201.ImportedFromIB2
- 202.IBPluginDependency
- 202.ImportedFromIB2
- 203.IBPluginDependency
- 203.ImportedFromIB2
- 204.IBPluginDependency
- 204.ImportedFromIB2
- 205.IBEditorWindowLastContentRect
- 205.IBPluginDependency
- 205.ImportedFromIB2
- 205.editorWindowContentRectSynchronizationRect
- 206.IBPluginDependency
- 206.ImportedFromIB2
- 207.IBPluginDependency
- 207.ImportedFromIB2
- 208.IBPluginDependency
- 208.ImportedFromIB2
- 209.IBPluginDependency
- 209.ImportedFromIB2
- 210.IBPluginDependency
- 210.ImportedFromIB2
- 211.IBPluginDependency
- 211.ImportedFromIB2
- 212.IBPluginDependency
- 212.ImportedFromIB2
- 212.editorWindowContentRectSynchronizationRect
- 213.IBPluginDependency
- 213.ImportedFromIB2
- 214.IBPluginDependency
- 214.ImportedFromIB2
- 215.IBPluginDependency
- 215.ImportedFromIB2
- 216.IBPluginDependency
- 216.ImportedFromIB2
- 217.IBPluginDependency
- 217.ImportedFromIB2
- 218.IBPluginDependency
- 218.ImportedFromIB2
- 219.IBPluginDependency
- 219.ImportedFromIB2
- 220.IBEditorWindowLastContentRect
- 220.IBPluginDependency
- 220.ImportedFromIB2
- 220.editorWindowContentRectSynchronizationRect
- 221.IBPluginDependency
- 221.ImportedFromIB2
- 23.IBPluginDependency
- 23.ImportedFromIB2
- 236.IBPluginDependency
- 236.ImportedFromIB2
- 239.IBPluginDependency
- 239.ImportedFromIB2
- 24.IBEditorWindowLastContentRect
- 24.IBPluginDependency
- 24.ImportedFromIB2
- 24.editorWindowContentRectSynchronizationRect
- 29.IBEditorWindowLastContentRect
- 29.IBPluginDependency
- 29.ImportedFromIB2
- 29.WindowOrigin
- 29.editorWindowContentRectSynchronizationRect
- 295.IBPluginDependency
- 296.IBEditorWindowLastContentRect
- 296.IBPluginDependency
- 296.editorWindowContentRectSynchronizationRect
- 297.IBPluginDependency
- 298.IBPluginDependency
- 346.IBPluginDependency
- 346.ImportedFromIB2
- 348.IBPluginDependency
- 348.ImportedFromIB2
- 349.IBEditorWindowLastContentRect
- 349.IBPluginDependency
- 349.ImportedFromIB2
- 349.editorWindowContentRectSynchronizationRect
- 350.IBPluginDependency
- 350.ImportedFromIB2
- 351.IBPluginDependency
- 351.ImportedFromIB2
- 354.IBPluginDependency
- 354.ImportedFromIB2
- 371.IBEditorWindowLastContentRect
- 371.IBPluginDependency
- 371.IBWindowTemplateEditedContentRect
- 371.NSWindowTemplate.visibleAtLaunch
- 371.editorWindowContentRectSynchronizationRect
- 371.windowTemplate.maxSize
- 372.IBPluginDependency
- 375.IBPluginDependency
- 376.IBEditorWindowLastContentRect
- 376.IBPluginDependency
- 377.IBPluginDependency
- 388.IBEditorWindowLastContentRect
- 388.IBPluginDependency
- 389.IBPluginDependency
- 390.IBPluginDependency
- 391.IBPluginDependency
- 392.IBPluginDependency
- 393.IBPluginDependency
- 394.IBPluginDependency
- 395.IBPluginDependency
- 396.IBPluginDependency
- 397.IBPluginDependency
- 398.IBPluginDependency
- 399.IBPluginDependency
- 400.IBPluginDependency
- 401.IBPluginDependency
- 402.IBPluginDependency
- 403.IBPluginDependency
- 404.IBPluginDependency
- 405.IBPluginDependency
- 406.IBPluginDependency
- 407.IBPluginDependency
- 408.IBPluginDependency
- 409.IBPluginDependency
- 410.IBPluginDependency
- 411.IBPluginDependency
- 412.IBPluginDependency
- 413.IBPluginDependency
- 414.IBPluginDependency
- 415.IBPluginDependency
- 416.IBPluginDependency
- 417.IBPluginDependency
- 418.IBPluginDependency
- 419.IBPluginDependency
- 450.IBPluginDependency
- 451.IBEditorWindowLastContentRect
- 451.IBPluginDependency
- 452.IBPluginDependency
- 453.IBPluginDependency
- 454.IBPluginDependency
- 457.IBPluginDependency
- 459.IBPluginDependency
- 460.IBPluginDependency
- 462.IBPluginDependency
- 465.IBPluginDependency
- 466.IBPluginDependency
- 485.IBPluginDependency
- 490.IBPluginDependency
- 491.IBEditorWindowLastContentRect
- 491.IBPluginDependency
- 492.IBPluginDependency
- 496.IBPluginDependency
- 497.IBEditorWindowLastContentRect
- 497.IBPluginDependency
- 498.IBPluginDependency
- 499.IBPluginDependency
- 5.IBPluginDependency
- 5.ImportedFromIB2
- 500.IBPluginDependency
- 501.IBPluginDependency
- 502.IBPluginDependency
- 503.IBPluginDependency
- 504.IBPluginDependency
- 505.IBPluginDependency
- 506.IBPluginDependency
- 507.IBPluginDependency
- 508.IBEditorWindowLastContentRect
- 508.IBPluginDependency
- 509.IBPluginDependency
- 510.IBPluginDependency
- 511.IBPluginDependency
- 512.IBPluginDependency
- 513.IBPluginDependency
- 514.IBPluginDependency
- 515.IBPluginDependency
- 516.IBPluginDependency
- 517.IBPluginDependency
- 56.IBPluginDependency
- 56.ImportedFromIB2
- 57.IBEditorWindowLastContentRect
- 57.IBPluginDependency
- 57.ImportedFromIB2
- 57.editorWindowContentRectSynchronizationRect
- 58.IBPluginDependency
- 58.ImportedFromIB2
- 72.IBPluginDependency
- 72.ImportedFromIB2
- 73.IBPluginDependency
- 73.ImportedFromIB2
- 74.IBPluginDependency
- 74.ImportedFromIB2
- 75.IBPluginDependency
- 75.ImportedFromIB2
- 77.IBPluginDependency
- 77.ImportedFromIB2
- 78.IBPluginDependency
- 78.ImportedFromIB2
- 79.IBPluginDependency
- 79.ImportedFromIB2
- 80.IBPluginDependency
- 80.ImportedFromIB2
- 81.IBEditorWindowLastContentRect
- 81.IBPluginDependency
- 81.ImportedFromIB2
- 81.editorWindowContentRectSynchronizationRect
- 82.IBPluginDependency
- 82.ImportedFromIB2
- 83.IBPluginDependency
- 83.ImportedFromIB2
- 92.IBPluginDependency
- 92.ImportedFromIB2
-
-
- YES
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- {{522, 812}, {146, 23}}
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- {{436, 809}, {64, 6}}
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- {{753, 187}, {275, 113}}
- com.apple.InterfaceBuilder.CocoaPlugin
-
- {{608, 612}, {275, 83}}
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- {{547, 180}, {254, 283}}
- com.apple.InterfaceBuilder.CocoaPlugin
-
- {{187, 434}, {243, 243}}
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- {{608, 612}, {167, 43}}
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- {{753, 217}, {238, 103}}
- com.apple.InterfaceBuilder.CocoaPlugin
-
- {{608, 612}, {241, 103}}
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- {{654, 239}, {194, 73}}
- com.apple.InterfaceBuilder.CocoaPlugin
-
- {{525, 802}, {197, 73}}
- {{380, 736}, {444, 20}}
- com.apple.InterfaceBuilder.CocoaPlugin
-
- {74, 862}
- {{6, 978}, {478, 20}}
- com.apple.InterfaceBuilder.CocoaPlugin
- {{604, 269}, {231, 43}}
- com.apple.InterfaceBuilder.CocoaPlugin
- {{475, 832}, {234, 43}}
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- {{746, 287}, {220, 133}}
- com.apple.InterfaceBuilder.CocoaPlugin
-
- {{608, 612}, {215, 63}}
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- {{380, 396}, {480, 360}}
- com.apple.InterfaceBuilder.CocoaPlugin
- {{380, 396}, {480, 360}}
-
- {{33, 99}, {480, 360}}
- {3.40282e+38, 3.40282e+38}
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- {{591, 420}, {83, 43}}
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- {{523, 2}, {178, 283}}
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- {{753, 197}, {170, 63}}
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- {{725, 289}, {246, 23}}
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- {{674, 260}, {204, 183}}
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- {{878, 180}, {164, 173}}
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
-
- {{286, 129}, {275, 183}}
- com.apple.InterfaceBuilder.CocoaPlugin
-
- {{23, 794}, {245, 183}}
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- {{452, 109}, {196, 203}}
- com.apple.InterfaceBuilder.CocoaPlugin
-
- {{145, 474}, {199, 203}}
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
- com.apple.InterfaceBuilder.CocoaPlugin
-
-
-
-
- YES
-
-
- YES
-
-
-
-
- YES
-
-
- YES
-
-
-
- 532
-
-
-
- YES
-
- QuietUnrarAppDelegate
- NSObject
-
- window
- NSWindow
-
-
- IBProjectSource
- QuietUnrarAppDelegate.h
-
-
-
-
- YES
-
- NSApplication
- NSResponder
-
- IBFrameworkSource
- AppKit.framework/Headers/NSApplication.h
-
-
-
- NSApplication
-
- IBFrameworkSource
- AppKit.framework/Headers/NSApplicationScripting.h
-
-
-
- NSApplication
-
- IBFrameworkSource
- AppKit.framework/Headers/NSColorPanel.h
-
-
-
- NSApplication
-
- IBFrameworkSource
- AppKit.framework/Headers/NSHelpManager.h
-
-
-
- NSApplication
-
- IBFrameworkSource
- AppKit.framework/Headers/NSPageLayout.h
-
-
-
- NSApplication
-
- IBFrameworkSource
- AppKit.framework/Headers/NSUserInterfaceItemSearching.h
-
-
-
- NSBrowser
- NSControl
-
- IBFrameworkSource
- AppKit.framework/Headers/NSBrowser.h
-
-
-
- NSControl
- NSView
-
- IBFrameworkSource
- AppKit.framework/Headers/NSControl.h
-
-
-
- NSDocument
- NSObject
-
- YES
-
- YES
- printDocument:
- revertDocumentToSaved:
- runPageLayout:
- saveDocument:
- saveDocumentAs:
- saveDocumentTo:
-
-
- YES
- id
- id
- id
- id
- id
- id
-
-
-
- IBFrameworkSource
- AppKit.framework/Headers/NSDocument.h
-
-
-
- NSDocument
-
- IBFrameworkSource
- AppKit.framework/Headers/NSDocumentScripting.h
-
-
-
- NSDocumentController
- NSObject
-
- YES
-
- YES
- clearRecentDocuments:
- newDocument:
- openDocument:
- saveAllDocuments:
-
-
- YES
- id
- id
- id
- id
-
-
-
- IBFrameworkSource
- AppKit.framework/Headers/NSDocumentController.h
-
-
-
- NSFontManager
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSFontManager.h
-
-
-
- NSFormatter
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSFormatter.h
-
-
-
- NSMatrix
- NSControl
-
- IBFrameworkSource
- AppKit.framework/Headers/NSMatrix.h
-
-
-
- NSMenu
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSMenu.h
-
-
-
- NSMenuItem
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSMenuItem.h
-
-
-
- NSMovieView
- NSView
-
- IBFrameworkSource
- AppKit.framework/Headers/NSMovieView.h
-
-
-
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSAccessibility.h
-
-
-
- NSObject
-
-
-
- NSObject
-
-
-
- NSObject
-
-
-
- NSObject
-
-
-
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSDictionaryController.h
-
-
-
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSDragging.h
-
-
-
- NSObject
-
-
-
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSFontPanel.h
-
-
-
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSKeyValueBinding.h
-
-
-
- NSObject
-
-
-
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSNibLoading.h
-
-
-
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSOutlineView.h
-
-
-
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSPasteboard.h
-
-
-
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSSavePanel.h
-
-
-
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSTableView.h
-
-
-
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSToolbarItem.h
-
-
-
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSView.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSArchiver.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSClassDescription.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSError.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSFileManager.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSKeyValueCoding.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSKeyValueObserving.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSKeyedArchiver.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSObject.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSObjectScripting.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSPortCoder.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSRunLoop.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSScriptClassDescription.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSScriptKeyValueCoding.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSScriptObjectSpecifiers.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSScriptWhoseTests.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSThread.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSURL.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSURLConnection.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSURLDownload.h
-
-
-
- NSResponder
-
- IBFrameworkSource
- AppKit.framework/Headers/NSInterfaceStyle.h
-
-
-
- NSResponder
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSResponder.h
-
-
-
- NSTableView
- NSControl
-
-
-
- NSText
- NSView
-
- IBFrameworkSource
- AppKit.framework/Headers/NSText.h
-
-
-
- NSTextView
- NSText
-
- IBFrameworkSource
- AppKit.framework/Headers/NSTextView.h
-
-
-
- NSView
-
- IBFrameworkSource
- AppKit.framework/Headers/NSClipView.h
-
-
-
- NSView
-
-
-
- NSView
-
- IBFrameworkSource
- AppKit.framework/Headers/NSRulerView.h
-
-
-
- NSView
- NSResponder
-
-
-
- NSWindow
-
- IBFrameworkSource
- AppKit.framework/Headers/NSDrawer.h
-
-
-
- NSWindow
- NSResponder
-
- IBFrameworkSource
- AppKit.framework/Headers/NSWindow.h
-
-
-
- NSWindow
-
- IBFrameworkSource
- AppKit.framework/Headers/NSWindowScripting.h
-
-
-
-
- 0
-
- com.apple.InterfaceBuilder.CocoaPlugin.macosx
-
-
-
- com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3
-
-
- YES
- ../QuietUnrar.xcodeproj
- 3
-
-
diff --git a/Frameworks/Growl.framework/Versions/A/Headers/Growl.h b/Frameworks/Growl.framework/Versions/A/Headers/Growl.h
index e2a4425..72dfcdf 100644
--- a/Frameworks/Growl.framework/Versions/A/Headers/Growl.h
+++ b/Frameworks/Growl.framework/Versions/A/Headers/Growl.h
@@ -1,6 +1,6 @@
-#include "GrowlDefines.h"
+#include
#ifdef __OBJC__
-# include "GrowlApplicationBridge.h"
+# include
#endif
-#include "GrowlApplicationBridge-Carbon.h"
+#include
diff --git a/PasswordView.xib b/PasswordView.xib
index e79c3c3..c8ff849 100644
--- a/PasswordView.xib
+++ b/PasswordView.xib
@@ -1,715 +1,46 @@
-
-
- 1060
- 10C540
- 740
- 1038.25
- 458.00
-
- com.apple.InterfaceBuilder.CocoaPlugin
- 740
-
-
- YES
-
-
-
- YES
- com.apple.InterfaceBuilder.CocoaPlugin
-
-
- YES
-
- YES
-
-
- YES
-
-
-
- YES
-
- QuietUnrarAppDelegate
-
-
- FirstResponder
-
-
- NSApplication
-
-
-
- 268
-
- YES
-
-
- 268
- {{66, 0}, {207, 22}}
-
- YES
-
- 343014976
- 272630848
-
-
- LucidaGrande
- 13
- 1044
-
-
- YES
-
- 6
- System
- textBackgroundColor
-
- 3
- MQA
-
-
-
- 6
- System
- textColor
-
- 3
- MAA
-
-
-
- YES
- NSAllRomanInputSourcesLocaleIdentifier
-
-
-
-
-
- 268
- {{-3, 2}, {64, 17}}
-
- YES
-
- 68288064
- 272630784
- Password
-
-
-
- 6
- System
- controlColor
-
- 3
- MC42NjY2NjY2NjY3AA
-
-
-
- 6
- System
- controlTextColor
-
-
-
-
-
- {273, 22}
-
- NSView
-
-
-
-
- YES
-
-
- passwordView
-
-
-
- 14
-
-
-
- passwordField
-
-
-
- 15
-
-
-
-
- YES
-
- 0
-
-
-
-
-
- -2
-
-
- File's Owner
-
-
- -1
-
-
- First Responder
-
-
- -3
-
-
- Application
-
-
- 1
-
-
- YES
-
-
-
-
-
-
- 4
-
-
- YES
-
-
-
-
-
- 5
-
-
- YES
-
-
-
-
-
- 8
-
-
-
-
- 9
-
-
-
-
-
-
- YES
-
- YES
- 1.IBEditorWindowLastContentRect
- 1.IBPluginDependency
- 1.WindowOrigin
- 1.editorWindowContentRectSynchronizationRect
- 4.IBPluginDependency
- 5.IBPluginDependency
- 8.IBPluginDependency
- 9.IBPluginDependency
-
-
- YES
- {{414, 718}, {273, 22}}
- com.apple.InterfaceBuilder.CocoaPlugin
- {628, 654}
- {{217, 442}, {480, 272}}
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
- com.apple.InterfaceBuilder.CocoaPlugin
-
-
-
- YES
-
-
- YES
-
-
-
-
- YES
-
-
- YES
-
-
-
- 15
-
-
-
- YES
-
- QuietUnrarAppDelegate
- NSObject
-
- YES
-
- YES
- passwordField
- passwordView
- window
-
-
- YES
- NSSecureTextField
- NSView
- NSWindow
-
-
-
- IBProjectSource
- QuietUnrarAppDelegate.h
-
-
-
-
- YES
-
- NSActionCell
- NSCell
-
- IBFrameworkSource
- AppKit.framework/Headers/NSActionCell.h
-
-
-
- NSApplication
- NSResponder
-
- IBFrameworkSource
- AppKit.framework/Headers/NSApplication.h
-
-
-
- NSApplication
-
- IBFrameworkSource
- AppKit.framework/Headers/NSApplicationScripting.h
-
-
-
- NSApplication
-
- IBFrameworkSource
- AppKit.framework/Headers/NSColorPanel.h
-
-
-
- NSApplication
-
- IBFrameworkSource
- AppKit.framework/Headers/NSHelpManager.h
-
-
-
- NSApplication
-
- IBFrameworkSource
- AppKit.framework/Headers/NSPageLayout.h
-
-
-
- NSApplication
-
- IBFrameworkSource
- AppKit.framework/Headers/NSUserInterfaceItemSearching.h
-
-
-
- NSCell
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSCell.h
-
-
-
- NSControl
- NSView
-
- IBFrameworkSource
- AppKit.framework/Headers/NSControl.h
-
-
-
- NSFormatter
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSFormatter.h
-
-
-
- NSMenu
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSMenu.h
-
-
-
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSAccessibility.h
-
-
-
- NSObject
-
-
-
- NSObject
-
-
-
- NSObject
-
-
-
- NSObject
-
-
-
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSDictionaryController.h
-
-
-
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSDragging.h
-
-
-
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSFontManager.h
-
-
-
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSFontPanel.h
-
-
-
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSKeyValueBinding.h
-
-
-
- NSObject
-
-
-
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSNibLoading.h
-
-
-
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSOutlineView.h
-
-
-
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSPasteboard.h
-
-
-
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSSavePanel.h
-
-
-
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSTableView.h
-
-
-
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSToolbarItem.h
-
-
-
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSView.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSArchiver.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSClassDescription.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSError.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSFileManager.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSKeyValueCoding.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSKeyValueObserving.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSKeyedArchiver.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSObject.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSObjectScripting.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSPortCoder.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSRunLoop.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSScriptClassDescription.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSScriptKeyValueCoding.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSScriptObjectSpecifiers.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSScriptWhoseTests.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSThread.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSURL.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSURLConnection.h
-
-
-
- NSObject
-
- IBFrameworkSource
- Foundation.framework/Headers/NSURLDownload.h
-
-
-
- NSResponder
-
- IBFrameworkSource
- AppKit.framework/Headers/NSInterfaceStyle.h
-
-
-
- NSResponder
- NSObject
-
- IBFrameworkSource
- AppKit.framework/Headers/NSResponder.h
-
-
-
- NSSecureTextField
- NSTextField
-
- IBFrameworkSource
- AppKit.framework/Headers/NSSecureTextField.h
-
-
-
- NSSecureTextFieldCell
- NSTextFieldCell
-
-
-
- NSTextField
- NSControl
-
- IBFrameworkSource
- AppKit.framework/Headers/NSTextField.h
-
-
-
- NSTextFieldCell
- NSActionCell
-
- IBFrameworkSource
- AppKit.framework/Headers/NSTextFieldCell.h
-
-
-
- NSView
-
- IBFrameworkSource
- AppKit.framework/Headers/NSClipView.h
-
-
-
- NSView
-
- IBFrameworkSource
- AppKit.framework/Headers/NSMenuItem.h
-
-
-
- NSView
-
- IBFrameworkSource
- AppKit.framework/Headers/NSRulerView.h
-
-
-
- NSView
- NSResponder
-
-
-
- NSWindow
-
- IBFrameworkSource
- AppKit.framework/Headers/NSDrawer.h
-
-
-
- NSWindow
- NSResponder
-
- IBFrameworkSource
- AppKit.framework/Headers/NSWindow.h
-
-
-
- NSWindow
-
- IBFrameworkSource
- AppKit.framework/Headers/NSWindowScripting.h
-
-
-
-
- 0
-
- com.apple.InterfaceBuilder.CocoaPlugin.macosx
-
-
-
- com.apple.InterfaceBuilder.CocoaPlugin.InterfaceBuilder3
-
-
- YES
- QuietUnrar.xcodeproj
- 3
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ NSAllRomanInputSourcesLocaleIdentifier
+
+
+
+
+
+
+
+
diff --git a/QuietUnrar-Info.plist b/QuietUnrar-Info.plist
index 6453810..b02e193 100644
--- a/QuietUnrar-Info.plist
+++ b/QuietUnrar-Info.plist
@@ -130,7 +130,7 @@
CFBundleIconFile
CFBundleIdentifier
- com.yourcompany.${PRODUCT_NAME:rfc1034identifier}
+ $(PRODUCT_BUNDLE_IDENTIFIER)
CFBundleInfoDictionaryVersion
6.0
CFBundleName
diff --git a/QuietUnrar.xcodeproj/project.pbxproj b/QuietUnrar.xcodeproj/project.pbxproj
index 2be9f53..3e2dda8 100644
--- a/QuietUnrar.xcodeproj/project.pbxproj
+++ b/QuietUnrar.xcodeproj/project.pbxproj
@@ -11,10 +11,8 @@
8D11072D0486CEB800E47090 /* main.m in Sources */ = {isa = PBXBuildFile; fileRef = 29B97316FDCFA39411CA2CEA /* main.m */; settings = {ATTRIBUTES = (); }; };
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */; };
D488BC6810AF437B00B3451C /* libunrar.so in Frameworks */ = {isa = PBXBuildFile; fileRef = D488BC6710AF437B00B3451C /* libunrar.so */; };
- D488BCC110AF49C700B3451C /* libunrar.so in CopyFiles */ = {isa = PBXBuildFile; fileRef = D488BC6710AF437B00B3451C /* libunrar.so */; };
+ D488BCC110AF49C700B3451C /* libunrar.so in CopyFiles */ = {isa = PBXBuildFile; fileRef = D488BC6710AF437B00B3451C /* libunrar.so */; settings = {ATTRIBUTES = (CodeSignOnCopy, ); }; };
D488BE5510B05F3800B3451C /* PasswordView.xib in Resources */ = {isa = PBXBuildFile; fileRef = D488BE5410B05F3800B3451C /* PasswordView.xib */; };
- D488BFB010B1F97F00B3451C /* Growl.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D488BFAF10B1F97F00B3451C /* Growl.framework */; };
- D488BFBF10B1FBC700B3451C /* Growl.framework in CopyFiles */ = {isa = PBXBuildFile; fileRef = D488BFAF10B1F97F00B3451C /* Growl.framework */; };
D488BFCC10B1FD4500B3451C /* Growl Registration Ticket.growlRegDict in Resources */ = {isa = PBXBuildFile; fileRef = D488BFCB10B1FD4500B3451C /* Growl Registration Ticket.growlRegDict */; };
D4A49691105435BE00BE38AE /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 089C165CFE840E0CC02AAC07 /* InfoPlist.strings */; };
D4A49692105435C100BE38AE /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 1DDD58140DA1D0A300B32029 /* MainMenu.xib */; };
@@ -38,7 +36,6 @@
dstPath = "";
dstSubfolderSpec = 10;
files = (
- D488BFBF10B1FBC700B3451C /* Growl.framework in CopyFiles */,
D488BCC110AF49C700B3451C /* libunrar.so in CopyFiles */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -46,10 +43,8 @@
/* End PBXCopyFilesBuildPhase section */
/* Begin PBXFileReference section */
- 089C165DFE840E0CC02AAC07 /* English */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.strings; name = English; path = English.lproj/InfoPlist.strings; sourceTree = ""; };
1058C7A1FEA54F0111CA2CBB /* Cocoa.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Cocoa.framework; path = /System/Library/Frameworks/Cocoa.framework; sourceTree = ""; };
13E42FB307B3F0F600E4EEF1 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = /System/Library/Frameworks/CoreData.framework; sourceTree = ""; };
- 1DDD58150DA1D0A300B32029 /* English */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = English; path = English.lproj/MainMenu.xib; sourceTree = ""; };
256AC3D80F4B6AC300CF3369 /* QuietUnrarAppDelegate.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QuietUnrarAppDelegate.h; sourceTree = ""; };
256AC3D90F4B6AC300CF3369 /* QuietUnrarAppDelegate.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = QuietUnrarAppDelegate.m; sourceTree = ""; };
256AC3F00F4B6AF500CF3369 /* QuietUnrar_Prefix.pch */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = QuietUnrar_Prefix.pch; sourceTree = ""; };
@@ -68,7 +63,6 @@
D4A495761054177300BE38AE /* archive.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = archive.hpp; path = libunrar/archive.hpp; sourceTree = ""; };
D4A495771054177300BE38AE /* arcread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = arcread.cpp; path = libunrar/arcread.cpp; sourceTree = ""; };
D4A495781054177300BE38AE /* array.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = array.hpp; path = libunrar/array.hpp; sourceTree = ""; };
- D4A495791054177300BE38AE /* beosea.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = beosea.cpp; path = libunrar/beosea.cpp; sourceTree = ""; };
D4A4957A1054177300BE38AE /* cmddata.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = cmddata.cpp; path = libunrar/cmddata.cpp; sourceTree = ""; };
D4A4957B1054177300BE38AE /* cmddata.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = cmddata.hpp; path = libunrar/cmddata.hpp; sourceTree = ""; };
D4A4957C1054177300BE38AE /* coder.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = coder.cpp; path = libunrar/coder.cpp; sourceTree = ""; };
@@ -112,7 +106,6 @@
D4A495A21054177300BE38AE /* loclang.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = loclang.hpp; path = libunrar/loclang.hpp; sourceTree = ""; };
D4A495A31054177300BE38AE /* log.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = log.cpp; path = libunrar/log.cpp; sourceTree = ""; };
D4A495A41054177300BE38AE /* log.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = log.hpp; path = libunrar/log.hpp; sourceTree = ""; };
- D4A495A51054177300BE38AE /* makefile.unix */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; name = makefile.unix; path = libunrar/makefile.unix; sourceTree = ""; };
D4A495A61054177300BE38AE /* match.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = match.cpp; path = libunrar/match.cpp; sourceTree = ""; };
D4A495A71054177300BE38AE /* match.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = match.hpp; path = libunrar/match.hpp; sourceTree = ""; };
D4A495A81054177300BE38AE /* model.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = model.cpp; path = libunrar/model.cpp; sourceTree = ""; };
@@ -120,7 +113,6 @@
D4A495AA1054177300BE38AE /* options.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = options.cpp; path = libunrar/options.cpp; sourceTree = ""; };
D4A495AB1054177300BE38AE /* options.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = options.hpp; path = libunrar/options.hpp; sourceTree = ""; };
D4A495AC1054177300BE38AE /* os.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = os.hpp; path = libunrar/os.hpp; sourceTree = ""; };
- D4A495AD1054177300BE38AE /* os2ea.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = os2ea.cpp; path = libunrar/os2ea.cpp; sourceTree = ""; };
D4A495AE1054177300BE38AE /* pathfn.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = pathfn.cpp; path = libunrar/pathfn.cpp; sourceTree = ""; };
D4A495AF1054177300BE38AE /* pathfn.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = pathfn.hpp; path = libunrar/pathfn.hpp; sourceTree = ""; };
D4A495B01054177300BE38AE /* rar.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rar.cpp; path = libunrar/rar.cpp; sourceTree = ""; };
@@ -131,7 +123,6 @@
D4A495B51054177300BE38AE /* rartypes.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = rartypes.hpp; path = libunrar/rartypes.hpp; sourceTree = ""; };
D4A495B61054177300BE38AE /* rarvm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rarvm.cpp; path = libunrar/rarvm.cpp; sourceTree = ""; };
D4A495B71054177300BE38AE /* rarvm.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = rarvm.hpp; path = libunrar/rarvm.hpp; sourceTree = ""; };
- D4A495B81054177300BE38AE /* rarvmtbl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rarvmtbl.cpp; path = libunrar/rarvmtbl.cpp; sourceTree = ""; };
D4A495B91054177300BE38AE /* rawread.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rawread.cpp; path = libunrar/rawread.cpp; sourceTree = ""; };
D4A495BA1054177300BE38AE /* rawread.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = rawread.hpp; path = libunrar/rawread.hpp; sourceTree = ""; };
D4A495BB1054177300BE38AE /* rdwrfn.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rdwrfn.cpp; path = libunrar/rdwrfn.cpp; sourceTree = ""; };
@@ -144,7 +135,6 @@
D4A495C21054177300BE38AE /* rijndael.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = rijndael.hpp; path = libunrar/rijndael.hpp; sourceTree = ""; };
D4A495C31054177300BE38AE /* rs.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = rs.cpp; path = libunrar/rs.cpp; sourceTree = ""; };
D4A495C41054177300BE38AE /* rs.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = rs.hpp; path = libunrar/rs.hpp; sourceTree = ""; };
- D4A495C51054177300BE38AE /* savepos.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = savepos.cpp; path = libunrar/savepos.cpp; sourceTree = ""; };
D4A495C61054177300BE38AE /* savepos.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = savepos.hpp; path = libunrar/savepos.hpp; sourceTree = ""; };
D4A495C71054177300BE38AE /* scantree.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = scantree.cpp; path = libunrar/scantree.cpp; sourceTree = ""; };
D4A495C81054177300BE38AE /* scantree.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = scantree.hpp; path = libunrar/scantree.hpp; sourceTree = ""; };
@@ -163,10 +153,8 @@
D4A495D51054177300BE38AE /* timefn.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = timefn.cpp; path = libunrar/timefn.cpp; sourceTree = ""; };
D4A495D61054177300BE38AE /* timefn.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = timefn.hpp; path = libunrar/timefn.hpp; sourceTree = ""; };
D4A495D71054177300BE38AE /* ulinks.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = ulinks.cpp; path = libunrar/ulinks.cpp; sourceTree = ""; };
- D4A495D81054177300BE38AE /* ulinks.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = ulinks.hpp; path = libunrar/ulinks.hpp; sourceTree = ""; };
D4A495D91054177300BE38AE /* unicode.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = unicode.cpp; path = libunrar/unicode.cpp; sourceTree = ""; };
D4A495DA1054177300BE38AE /* unicode.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = unicode.hpp; path = libunrar/unicode.hpp; sourceTree = ""; };
- D4A495DB1054177300BE38AE /* unios2.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = unios2.cpp; path = libunrar/unios2.cpp; sourceTree = ""; };
D4A495DC1054177300BE38AE /* unpack.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = unpack.cpp; path = libunrar/unpack.cpp; sourceTree = ""; };
D4A495DD1054177300BE38AE /* unpack.hpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.h; name = unpack.hpp; path = libunrar/unpack.hpp; sourceTree = ""; };
D4A495DE1054177300BE38AE /* unpack15.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = unpack15.cpp; path = libunrar/unpack15.cpp; sourceTree = ""; };
@@ -178,6 +166,8 @@
D4A495E41054177300BE38AE /* win32acl.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = win32acl.cpp; path = libunrar/win32acl.cpp; sourceTree = ""; };
D4A495E51054177300BE38AE /* win32stm.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = win32stm.cpp; path = libunrar/win32stm.cpp; sourceTree = ""; };
D4A96E2010545E9A0091ECB4 /* Carbon.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Carbon.framework; path = System/Library/Frameworks/Carbon.framework; sourceTree = SDKROOT; };
+ E296811D24BE4BCD00974229 /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/InfoPlist.strings; sourceTree = ""; };
+ E296811E24BE4BCD00974229 /* en */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = en; path = en.lproj/MainMenu.xib; sourceTree = ""; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -188,7 +178,6 @@
D488BC6810AF437B00B3451C /* libunrar.so in Frameworks */,
8D11072F0486CEB800E47090 /* Cocoa.framework in Frameworks */,
D4A96E2110545E9A0091ECB4 /* Carbon.framework in Frameworks */,
- D488BFB010B1F97F00B3451C /* Growl.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -285,7 +274,6 @@
D4A495761054177300BE38AE /* archive.hpp */,
D4A495771054177300BE38AE /* arcread.cpp */,
D4A495781054177300BE38AE /* array.hpp */,
- D4A495791054177300BE38AE /* beosea.cpp */,
D4A4957A1054177300BE38AE /* cmddata.cpp */,
D4A4957B1054177300BE38AE /* cmddata.hpp */,
D4A4957C1054177300BE38AE /* coder.cpp */,
@@ -329,7 +317,6 @@
D4A495A21054177300BE38AE /* loclang.hpp */,
D4A495A31054177300BE38AE /* log.cpp */,
D4A495A41054177300BE38AE /* log.hpp */,
- D4A495A51054177300BE38AE /* makefile.unix */,
D4A495A61054177300BE38AE /* match.cpp */,
D4A495A71054177300BE38AE /* match.hpp */,
D4A495A81054177300BE38AE /* model.cpp */,
@@ -337,7 +324,6 @@
D4A495AA1054177300BE38AE /* options.cpp */,
D4A495AB1054177300BE38AE /* options.hpp */,
D4A495AC1054177300BE38AE /* os.hpp */,
- D4A495AD1054177300BE38AE /* os2ea.cpp */,
D4A495AE1054177300BE38AE /* pathfn.cpp */,
D4A495AF1054177300BE38AE /* pathfn.hpp */,
D4A495B01054177300BE38AE /* rar.cpp */,
@@ -348,7 +334,6 @@
D4A495B51054177300BE38AE /* rartypes.hpp */,
D4A495B61054177300BE38AE /* rarvm.cpp */,
D4A495B71054177300BE38AE /* rarvm.hpp */,
- D4A495B81054177300BE38AE /* rarvmtbl.cpp */,
D4A495B91054177300BE38AE /* rawread.cpp */,
D4A495BA1054177300BE38AE /* rawread.hpp */,
D4A495BB1054177300BE38AE /* rdwrfn.cpp */,
@@ -361,7 +346,6 @@
D4A495C21054177300BE38AE /* rijndael.hpp */,
D4A495C31054177300BE38AE /* rs.cpp */,
D4A495C41054177300BE38AE /* rs.hpp */,
- D4A495C51054177300BE38AE /* savepos.cpp */,
D4A495C61054177300BE38AE /* savepos.hpp */,
D4A495C71054177300BE38AE /* scantree.cpp */,
D4A495C81054177300BE38AE /* scantree.hpp */,
@@ -380,10 +364,8 @@
D4A495D51054177300BE38AE /* timefn.cpp */,
D4A495D61054177300BE38AE /* timefn.hpp */,
D4A495D71054177300BE38AE /* ulinks.cpp */,
- D4A495D81054177300BE38AE /* ulinks.hpp */,
D4A495D91054177300BE38AE /* unicode.cpp */,
D4A495DA1054177300BE38AE /* unicode.hpp */,
- D4A495DB1054177300BE38AE /* unios2.cpp */,
D4A495DC1054177300BE38AE /* unpack.cpp */,
D4A495DD1054177300BE38AE /* unpack.hpp */,
D4A495DE1054177300BE38AE /* unpack15.cpp */,
@@ -403,7 +385,7 @@
/* Begin PBXLegacyTarget section */
D4A4962A105419AA00BE38AE /* libunrar */ = {
isa = PBXLegacyTarget;
- buildArgumentsString = "-f makefile.unix lib $(ACTION)";
+ buildArgumentsString = "-f makefile lib";
buildConfigurationList = D4A4962D105419C800BE38AE /* Build configuration list for PBXLegacyTarget "libunrar" */;
buildPhases = (
);
@@ -444,9 +426,17 @@
/* Begin PBXProject section */
29B97313FDCFA39411CA2CEA /* Project object */ = {
isa = PBXProject;
+ attributes = {
+ LastUpgradeCheck = 1200;
+ };
buildConfigurationList = C01FCF4E08A954540054247B /* Build configuration list for PBXProject "QuietUnrar" */;
compatibilityVersion = "Xcode 3.2";
+ developmentRegion = en;
hasScannedForEncodings = 1;
+ knownRegions = (
+ en,
+ Base,
+ );
mainGroup = 29B97314FDCFA39411CA2CEA /* QuietUnrar */;
projectDirPath = "";
projectRoot = "";
@@ -511,7 +501,7 @@
089C165CFE840E0CC02AAC07 /* InfoPlist.strings */ = {
isa = PBXVariantGroup;
children = (
- 089C165DFE840E0CC02AAC07 /* English */,
+ E296811D24BE4BCD00974229 /* en */,
);
name = InfoPlist.strings;
sourceTree = "";
@@ -519,7 +509,7 @@
1DDD58140DA1D0A300B32029 /* MainMenu.xib */ = {
isa = PBXVariantGroup;
children = (
- 1DDD58150DA1D0A300B32029 /* English */,
+ E296811E24BE4BCD00974229 /* en */,
);
name = MainMenu.xib;
sourceTree = "";
@@ -531,13 +521,14 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CODE_SIGN_IDENTITY = "-";
COPY_PHASE_STRIP = NO;
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
"\"$(SRCROOT)/Frameworks\"",
);
GCC_DYNAMIC_NO_PIC = NO;
- GCC_ENABLE_FIX_AND_CONTINUE = YES;
GCC_MODEL_TUNING = G5;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PRECOMPILE_PREFIX_HEADER = YES;
@@ -551,6 +542,7 @@
"$(inherited)",
"\"$(SRCROOT)/libunrar\"",
);
+ PRODUCT_BUNDLE_IDENTIFIER = "com.yourcompany.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = QuietUnrar;
};
name = Debug;
@@ -559,6 +551,8 @@
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
+ CLANG_ENABLE_OBJC_WEAK = YES;
+ CODE_SIGN_IDENTITY = "-";
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
FRAMEWORK_SEARCH_PATHS = (
"$(inherited)",
@@ -576,6 +570,7 @@
"$(inherited)",
"\"$(SRCROOT)/libunrar\"",
);
+ PRODUCT_BUNDLE_IDENTIFIER = "com.yourcompany.${PRODUCT_NAME:rfc1034identifier}";
PRODUCT_NAME = QuietUnrar;
};
name = Release;
@@ -583,52 +578,102 @@
C01FCF4F08A954540054247B /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
- ARCHS = "$(ARCHS_STANDARD_64_BIT)";
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
+ ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
- GCC_ENABLE_OBJC_GC = required;
+ GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
+ MACOSX_DEPLOYMENT_TARGET = 10.15;
ONLY_ACTIVE_ARCH = YES;
- PREBINDING = NO;
- SDKROOT = macosx10.6;
- VALID_ARCHS = "i386 x86_64";
+ SDKROOT = macosx;
+ VALID_ARCHS = "arm64 arm64e i386 x86_64";
};
name = Debug;
};
C01FCF5008A954540054247B /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
- ARCHS = "$(ARCHS_STANDARD_64_BIT)";
+ CLANG_ANALYZER_LOCALIZABILITY_NONLOCALIZED = YES;
+ CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
+ CLANG_WARN_BOOL_CONVERSION = YES;
+ CLANG_WARN_COMMA = YES;
+ CLANG_WARN_CONSTANT_CONVERSION = YES;
+ CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
+ CLANG_WARN_EMPTY_BODY = YES;
+ CLANG_WARN_ENUM_CONVERSION = YES;
+ CLANG_WARN_INFINITE_RECURSION = YES;
+ CLANG_WARN_INT_CONVERSION = YES;
+ CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
+ CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
+ CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
+ CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
+ CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
+ CLANG_WARN_STRICT_PROTOTYPES = YES;
+ CLANG_WARN_SUSPICIOUS_MOVE = YES;
+ CLANG_WARN_UNREACHABLE_CODE = YES;
+ CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
+ ENABLE_STRICT_OBJC_MSGSEND = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
- GCC_ENABLE_OBJC_GC = required;
+ GCC_NO_COMMON_BLOCKS = YES;
GCC_VERSION = com.apple.compilers.llvm.clang.1_0;
+ GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES;
+ GCC_WARN_UNDECLARED_SELECTOR = YES;
+ GCC_WARN_UNINITIALIZED_AUTOS = YES;
+ GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
- PREBINDING = NO;
- SDKROOT = macosx10.6;
- VALID_ARCHS = "i386 x86_64";
+ MACOSX_DEPLOYMENT_TARGET = 10.15;
+ SDKROOT = macosx;
+ VALID_ARCHS = "arm64 arm64e i386 x86_64";
};
name = Release;
};
D4A4962B105419AA00BE38AE /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ CLANG_ENABLE_OBJC_WEAK = YES;
COPY_PHASE_STRIP = NO;
GCC_DYNAMIC_NO_PIC = NO;
GCC_OPTIMIZATION_LEVEL = 0;
PRODUCT_NAME = libunrar;
+ SDKROOT = macosx;
};
name = Debug;
};
D4A4962C105419AA00BE38AE /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
+ CLANG_ENABLE_OBJC_WEAK = YES;
COPY_PHASE_STRIP = YES;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
- GCC_ENABLE_FIX_AND_CONTINUE = NO;
PRODUCT_NAME = libunrar;
+ SDKROOT = macosx;
ZERO_LINK = NO;
};
name = Release;
diff --git a/QuietUnrarAppDelegate.m b/QuietUnrarAppDelegate.m
index eb27302..bb0393d 100644
--- a/QuietUnrarAppDelegate.m
+++ b/QuietUnrarAppDelegate.m
@@ -7,7 +7,7 @@
//
#import
-#import
+//#import
#import "QuietUnrarAppDelegate.h"
#import "libunrar/dll.hpp"
@@ -91,16 +91,16 @@ int callbackFunction(UINT message, LPARAM userData, LPARAM parameterOne, LPARAM
for (NSString * filename in arrayOfFilenames) {
BOOL extracted = [self extractRarWith:filename];
if (extracted) {
- [GrowlApplicationBridge setGrowlDelegate:@""];
+ //[GrowlApplicationBridge setGrowlDelegate:@""];
- [GrowlApplicationBridge
- notifyWithTitle:@"QuietUnrar: Extraction Complete"
- description:[NSString stringWithFormat:@"The archive %@ was successfully extracted", filename]
- notificationName:@"QuietUnrarExtractionComplete"
- iconData:nil
- priority:0
- isSticky:NO
- clickContext:nil];
+// [GrowlApplicationBridge
+// notifyWithTitle:@"QuietUnrar: Extraction Complete"
+// description:[NSString stringWithFormat:@"The archive %@ was successfully extracted", filename]
+// notificationName:@"QuietUnrarExtractionComplete"
+// iconData:nil
+// priority:0
+// isSticky:NO
+// clickContext:nil];
}
}
}
diff --git a/English.lproj/InfoPlist.strings b/en.lproj/InfoPlist.strings
similarity index 100%
rename from English.lproj/InfoPlist.strings
rename to en.lproj/InfoPlist.strings
diff --git a/en.lproj/MainMenu.xib b/en.lproj/MainMenu.xib
new file mode 100644
index 0000000..11bada9
--- /dev/null
+++ b/en.lproj/MainMenu.xib
@@ -0,0 +1,669 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/libunrar/UnRAR.vcxproj b/libunrar/UnRAR.vcxproj
new file mode 100644
index 0000000..512bcf1
--- /dev/null
+++ b/libunrar/UnRAR.vcxproj
@@ -0,0 +1,279 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Debug
+ x64
+
+
+ Release
+ Win32
+
+
+ Release
+ x64
+
+
+
+ {95CC809B-03FC-4EDB-BB20-FD07A698C05F}
+ UnRAR
+ Win32Proj
+ 8.1
+
+
+
+ Application
+ v140_xp
+ MultiByte
+ true
+
+
+ Application
+ v140_xp
+ MultiByte
+
+
+ Application
+ v140_xp
+ MultiByte
+ false
+
+
+ Application
+ v140_xp
+ MultiByte
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_ProjectFileVersion>14.0.24720.0
+
+
+ build\unrar32\$(Configuration)\
+ build\unrar32\$(Configuration)\obj\
+ true
+ false
+
+
+ build\unrar64\$(Configuration)\
+ build\unrar64\$(Configuration)\obj\
+ true
+ false
+
+
+ build\unrar32\$(Configuration)\
+ build\unrar32\$(Configuration)\obj\
+ false
+ false
+
+
+ build\unrar64\$(Configuration)\
+ build\unrar64\$(Configuration)\obj\
+ false
+ false
+
+
+
+ /MP %(AdditionalOptions)
+ Disabled
+ UNRAR;%(PreprocessorDefinitions)
+ false
+ EnableFastChecks
+ MultiThreadedDebug
+ false
+ Use
+ rar.hpp
+ Level3
+ ProgramDatabase
+ StdCall
+ 4007;4996;%(DisableSpecificWarnings)
+ NoExtensions
+
+
+ true
+ Console
+ MachineX86
+
+
+
+
+ X64
+
+
+ /MP %(AdditionalOptions)
+ Disabled
+ UNRAR;%(PreprocessorDefinitions)
+ false
+ EnableFastChecks
+ MultiThreadedDebug
+ false
+ Use
+ rar.hpp
+ Level3
+ ProgramDatabase
+ StdCall
+ 4007;4996;%(DisableSpecificWarnings)
+ NotSet
+
+
+ true
+ Console
+ MachineX64
+
+
+
+
+ /MP %(AdditionalOptions)
+ MaxSpeed
+ true
+ Neither
+ true
+ false
+ UNRAR;%(PreprocessorDefinitions)
+ false
+ MultiThreaded
+ Default
+ true
+ true
+ NoExtensions
+ Precise
+ false
+ Use
+ rar.hpp
+ Level3
+ ProgramDatabase
+ StdCall
+ 4007;4996;%(DisableSpecificWarnings)
+
+
+ true
+ Console
+ true
+ true
+
+ MachineX86
+
+
+
+
+ X64
+
+
+ /MP %(AdditionalOptions)
+ MinSpace
+ true
+ Neither
+ true
+ false
+ UNRAR;%(PreprocessorDefinitions)
+ false
+ false
+ MultiThreaded
+ true
+ true
+ false
+ Use
+ rar.hpp
+ Level3
+ ProgramDatabase
+ StdCall
+ 4007;4996;%(DisableSpecificWarnings)
+ NotSet
+
+
+ true
+ Console
+ true
+ true
+
+ MachineX64
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Create
+ Create
+ Create
+ Create
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libunrar/UnRARDll.vcxproj b/libunrar/UnRARDll.vcxproj
new file mode 100644
index 0000000..ec5c17b
--- /dev/null
+++ b/libunrar/UnRARDll.vcxproj
@@ -0,0 +1,420 @@
+
+
+
+
+ Debug
+ Win32
+
+
+ Debug
+ x64
+
+
+ release_nocrypt
+ Win32
+
+
+ release_nocrypt
+ x64
+
+
+ Release
+ Win32
+
+
+ Release
+ x64
+
+
+
+ UnRAR
+ {E815C46C-36C4-499F-BBC2-E772C6B17971}
+ UnRAR
+ Win32Proj
+ 8.1
+
+
+
+ DynamicLibrary
+ v140_xp
+ MultiByte
+ true
+
+
+ DynamicLibrary
+ v140_xp
+ MultiByte
+ true
+
+
+ DynamicLibrary
+ v140_xp
+ MultiByte
+
+
+ DynamicLibrary
+ v140_xp
+ MultiByte
+ false
+
+
+ DynamicLibrary
+ v140_xp
+ MultiByte
+ false
+
+
+ DynamicLibrary
+ v140_xp
+ MultiByte
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ <_ProjectFileVersion>14.0.24720.0
+
+
+ build\unrardll32\$(Configuration)\
+ build\unrardll32\$(Configuration)\obj\
+ true
+ true
+
+
+ build\unrardll64\$(Configuration)\
+ build\unrardll64\$(Configuration)\obj\
+ true
+ true
+
+
+ build\unrardll32\$(Configuration)\
+ build\unrardll32\$(Configuration)\obj\
+ false
+ true
+
+
+ build\unrardll64\$(Configuration)\
+ build\unrardll64\$(Configuration)\obj\
+ false
+ true
+
+
+ build\unrardll32\$(Configuration)\
+ build\unrardll32\$(Configuration)\obj\
+ false
+ true
+
+
+ build\unrardll64\$(Configuration)\
+ build\unrardll64\$(Configuration)\obj\
+ false
+ true
+
+
+
+ /MP %(AdditionalOptions)
+ Disabled
+ RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions)
+ false
+ Sync
+ EnableFastChecks
+ MultiThreadedDebug
+ 4Bytes
+ false
+ Use
+ rar.hpp
+ Level3
+ ProgramDatabase
+ Cdecl
+ 4007;4996;%(DisableSpecificWarnings)
+ NoExtensions
+
+
+ $(OutDir)unrar.dll
+ dll.def
+ true
+ Console
+ MachineX86
+
+
+
+
+ X64
+
+
+ /MP %(AdditionalOptions)
+ Disabled
+ RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions)
+ false
+ Sync
+ EnableFastChecks
+ MultiThreadedDebug
+ 4Bytes
+ false
+ Use
+ rar.hpp
+ Level3
+ ProgramDatabase
+ Cdecl
+ 4007;4996;%(DisableSpecificWarnings)
+ NotSet
+
+
+ $(OutDir)unrar.dll
+ dll.def
+ true
+ Console
+ MachineX64
+
+
+
+
+ /MP %(AdditionalOptions)
+ MaxSpeed
+ true
+ Neither
+ true
+ false
+ RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions)
+ false
+ Sync
+ MultiThreaded
+ 4Bytes
+ true
+ true
+ NoExtensions
+ Precise
+ false
+ Use
+ rar.hpp
+ Level3
+ ProgramDatabase
+ Cdecl
+ 4007;4996;%(DisableSpecificWarnings)
+
+
+ /SAFESEH %(AdditionalOptions)
+ $(OutDir)unrar.dll
+ dll.def
+ true
+ Console
+ true
+ true
+
+ MachineX86
+
+
+
+
+ X64
+
+
+ /MP %(AdditionalOptions)
+ MaxSpeed
+ true
+ Neither
+ true
+ false
+ RARDLL;UNRAR;SILENT;%(PreprocessorDefinitions)
+ false
+ false
+ Sync
+ MultiThreaded
+ 4Bytes
+ true
+ true
+ false
+ Use
+ rar.hpp
+ Level3
+ ProgramDatabase
+ Cdecl
+ 4007;4996;%(DisableSpecificWarnings)
+ NotSet
+
+
+ $(OutDir)unrar.dll
+ dll.def
+ true
+ Console
+ true
+ true
+
+ MachineX64
+
+
+
+
+ /MP %(AdditionalOptions)
+ MaxSpeed
+ true
+ Neither
+ true
+ false
+ RARDLL;UNRAR;SILENT;RAR_NOCRYPT;%(PreprocessorDefinitions)
+ false
+ Sync
+ MultiThreaded
+ 4Bytes
+ true
+ true
+ NoExtensions
+ Precise
+ false
+ Use
+ rar.hpp
+ Level3
+ ProgramDatabase
+ Cdecl
+ 4007;4996;%(DisableSpecificWarnings)
+
+
+ /SAFESEH %(AdditionalOptions)
+ $(OutDir)unrar.dll
+ dll_nocrypt.def
+ true
+ Console
+ true
+ true
+
+ MachineX86
+
+
+
+
+ X64
+
+
+ /MP %(AdditionalOptions)
+ MaxSpeed
+ true
+ Neither
+ true
+ false
+ RARDLL;UNRAR;SILENT;RAR_NOCRYPT;%(PreprocessorDefinitions)
+ false
+ false
+ Sync
+ MultiThreaded
+ 4Bytes
+ true
+ true
+ false
+ Use
+ rar.hpp
+ Level3
+ ProgramDatabase
+ StdCall
+ 4007;4996;%(DisableSpecificWarnings)
+ NotSet
+
+
+ $(OutDir)unrar.dll
+ dll_nocrypt.def
+ true
+ Console
+ true
+ true
+
+ MachineX64
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Create
+ Create
+ Create
+ Create
+ Create
+ Create
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/libunrar/acknow.txt b/libunrar/acknow.txt
new file mode 100644
index 0000000..a68b672
--- /dev/null
+++ b/libunrar/acknow.txt
@@ -0,0 +1,92 @@
+ ACKNOWLEDGMENTS
+
+* We used "Screaming Fast Galois Field Arithmetic Using Intel
+ SIMD Instructions" paper by James S. Plank, Kevin M. Greenan
+ and Ethan L. Miller to improve Reed-Solomon coding performance.
+ Also we are grateful to Artem Drobanov and Bulat Ziganshin
+ for samples and ideas allowed to make Reed-Solomon coding
+ more efficient.
+
+* RAR text compression algorithm is based on Dmitry Shkarin PPMII
+ and Dmitry Subbotin carryless rangecoder public domain source code.
+ You may find it in ftp.elf.stuba.sk/pub/pc/pack.
+
+* RAR encryption includes parts of code from Szymon Stefanek
+ and Brian Gladman AES implementations also as Steve Reid SHA-1 source.
+
+ ---------------------------------------------------------------------------
+ Copyright (c) 2002, Dr Brian Gladman < >, Worcester, UK.
+ All rights reserved.
+
+ LICENSE TERMS
+
+ The free distribution and use of this software in both source and binary
+ form is allowed (with or without changes) provided that:
+
+ 1. distributions of this source code include the above copyright
+ notice, this list of conditions and the following disclaimer;
+
+ 2. distributions in binary form include the above copyright
+ notice, this list of conditions and the following disclaimer
+ in the documentation and/or other associated materials;
+
+ 3. the copyright holder's name is not used to endorse products
+ built using this software without specific written permission.
+
+ ALTERNATIVELY, provided that this notice is retained in full, this product
+ may be distributed under the terms of the GNU General Public License (GPL),
+ in which case the provisions of the GPL apply INSTEAD OF those given above.
+
+ DISCLAIMER
+
+ This software is provided 'as is' with no explicit or implied warranties
+ in respect of its properties, including, but not limited to, correctness
+ and/or fitness for purpose.
+ ---------------------------------------------------------------------------
+
+ Source code of this package also as other cryptographic technology
+ and computing project related links are available on Brian Gladman's
+ web site: http://www.gladman.me.uk
+
+* RAR uses CRC32 function based on Intel Slicing-by-8 algorithm.
+ Original Intel Slicing-by-8 code is available here:
+
+ http://sourceforge.net/projects/slicing-by-8/
+
+ Original Intel Slicing-by-8 code is licensed under BSD License
+ available at http://www.opensource.org/licenses/bsd-license.html
+
+ Copyright (c) 2004-2006 Intel Corporation.
+ All Rights Reserved
+
+ Redistribution and use in source and binary forms, with or without
+ modification, are permitted provided that the following conditions
+ are met:
+
+ Redistributions of source code must retain the above copyright notice,
+ this list of conditions and the following disclaimer.
+
+ Redistributions in binary form must reproduce the above copyright
+ notice, this list of conditions and the following disclaimer
+ in the documentation and/or other materials provided with
+ the distribution.
+
+ THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
+ FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
+ HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+ DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ SUCH DAMAGE.
+
+* RAR archives may optionally include BLAKE2sp hash ( https://blake2.net ),
+ designed by Jean-Philippe Aumasson, Samuel Neves, Zooko Wilcox-O'Hearn
+ and Christian Winnerlein.
+
+* Useful hints provided by Alexander Khoroshev and Bulat Ziganshin allowed
+ to significantly improve RAR compression and speed.
diff --git a/libunrar/arccmt.cpp b/libunrar/arccmt.cpp
index 0960425..8b7e498 100644
--- a/libunrar/arccmt.cpp
+++ b/libunrar/arccmt.cpp
@@ -1,59 +1,74 @@
-bool Archive::GetComment(Array *CmtData,Array *CmtDataW)
+static bool IsAnsiEscComment(const wchar *Data,size_t Size);
+
+bool Archive::GetComment(Array *CmtData)
{
if (!MainComment)
- return(false);
- SaveFilePos SavePos(*this);
+ return false;
+ int64 SavePos=Tell();
+ bool Success=DoGetComment(CmtData);
+ Seek(SavePos,SEEK_SET);
+ return Success;
+}
+
+bool Archive::DoGetComment(Array *CmtData)
+{
#ifndef SFX_MODULE
- ushort CmtLength;
- if (OldFormat)
+ uint CmtLength;
+ if (Format==RARFMT14)
{
- Seek(SFXSize+SIZEOF_OLDMHD,SEEK_SET);
+ Seek(SFXSize+SIZEOF_MAINHEAD14,SEEK_SET);
CmtLength=GetByte();
CmtLength+=(GetByte()<<8);
}
else
#endif
{
- if (NewMhd.Flags & MHD_COMMENT)
+ if (MainHead.CommentInHeader)
{
- Seek(SFXSize+SIZEOF_MARKHEAD+SIZEOF_NEWMHD,SEEK_SET);
- ReadHeader();
+ // Old style (RAR 2.9) archive comment embedded into the main
+ // archive header.
+ Seek(SFXSize+SIZEOF_MARKHEAD3+SIZEOF_MAINHEAD3,SEEK_SET);
+ if (!ReadHeader() || GetHeaderType()!=HEAD3_CMT)
+ return false;
}
else
{
- Seek(SFXSize+SIZEOF_MARKHEAD+NewMhd.HeadSize,SEEK_SET);
- return(SearchSubBlock(SUBHEAD_TYPE_CMT)!=0 && ReadCommentData(CmtData,CmtDataW)!=0);
+ // Current (RAR 3.0+) version of archive comment.
+ Seek(GetStartPos(),SEEK_SET);
+ return SearchSubBlock(SUBHEAD_TYPE_CMT)!=0 && ReadCommentData(CmtData);
}
#ifndef SFX_MODULE
- if (CommHead.HeadCRC!=HeaderCRC)
+ // Old style (RAR 2.9) comment header embedded into the main
+ // archive header.
+ if (BrokenHeader || CommHead.HeadSize UNP_VER || CommHead.Method > 0x35))
- return(false);
+ if (Format!=RARFMT14 && (CommHead.UnpVer < 15 || CommHead.UnpVer > VER_UNPACK || CommHead.Method > 0x35))
+ return false;
ComprDataIO DataIO;
- Unpack Unpack(&DataIO);
- Unpack.Init();
DataIO.SetTestMode(true);
uint UnpCmtLength;
- if (OldFormat)
+ if (Format==RARFMT14)
{
-#ifdef NOCRYPT
- return(false);
+#ifdef RAR_NOCRYPT
+ return false;
#else
UnpCmtLength=GetByte();
UnpCmtLength+=(GetByte()<<8);
+ if (CmtLength<2)
+ return false;
CmtLength-=2;
DataIO.SetCmt13Encryption();
+ CommHead.UnpVer=15;
#endif
}
else
@@ -61,157 +76,110 @@ bool Archive::GetComment(Array *CmtData,Array *CmtDataW)
DataIO.SetFiles(this,NULL);
DataIO.EnableShowProgress(false);
DataIO.SetPackedSizeToRead(CmtLength);
- Unpack.SetDestSize(UnpCmtLength);
- Unpack.DoUnpack(CommHead.UnpVer,false);
+ DataIO.UnpHash.Init(HASH_CRC32,1);
+ DataIO.SetNoFileHeader(true); // this->FileHead is not filled yet.
- if (!OldFormat && ((~DataIO.UnpFileCRC)&0xffff)!=CommHead.CommCRC)
+ Unpack CmtUnpack(&DataIO);
+ CmtUnpack.Init(0x10000,false);
+ CmtUnpack.SetDestSize(UnpCmtLength);
+ CmtUnpack.DoUnpack(CommHead.UnpVer,false);
+
+ if (Format!=RARFMT14 && (DataIO.UnpHash.GetCRC32()&0xffff)!=CommHead.CommCRC)
{
- Log(FileName,St(MLogCommBrk));
- Alarm();
- return(false);
+ uiMsg(UIERROR_CMTBROKEN,FileName);
+ return false;
}
else
{
byte *UnpData;
size_t UnpDataSize;
DataIO.GetUnpackedData(&UnpData,&UnpDataSize);
- CmtData->Alloc(UnpDataSize);
- memcpy(&((*CmtData)[0]),UnpData,UnpDataSize);
+ if (UnpDataSize>0)
+ {
+#ifdef _WIN_ALL
+ // If we ever decide to extend it to Android, we'll need to alloc
+ // 4x memory for OEM to UTF-8 output here.
+ OemToCharBuffA((char *)UnpData,(char *)UnpData,(DWORD)UnpDataSize);
+#endif
+ CmtData->Alloc(UnpDataSize+1);
+ memset(CmtData->Addr(0),0,CmtData->Size()*sizeof(wchar));
+ CharToWide((char *)UnpData,CmtData->Addr(0),CmtData->Size());
+ CmtData->Alloc(wcslen(CmtData->Addr(0)));
+ }
}
}
else
{
- CmtData->Alloc(CmtLength);
-
- Read(&((*CmtData)[0]),CmtLength);
- if (!OldFormat && CommHead.CommCRC!=(~CRC(0xffffffff,&((*CmtData)[0]),CmtLength)&0xffff))
+ if (CmtLength==0)
+ return false;
+ Array CmtRaw(CmtLength);
+ int ReadSize=Read(&CmtRaw[0],CmtLength);
+ if (ReadSize>=0 && (uint)ReadSizeReset();
- return(false);
+ CmtLength=ReadSize;
+ CmtRaw.Alloc(CmtLength);
}
- }
-#endif
-#if defined(_WIN_32) && !defined(_WIN_CE)
- if (CmtData->Size()>0)
- {
- size_t CmtSize=CmtData->Size();
- OemToCharBuff((char *)CmtData->Addr(),(char *)CmtData->Addr(),(DWORD)CmtSize);
- if (CmtDataW!=NULL)
+ if (Format!=RARFMT14 && CommHead.CommCRC!=(~CRC32(0xffffffff,&CmtRaw[0],CmtLength)&0xffff))
{
- CmtDataW->Alloc(CmtSize+1);
- CmtData->Push(0);
- CharToWide((char *)CmtData->Addr(),CmtDataW->Addr(),CmtSize+1);
- CmtData->Alloc(CmtSize);
- CmtDataW->Alloc(strlenw(CmtDataW->Addr()));
+ uiMsg(UIERROR_CMTBROKEN,FileName);
+ return false;
}
+ CmtData->Alloc(CmtLength+1);
+ CmtRaw.Push(0);
+#ifdef _WIN_ALL
+ // If we ever decide to extend it to Android, we'll need to alloc
+ // 4x memory for OEM to UTF-8 output here.
+ OemToCharA((char *)&CmtRaw[0],(char *)&CmtRaw[0]);
+#endif
+ CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size());
+ CmtData->Alloc(wcslen(CmtData->Addr(0)));
}
#endif
- return(CmtData->Size()>0);
+ return CmtData->Size() > 0;
}
-size_t Archive::ReadCommentData(Array *CmtData,Array *CmtDataW)
+bool Archive::ReadCommentData(Array *CmtData)
{
- bool Unicode=SubHead.SubFlags & SUBHEAD_FLAGS_CMT_UNICODE;
- if (!ReadSubData(CmtData,NULL))
- return(0);
- size_t CmtSize=CmtData->Size();
- if (Unicode)
- {
- CmtSize/=2;
- Array DataW(CmtSize+1);
- RawToWide(CmtData->Addr(),DataW.Addr(),CmtSize);
- DataW[CmtSize]=0;
- size_t DestSize=CmtSize*4;
- CmtData->Alloc(DestSize+1);
- WideToChar(DataW.Addr(),(char *)CmtData->Addr(),DestSize);
- (*CmtData)[DestSize]=0;
- CmtSize=strlen((char *)CmtData->Addr());
- CmtData->Alloc(CmtSize);
- if (CmtDataW!=NULL)
- {
- *CmtDataW=DataW;
- CmtDataW->Alloc(CmtSize);
- }
- }
+ Array CmtRaw;
+ if (!ReadSubData(&CmtRaw,NULL,false))
+ return false;
+ size_t CmtSize=CmtRaw.Size();
+ CmtRaw.Push(0);
+ CmtData->Alloc(CmtSize+1);
+ if (Format==RARFMT50)
+ UtfToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size());
else
- if (CmtDataW!=NULL)
+ if ((SubHead.SubFlags & SUBHEAD_FLAGS_CMT_UNICODE)!=0)
{
- CmtData->Push(0);
- CmtDataW->Alloc(CmtSize+1);
- CharToWide((char *)CmtData->Addr(),CmtDataW->Addr(),CmtSize+1);
- CmtData->Alloc(CmtSize);
- CmtDataW->Alloc(strlenw(CmtDataW->Addr()));
+ RawToWide(&CmtRaw[0],CmtData->Addr(0),CmtSize/2);
+ (*CmtData)[CmtSize/2]=0;
+
}
- return(CmtSize);
+ else
+ {
+ CharToWide((char *)&CmtRaw[0],CmtData->Addr(0),CmtData->Size());
+ }
+ CmtData->Alloc(wcslen(CmtData->Addr(0))); // Set buffer size to actual comment length.
+ return true;
}
void Archive::ViewComment()
{
-#ifndef GUI
if (Cmd->DisableComment)
return;
- Array CmtBuf;
- if (GetComment(&CmtBuf,NULL))
+ Array CmtBuf;
+ if (GetComment(&CmtBuf)) // In GUI too, so "Test" command detects broken comments.
{
size_t CmtSize=CmtBuf.Size();
- char *ChPtr=(char *)memchr(&CmtBuf[0],0x1A,CmtSize);
+ wchar *ChPtr=wcschr(&CmtBuf[0],0x1A);
if (ChPtr!=NULL)
- CmtSize=ChPtr-(char *)&CmtBuf[0];
- mprintf("\n");
- OutComment((char *)&CmtBuf[0],CmtSize);
- }
-#endif
-}
-
-
-#ifndef SFX_MODULE
-void Archive::ViewFileComment()
-{
- if (!(NewLhd.Flags & LHD_COMMENT) || Cmd->DisableComment || OldFormat)
- return;
-#ifndef GUI
- mprintf(St(MFileComment));
-#endif
- const int MaxSize=0x8000;
- Array CmtBuf(MaxSize);
- SaveFilePos SavePos(*this);
- Seek(CurBlockPos+SIZEOF_NEWLHD+NewLhd.NameSize,SEEK_SET);
- int64 SaveCurBlockPos=CurBlockPos;
- int64 SaveNextBlockPos=NextBlockPos;
-
- size_t Size=ReadHeader();
-
- CurBlockPos=SaveCurBlockPos;
- NextBlockPos=SaveNextBlockPos;
-
- if (Size<7 || CommHead.HeadType!=COMM_HEAD)
- return;
- if (CommHead.HeadCRC!=HeaderCRC)
- {
-#ifndef GUI
- Log(FileName,St(MLogCommHead));
-#endif
- return;
- }
- if (CommHead.UnpVer < 15 || CommHead.UnpVer > UNP_VER ||
- CommHead.Method > 0x30 || CommHead.UnpSize > MaxSize)
- return;
- Read(&CmtBuf[0],CommHead.UnpSize);
- if (CommHead.CommCRC!=((~CRC(0xffffffff,&CmtBuf[0],CommHead.UnpSize)&0xffff)))
- {
- Log(FileName,St(MLogBrokFCmt));
- }
- else
- {
- OutComment(&CmtBuf[0],CommHead.UnpSize);
-#ifndef GUI
- mprintf("\n");
-#endif
+ CmtSize=ChPtr-&CmtBuf[0];
+ mprintf(L"\n");
+ OutComment(&CmtBuf[0],CmtSize);
}
}
-#endif
+
+
diff --git a/libunrar/archive.cpp b/libunrar/archive.cpp
index 5f807b5..8c5a1da 100644
--- a/libunrar/archive.cpp
+++ b/libunrar/archive.cpp
@@ -1,133 +1,150 @@
#include "rar.hpp"
-#ifndef SHELL_EXT
#include "arccmt.cpp"
-#endif
Archive::Archive(RAROptions *InitCmd)
{
- Cmd=InitCmd==NULL ? &DummyCmd:InitCmd;
+ Cmd=NULL; // Just in case we'll have an exception in 'new' below.
+
+ DummyCmd=(InitCmd==NULL);
+ Cmd=DummyCmd ? (new RAROptions):InitCmd;
+
OpenShared=Cmd->OpenShared;
- OldFormat=false;
+ Format=RARFMT15;
Solid=false;
Volume=false;
MainComment=false;
Locked=false;
Signed=false;
- NotFirstVolume=false;
+ FirstVolume=false;
+ NewNumbering=false;
SFXSize=0;
LatestTime.Reset();
Protected=false;
Encrypted=false;
- BrokenFileHeader=false;
+ FailedHeaderDecryption=false;
+ BrokenHeader=false;
LastReadBlock=0;
CurBlockPos=0;
NextBlockPos=0;
- RecoveryPos=SIZEOF_MARKHEAD;
- RecoverySectors=-1;
- memset(&NewMhd,0,sizeof(NewMhd));
- NewMhd.HeadType=MAIN_HEAD;
- NewMhd.HeadSize=SIZEOF_NEWMHD;
- HeaderCRC=0;
+ memset(&MainHead,0,sizeof(MainHead));
+ memset(&CryptHead,0,sizeof(CryptHead));
+ memset(&EndArcHead,0,sizeof(EndArcHead));
+
+ VolNumber=0;
VolWrite=0;
AddingFilesSize=0;
AddingHeadersSize=0;
-#if !defined(SHELL_EXT) && !defined(NOCRYPT)
- *HeadersSalt=0;
- *SubDataSalt=0;
-#endif
*FirstVolumeName=0;
- *FirstVolumeNameW=0;
Splitting=false;
NewArchive=false;
SilentOpen=false;
+#ifdef USE_QOPEN
+ ProhibitQOpen=false;
+#endif
+
+}
+
+
+Archive::~Archive()
+{
+ if (DummyCmd)
+ delete Cmd;
}
-#ifndef SHELL_EXT
void Archive::CheckArc(bool EnableBroken)
{
if (!IsArchive(EnableBroken))
{
- Log(FileName,St(MBadArc),FileName);
- ErrHandler.Exit(FATAL_ERROR);
+ // If FailedHeaderDecryption is set, we already reported that archive
+ // password is incorrect.
+ if (!FailedHeaderDecryption)
+ uiMsg(UIERROR_BADARCHIVE,FileName);
+ ErrHandler.Exit(RARX_FATAL);
}
}
-#endif
-#if !defined(SHELL_EXT) && !defined(SFX_MODULE)
-void Archive::CheckOpen(char *Name,wchar *NameW)
+#if !defined(SFX_MODULE)
+void Archive::CheckOpen(const wchar *Name)
{
- TOpen(Name,NameW);
+ TOpen(Name);
CheckArc(false);
}
#endif
-bool Archive::WCheckOpen(char *Name,wchar *NameW)
+bool Archive::WCheckOpen(const wchar *Name)
{
- if (!WOpen(Name,NameW))
- return(false);
+ if (!WOpen(Name))
+ return false;
if (!IsArchive(false))
{
-#ifndef SHELL_EXT
- Log(FileName,St(MNotRAR),FileName);
-#endif
+ uiMsg(UIERROR_BADARCHIVE,FileName);
Close();
- return(false);
+ return false;
}
- return(true);
+ return true;
}
-bool Archive::IsSignature(byte *D)
+RARFORMAT Archive::IsSignature(const byte *D,size_t Size)
{
- bool Valid=false;
- if (D[0]==0x52)
+ RARFORMAT Type=RARFMT_NONE;
+ if (Size>=1 && D[0]==0x52)
#ifndef SFX_MODULE
- if (D[1]==0x45 && D[2]==0x7e && D[3]==0x5e)
- {
- OldFormat=true;
- Valid=true;
- }
+ if (Size>=4 && D[1]==0x45 && D[2]==0x7e && D[3]==0x5e)
+ Type=RARFMT14;
else
#endif
- if (D[1]==0x61 && D[2]==0x72 && D[3]==0x21 && D[4]==0x1a && D[5]==0x07 && D[6]==0x00)
+ if (Size>=7 && D[1]==0x61 && D[2]==0x72 && D[3]==0x21 && D[4]==0x1a && D[5]==0x07)
{
- OldFormat=false;
- Valid=true;
+ // We check the last signature byte, so we can return a sensible
+ // warning in case we'll want to change the archive format
+ // sometimes in the future.
+ if (D[6]==0)
+ Type=RARFMT15;
+ else
+ if (D[6]==1)
+ Type=RARFMT50;
+ else
+ if (D[6]>1 && D[6]<5)
+ Type=RARFMT_FUTURE;
}
- return(Valid);
+ return Type;
}
bool Archive::IsArchive(bool EnableBroken)
{
Encrypted=false;
+ BrokenHeader=false; // Might be left from previous volume.
+
#ifndef SFX_MODULE
if (IsDevice())
{
-#ifndef SHELL_EXT
- Log(FileName,St(MInvalidName),FileName);
-#endif
- return(false);
+ uiMsg(UIERROR_INVALIDNAME,FileName,FileName);
+ return false;
}
#endif
- if (Read(MarkHead.Mark,SIZEOF_MARKHEAD)!=SIZEOF_MARKHEAD)
- return(false);
+ if (Read(MarkHead.Mark,SIZEOF_MARKHEAD3)!=SIZEOF_MARKHEAD3)
+ return false;
SFXSize=0;
- if (IsSignature(MarkHead.Mark))
+
+ RARFORMAT Type;
+ if ((Type=IsSignature(MarkHead.Mark,SIZEOF_MARKHEAD3))!=RARFMT_NONE)
{
- if (OldFormat)
- Seek(0,SEEK_SET);
+ Format=Type;
+ if (Format==RARFMT14)
+ Seek(Tell()-SIZEOF_MARKHEAD3,SEEK_SET);
}
else
{
@@ -135,9 +152,10 @@ bool Archive::IsArchive(bool EnableBroken)
long CurPos=(long)Tell();
int ReadSize=Read(&Buffer[0],Buffer.Size()-16);
for (int I=0;I0 && CurPos<28 && ReadSize>31)
+ Format=Type;
+ if (Format==RARFMT14 && I>0 && CurPos<28 && ReadSize>31)
{
char *D=&Buffer[28-CurPos];
if (D[0]!=0x52 || D[1]!=0x53 || D[2]!=0x46 || D[3]!=0x58)
@@ -145,98 +163,111 @@ bool Archive::IsArchive(bool EnableBroken)
}
SFXSize=CurPos+I;
Seek(SFXSize,SEEK_SET);
- if (!OldFormat)
- Read(MarkHead.Mark,SIZEOF_MARKHEAD);
+ if (Format==RARFMT15 || Format==RARFMT50)
+ Read(MarkHead.Mark,SIZEOF_MARKHEAD3);
break;
}
if (SFXSize==0)
- return(false);
+ return false;
}
- ReadHeader();
- SeekToNext();
-#ifndef SFX_MODULE
- if (OldFormat)
+ if (Format==RARFMT_FUTURE)
{
- NewMhd.Flags=OldMhd.Flags & 0x3f;
- NewMhd.HeadSize=OldMhd.HeadSize;
+ uiMsg(UIERROR_NEWRARFORMAT,FileName);
+ return false;
+ }
+ if (Format==RARFMT50) // RAR 5.0 signature is by one byte longer.
+ {
+ if (Read(MarkHead.Mark+SIZEOF_MARKHEAD3,1)!=1 || MarkHead.Mark[SIZEOF_MARKHEAD3]!=0)
+ return false;
+ MarkHead.HeadSize=SIZEOF_MARKHEAD5;
}
else
-#endif
- {
- if (HeaderCRC!=NewMhd.HeadCRC)
- {
-#ifndef SHELL_EXT
- Log(FileName,St(MLogMainHead));
-#endif
- Alarm();
- if (!EnableBroken)
- return(false);
- }
- }
- Volume=(NewMhd.Flags & MHD_VOLUME);
- Solid=(NewMhd.Flags & MHD_SOLID)!=0;
- MainComment=(NewMhd.Flags & MHD_COMMENT)!=0;
- Locked=(NewMhd.Flags & MHD_LOCK)!=0;
- Signed=(NewMhd.PosAV!=0);
- Protected=(NewMhd.Flags & MHD_PROTECT)!=0;
- Encrypted=(NewMhd.Flags & MHD_PASSWORD)!=0;
+ MarkHead.HeadSize=SIZEOF_MARKHEAD3;
- if (NewMhd.EncryptVer>UNP_VER)
- {
#ifdef RARDLL
- Cmd->DllError=ERAR_UNKNOWN_FORMAT;
-#else
- ErrHandler.SetErrorCode(WARNING);
- #if !defined(SILENT) && !defined(SFX_MODULE)
- Log(FileName,St(MUnknownMeth),FileName);
- Log(FileName,St(MVerRequired),NewMhd.EncryptVer/10,NewMhd.EncryptVer%10);
- #endif
-#endif
- return(false);
- }
-#ifdef RARDLL
- SilentOpen=true;
+ // If callback function is not set, we cannot get the password,
+ // so we skip the initial header processing for encrypted header archive.
+ // It leads to skipped archive comment, but the rest of archive data
+ // is processed correctly.
+ if (Cmd->Callback==NULL)
+ SilentOpen=true;
#endif
- //if not encrypted, we'll check it below
- NotFirstVolume=Encrypted && (NewMhd.Flags & MHD_FIRSTVOLUME)==0;
-
- if (!SilentOpen || !Encrypted)
+ bool HeadersLeft; // Any headers left to read.
+ bool StartFound=false; // Main or encryption headers found.
+ // Skip the archive encryption header if any and read the main header.
+ while ((HeadersLeft=(ReadHeader()!=0))==true) // Additional parentheses to silence Clang.
{
- SaveFilePos SavePos(*this);
+ SeekToNext();
+
+ HEADER_TYPE Type=GetHeaderType();
+ // In RAR 5.0 we need to quit after reading HEAD_CRYPT if we wish to
+ // avoid the password prompt.
+ StartFound=Type==HEAD_MAIN || SilentOpen && Type==HEAD_CRYPT;
+ if (StartFound)
+ break;
+ }
+
+
+ // We should not do it for EnableBroken or we'll get 'not RAR archive'
+ // messages when extracting encrypted archives with wrong password.
+ if (FailedHeaderDecryption && !EnableBroken)
+ return false;
+
+ if (BrokenHeader || !StartFound) // Main archive header is corrupt or missing.
+ {
+ if (!FailedHeaderDecryption) // If not reported a wrong password already.
+ uiMsg(UIERROR_MHEADERBROKEN,FileName);
+ if (!EnableBroken)
+ return false;
+ }
+
+ MainComment=MainHead.CommentInHeader;
+
+ // If we process non-encrypted archive or can request a password,
+ // we set 'first volume' flag based on file attributes below.
+ // It is necessary for RAR 2.x archives, which did not have 'first volume'
+ // flag in main header. Also for all RAR formats we need to scan until
+ // first file header to set "comment" flag when reading service header.
+ // Unless we are in silent mode, we need to know about presence of comment
+ // immediately after IsArchive call.
+ if (HeadersLeft && (!SilentOpen || !Encrypted))
+ {
+ int64 SavePos=Tell();
int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos;
+ HEADER_TYPE SaveCurHeaderType=CurHeaderType;
- NotFirstVolume=false;
while (ReadHeader()!=0)
{
- int HeaderType=GetHeaderType();
- if (HeaderType==NEWSUB_HEAD)
+ HEADER_TYPE HeaderType=GetHeaderType();
+ if (HeaderType==HEAD_SERVICE)
{
- if (SubHead.CmpName(SUBHEAD_TYPE_CMT))
- MainComment=true;
- if ((SubHead.Flags & LHD_SPLIT_BEFORE) ||
- Volume && (NewMhd.Flags & MHD_FIRSTVOLUME)==0)
- NotFirstVolume=true;
+ // If we have a split service headers, it surely indicates non-first
+ // volume. But not split service header does not guarantee the first
+ // volume, because we can have split file after non-split archive
+ // comment. So we do not quit from loop here.
+ FirstVolume=Volume && !SubHead.SplitBefore;
}
else
- {
- if (HeaderType==FILE_HEAD && ((NewLhd.Flags & LHD_SPLIT_BEFORE)!=0 ||
- Volume && NewLhd.UnpVer>=29 && (NewMhd.Flags & MHD_FIRSTVOLUME)==0))
- NotFirstVolume=true;
- break;
- }
+ if (HeaderType==HEAD_FILE)
+ {
+ FirstVolume=Volume && !FileHead.SplitBefore;
+ break;
+ }
+ else
+ if (HeaderType==HEAD_ENDARC) // Might happen if archive contains only a split service header.
+ break;
SeekToNext();
}
CurBlockPos=SaveCurBlockPos;
NextBlockPos=SaveNextBlockPos;
+ CurHeaderType=SaveCurHeaderType;
+ Seek(SavePos,SEEK_SET);
}
- if (!Volume || !NotFirstVolume)
- {
- strcpy(FirstVolumeName,FileName);
- strcpyw(FirstVolumeNameW,FileNameW);
- }
+ if (!Volume || FirstVolume)
+ wcsncpyz(FirstVolumeName,FileName,ASIZE(FirstVolumeName));
- return(true);
+ return true;
}
@@ -248,20 +279,60 @@ void Archive::SeekToNext()
}
-#ifndef SFX_MODULE
-int Archive::GetRecoverySize(bool Required)
+
+
+
+
+// Calculate the block size including encryption fields and padding if any.
+uint Archive::FullHeaderSize(size_t Size)
{
- if (!Protected)
- return(0);
- if (RecoverySectors!=-1 || !Required)
- return(RecoverySectors);
- SaveFilePos SavePos(*this);
- Seek(SFXSize,SEEK_SET);
- SearchSubBlock(SUBHEAD_TYPE_RR);
- return(RecoverySectors);
+ if (Encrypted)
+ {
+ Size = ALIGN_VALUE(Size, CRYPT_BLOCK_SIZE); // Align to encryption block size.
+ if (Format == RARFMT50)
+ Size += SIZE_INITV;
+ else
+ Size += SIZE_SALT30;
+ }
+ return uint(Size);
+}
+
+
+
+
+#ifdef USE_QOPEN
+bool Archive::Open(const wchar *Name,uint Mode)
+{
+ // Important if we reuse Archive object and it has virtual QOpen
+ // file position not matching real. For example, for 'l -v volname'.
+ QOpen.Unload();
+
+ return File::Open(Name,Mode);
+}
+
+
+int Archive::Read(void *Data,size_t Size)
+{
+ size_t Result;
+ if (QOpen.Read(Data,Size,Result))
+ return (int)Result;
+ return File::Read(Data,Size);
+}
+
+
+void Archive::Seek(int64 Offset,int Method)
+{
+ if (!QOpen.Seek(Offset,Method))
+ File::Seek(Offset,Method);
+}
+
+
+int64 Archive::Tell()
+{
+ int64 QPos;
+ if (QOpen.Tell(&QPos))
+ return QPos;
+ return File::Tell();
}
#endif
-
-
-
diff --git a/libunrar/archive.hpp b/libunrar/archive.hpp
index fa1bf35..d9518f1 100644
--- a/libunrar/archive.hpp
+++ b/libunrar/archive.hpp
@@ -1,126 +1,148 @@
#ifndef _RAR_ARCHIVE_
#define _RAR_ARCHIVE_
-class Pack;
+class PPack;
+class RawRead;
+class RawWrite;
-enum {EN_LOCK=1,EN_VOL=2,EN_FIRSTVOL=4};
+enum NOMODIFY_FLAGS
+{
+ NMDF_ALLOWLOCK=1,NMDF_ALLOWANYVOLUME=2,NMDF_ALLOWFIRSTVOLUME=4
+};
+
+enum RARFORMAT {RARFMT_NONE,RARFMT14,RARFMT15,RARFMT50,RARFMT_FUTURE};
+
+enum ADDSUBDATA_FLAGS
+{
+ ASDF_SPLIT = 1, // Allow to split archive just before header if necessary.
+ ASDF_COMPRESS = 2, // Allow to compress data following subheader.
+ ASDF_CRYPT = 4, // Encrypt data after subheader if password is set.
+ ASDF_CRYPTIFHEADERS = 8 // Encrypt data after subheader only in -hp mode.
+};
+
+// RAR5 headers must not exceed 2 MB.
+#define MAX_HEADER_SIZE_RAR5 0x200000
class Archive:public File
{
private:
- bool IsSignature(byte *D);
void UpdateLatestTime(FileHeader *CurBlock);
- void ConvertNameCase(char *Name);
void ConvertNameCase(wchar *Name);
- void ConvertUnknownHeader();
- size_t ReadOldHeader();
+ void ConvertFileHeader(FileHeader *hd);
+ size_t ReadHeader14();
+ size_t ReadHeader15();
+ size_t ReadHeader50();
+ void ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb);
+ void RequestArcPassword();
void UnexpEndArcMsg();
+ void BrokenHeaderMsg();
+ void UnkEncVerMsg(const wchar *Name,const wchar *Info);
+ bool DoGetComment(Array *CmtData);
+ bool ReadCommentData(Array *CmtData);
-#if !defined(SHELL_EXT) && !defined(NOCRYPT)
+#if !defined(RAR_NOCRYPT)
CryptData HeadersCrypt;
- byte HeadersSalt[SALT_SIZE];
#endif
-#ifndef SHELL_EXT
ComprDataIO SubDataIO;
- byte SubDataSalt[SALT_SIZE];
-#endif
- RAROptions *Cmd,DummyCmd;
+ bool DummyCmd;
+ RAROptions *Cmd;
- MarkHeader MarkHead;
- OldMainHeader OldMhd;
-
- int RecoverySectors;
- int64 RecoveryPos;
RarTime LatestTime;
int LastReadBlock;
- int CurHeaderType;
+ HEADER_TYPE CurHeaderType;
bool SilentOpen;
+#ifdef USE_QOPEN
+ QuickOpen QOpen;
+ bool ProhibitQOpen;
+#endif
public:
Archive(RAROptions *InitCmd=NULL);
+ ~Archive();
+ static RARFORMAT IsSignature(const byte *D,size_t Size);
bool IsArchive(bool EnableBroken);
- size_t SearchBlock(int BlockType);
- size_t SearchSubBlock(const char *Type);
- int ReadBlock(int BlockType);
- void WriteBlock(int BlockType,BaseBlock *wb=NULL);
- int PrepareNamesToWrite(char *Name,wchar *NameW,char *DestName,byte *DestNameW);
- void SetLhdSize();
+ size_t SearchBlock(HEADER_TYPE HeaderType);
+ size_t SearchSubBlock(const wchar *Type);
+ size_t SearchRR();
size_t ReadHeader();
void CheckArc(bool EnableBroken);
- void CheckOpen(char *Name,wchar *NameW=NULL);
- bool WCheckOpen(char *Name,wchar *NameW=NULL);
- bool TestLock(int Mode);
- void MakeTemp();
- void CopyMainHeader(Archive &Src,bool CopySFX=true,char *NameToDisplay=NULL);
- bool ProcessToFileHead(Archive &Src,bool LastBlockAdded,
- Pack *Pack=NULL,const char *SkipName=NULL);
- void TmpToArc(Archive &Src);
- void CloseNew(int AdjustRecovery,bool CloseVolume);
- void WriteEndBlock(bool CloseVolume);
- void CopyFileRecord(Archive &Src);
- void CopyArchiveData(Archive &Src);
- bool GetComment(Array *CmtData,Array *CmtDataW);
+ void CheckOpen(const wchar *Name);
+ bool WCheckOpen(const wchar *Name);
+ bool GetComment(Array *CmtData);
void ViewComment();
- void ViewFileComment();
void SetLatestTime(RarTime *NewTime);
void SeekToNext();
bool CheckAccess();
bool IsArcDir();
- bool IsArcLabel();
void ConvertAttributes();
- int GetRecoverySize(bool Required);
void VolSubtractHeaderSize(size_t SubSize);
- void AddSubData(byte *SrcData,size_t DataSize,File *SrcFile,const char *Name,bool AllowSplit);
- bool ReadSubData(Array *UnpData,File *DestFile);
- int GetHeaderType() {return(CurHeaderType);};
- size_t ReadCommentData(Array *CmtData,Array *CmtDataW);
- void WriteCommentData(byte *Data,size_t DataSize,bool FileComment);
- RAROptions* GetRAROptions() {return(Cmd);}
+ uint FullHeaderSize(size_t Size);
+ int64 GetStartPos();
+ void AddSubData(byte *SrcData,uint64 DataSize,File *SrcFile,
+ const wchar *Name,uint Flags);
+ bool ReadSubData(Array *UnpData,File *DestFile,bool TestMode);
+ HEADER_TYPE GetHeaderType() {return CurHeaderType;}
+ RAROptions* GetRAROptions() {return Cmd;}
void SetSilentOpen(bool Mode) {SilentOpen=Mode;}
+#if 0
+ void GetRecoveryInfo(bool Required,int64 *Size,int *Percent);
+#endif
+#ifdef USE_QOPEN
+ bool Open(const wchar *Name,uint Mode=FMF_READ);
+ int Read(void *Data,size_t Size);
+ void Seek(int64 Offset,int Method);
+ int64 Tell();
+ void QOpenUnload() {QOpen.Unload();}
+ void SetProhibitQOpen(bool Mode) {ProhibitQOpen=Mode;}
+#endif
BaseBlock ShortBlock;
- MainHeader NewMhd;
- FileHeader NewLhd;
+ MarkHeader MarkHead;
+ MainHeader MainHead;
+ CryptHeader CryptHead;
+ FileHeader FileHead;
EndArcHeader EndArcHead;
SubBlockHeader SubBlockHead;
FileHeader SubHead;
CommentHeader CommHead;
ProtectHeader ProtectHead;
- AVHeader AVHead;
- SignHeader SignHead;
UnixOwnersHeader UOHead;
- MacFInfoHeader MACHead;
EAHeader EAHead;
StreamHeader StreamHead;
int64 CurBlockPos;
int64 NextBlockPos;
- bool OldFormat;
+ RARFORMAT Format;
bool Solid;
bool Volume;
bool MainComment;
bool Locked;
bool Signed;
- bool NotFirstVolume;
+ bool FirstVolume;
+ bool NewNumbering;
bool Protected;
bool Encrypted;
size_t SFXSize;
- bool BrokenFileHeader;
+ bool BrokenHeader;
+ bool FailedHeaderDecryption;
+
+#if !defined(RAR_NOCRYPT)
+ byte ArcSalt[SIZE_SALT50];
+#endif
bool Splitting;
- ushort HeaderCRC;
-
+ uint VolNumber;
int64 VolWrite;
- int64 AddingFilesSize;
- size_t AddingHeadersSize;
+ uint64 AddingFilesSize;
+ uint64 AddingHeadersSize;
bool NewArchive;
- char FirstVolumeName[NM];
- wchar FirstVolumeNameW[NM];
+ wchar FirstVolumeName[NM];
};
+
#endif
diff --git a/libunrar/arcread.cpp b/libunrar/arcread.cpp
index 256287d..b0cf39f 100644
--- a/libunrar/arcread.cpp
+++ b/libunrar/arcread.cpp
@@ -1,87 +1,159 @@
#include "rar.hpp"
-size_t Archive::SearchBlock(int BlockType)
+size_t Archive::ReadHeader()
{
- size_t Size,Count=0;
- while ((Size=ReadHeader())!=0 &&
- (BlockType==ENDARC_HEAD || GetHeaderType()!=ENDARC_HEAD))
+ // Once we failed to decrypt an encrypted block, there is no reason to
+ // attempt to do it further. We'll never be successful and only generate
+ // endless errors.
+ if (FailedHeaderDecryption)
+ return 0;
+
+ CurBlockPos=Tell();
+
+ // Other developers asked us to initialize it to suppress "may be used
+ // uninitialized" warning in code below in some compilers.
+ size_t ReadSize=0;
+
+ switch(Format)
{
- if ((++Count & 127)==0)
- Wait();
- if (GetHeaderType()==BlockType)
- return(Size);
- SeekToNext();
+#ifndef SFX_MODULE
+ case RARFMT14:
+ ReadSize=ReadHeader14();
+ break;
+#endif
+ case RARFMT15:
+ ReadSize=ReadHeader15();
+ break;
+ case RARFMT50:
+ ReadSize=ReadHeader50();
+ break;
}
- return(0);
+
+ // It is important to check ReadSize>0 here, because it is normal
+ // for RAR2 and RAR3 archives without end of archive block to have
+ // NextBlockPos==CurBlockPos after the end of archive has reached.
+ if (ReadSize>0 && NextBlockPos<=CurBlockPos)
+ {
+ BrokenHeaderMsg();
+ ReadSize=0;
+ }
+
+ if (ReadSize==0)
+ CurHeaderType=HEAD_UNKNOWN;
+
+ return ReadSize;
}
-size_t Archive::SearchSubBlock(const char *Type)
+size_t Archive::SearchBlock(HEADER_TYPE HeaderType)
{
- size_t Size;
- while ((Size=ReadHeader())!=0 && GetHeaderType()!=ENDARC_HEAD)
+ size_t Size,Count=0;
+ while ((Size=ReadHeader())!=0 &&
+ (HeaderType==HEAD_ENDARC || GetHeaderType()!=HEAD_ENDARC))
{
- if (GetHeaderType()==NEWSUB_HEAD && SubHead.CmpName(Type))
- return(Size);
+ if ((++Count & 127)==0)
+ Wait();
+ if (GetHeaderType()==HeaderType)
+ return Size;
SeekToNext();
}
- return(0);
+ return 0;
+}
+
+
+size_t Archive::SearchSubBlock(const wchar *Type)
+{
+ size_t Size,Count=0;
+ while ((Size=ReadHeader())!=0 && GetHeaderType()!=HEAD_ENDARC)
+ {
+ if ((++Count & 127)==0)
+ Wait();
+ if (GetHeaderType()==HEAD_SERVICE && SubHead.CmpName(Type))
+ return Size;
+ SeekToNext();
+ }
+ return 0;
+}
+
+
+size_t Archive::SearchRR()
+{
+ // If locator extra field is available for recovery record, let's utilize it.
+ if (MainHead.Locator && MainHead.RROffset!=0)
+ {
+ uint64 CurPos=Tell();
+ Seek(MainHead.RROffset,SEEK_SET);
+ size_t Size=ReadHeader();
+ if (Size!=0 && !BrokenHeader && GetHeaderType()==HEAD_SERVICE && SubHead.CmpName(SUBHEAD_TYPE_RR))
+ return Size;
+ Seek(CurPos,SEEK_SET);
+ }
+ // Otherwise scan the entire archive to find the recovery record.
+ return SearchSubBlock(SUBHEAD_TYPE_RR);
}
void Archive::UnexpEndArcMsg()
{
int64 ArcSize=FileLength();
- if (CurBlockPos>ArcSize || NextBlockPos>ArcSize)
+
+ // If block positions are equal to file size, this is not an error.
+ // It can happen when we reached the end of older RAR 1.5 archive,
+ // which did not have the end of archive block.
+ if (CurBlockPos!=ArcSize || NextBlockPos!=ArcSize)
{
-#ifndef SHELL_EXT
- Log(FileName,St(MLogUnexpEOF));
-#endif
- ErrHandler.SetErrorCode(WARNING);
+ uiMsg(UIERROR_UNEXPEOF,FileName);
+ ErrHandler.SetErrorCode(RARX_WARNING);
}
}
-size_t Archive::ReadHeader()
+void Archive::BrokenHeaderMsg()
{
- CurBlockPos=Tell();
+ uiMsg(UIERROR_HEADERBROKEN,FileName);
+ BrokenHeader=true;
+ ErrHandler.SetErrorCode(RARX_CRC);
+}
-#ifndef SFX_MODULE
- if (OldFormat)
- return(ReadOldHeader());
-#endif
+void Archive::UnkEncVerMsg(const wchar *Name,const wchar *Info)
+{
+ uiMsg(UIERROR_UNKNOWNENCMETHOD,FileName,Name,Info);
+ ErrHandler.SetErrorCode(RARX_WARNING);
+}
+
+
+// Return f in case of signed integer overflow or negative parameters
+// or v1+v2 otherwise. We use it for file offsets, which are signed
+// for compatibility with off_t in POSIX file functions and third party code.
+// Signed integer overflow is the undefined behavior according to
+// C++ standard and it causes fuzzers to complain.
+inline int64 SafeAdd(int64 v1,int64 v2,int64 f)
+{
+ return v1>=0 && v2>=0 && v1<=MAX_INT64-v2 ? v1+v2 : f;
+}
+
+
+size_t Archive::ReadHeader15()
+{
RawRead Raw(this);
- bool Decrypt=Encrypted && CurBlockPos>=(int64)SFXSize+SIZEOF_MARKHEAD+SIZEOF_NEWMHD;
+ bool Decrypt=Encrypted && CurBlockPos>(int64)SFXSize+SIZEOF_MARKHEAD3;
if (Decrypt)
{
-#if defined(SHELL_EXT) || defined(NOCRYPT)
- return(0);
+#ifdef RAR_NOCRYPT // For rarext.dll and unrar_nocrypt.dll.
+ return 0;
#else
- if (Read(HeadersSalt,SALT_SIZE)!=SALT_SIZE)
+ RequestArcPassword();
+
+ byte Salt[SIZE_SALT30];
+ if (Read(Salt,SIZE_SALT30)!=SIZE_SALT30)
{
UnexpEndArcMsg();
- return(0);
+ return 0;
}
- if (*Cmd->Password==0)
-#ifdef RARDLL
- if (Cmd->Callback==NULL ||
- Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)Cmd->Password,sizeof(Cmd->Password))==-1)
- {
- Close();
- ErrHandler.Exit(USER_BREAK);
- }
-
-#else
- if (!GetPassword(PASSWORD_ARCHIVE,FileName,Cmd->Password,sizeof(Cmd->Password)))
- {
- Close();
- ErrHandler.Exit(USER_BREAK);
- }
-#endif
- HeadersCrypt.SetCryptKeys(Cmd->Password,HeadersSalt,false,false,NewMhd.EncryptVer>=36);
+ HeadersCrypt.SetCryptKeys(false,CRYPT_RAR30,&Cmd->Password,Salt,NULL,0,NULL,NULL);
Raw.SetCrypt(&HeadersCrypt);
#endif
}
@@ -90,164 +162,239 @@ size_t Archive::ReadHeader()
if (Raw.Size()==0)
{
UnexpEndArcMsg();
- return(0);
+ return 0;
}
- Raw.Get(ShortBlock.HeadCRC);
- byte HeadType;
- Raw.Get(HeadType);
- ShortBlock.HeadType=(HEADER_TYPE)HeadType;
- Raw.Get(ShortBlock.Flags);
- Raw.Get(ShortBlock.HeadSize);
+ ShortBlock.HeadCRC=Raw.Get2();
+
+ ShortBlock.Reset();
+
+ uint HeaderType=Raw.Get1();
+ ShortBlock.Flags=Raw.Get2();
+ ShortBlock.SkipIfUnknown=(ShortBlock.Flags & SKIP_IF_UNKNOWN)!=0;
+ ShortBlock.HeadSize=Raw.Get2();
+
+ ShortBlock.HeaderType=(HEADER_TYPE)HeaderType;
if (ShortBlock.HeadSizeReset();
+
*(BaseBlock *)hd=ShortBlock;
- Raw.Get(hd->PackSize);
- Raw.Get(hd->UnpSize);
- Raw.Get(hd->HostOS);
- Raw.Get(hd->FileCRC);
- Raw.Get(hd->FileTime);
- Raw.Get(hd->UnpVer);
- Raw.Get(hd->Method);
- Raw.Get(hd->NameSize);
- Raw.Get(hd->FileAttr);
- if (hd->Flags & LHD_LARGE)
- {
- Raw.Get(hd->HighPackSize);
- Raw.Get(hd->HighUnpSize);
- }
- else
- {
- hd->HighPackSize=hd->HighUnpSize=0;
- if (hd->UnpSize==0xffffffff)
+
+ hd->SplitBefore=(hd->Flags & LHD_SPLIT_BEFORE)!=0;
+ hd->SplitAfter=(hd->Flags & LHD_SPLIT_AFTER)!=0;
+ hd->Encrypted=(hd->Flags & LHD_PASSWORD)!=0;
+ hd->SaltSet=(hd->Flags & LHD_SALT)!=0;
+ hd->Solid=FileBlock && (hd->Flags & LHD_SOLID)!=0;
+ hd->SubBlock=!FileBlock && (hd->Flags & LHD_SOLID)!=0;
+ hd->Dir=(hd->Flags & LHD_WINDOWMASK)==LHD_DIRECTORY;
+ hd->WinSize=hd->Dir ? 0:0x10000<<((hd->Flags & LHD_WINDOWMASK)>>5);
+ hd->CommentInHeader=(hd->Flags & LHD_COMMENT)!=0;
+ hd->Version=(hd->Flags & LHD_VERSION)!=0;
+
+ hd->DataSize=Raw.Get4();
+ uint LowUnpSize=Raw.Get4();
+ hd->HostOS=Raw.Get1();
+
+ hd->FileHash.Type=HASH_CRC32;
+ hd->FileHash.CRC32=Raw.Get4();
+
+ uint FileTime=Raw.Get4();
+ hd->UnpVer=Raw.Get1();
+
+ hd->Method=Raw.Get1()-0x30;
+ size_t NameSize=Raw.Get2();
+ hd->FileAttr=Raw.Get4();
+
+ // RAR15 did not use the special dictionary size to mark dirs.
+ if (hd->UnpVer<20 && (hd->FileAttr & 0x10)!=0)
+ hd->Dir=true;
+
+ hd->CryptMethod=CRYPT_NONE;
+ if (hd->Encrypted)
+ switch(hd->UnpVer)
{
- // UnpSize equal to 0xffffffff without LHD_LARGE flag indicates
- // that we do not know the unpacked file size and must unpack it
- // until we find the end of file marker in compressed data.
- hd->UnpSize=(uint)(INT64NDF);
- hd->HighUnpSize=(uint)(INT64NDF>>32);
+ case 13: hd->CryptMethod=CRYPT_RAR13; break;
+ case 15: hd->CryptMethod=CRYPT_RAR15; break;
+ case 20:
+ case 26: hd->CryptMethod=CRYPT_RAR20; break;
+ default: hd->CryptMethod=CRYPT_RAR30; break;
}
- }
- hd->FullPackSize=INT32TO64(hd->HighPackSize,hd->PackSize);
- hd->FullUnpSize=INT32TO64(hd->HighUnpSize,hd->UnpSize);
- char FileName[NM*4];
- int NameSize=Min(hd->NameSize,sizeof(FileName)-1);
- Raw.Get((byte *)FileName,NameSize);
- FileName[NameSize]=0;
+ hd->HSType=HSYS_UNKNOWN;
+ if (hd->HostOS==HOST_UNIX || hd->HostOS==HOST_BEOS)
+ hd->HSType=HSYS_UNIX;
+ else
+ if (hd->HostOSHSType=HSYS_WINDOWS;
- strncpyz(hd->FileName,FileName,ASIZE(hd->FileName));
+ hd->RedirType=FSREDIR_NONE;
- if (hd->HeadType==NEWSUB_HEAD)
+ // RAR 4.x Unix symlink.
+ if (hd->HostOS==HOST_UNIX && (hd->FileAttr & 0xF000)==0xA000)
{
- int DataSize=hd->HeadSize-hd->NameSize-SIZEOF_NEWLHD;
- if (hd->Flags & LHD_SALT)
- DataSize-=SALT_SIZE;
- if (DataSize>0)
- {
- hd->SubData.Alloc(DataSize);
- Raw.Get(&hd->SubData[0],DataSize);
- if (hd->CmpName(SUBHEAD_TYPE_RR))
- {
- byte *D=&hd->SubData[8];
- RecoverySectors=D[0]+((uint)D[1]<<8)+((uint)D[2]<<16)+((uint)D[3]<<24);
- }
- }
+ hd->RedirType=FSREDIR_UNIXSYMLINK;
+ *hd->RedirName=0;
+ }
+
+ hd->Inherited=!FileBlock && (hd->SubFlags & SUBHEAD_FLAGS_INHERITED)!=0;
+
+ hd->LargeFile=(hd->Flags & LHD_LARGE)!=0;
+
+ uint HighPackSize,HighUnpSize;
+ if (hd->LargeFile)
+ {
+ HighPackSize=Raw.Get4();
+ HighUnpSize=Raw.Get4();
+ hd->UnknownUnpSize=(LowUnpSize==0xffffffff && HighUnpSize==0xffffffff);
}
else
- if (hd->HeadType==FILE_HEAD)
- {
- if (hd->Flags & LHD_UNICODE)
- {
- EncodeFileName NameCoder;
- size_t Length=strlen(FileName);
- if (Length==hd->NameSize)
- {
- UtfToWide(FileName,hd->FileNameW,sizeof(hd->FileNameW)/sizeof(hd->FileNameW[0])-1);
- WideToChar(hd->FileNameW,hd->FileName,sizeof(hd->FileName)/sizeof(hd->FileName[0])-1);
- ExtToInt(hd->FileName,hd->FileName);
- }
- else
- {
- Length++;
- NameCoder.Decode(FileName,(byte *)FileName+Length,
- hd->NameSize-Length,hd->FileNameW,
- sizeof(hd->FileNameW)/sizeof(hd->FileNameW[0]));
- }
- if (*hd->FileNameW==0)
- hd->Flags &= ~LHD_UNICODE;
- }
- else
- *hd->FileNameW=0;
-#ifndef SFX_MODULE
- ConvertNameCase(hd->FileName);
- ConvertNameCase(hd->FileNameW);
-#endif
- ConvertUnknownHeader();
- }
- if (hd->Flags & LHD_SALT)
- Raw.Get(hd->Salt,SALT_SIZE);
- hd->mtime.SetDos(hd->FileTime);
- hd->ctime.Reset();
- hd->atime.Reset();
- hd->arctime.Reset();
- if (hd->Flags & LHD_EXTTIME)
{
- ushort Flags;
- Raw.Get(Flags);
+ HighPackSize=HighUnpSize=0;
+ // UnpSize equal to 0xffffffff without LHD_LARGE flag indicates
+ // that we do not know the unpacked file size and must unpack it
+ // until we find the end of file marker in compressed data.
+ hd->UnknownUnpSize=(LowUnpSize==0xffffffff);
+ }
+ hd->PackSize=INT32TO64(HighPackSize,hd->DataSize);
+ hd->UnpSize=INT32TO64(HighUnpSize,LowUnpSize);
+ if (hd->UnknownUnpSize)
+ hd->UnpSize=INT64NDF;
+
+ char FileName[NM*4];
+ size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1);
+ Raw.GetB((byte *)FileName,ReadNameSize);
+ FileName[ReadNameSize]=0;
+
+ if (FileBlock)
+ {
+ *hd->FileName=0;
+ if ((hd->Flags & LHD_UNICODE)!=0)
+ {
+ EncodeFileName NameCoder;
+ size_t Length=strlen(FileName);
+ Length++;
+ if (ReadNameSize>Length)
+ NameCoder.Decode(FileName,ReadNameSize,(byte *)FileName+Length,
+ ReadNameSize-Length,hd->FileName,
+ ASIZE(hd->FileName));
+ }
+
+ if (*hd->FileName==0)
+ ArcCharToWide(FileName,hd->FileName,ASIZE(hd->FileName),ACTW_OEM);
+
+#ifndef SFX_MODULE
+ ConvertNameCase(hd->FileName);
+#endif
+ ConvertFileHeader(hd);
+ }
+ else
+ {
+ CharToWide(FileName,hd->FileName,ASIZE(hd->FileName));
+
+ // Calculate the size of optional data.
+ int DataSize=int(hd->HeadSize-NameSize-SIZEOF_FILEHEAD3);
+ if ((hd->Flags & LHD_SALT)!=0)
+ DataSize-=SIZE_SALT30;
+
+ if (DataSize>0)
+ {
+ // Here we read optional additional fields for subheaders.
+ // They are stored after the file name and before salt.
+ hd->SubData.Alloc(DataSize);
+ Raw.GetB(&hd->SubData[0],DataSize);
+
+ }
+
+ if (hd->CmpName(SUBHEAD_TYPE_CMT))
+ MainComment=true;
+ }
+ if ((hd->Flags & LHD_SALT)!=0)
+ Raw.GetB(hd->Salt,SIZE_SALT30);
+ hd->mtime.SetDos(FileTime);
+ if ((hd->Flags & LHD_EXTTIME)!=0)
+ {
+ ushort Flags=Raw.Get2();
RarTime *tbl[4];
- tbl[0]=&NewLhd.mtime;
- tbl[1]=&NewLhd.ctime;
- tbl[2]=&NewLhd.atime;
- tbl[3]=&NewLhd.arctime;
+ tbl[0]=&FileHead.mtime;
+ tbl[1]=&FileHead.ctime;
+ tbl[2]=&FileHead.atime;
+ tbl[3]=NULL; // Archive time is not used now.
for (int I=0;I<4;I++)
{
RarTime *CurTime=tbl[I];
uint rmode=Flags>>(3-I)*4;
- if ((rmode & 8)==0)
+ if ((rmode & 8)==0 || CurTime==NULL)
continue;
if (I!=0)
{
- uint DosTime;
- Raw.Get(DosTime);
+ uint DosTime=Raw.Get4();
CurTime->SetDos(DosTime);
}
RarLocalTime rlt;
@@ -255,108 +402,103 @@ size_t Archive::ReadHeader()
if (rmode & 4)
rlt.Second++;
rlt.Reminder=0;
- int count=rmode&3;
- for (int J=0;JSetLocal(&rlt);
}
}
- NextBlockPos+=hd->FullPackSize;
- bool CRCProcessedOnly=(hd->Flags & LHD_COMMENT)!=0;
- HeaderCRC=~Raw.GetCRC(CRCProcessedOnly)&0xffff;
+ // Set to 0 in case of overflow, so end of ReadHeader cares about it.
+ NextBlockPos=SafeAdd(NextBlockPos,hd->PackSize,0);
+
+ bool CRCProcessedOnly=hd->CommentInHeader;
+ ushort HeaderCRC=Raw.GetCRC15(CRCProcessedOnly);
if (hd->HeadCRC!=HeaderCRC)
{
- if (hd->HeadType==NEWSUB_HEAD)
- strcat(hd->FileName,"- ???");
- BrokenFileHeader=true;
- ErrHandler.SetErrorCode(WARNING);
-#ifndef SHELL_EXT
- Log(Archive::FileName,St(MLogFileHead),IntNameToExt(hd->FileName));
- Alarm();
-#endif
+ BrokenHeader=true;
+ ErrHandler.SetErrorCode(RARX_WARNING);
+
+ // If we have a broken encrypted header, we do not need to display
+ // the error message here, because it will be displayed for such
+ // headers later in this function. Also such headers are unlikely
+ // to have anything sensible in file name field, so it is useless
+ // to display the file name.
+ if (!Decrypt)
+ uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName);
}
}
break;
+ case HEAD_ENDARC:
+ *(BaseBlock *)&EndArcHead=ShortBlock;
+ EndArcHead.NextVolume=(EndArcHead.Flags & EARC_NEXT_VOLUME)!=0;
+ EndArcHead.DataCRC=(EndArcHead.Flags & EARC_DATACRC)!=0;
+ EndArcHead.RevSpace=(EndArcHead.Flags & EARC_REVSPACE)!=0;
+ EndArcHead.StoreVolNumber=(EndArcHead.Flags & EARC_VOLNUMBER)!=0;
+ if (EndArcHead.DataCRC)
+ EndArcHead.ArcDataCRC=Raw.Get4();
+ if (EndArcHead.StoreVolNumber)
+ VolNumber=EndArcHead.VolNumber=Raw.Get2();
+ break;
#ifndef SFX_MODULE
- case COMM_HEAD:
+ case HEAD3_CMT:
*(BaseBlock *)&CommHead=ShortBlock;
- Raw.Get(CommHead.UnpSize);
- Raw.Get(CommHead.UnpVer);
- Raw.Get(CommHead.Method);
- Raw.Get(CommHead.CommCRC);
+ CommHead.UnpSize=Raw.Get2();
+ CommHead.UnpVer=Raw.Get1();
+ CommHead.Method=Raw.Get1();
+ CommHead.CommCRC=Raw.Get2();
break;
- case SIGN_HEAD:
- *(BaseBlock *)&SignHead=ShortBlock;
- Raw.Get(SignHead.CreationTime);
- Raw.Get(SignHead.ArcNameSize);
- Raw.Get(SignHead.UserNameSize);
- break;
- case AV_HEAD:
- *(BaseBlock *)&AVHead=ShortBlock;
- Raw.Get(AVHead.UnpVer);
- Raw.Get(AVHead.Method);
- Raw.Get(AVHead.AVVer);
- Raw.Get(AVHead.AVInfoCRC);
- break;
- case PROTECT_HEAD:
+ case HEAD3_PROTECT:
*(BaseBlock *)&ProtectHead=ShortBlock;
- Raw.Get(ProtectHead.DataSize);
- Raw.Get(ProtectHead.Version);
- Raw.Get(ProtectHead.RecSectors);
- Raw.Get(ProtectHead.TotalBlocks);
- Raw.Get(ProtectHead.Mark,8);
+ ProtectHead.DataSize=Raw.Get4();
+ ProtectHead.Version=Raw.Get1();
+ ProtectHead.RecSectors=Raw.Get2();
+ ProtectHead.TotalBlocks=Raw.Get4();
+ Raw.GetB(ProtectHead.Mark,8);
NextBlockPos+=ProtectHead.DataSize;
- RecoverySectors=ProtectHead.RecSectors;
break;
- case SUB_HEAD:
+ case HEAD3_OLDSERVICE: // RAR 2.9 and earlier.
*(BaseBlock *)&SubBlockHead=ShortBlock;
- Raw.Get(SubBlockHead.DataSize);
+ SubBlockHead.DataSize=Raw.Get4();
NextBlockPos+=SubBlockHead.DataSize;
- Raw.Get(SubBlockHead.SubType);
- Raw.Get(SubBlockHead.Level);
+ SubBlockHead.SubType=Raw.Get2();
+ SubBlockHead.Level=Raw.Get1();
switch(SubBlockHead.SubType)
{
case UO_HEAD:
*(SubBlockHeader *)&UOHead=SubBlockHead;
- Raw.Get(UOHead.OwnerNameSize);
- Raw.Get(UOHead.GroupNameSize);
- if (UOHead.OwnerNameSize>NM-1)
- UOHead.OwnerNameSize=NM-1;
- if (UOHead.GroupNameSize>NM-1)
- UOHead.GroupNameSize=NM-1;
- Raw.Get((byte *)UOHead.OwnerName,UOHead.OwnerNameSize);
- Raw.Get((byte *)UOHead.GroupName,UOHead.GroupNameSize);
+ UOHead.OwnerNameSize=Raw.Get2();
+ UOHead.GroupNameSize=Raw.Get2();
+ if (UOHead.OwnerNameSize>=ASIZE(UOHead.OwnerName))
+ UOHead.OwnerNameSize=ASIZE(UOHead.OwnerName)-1;
+ if (UOHead.GroupNameSize>=ASIZE(UOHead.GroupName))
+ UOHead.GroupNameSize=ASIZE(UOHead.GroupName)-1;
+ Raw.GetB(UOHead.OwnerName,UOHead.OwnerNameSize);
+ Raw.GetB(UOHead.GroupName,UOHead.GroupNameSize);
UOHead.OwnerName[UOHead.OwnerNameSize]=0;
UOHead.GroupName[UOHead.GroupNameSize]=0;
break;
- case MAC_HEAD:
- *(SubBlockHeader *)&MACHead=SubBlockHead;
- Raw.Get(MACHead.fileType);
- Raw.Get(MACHead.fileCreator);
- break;
- case EA_HEAD:
- case BEEA_HEAD:
case NTACL_HEAD:
*(SubBlockHeader *)&EAHead=SubBlockHead;
- Raw.Get(EAHead.UnpSize);
- Raw.Get(EAHead.UnpVer);
- Raw.Get(EAHead.Method);
- Raw.Get(EAHead.EACRC);
+ EAHead.UnpSize=Raw.Get4();
+ EAHead.UnpVer=Raw.Get1();
+ EAHead.Method=Raw.Get1();
+ EAHead.EACRC=Raw.Get4();
break;
case STREAM_HEAD:
*(SubBlockHeader *)&StreamHead=SubBlockHead;
- Raw.Get(StreamHead.UnpSize);
- Raw.Get(StreamHead.UnpVer);
- Raw.Get(StreamHead.Method);
- Raw.Get(StreamHead.StreamCRC);
- Raw.Get(StreamHead.StreamNameSize);
- if (StreamHead.StreamNameSize>NM-1)
- StreamHead.StreamNameSize=NM-1;
- Raw.Get((byte *)StreamHead.StreamName,StreamHead.StreamNameSize);
+ StreamHead.UnpSize=Raw.Get4();
+ StreamHead.UnpVer=Raw.Get1();
+ StreamHead.Method=Raw.Get1();
+ StreamHead.StreamCRC=Raw.Get4();
+ StreamHead.StreamNameSize=Raw.Get2();
+ if (StreamHead.StreamNameSize>=ASIZE(StreamHead.StreamName))
+ StreamHead.StreamNameSize=ASIZE(StreamHead.StreamName)-1;
+ Raw.GetB(StreamHead.StreamName,StreamHead.StreamNameSize);
StreamHead.StreamName[StreamHead.StreamNameSize]=0;
break;
}
@@ -364,349 +506,980 @@ size_t Archive::ReadHeader()
#endif
default:
if (ShortBlock.Flags & LONG_BLOCK)
- {
- uint DataSize;
- Raw.Get(DataSize);
- NextBlockPos+=DataSize;
- }
+ NextBlockPos+=Raw.Get4();
break;
}
- HeaderCRC=~Raw.GetCRC(false)&0xffff;
- CurHeaderType=ShortBlock.HeadType;
- if (Decrypt)
+
+ ushort HeaderCRC=Raw.GetCRC15(false);
+
+ // Old AV header does not have header CRC properly set.
+ if (ShortBlock.HeadCRC!=HeaderCRC && ShortBlock.HeaderType!=HEAD3_SIGN &&
+ ShortBlock.HeaderType!=HEAD3_AV)
{
- NextBlockPos+=Raw.PaddedSize()+SALT_SIZE;
-
- if (ShortBlock.HeadCRC!=HeaderCRC)
+ bool Recovered=false;
+ if (ShortBlock.HeaderType==HEAD_ENDARC && EndArcHead.RevSpace)
{
- bool Recovered=false;
- if (ShortBlock.HeadType==ENDARC_HEAD && (EndArcHead.Flags & EARC_REVSPACE)!=0)
- {
- // Last 7 bytes of recovered volume can contain zeroes, because
- // REV files store its own information (volume number, etc.) here.
- SaveFilePos SavePos(*this);
- int64 Length=Tell();
- Seek(Length-7,SEEK_SET);
- Recovered=true;
- for (int J=0;J<7;J++)
- if (GetByte()!=0)
- Recovered=false;
- }
- if (!Recovered)
- {
-#ifndef SILENT
- Log(FileName,St(MEncrBadCRC),FileName);
-#endif
- Close();
+ // Last 7 bytes of recovered volume can contain zeroes, because
+ // REV files store its own information (volume number, etc.) here.
+ int64 Length=Tell();
+ Seek(Length-7,SEEK_SET);
+ Recovered=true;
+ for (int J=0;J<7;J++)
+ if (GetByte()!=0)
+ Recovered=false;
+ }
+ if (!Recovered)
+ {
+ BrokenHeader=true;
+ ErrHandler.SetErrorCode(RARX_CRC);
- BrokenFileHeader=true;
- ErrHandler.SetErrorCode(CRC_ERROR);
- return(0);
-// ErrHandler.Exit(CRC_ERROR);
+ if (Decrypt)
+ {
+ uiMsg(UIERROR_CHECKSUMENC,FileName,FileName);
+ FailedHeaderDecryption=true;
+ return 0;
}
}
}
- if (NextBlockPos<=CurBlockPos)
+ return Raw.Size();
+}
+
+
+size_t Archive::ReadHeader50()
+{
+ RawRead Raw(this);
+
+ bool Decrypt=Encrypted && CurBlockPos>(int64)SFXSize+SIZEOF_MARKHEAD5;
+
+ if (Decrypt)
{
-#ifndef SHELL_EXT
- Log(FileName,St(MLogFileHead),"???");
+#if defined(RAR_NOCRYPT)
+ return 0;
+#else
+
+ byte HeadersInitV[SIZE_INITV];
+ if (Read(HeadersInitV,SIZE_INITV)!=SIZE_INITV)
+ {
+ UnexpEndArcMsg();
+ return 0;
+ }
+
+ // We repeat the password request only for manually entered passwords
+ // and not for -p. Wrong password can be intentionally provided
+ // in -p to not stop batch processing for encrypted archives.
+ bool GlobalPassword=Cmd->Password.IsSet() || uiIsGlobalPasswordSet();
+
+ while (true) // Repeat the password prompt for wrong passwords.
+ {
+ RequestArcPassword();
+
+ byte PswCheck[SIZE_PSWCHECK];
+ HeadersCrypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,NULL,PswCheck);
+ // Verify password validity.
+ if (CryptHead.UsePswCheck && memcmp(PswCheck,CryptHead.PswCheck,SIZE_PSWCHECK)!=0)
+ {
+ if (GlobalPassword) // For -p or Ctrl+P.
+ {
+ // This message is used by Android GUI to reset cached passwords.
+ // Update appropriate code if changed.
+ uiMsg(UIERROR_BADPSW,FileName,FileName);
+ FailedHeaderDecryption=true;
+ ErrHandler.SetErrorCode(RARX_BADPWD);
+ return 0;
+ }
+ else // For passwords entered manually.
+ {
+ // This message is used by Android GUI and Windows GUI and SFX to
+ // reset cached passwords. Update appropriate code if changed.
+ uiMsg(UIWAIT_BADPSW,FileName,FileName);
+ Cmd->Password.Clean();
+ }
+
+#ifdef RARDLL
+ // Avoid new requests for unrar.dll to prevent the infinite loop
+ // if app always returns the same password.
+ ErrHandler.SetErrorCode(RARX_BADPWD);
+ Cmd->DllError=ERAR_BAD_PASSWORD;
+ ErrHandler.Exit(RARX_BADPWD);
+#else
+ continue; // Request a password again.
+#endif
+ }
+ break;
+ }
+
+ Raw.SetCrypt(&HeadersCrypt);
#endif
- BrokenFileHeader=true;
- ErrHandler.SetErrorCode(CRC_ERROR);
- return(0);
}
- return(Raw.Size());
+
+ // Header size must not occupy more than 3 variable length integer bytes
+ // resulting in 2 MB maximum header size (MAX_HEADER_SIZE_RAR5),
+ // so here we read 4 byte CRC32 followed by 3 bytes or less of header size.
+ const size_t FirstReadSize=7; // Smallest possible block size.
+ if (Raw.Read(FirstReadSize)=ShortBlock.HeadSize)
+ {
+ BrokenHeaderMsg();
+ return 0;
+ }
+ }
+
+ uint64 DataSize=0;
+ if ((ShortBlock.Flags & HFL_DATA)!=0)
+ DataSize=Raw.GetV();
+
+ NextBlockPos=CurBlockPos+FullHeaderSize(ShortBlock.HeadSize);
+ // Set to 0 in case of overflow, so end of ReadHeader cares about it.
+ NextBlockPos=SafeAdd(NextBlockPos,DataSize,0);
+
+ switch(ShortBlock.HeaderType)
+ {
+ case HEAD_CRYPT:
+ {
+ *(BaseBlock *)&CryptHead=ShortBlock;
+ uint CryptVersion=(uint)Raw.GetV();
+ if (CryptVersion>CRYPT_VERSION)
+ {
+ wchar Info[20];
+ swprintf(Info,ASIZE(Info),L"h%u",CryptVersion);
+ UnkEncVerMsg(FileName,Info);
+ return 0;
+ }
+ uint EncFlags=(uint)Raw.GetV();
+ CryptHead.UsePswCheck=(EncFlags & CHFL_CRYPT_PSWCHECK)!=0;
+ CryptHead.Lg2Count=Raw.Get1();
+ if (CryptHead.Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX)
+ {
+ wchar Info[20];
+ swprintf(Info,ASIZE(Info),L"hc%u",CryptHead.Lg2Count);
+ UnkEncVerMsg(FileName,Info);
+ return 0;
+ }
+
+ Raw.GetB(CryptHead.Salt,SIZE_SALT50);
+ if (CryptHead.UsePswCheck)
+ {
+ Raw.GetB(CryptHead.PswCheck,SIZE_PSWCHECK);
+
+ byte csum[SIZE_PSWCHECK_CSUM];
+ Raw.GetB(csum,SIZE_PSWCHECK_CSUM);
+
+ sha256_context ctx;
+ sha256_init(&ctx);
+ sha256_process(&ctx, CryptHead.PswCheck, SIZE_PSWCHECK);
+
+ byte Digest[SHA256_DIGEST_SIZE];
+ sha256_done(&ctx, Digest);
+
+ CryptHead.UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0;
+ }
+ Encrypted=true;
+ }
+ break;
+ case HEAD_MAIN:
+ {
+ MainHead.Reset();
+ *(BaseBlock *)&MainHead=ShortBlock;
+ uint ArcFlags=(uint)Raw.GetV();
+
+ Volume=(ArcFlags & MHFL_VOLUME)!=0;
+ Solid=(ArcFlags & MHFL_SOLID)!=0;
+ Locked=(ArcFlags & MHFL_LOCK)!=0;
+ Protected=(ArcFlags & MHFL_PROTECT)!=0;
+ Signed=false;
+ NewNumbering=true;
+
+ if ((ArcFlags & MHFL_VOLNUMBER)!=0)
+ VolNumber=(uint)Raw.GetV();
+ else
+ VolNumber=0;
+ FirstVolume=Volume && VolNumber==0;
+
+ if (ExtraSize!=0)
+ ProcessExtra50(&Raw,(size_t)ExtraSize,&MainHead);
+
+#ifdef USE_QOPEN
+ if (!ProhibitQOpen && MainHead.Locator && MainHead.QOpenOffset>0 && Cmd->QOpenMode!=QOPEN_NONE)
+ {
+ // We seek to QO block in the end of archive when processing
+ // QOpen.Load, so we need to preserve current block positions
+ // to not break normal archive processing by calling function.
+ int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos;
+ HEADER_TYPE SaveCurHeaderType=CurHeaderType;
+
+ QOpen.Init(this,false);
+ QOpen.Load(MainHead.QOpenOffset);
+
+ CurBlockPos=SaveCurBlockPos;
+ NextBlockPos=SaveNextBlockPos;
+ CurHeaderType=SaveCurHeaderType;
+ }
+#endif
+ }
+ break;
+ case HEAD_FILE:
+ case HEAD_SERVICE:
+ {
+ FileHeader *hd=ShortBlock.HeaderType==HEAD_FILE ? &FileHead:&SubHead;
+ hd->Reset();
+ *(BaseBlock *)hd=ShortBlock;
+
+ bool FileBlock=ShortBlock.HeaderType==HEAD_FILE;
+
+ hd->LargeFile=true;
+
+ hd->PackSize=DataSize;
+ hd->FileFlags=(uint)Raw.GetV();
+ hd->UnpSize=Raw.GetV();
+
+ hd->UnknownUnpSize=(hd->FileFlags & FHFL_UNPUNKNOWN)!=0;
+ if (hd->UnknownUnpSize)
+ hd->UnpSize=INT64NDF;
+
+ hd->MaxSize=Max(hd->PackSize,hd->UnpSize);
+ hd->FileAttr=(uint)Raw.GetV();
+ if ((hd->FileFlags & FHFL_UTIME)!=0)
+ hd->mtime.SetUnix((time_t)Raw.Get4());
+
+ hd->FileHash.Type=HASH_NONE;
+ if ((hd->FileFlags & FHFL_CRC32)!=0)
+ {
+ hd->FileHash.Type=HASH_CRC32;
+ hd->FileHash.CRC32=Raw.Get4();
+ }
+
+ hd->RedirType=FSREDIR_NONE;
+
+ uint CompInfo=(uint)Raw.GetV();
+ hd->Method=(CompInfo>>7) & 7;
+
+ // "+ 50" to not mix with old RAR format algorithms. For example,
+ // we may need to use the compression algorithm 15 in the future,
+ // but it was already used in RAR 1.5 and Unpack needs to distinguish
+ // them.
+ hd->UnpVer=(CompInfo & 0x3f) + 50;
+ if (hd->UnpVer!=50) // Only 5.0 compression is known now.
+ hd->UnpVer=VER_UNKNOWN;
+
+ hd->HostOS=(byte)Raw.GetV();
+ size_t NameSize=(size_t)Raw.GetV();
+ hd->Inherited=(ShortBlock.Flags & HFL_INHERITED)!=0;
+
+ hd->HSType=HSYS_UNKNOWN;
+ if (hd->HostOS==HOST5_UNIX)
+ hd->HSType=HSYS_UNIX;
+ else
+ if (hd->HostOS==HOST5_WINDOWS)
+ hd->HSType=HSYS_WINDOWS;
+
+ hd->SplitBefore=(hd->Flags & HFL_SPLITBEFORE)!=0;
+ hd->SplitAfter=(hd->Flags & HFL_SPLITAFTER)!=0;
+ hd->SubBlock=(hd->Flags & HFL_CHILD)!=0;
+ hd->Solid=FileBlock && (CompInfo & FCI_SOLID)!=0;
+ hd->Dir=(hd->FileFlags & FHFL_DIRECTORY)!=0;
+ hd->WinSize=hd->Dir ? 0:size_t(0x20000)<<((CompInfo>>10)&0xf);
+
+ hd->CryptMethod=hd->Encrypted ? CRYPT_RAR50:CRYPT_NONE;
+
+ char FileName[NM*4];
+ size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1);
+ Raw.GetB((byte *)FileName,ReadNameSize);
+ FileName[ReadNameSize]=0;
+
+ UtfToWide(FileName,hd->FileName,ASIZE(hd->FileName));
+
+ // Should do it before converting names, because extra fields can
+ // affect name processing, like in case of NTFS streams.
+ if (ExtraSize!=0)
+ ProcessExtra50(&Raw,(size_t)ExtraSize,hd);
+
+ if (FileBlock)
+ {
+#ifndef SFX_MODULE
+ ConvertNameCase(hd->FileName);
+#endif
+ ConvertFileHeader(hd);
+ }
+
+ if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_CMT))
+ MainComment=true;
+
+#if 0
+ // For RAR5 format we read the user specified recovery percent here.
+ // It would be useful to do it for shell extension too, so we display
+ // the correct recovery record size in archive properties. But then
+ // we would need to include the entire recovery record processing
+ // code to shell extension, which is not done now.
+ if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_RR) && hd->SubData.Size()>0)
+ {
+ RecoveryPercent=hd->SubData[0];
+ RSBlockHeader Header;
+ GetRRInfo(this,&Header);
+ RecoverySize=Header.RecSectionSize*Header.RecCount;
+ }
+#endif
+
+ if (BadCRC) // Add the file name to broken header message displayed above.
+ uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName);
+ }
+ break;
+ case HEAD_ENDARC:
+ {
+ *(BaseBlock *)&EndArcHead=ShortBlock;
+ uint ArcFlags=(uint)Raw.GetV();
+ EndArcHead.NextVolume=(ArcFlags & EHFL_NEXTVOLUME)!=0;
+ EndArcHead.StoreVolNumber=false;
+ EndArcHead.DataCRC=false;
+ EndArcHead.RevSpace=false;
+ }
+ break;
+ }
+
+ return Raw.Size();
+}
+
+
+#if !defined(RAR_NOCRYPT)
+void Archive::RequestArcPassword()
+{
+ if (!Cmd->Password.IsSet())
+ {
+#ifdef RARDLL
+ if (Cmd->Callback!=NULL)
+ {
+ wchar PasswordW[MAXPASSWORD];
+ *PasswordW=0;
+ if (Cmd->Callback(UCM_NEEDPASSWORDW,Cmd->UserData,(LPARAM)PasswordW,ASIZE(PasswordW))==-1)
+ *PasswordW=0;
+ if (*PasswordW==0)
+ {
+ char PasswordA[MAXPASSWORD];
+ *PasswordA=0;
+ if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1)
+ *PasswordA=0;
+ GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW));
+ cleandata(PasswordA,sizeof(PasswordA));
+ }
+ Cmd->Password.Set(PasswordW);
+ cleandata(PasswordW,sizeof(PasswordW));
+ }
+ if (!Cmd->Password.IsSet())
+ {
+ Close();
+ Cmd->DllError=ERAR_MISSING_PASSWORD;
+ ErrHandler.Exit(RARX_USERBREAK);
+ }
+#else
+ if (!uiGetPassword(UIPASSWORD_ARCHIVE,FileName,&Cmd->Password))
+ {
+ Close();
+ uiMsg(UIERROR_INCERRCOUNT); // Prevent archive deleting if delete after extraction is on.
+ ErrHandler.Exit(RARX_USERBREAK);
+ }
+#endif
+ Cmd->ManualPassword=true;
+ }
+}
+#endif
+
+
+void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb)
+{
+ // Read extra data from the end of block skipping any fields before it.
+ size_t ExtraStart=Raw->Size()-ExtraSize;
+ if (ExtraStartGetPos())
+ return;
+ Raw->SetPos(ExtraStart);
+ while (Raw->DataLeft()>=2)
+ {
+ int64 FieldSize=Raw->GetV(); // Needs to be signed for check below and can be negative.
+ if (FieldSize<=0 || Raw->DataLeft()==0 || FieldSize>(int64)Raw->DataLeft())
+ break;
+ size_t NextPos=size_t(Raw->GetPos()+FieldSize);
+ uint64 FieldType=Raw->GetV();
+
+ FieldSize=int64(NextPos-Raw->GetPos()); // Field size without size and type fields.
+
+ if (FieldSize<0) // FieldType is longer than expected extra field size.
+ break;
+
+ if (bb->HeaderType==HEAD_MAIN)
+ {
+ MainHeader *hd=(MainHeader *)bb;
+ if (FieldType==MHEXTRA_LOCATOR)
+ {
+ hd->Locator=true;
+ uint Flags=(uint)Raw->GetV();
+ if ((Flags & MHEXTRA_LOCATOR_QLIST)!=0)
+ {
+ uint64 Offset=Raw->GetV();
+ if (Offset!=0) // 0 means that reserved space was not enough to write the offset.
+ hd->QOpenOffset=Offset+CurBlockPos;
+ }
+ if ((Flags & MHEXTRA_LOCATOR_RR)!=0)
+ {
+ uint64 Offset=Raw->GetV();
+ if (Offset!=0) // 0 means that reserved space was not enough to write the offset.
+ hd->RROffset=Offset+CurBlockPos;
+ }
+ }
+ }
+
+ if (bb->HeaderType==HEAD_FILE || bb->HeaderType==HEAD_SERVICE)
+ {
+ FileHeader *hd=(FileHeader *)bb;
+ switch(FieldType)
+ {
+ case FHEXTRA_CRYPT:
+ {
+ FileHeader *hd=(FileHeader *)bb;
+ uint EncVersion=(uint)Raw->GetV();
+ if (EncVersion>CRYPT_VERSION)
+ {
+ wchar Info[20];
+ swprintf(Info,ASIZE(Info),L"x%u",EncVersion);
+ UnkEncVerMsg(hd->FileName,Info);
+ }
+ else
+ {
+ uint Flags=(uint)Raw->GetV();
+ hd->UsePswCheck=(Flags & FHEXTRA_CRYPT_PSWCHECK)!=0;
+ hd->UseHashKey=(Flags & FHEXTRA_CRYPT_HASHMAC)!=0;
+ hd->Lg2Count=Raw->Get1();
+ if (hd->Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX)
+ {
+ wchar Info[20];
+ swprintf(Info,ASIZE(Info),L"xc%u",hd->Lg2Count);
+ UnkEncVerMsg(hd->FileName,Info);
+ }
+ Raw->GetB(hd->Salt,SIZE_SALT50);
+ Raw->GetB(hd->InitV,SIZE_INITV);
+ if (hd->UsePswCheck)
+ {
+ Raw->GetB(hd->PswCheck,SIZE_PSWCHECK);
+
+ // It is important to know if password check data is valid.
+ // If it is damaged and header CRC32 fails to detect it,
+ // archiver would refuse to decompress a possibly valid file.
+ // Since we want to be sure distinguishing a wrong password
+ // or corrupt file data, we use 64-bit password check data
+ // and to control its validity we use 32 bits of password
+ // check data SHA-256 additionally to 32-bit header CRC32.
+ byte csum[SIZE_PSWCHECK_CSUM];
+ Raw->GetB(csum,SIZE_PSWCHECK_CSUM);
+
+ sha256_context ctx;
+ sha256_init(&ctx);
+ sha256_process(&ctx, hd->PswCheck, SIZE_PSWCHECK);
+
+ byte Digest[SHA256_DIGEST_SIZE];
+ sha256_done(&ctx, Digest);
+
+ hd->UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0;
+
+ // RAR 5.21 and earlier set PswCheck field in service records to 0
+ // even if UsePswCheck was present.
+ if (bb->HeaderType==HEAD_SERVICE && memcmp(hd->PswCheck,"\0\0\0\0\0\0\0\0",SIZE_PSWCHECK)==0)
+ hd->UsePswCheck=0;
+ }
+ hd->SaltSet=true;
+ hd->CryptMethod=CRYPT_RAR50;
+ hd->Encrypted=true;
+ }
+ }
+ break;
+ case FHEXTRA_HASH:
+ {
+ FileHeader *hd=(FileHeader *)bb;
+ uint Type=(uint)Raw->GetV();
+ if (Type==FHEXTRA_HASH_BLAKE2)
+ {
+ hd->FileHash.Type=HASH_BLAKE2;
+ Raw->GetB(hd->FileHash.Digest,BLAKE2_DIGEST_SIZE);
+ }
+ }
+ break;
+ case FHEXTRA_HTIME:
+ if (FieldSize>=5)
+ {
+ byte Flags=(byte)Raw->GetV();
+ bool UnixTime=(Flags & FHEXTRA_HTIME_UNIXTIME)!=0;
+ if ((Flags & FHEXTRA_HTIME_MTIME)!=0)
+ if (UnixTime)
+ hd->mtime.SetUnix(Raw->Get4());
+ else
+ hd->mtime.SetWin(Raw->Get8());
+ if ((Flags & FHEXTRA_HTIME_CTIME)!=0)
+ if (UnixTime)
+ hd->ctime.SetUnix(Raw->Get4());
+ else
+ hd->ctime.SetWin(Raw->Get8());
+ if ((Flags & FHEXTRA_HTIME_ATIME)!=0)
+ if (UnixTime)
+ hd->atime.SetUnix((time_t)Raw->Get4());
+ else
+ hd->atime.SetWin(Raw->Get8());
+ if (UnixTime && (Flags & FHEXTRA_HTIME_UNIX_NS)!=0) // Add nanoseconds.
+ {
+ uint ns;
+ if ((Flags & FHEXTRA_HTIME_MTIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000)
+ hd->mtime.Adjust(ns);
+ if ((Flags & FHEXTRA_HTIME_CTIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000)
+ hd->ctime.Adjust(ns);
+ if ((Flags & FHEXTRA_HTIME_ATIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000)
+ hd->atime.Adjust(ns);
+ }
+ }
+ break;
+ case FHEXTRA_VERSION:
+ if (FieldSize>=1)
+ {
+ Raw->GetV(); // Skip flags field.
+ uint Version=(uint)Raw->GetV();
+ if (Version!=0)
+ {
+ hd->Version=true;
+
+ wchar VerText[20];
+ swprintf(VerText,ASIZE(VerText),L";%u",Version);
+ wcsncatz(hd->FileName,VerText,ASIZE(hd->FileName));
+ }
+ }
+ break;
+ case FHEXTRA_REDIR:
+ {
+ hd->RedirType=(FILE_SYSTEM_REDIRECT)Raw->GetV();
+ uint Flags=(uint)Raw->GetV();
+ hd->DirTarget=(Flags & FHEXTRA_REDIR_DIR)!=0;
+ size_t NameSize=(size_t)Raw->GetV();
+
+ char UtfName[NM*4];
+ *UtfName=0;
+ if (NameSizeGetB(UtfName,NameSize);
+ UtfName[NameSize]=0;
+ }
+#ifdef _WIN_ALL
+ UnixSlashToDos(UtfName,UtfName,ASIZE(UtfName));
+#endif
+ UtfToWide(UtfName,hd->RedirName,ASIZE(hd->RedirName));
+ }
+ break;
+ case FHEXTRA_UOWNER:
+ {
+ uint Flags=(uint)Raw->GetV();
+ hd->UnixOwnerNumeric=(Flags & FHEXTRA_UOWNER_NUMUID)!=0;
+ hd->UnixGroupNumeric=(Flags & FHEXTRA_UOWNER_NUMGID)!=0;
+ *hd->UnixOwnerName=*hd->UnixGroupName=0;
+ if ((Flags & FHEXTRA_UOWNER_UNAME)!=0)
+ {
+ size_t Length=(size_t)Raw->GetV();
+ Length=Min(Length,ASIZE(hd->UnixOwnerName)-1);
+ Raw->GetB(hd->UnixOwnerName,Length);
+ hd->UnixOwnerName[Length]=0;
+ }
+ if ((Flags & FHEXTRA_UOWNER_GNAME)!=0)
+ {
+ size_t Length=(size_t)Raw->GetV();
+ Length=Min(Length,ASIZE(hd->UnixGroupName)-1);
+ Raw->GetB(hd->UnixGroupName,Length);
+ hd->UnixGroupName[Length]=0;
+ }
+#ifdef _UNIX
+ if (hd->UnixOwnerNumeric)
+ hd->UnixOwnerID=(uid_t)Raw->GetV();
+ if (hd->UnixGroupNumeric)
+ hd->UnixGroupID=(gid_t)Raw->GetV();
+#else
+ // Need these fields in Windows too for 'list' command,
+ // but uid_t and gid_t are not defined.
+ if (hd->UnixOwnerNumeric)
+ hd->UnixOwnerID=(uint)Raw->GetV();
+ if (hd->UnixGroupNumeric)
+ hd->UnixGroupID=(uint)Raw->GetV();
+#endif
+ hd->UnixOwnerSet=true;
+ }
+ break;
+ case FHEXTRA_SUBDATA:
+ {
+ // RAR 5.21 and earlier set FHEXTRA_SUBDATA size to 1 less than
+ // required. It did not hurt extraction, because UnRAR 5.21
+ // and earlier ignored this field and set FieldSize as data left
+ // in entire extra area. But now we set the correct field size
+ // and set FieldSize based on the actual extra record size,
+ // so we need to adjust it for those older archives here.
+ // FHEXTRA_SUBDATA in those archives always belongs to HEAD_SERVICE
+ // and always is last in extra area. So since its size is by 1
+ // less than needed, we always have 1 byte left in extra area,
+ // which fact we use here to detect such archives.
+ if (bb->HeaderType==HEAD_SERVICE && Raw->Size()-NextPos==1)
+ FieldSize++;
+
+ // We cannot allocate too much memory here, because above
+ // we check FieldSize againt Raw size and we control that Raw size
+ // is sensible when reading headers.
+ hd->SubData.Alloc((size_t)FieldSize);
+ Raw->GetB(hd->SubData.Addr(0),(size_t)FieldSize);
+ }
+ break;
+ }
+ }
+
+ Raw->SetPos(NextPos);
+ }
}
#ifndef SFX_MODULE
-size_t Archive::ReadOldHeader()
+size_t Archive::ReadHeader14()
{
RawRead Raw(this);
if (CurBlockPos<=(int64)SFXSize)
{
- Raw.Read(SIZEOF_OLDMHD);
- Raw.Get(OldMhd.Mark,4);
- Raw.Get(OldMhd.HeadSize);
- Raw.Get(OldMhd.Flags);
- NextBlockPos=CurBlockPos+OldMhd.HeadSize;
- CurHeaderType=MAIN_HEAD;
+ Raw.Read(SIZEOF_MAINHEAD14);
+ MainHead.Reset();
+ byte Mark[4];
+ Raw.GetB(Mark,4);
+ uint HeadSize=Raw.Get2();
+ if (HeadSize<7)
+ return false;
+ byte Flags=Raw.Get1();
+ NextBlockPos=CurBlockPos+HeadSize;
+ CurHeaderType=HEAD_MAIN;
+
+ Volume=(Flags & MHD_VOLUME)!=0;
+ Solid=(Flags & MHD_SOLID)!=0;
+ Locked=(Flags & MHD_LOCK)!=0;
+ MainHead.CommentInHeader=(Flags & MHD_COMMENT)!=0;
+ MainHead.PackComment=(Flags & MHD_PACK_COMMENT)!=0;
}
else
{
- OldFileHeader OldLhd;
- Raw.Read(SIZEOF_OLDLHD);
- NewLhd.HeadType=FILE_HEAD;
- Raw.Get(NewLhd.PackSize);
- Raw.Get(NewLhd.UnpSize);
- Raw.Get(OldLhd.FileCRC);
- Raw.Get(NewLhd.HeadSize);
- Raw.Get(NewLhd.FileTime);
- Raw.Get(OldLhd.FileAttr);
- Raw.Get(OldLhd.Flags);
- Raw.Get(OldLhd.UnpVer);
- Raw.Get(OldLhd.NameSize);
- Raw.Get(OldLhd.Method);
+ Raw.Read(SIZEOF_FILEHEAD14);
+ FileHead.Reset();
- NewLhd.Flags=OldLhd.Flags|LONG_BLOCK;
- NewLhd.UnpVer=(OldLhd.UnpVer==2) ? 13 : 10;
- NewLhd.Method=OldLhd.Method+0x30;
- NewLhd.NameSize=OldLhd.NameSize;
- NewLhd.FileAttr=OldLhd.FileAttr;
- NewLhd.FileCRC=OldLhd.FileCRC;
- NewLhd.FullPackSize=NewLhd.PackSize;
- NewLhd.FullUnpSize=NewLhd.UnpSize;
+ FileHead.HeaderType=HEAD_FILE;
+ FileHead.DataSize=Raw.Get4();
+ FileHead.UnpSize=Raw.Get4();
+ FileHead.FileHash.Type=HASH_RAR14;
+ FileHead.FileHash.CRC32=Raw.Get2();
+ FileHead.HeadSize=Raw.Get2();
+ if (FileHead.HeadSize<21)
+ return false;
+ uint FileTime=Raw.Get4();
+ FileHead.FileAttr=Raw.Get1();
+ FileHead.Flags=Raw.Get1()|LONG_BLOCK;
+ FileHead.UnpVer=(Raw.Get1()==2) ? 13 : 10;
+ size_t NameSize=Raw.Get1();
+ FileHead.Method=Raw.Get1();
- NewLhd.mtime.SetDos(NewLhd.FileTime);
- NewLhd.ctime.Reset();
- NewLhd.atime.Reset();
- NewLhd.arctime.Reset();
+ FileHead.SplitBefore=(FileHead.Flags & LHD_SPLIT_BEFORE)!=0;
+ FileHead.SplitAfter=(FileHead.Flags & LHD_SPLIT_AFTER)!=0;
+ FileHead.Encrypted=(FileHead.Flags & LHD_PASSWORD)!=0;
+ FileHead.CryptMethod=FileHead.Encrypted ? CRYPT_RAR13:CRYPT_NONE;
- Raw.Read(OldLhd.NameSize);
- Raw.Get((byte *)NewLhd.FileName,OldLhd.NameSize);
- NewLhd.FileName[OldLhd.NameSize]=0;
- ConvertNameCase(NewLhd.FileName);
- *NewLhd.FileNameW=0;
+ FileHead.PackSize=FileHead.DataSize;
+ FileHead.WinSize=0x10000;
+ FileHead.Dir=(FileHead.FileAttr & 0x10)!=0;
+
+ FileHead.HostOS=HOST_MSDOS;
+ FileHead.HSType=HSYS_WINDOWS;
+
+ FileHead.mtime.SetDos(FileTime);
+
+ Raw.Read(NameSize);
+
+ char FileName[NM];
+ size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1);
+ Raw.GetB((byte *)FileName,ReadNameSize);
+ FileName[ReadNameSize]=0;
+ IntToExt(FileName,FileName,ASIZE(FileName));
+ CharToWide(FileName,FileHead.FileName,ASIZE(FileHead.FileName));
+ ConvertNameCase(FileHead.FileName);
+ ConvertFileHeader(&FileHead);
if (Raw.Size()!=0)
- NextBlockPos=CurBlockPos+NewLhd.HeadSize+NewLhd.PackSize;
- CurHeaderType=FILE_HEAD;
+ NextBlockPos=CurBlockPos+FileHead.HeadSize+FileHead.PackSize;
+ CurHeaderType=HEAD_FILE;
}
- return(NextBlockPos>CurBlockPos ? Raw.Size():0);
+ return NextBlockPos>CurBlockPos ? Raw.Size() : 0;
}
#endif
-void Archive::ConvertNameCase(char *Name)
-{
- if (Cmd->ConvertNames==NAMES_UPPERCASE)
- {
- IntToExt(Name,Name);
- strupper(Name);
- ExtToInt(Name,Name);
- }
- if (Cmd->ConvertNames==NAMES_LOWERCASE)
- {
- IntToExt(Name,Name);
- strlower(Name);
- ExtToInt(Name,Name);
- }
-}
-
-
#ifndef SFX_MODULE
void Archive::ConvertNameCase(wchar *Name)
{
if (Cmd->ConvertNames==NAMES_UPPERCASE)
- strupperw(Name);
+ wcsupper(Name);
if (Cmd->ConvertNames==NAMES_LOWERCASE)
- strlowerw(Name);
+ wcslower(Name);
}
#endif
bool Archive::IsArcDir()
{
- return((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY);
-}
-
-
-bool Archive::IsArcLabel()
-{
- return(NewLhd.HostOS<=HOST_WIN32 && (NewLhd.FileAttr & 8));
+ return FileHead.Dir;
}
void Archive::ConvertAttributes()
{
-#if defined(_WIN_32) || defined(_EMX)
- switch(NewLhd.HostOS)
- {
- case HOST_MSDOS:
- case HOST_OS2:
- case HOST_WIN32:
- break;
- case HOST_UNIX:
- case HOST_BEOS:
- if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY)
- NewLhd.FileAttr=0x10;
- else
- NewLhd.FileAttr=0x20;
- break;
- default:
- if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY)
- NewLhd.FileAttr=0x10;
- else
- NewLhd.FileAttr=0x20;
- break;
- }
+#if defined(_WIN_ALL) || defined(_EMX)
+ if (FileHead.HSType!=HSYS_WINDOWS)
+ FileHead.FileAttr=FileHead.Dir ? 0x10 : 0x20;
#endif
#ifdef _UNIX
// umask defines which permission bits must not be set by default
- // when creating a file or directory.
+ // when creating a file or directory. The typical default value
+ // for the process umask is S_IWGRP | S_IWOTH (octal 022),
+ // resulting in 0644 mode for new files.
+ // Normally umask is applied automatically when creating a file,
+ // but we set attributes with chmod later, so we need to calculate
+ // resulting attributes here. We do it only for non-Unix archives.
+ // We restore native Unix attributes as is, because it can be backup.
static mode_t mask = (mode_t) -1;
if (mask == (mode_t) -1)
{
- // umask call returns the current umask value. Argument (022) is not
- // important here.
+ // umask call returns the current umask value. Argument (022) is not
+ // really important here.
mask = umask(022);
// Restore the original umask value, which was changed to 022 above.
umask(mask);
}
- switch(NewLhd.HostOS)
+ switch(FileHead.HSType)
{
- case HOST_MSDOS:
- case HOST_OS2:
- case HOST_WIN32:
+ case HSYS_WINDOWS:
{
// Mapping MSDOS, OS/2 and Windows file attributes to Unix.
- if (NewLhd.FileAttr & 0x10) // FILE_ATTRIBUTE_DIRECTORY
+ if (FileHead.FileAttr & 0x10) // FILE_ATTRIBUTE_DIRECTORY
{
// For directories we use 0777 mask.
- NewLhd.FileAttr=0777 & ~mask;
+ FileHead.FileAttr=0777 & ~mask;
}
else
- if (NewLhd.FileAttr & 1) // FILE_ATTRIBUTE_READONLY
+ if (FileHead.FileAttr & 1) // FILE_ATTRIBUTE_READONLY
{
// For read only files we use 0444 mask with 'w' bits turned off.
- NewLhd.FileAttr=0444 & ~mask;
+ FileHead.FileAttr=0444 & ~mask;
}
else
{
// umask does not set +x for regular files, so we use 0666
// instead of 0777 as for directories.
- NewLhd.FileAttr=0666 & ~mask;
+ FileHead.FileAttr=0666 & ~mask;
}
}
break;
- case HOST_UNIX:
- case HOST_BEOS:
+ case HSYS_UNIX:
break;
default:
- if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY)
- NewLhd.FileAttr=0x41ff & ~mask;
+ if (FileHead.Dir)
+ FileHead.FileAttr=0x41ff & ~mask;
else
- NewLhd.FileAttr=0x81b6 & ~mask;
+ FileHead.FileAttr=0x81b6 & ~mask;
break;
}
#endif
}
-void Archive::ConvertUnknownHeader()
+void Archive::ConvertFileHeader(FileHeader *hd)
{
- if (NewLhd.UnpVer<20 && (NewLhd.FileAttr & 0x10))
- NewLhd.Flags|=LHD_DIRECTORY;
- if (NewLhd.HostOS>=HOST_MAX)
- {
- if ((NewLhd.Flags & LHD_WINDOWMASK)==LHD_DIRECTORY)
- NewLhd.FileAttr=0x10;
+ if (hd->HSType==HSYS_UNKNOWN)
+ if (hd->Dir)
+ hd->FileAttr=0x10;
else
- NewLhd.FileAttr=0x20;
- }
- for (char *s=NewLhd.FileName;*s!=0;s=charnext(s))
+ hd->FileAttr=0x20;
+
+#ifdef _WIN_ALL
+ if (hd->HSType==HSYS_UNIX) // Convert Unix, OS X and Android decomposed chracters to Windows precomposed.
+ ConvertToPrecomposed(hd->FileName,ASIZE(hd->FileName));
+#endif
+
+ for (wchar *s=hd->FileName;*s!=0;s++)
{
- if (*s=='/' || *s=='\\')
- *s=CPATHDIVIDER;
-#if defined(_APPLE) && !defined(UNICODE_SUPPORTED)
- if ((byte)*s<32 || (byte)*s>127)
+#ifdef _UNIX
+ // Backslash is the invalid character for Windows file headers,
+ // but it can present in Unix file names extracted in Unix.
+ if (*s=='\\' && Format==RARFMT50 && hd->HSType==HSYS_WINDOWS)
*s='_';
#endif
-#if defined(_WIN_32) || defined(_EMX)
+#if defined(_WIN_ALL) || defined(_EMX)
+ // RAR 5.0 archives do not use '\' as path separator, so if we see it,
+ // it means that it is a part of Unix file name, which we cannot
+ // extract in Windows.
+ if (*s=='\\' && Format==RARFMT50)
+ *s='_';
+
// ':' in file names is allowed in Unix, but not in Windows.
// Even worse, file data will be written to NTFS stream on NTFS,
- // so automatic name correction on file create error in extraction
- // routine does not work. In Windows and DOS versions we better
+ // so automatic name correction on file create error in extraction
+ // routine does not work. In Windows and DOS versions we better
// replace ':' now.
if (*s==':')
*s='_';
#endif
- }
-
- for (wchar *s=NewLhd.FileNameW;*s!=0;s++)
- {
- if (*s=='/' || *s=='\\')
+ // This code must be performed only after other path separator checks,
+ // because it produces backslashes illegal for some of checks above.
+ // Backslash is allowed in file names in Unix, but not in Windows.
+ // Still, RAR 4.x uses backslashes as path separator even in Unix.
+ // Forward slash is not allowed in both systems. In RAR 5.0 we use
+ // the forward slash as universal path separator.
+ if (*s=='/' || *s=='\\' && Format!=RARFMT50)
*s=CPATHDIVIDER;
-
-#if defined(_WIN_32) || defined(_EMX)
- // ':' in file names is allowed in Unix, but not in Windows.
- // Even worse, file data will be written to NTFS stream on NTFS,
- // so automatic name correction on file create error in extraction
- // routine does not work. In Windows and DOS versions we better
- // replace ':' now.
- if (*s==':')
- *s='_';
-#endif
}
}
-#ifndef SHELL_EXT
-bool Archive::ReadSubData(Array *UnpData,File *DestFile)
+int64 Archive::GetStartPos()
{
- if (HeaderCRC!=SubHead.HeadCRC)
+ int64 StartPos=SFXSize+MarkHead.HeadSize;
+ if (Format==RARFMT15)
+ StartPos+=MainHead.HeadSize;
+ else // RAR 5.0.
+ StartPos+=CryptHead.HeadSize+FullHeaderSize(MainHead.HeadSize);
+ return StartPos;
+}
+
+
+bool Archive::ReadSubData(Array *UnpData,File *DestFile,bool TestMode)
+{
+ if (BrokenHeader)
{
-#ifndef SHELL_EXT
- Log(FileName,St(MSubHeadCorrupt));
-#endif
- ErrHandler.SetErrorCode(CRC_ERROR);
- return(false);
+ uiMsg(UIERROR_SUBHEADERBROKEN,FileName);
+ ErrHandler.SetErrorCode(RARX_CRC);
+ return false;
}
- if (SubHead.Method<0x30 || SubHead.Method>0x35 || SubHead.UnpVer>/*PACK_VER*/36)
+ if (SubHead.Method>5 || SubHead.UnpVer>(Format==RARFMT50 ? VER_UNPACK5:VER_UNPACK))
{
-#ifndef SHELL_EXT
- Log(FileName,St(MSubHeadUnknown));
-#endif
- return(false);
+ uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName);
+ return false;
}
- if (SubHead.PackSize==0 && (SubHead.Flags & LHD_SPLIT_AFTER)==0)
- return(true);
+ if (SubHead.PackSize==0 && !SubHead.SplitAfter)
+ return true;
SubDataIO.Init();
Unpack Unpack(&SubDataIO);
- Unpack.Init();
+ Unpack.Init(SubHead.WinSize,false);
if (DestFile==NULL)
{
- UnpData->Alloc(SubHead.UnpSize);
- SubDataIO.SetUnpackToMemory(&(*UnpData)[0],SubHead.UnpSize);
- }
- if (SubHead.Flags & LHD_PASSWORD)
- if (*Cmd->Password)
- SubDataIO.SetEncryption(SubHead.UnpVer,Cmd->Password,
- (SubHead.Flags & LHD_SALT) ? SubHead.Salt:NULL,false,
- SubHead.UnpVer>=36);
+ if (SubHead.UnpSize>0x1000000)
+ {
+ // So huge allocation must never happen in valid archives.
+ uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName);
+ return false;
+ }
+ if (UnpData==NULL)
+ SubDataIO.SetTestMode(true);
else
- return(false);
+ {
+ UnpData->Alloc((size_t)SubHead.UnpSize);
+ SubDataIO.SetUnpackToMemory(&(*UnpData)[0],(uint)SubHead.UnpSize);
+ }
+ }
+ if (SubHead.Encrypted)
+ if (Cmd->Password.IsSet())
+ SubDataIO.SetEncryption(false,SubHead.CryptMethod,&Cmd->Password,
+ SubHead.SaltSet ? SubHead.Salt:NULL,SubHead.InitV,
+ SubHead.Lg2Count,SubHead.HashKey,SubHead.PswCheck);
+ else
+ return false;
+ SubDataIO.UnpHash.Init(SubHead.FileHash.Type,1);
SubDataIO.SetPackedSizeToRead(SubHead.PackSize);
SubDataIO.EnableShowProgress(false);
SubDataIO.SetFiles(this,DestFile);
- SubDataIO.UnpVolume=(SubHead.Flags & LHD_SPLIT_AFTER)!=0;
+ SubDataIO.SetTestMode(TestMode);
+ SubDataIO.UnpVolume=SubHead.SplitAfter;
SubDataIO.SetSubHeader(&SubHead,NULL);
Unpack.SetDestSize(SubHead.UnpSize);
- if (SubHead.Method==0x30)
+ if (SubHead.Method==0)
CmdExtract::UnstoreFile(SubDataIO,SubHead.UnpSize);
else
Unpack.DoUnpack(SubHead.UnpVer,false);
- if (SubHead.FileCRC!=~SubDataIO.UnpFileCRC)
+ if (!SubDataIO.UnpHash.Cmp(&SubHead.FileHash,SubHead.UseHashKey ? SubHead.HashKey:NULL))
{
-#ifndef SHELL_EXT
- Log(FileName,St(MSubHeadDataCRC),SubHead.FileName);
-#endif
- ErrHandler.SetErrorCode(CRC_ERROR);
+ uiMsg(UIERROR_SUBHEADERDATABROKEN,FileName,SubHead.FileName);
+ ErrHandler.SetErrorCode(RARX_CRC);
if (UnpData!=NULL)
UnpData->Reset();
- return(false);
+ return false;
}
- return(true);
+ return true;
}
-#endif
diff --git a/libunrar/array.hpp b/libunrar/array.hpp
index c67817a..20d258d 100644
--- a/libunrar/array.hpp
+++ b/libunrar/array.hpp
@@ -9,26 +9,39 @@ template class Array
T *Buffer;
size_t BufSize;
size_t AllocSize;
+ size_t MaxSize;
+ bool Secure; // Clean memory if true.
public:
Array();
Array(size_t Size);
+ Array(const Array &Src); // Copy constructor.
~Array();
inline void CleanData();
- inline T& operator [](size_t Item);
- inline size_t Size();
+ inline T& operator [](size_t Item) const;
+ inline T* operator + (size_t Pos);
+ inline size_t Size(); // Returns the size in items, not in bytes.
void Add(size_t Items);
void Alloc(size_t Items);
void Reset();
+ void SoftReset();
void operator = (Array &Src);
void Push(T Item);
- T* Addr() {return(Buffer);}
+ void Append(T *Item,size_t Count);
+ T* Addr(size_t Item) {return Buffer+Item;}
+ void SetMaxSize(size_t Size) {MaxSize=Size;}
+ T* Begin() {return Buffer;}
+ T* End() {return Buffer==NULL ? NULL:Buffer+BufSize;}
+ void SetSecure() {Secure=true;}
};
+
template void Array::CleanData()
{
Buffer=NULL;
BufSize=0;
AllocSize=0;
+ MaxSize=0;
+ Secure=false;
}
@@ -40,30 +53,47 @@ template Array::Array()
template Array::Array(size_t Size)
{
- Buffer=(T *)rarmalloc(sizeof(T)*Size);
- if (Buffer==NULL && Size!=0)
- ErrHandler.MemoryError();
+ CleanData();
+ Add(Size);
+}
- AllocSize=BufSize=Size;
+
+// Copy constructor in case we need to pass an object as value.
+template Array::Array(const Array &Src)
+{
+ CleanData();
+ Alloc(Src.BufSize);
+ if (Src.BufSize!=0)
+ memcpy((void *)Buffer,(void *)Src.Buffer,Src.BufSize*sizeof(T));
}
template Array::~Array()
{
if (Buffer!=NULL)
- rarfree(Buffer);
+ {
+ if (Secure)
+ cleandata(Buffer,AllocSize*sizeof(T));
+ free(Buffer);
+ }
}
-template inline T& Array::operator [](size_t Item)
+template inline T& Array::operator [](size_t Item) const
{
- return(Buffer[Item]);
+ return Buffer[Item];
+}
+
+
+template inline T* Array::operator +(size_t Pos)
+{
+ return Buffer+Pos;
}
template inline size_t Array::Size()
{
- return(BufSize);
+ return BufSize;
}
@@ -72,12 +102,35 @@ template void Array::Add(size_t Items)
BufSize+=Items;
if (BufSize>AllocSize)
{
+ if (MaxSize!=0 && BufSize>MaxSize)
+ {
+ ErrHandler.GeneralErrMsg(L"Maximum allowed array size (%u) is exceeded",MaxSize);
+ ErrHandler.MemoryError();
+ }
+
size_t Suggested=AllocSize+AllocSize/4+32;
size_t NewSize=Max(BufSize,Suggested);
- Buffer=(T *)rarrealloc(Buffer,NewSize*sizeof(T));
- if (Buffer==NULL)
- ErrHandler.MemoryError();
+ T *NewBuffer;
+ if (Secure)
+ {
+ NewBuffer=(T *)malloc(NewSize*sizeof(T));
+ if (NewBuffer==NULL)
+ ErrHandler.MemoryError();
+ if (Buffer!=NULL)
+ {
+ memcpy(NewBuffer,Buffer,AllocSize*sizeof(T));
+ cleandata(Buffer,AllocSize*sizeof(T));
+ free(Buffer);
+ }
+ }
+ else
+ {
+ NewBuffer=(T *)realloc(Buffer,NewSize*sizeof(T));
+ if (NewBuffer==NULL)
+ ErrHandler.MemoryError();
+ }
+ Buffer=NewBuffer;
AllocSize=NewSize;
}
}
@@ -96,7 +149,7 @@ template void Array::Reset()
{
if (Buffer!=NULL)
{
- rarfree(Buffer);
+ free(Buffer);
Buffer=NULL;
}
BufSize=0;
@@ -104,6 +157,14 @@ template void Array::Reset()
}
+// Reset buffer size, but preserve already allocated memory if any,
+// so we can reuse it without wasting time to allocation.
+template void Array::SoftReset()
+{
+ BufSize=0;
+}
+
+
template void Array::operator =(Array &Src)
{
Reset();
@@ -119,4 +180,12 @@ template void Array::Push(T Item)
(*this)[Size()-1]=Item;
}
+
+template void Array::Append(T *Items,size_t Count)
+{
+ size_t CurSize=Size();
+ Add(Count);
+ memcpy(Buffer+CurSize,Items,Count*sizeof(T));
+}
+
#endif
diff --git a/libunrar/beosea.cpp b/libunrar/beosea.cpp
deleted file mode 100644
index 86eb7d4..0000000
--- a/libunrar/beosea.cpp
+++ /dev/null
@@ -1,113 +0,0 @@
-
-
-void ExtractBeEA(Archive &Arc,char *FileName)
-{
- if (Arc.HeaderCRC!=Arc.EAHead.HeadCRC)
- {
- Log(Arc.FileName,St(MEABroken),FileName);
- ErrHandler.SetErrorCode(CRC_ERROR);
- return;
- }
- if (Arc.EAHead.Method<0x31 || Arc.EAHead.Method>0x35 || Arc.EAHead.UnpVer>PACK_VER)
- {
- Log(Arc.FileName,St(MEAUnknHeader),FileName);
- return;
- }
-
- ComprDataIO DataIO;
- Unpack Unpack(&DataIO);
- Unpack.Init();
-
- Array UnpData(Arc.EAHead.UnpSize);
- DataIO.SetUnpackToMemory(&UnpData[0],Arc.EAHead.UnpSize);
- DataIO.SetPackedSizeToRead(Arc.EAHead.DataSize);
- DataIO.EnableShowProgress(false);
- DataIO.SetFiles(&Arc,NULL);
- Unpack.SetDestSize(Arc.EAHead.UnpSize);
- Unpack.DoUnpack(Arc.EAHead.UnpVer,false);
-
- if (Arc.EAHead.EACRC!=~DataIO.UnpFileCRC)
- {
- Log(Arc.FileName,St(MEABroken),FileName);
- ErrHandler.SetErrorCode(CRC_ERROR);
- return;
- }
- int fd = open(FileName,O_WRONLY);
- if (fd==-1)
- {
- Log(Arc.FileName,St(MCannotSetEA),FileName);
- ErrHandler.SetErrorCode(WARNING);
- return;
- }
-
- int AttrPos=0;
- while (AttrPos=sizeof(Name))
- {
- Log(Arc.FileName,St(MCannotSetEA),FileName);
- ErrHandler.SetErrorCode(WARNING);
- break;
- }
- memcpy(Name,CurItem+10,NameSize);
- Name[NameSize]=0;
- if (fs_write_attr(fd,Name,Type,0,CurItem+10+NameSize,Size)==-1)
- {
- Log(Arc.FileName,St(MCannotSetEA),FileName);
- ErrHandler.SetErrorCode(WARNING);
- break;
- }
- AttrPos+=10+NameSize+Size;
- }
- close(fd);
- mprintf(St(MShowEA));
-}
-
-
-void ExtractBeEANew(Archive &Arc,char *FileName)
-{
- Array SubData;
- if (!Arc.ReadSubData(&SubData,NULL))
- return;
-
- int fd = open(FileName,O_WRONLY);
- if (fd==-1)
- {
- Log(Arc.FileName,St(MCannotSetEA),FileName);
- ErrHandler.SetErrorCode(WARNING);
- return;
- }
-
- int AttrPos=0;
- while (AttrPos=sizeof(Name))
- {
- Log(Arc.FileName,St(MCannotSetEA),FileName);
- ErrHandler.SetErrorCode(WARNING);
- break;
- }
- memcpy(Name,CurItem+10,NameSize);
- Name[NameSize]=0;
- if (fs_write_attr(fd,Name,Type,0,CurItem+10+NameSize,Size)==-1)
- {
- Log(Arc.FileName,St(MCannotSetEA),FileName);
- ErrHandler.SetErrorCode(WARNING);
- break;
- }
- AttrPos+=10+NameSize+Size;
- }
- close(fd);
- mprintf(St(MShowEA));
-}
-
diff --git a/libunrar/blake2s.cpp b/libunrar/blake2s.cpp
new file mode 100644
index 0000000..317603d
--- /dev/null
+++ b/libunrar/blake2s.cpp
@@ -0,0 +1,183 @@
+// Based on public domain code written in 2012 by Samuel Neves
+
+#include "rar.hpp"
+
+#ifdef USE_SSE
+#include "blake2s_sse.cpp"
+#endif
+
+static void blake2s_init_param( blake2s_state *S, uint32 node_offset, uint32 node_depth);
+static void blake2s_update( blake2s_state *S, const byte *in, size_t inlen );
+static void blake2s_final( blake2s_state *S, byte *digest );
+
+#include "blake2sp.cpp"
+
+static const uint32 blake2s_IV[8] =
+{
+ 0x6A09E667UL, 0xBB67AE85UL, 0x3C6EF372UL, 0xA54FF53AUL,
+ 0x510E527FUL, 0x9B05688CUL, 0x1F83D9ABUL, 0x5BE0CD19UL
+};
+
+static const byte blake2s_sigma[10][16] =
+{
+ { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15 } ,
+ { 14, 10, 4, 8, 9, 15, 13, 6, 1, 12, 0, 2, 11, 7, 5, 3 } ,
+ { 11, 8, 12, 0, 5, 2, 15, 13, 10, 14, 3, 6, 7, 1, 9, 4 } ,
+ { 7, 9, 3, 1, 13, 12, 11, 14, 2, 6, 5, 10, 4, 0, 15, 8 } ,
+ { 9, 0, 5, 7, 2, 4, 10, 15, 14, 1, 11, 12, 6, 8, 3, 13 } ,
+ { 2, 12, 6, 10, 0, 11, 8, 3, 4, 13, 7, 5, 15, 14, 1, 9 } ,
+ { 12, 5, 1, 15, 14, 13, 4, 10, 0, 7, 6, 3, 9, 2, 8, 11 } ,
+ { 13, 11, 7, 14, 12, 1, 3, 9, 5, 0, 15, 4, 8, 6, 2, 10 } ,
+ { 6, 15, 14, 9, 11, 3, 0, 8, 12, 2, 13, 7, 1, 4, 10, 5 } ,
+ { 10, 2, 8, 4, 7, 6, 1, 5, 15, 11, 9, 14, 3, 12, 13 , 0 } ,
+};
+
+static inline void blake2s_set_lastnode( blake2s_state *S )
+{
+ S->f[1] = ~0U;
+}
+
+
+/* Some helper functions, not necessarily useful */
+static inline void blake2s_set_lastblock( blake2s_state *S )
+{
+ if( S->last_node ) blake2s_set_lastnode( S );
+
+ S->f[0] = ~0U;
+}
+
+
+static inline void blake2s_increment_counter( blake2s_state *S, const uint32 inc )
+{
+ S->t[0] += inc;
+ S->t[1] += ( S->t[0] < inc );
+}
+
+
+/* init2 xors IV with input parameter block */
+void blake2s_init_param( blake2s_state *S, uint32 node_offset, uint32 node_depth)
+{
+#ifdef USE_SSE
+ if (_SSE_Version>=SSE_SSE2)
+ blake2s_init_sse();
+#endif
+
+ S->init(); // Clean data.
+ for( int i = 0; i < 8; ++i )
+ S->h[i] = blake2s_IV[i];
+
+ S->h[0] ^= 0x02080020; // We use BLAKE2sp parameters block.
+ S->h[2] ^= node_offset;
+ S->h[3] ^= (node_depth<<16)|0x20000000;
+}
+
+
+#define G(r,i,m,a,b,c,d) \
+ a = a + b + m[blake2s_sigma[r][2*i+0]]; \
+ d = rotr32(d ^ a, 16); \
+ c = c + d; \
+ b = rotr32(b ^ c, 12); \
+ a = a + b + m[blake2s_sigma[r][2*i+1]]; \
+ d = rotr32(d ^ a, 8); \
+ c = c + d; \
+ b = rotr32(b ^ c, 7);
+
+
+static void blake2s_compress( blake2s_state *S, const byte block[BLAKE2S_BLOCKBYTES] )
+{
+ uint32 m[16];
+ uint32 v[16];
+
+ for( size_t i = 0; i < 16; ++i )
+ m[i] = RawGet4( block + i * 4 );
+
+ for( size_t i = 0; i < 8; ++i )
+ v[i] = S->h[i];
+
+ v[ 8] = blake2s_IV[0];
+ v[ 9] = blake2s_IV[1];
+ v[10] = blake2s_IV[2];
+ v[11] = blake2s_IV[3];
+ v[12] = S->t[0] ^ blake2s_IV[4];
+ v[13] = S->t[1] ^ blake2s_IV[5];
+ v[14] = S->f[0] ^ blake2s_IV[6];
+ v[15] = S->f[1] ^ blake2s_IV[7];
+
+ for ( uint r = 0; r <= 9; ++r ) // No gain on i7 if unrolled, but exe size grows.
+ {
+ G(r,0,m,v[ 0],v[ 4],v[ 8],v[12]);
+ G(r,1,m,v[ 1],v[ 5],v[ 9],v[13]);
+ G(r,2,m,v[ 2],v[ 6],v[10],v[14]);
+ G(r,3,m,v[ 3],v[ 7],v[11],v[15]);
+ G(r,4,m,v[ 0],v[ 5],v[10],v[15]);
+ G(r,5,m,v[ 1],v[ 6],v[11],v[12]);
+ G(r,6,m,v[ 2],v[ 7],v[ 8],v[13]);
+ G(r,7,m,v[ 3],v[ 4],v[ 9],v[14]);
+ }
+
+ for( size_t i = 0; i < 8; ++i )
+ S->h[i] = S->h[i] ^ v[i] ^ v[i + 8];
+}
+
+
+void blake2s_update( blake2s_state *S, const byte *in, size_t inlen )
+{
+ while( inlen > 0 )
+ {
+ size_t left = S->buflen;
+ size_t fill = 2 * BLAKE2S_BLOCKBYTES - left;
+
+ if( inlen > fill )
+ {
+ memcpy( S->buf + left, in, fill ); // Fill buffer
+ S->buflen += fill;
+ blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
+
+#ifdef USE_SSE
+#ifdef _WIN_32 // We use SSSE3 _mm_shuffle_epi8 only in x64 mode.
+ if (_SSE_Version>=SSE_SSE2)
+#else
+ if (_SSE_Version>=SSE_SSSE3)
+#endif
+ blake2s_compress_sse( S, S->buf );
+ else
+ blake2s_compress( S, S->buf ); // Compress
+#else
+ blake2s_compress( S, S->buf ); // Compress
+#endif
+
+ memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES ); // Shift buffer left
+ S->buflen -= BLAKE2S_BLOCKBYTES;
+ in += fill;
+ inlen -= fill;
+ }
+ else // inlen <= fill
+ {
+ memcpy( S->buf + left, in, (size_t)inlen );
+ S->buflen += (size_t)inlen; // Be lazy, do not compress
+ in += inlen;
+ inlen = 0;
+ }
+ }
+}
+
+
+void blake2s_final( blake2s_state *S, byte *digest )
+{
+ if( S->buflen > BLAKE2S_BLOCKBYTES )
+ {
+ blake2s_increment_counter( S, BLAKE2S_BLOCKBYTES );
+ blake2s_compress( S, S->buf );
+ S->buflen -= BLAKE2S_BLOCKBYTES;
+ memcpy( S->buf, S->buf + BLAKE2S_BLOCKBYTES, S->buflen );
+ }
+
+ blake2s_increment_counter( S, ( uint32 )S->buflen );
+ blake2s_set_lastblock( S );
+ memset( S->buf + S->buflen, 0, 2 * BLAKE2S_BLOCKBYTES - S->buflen ); /* Padding */
+ blake2s_compress( S, S->buf );
+
+ for( int i = 0; i < 8; ++i ) /* Output full hash */
+ RawPut4( S->h[i], digest + 4 * i );
+}
+
diff --git a/libunrar/blake2s.hpp b/libunrar/blake2s.hpp
new file mode 100644
index 0000000..f88ef37
--- /dev/null
+++ b/libunrar/blake2s.hpp
@@ -0,0 +1,102 @@
+// Based on public domain code written in 2012 by Samuel Neves
+#ifndef _RAR_BLAKE2_
+#define _RAR_BLAKE2_
+
+#define BLAKE2_DIGEST_SIZE 32
+#define BLAKE2_THREADS_NUMBER 8
+
+enum blake2s_constant
+{
+ BLAKE2S_BLOCKBYTES = 64,
+ BLAKE2S_OUTBYTES = 32
+};
+
+
+// Alignment to 64 improves performance of both SSE and non-SSE versions.
+// Alignment to n*16 is required for SSE version, so we selected 64.
+// We use the custom alignment scheme instead of __declspec(align(x)),
+// because it is less compiler dependent. Also the compiler directive
+// does not help if structure is a member of class allocated through
+// 'new' operator.
+struct blake2s_state
+{
+ enum { BLAKE_ALIGNMENT = 64 };
+
+ // buffer and uint32 h[8], t[2], f[2];
+ enum { BLAKE_DATA_SIZE = 48 + 2 * BLAKE2S_BLOCKBYTES };
+
+ byte ubuf[BLAKE_DATA_SIZE + BLAKE_ALIGNMENT];
+
+ byte *buf; // byte buf[2 * BLAKE2S_BLOCKBYTES].
+ uint32 *h, *t, *f; // uint32 h[8], t[2], f[2].
+
+ size_t buflen;
+ byte last_node;
+
+ blake2s_state()
+ {
+ set_pointers();
+ }
+
+ // Required when we declare and assign in the same command.
+ blake2s_state(blake2s_state &st)
+ {
+ set_pointers();
+ *this=st;
+ }
+
+ void set_pointers()
+ {
+ // Set aligned pointers. Must be done in constructor, not in Init(),
+ // so assignments like 'blake2sp_state res=blake2ctx' work correctly
+ // even if blake2sp_init is not called for 'res'.
+ buf = (byte *) ALIGN_VALUE(ubuf, BLAKE_ALIGNMENT);
+ h = (uint32 *) (buf + 2 * BLAKE2S_BLOCKBYTES);
+ t = h + 8;
+ f = t + 2;
+ }
+
+ void init()
+ {
+ memset( ubuf, 0, sizeof( ubuf ) );
+ buflen = 0;
+ last_node = 0;
+ }
+
+ // Since we use pointers, the default = would work incorrectly.
+ blake2s_state& operator = (blake2s_state &st)
+ {
+ if (this != &st)
+ {
+ memcpy(buf, st.buf, BLAKE_DATA_SIZE);
+ buflen = st.buflen;
+ last_node = st.last_node;
+ }
+ return *this;
+ }
+};
+
+
+#ifdef RAR_SMP
+class ThreadPool;
+#endif
+
+struct blake2sp_state
+{
+ blake2s_state S[8];
+ blake2s_state R;
+ byte buf[8 * BLAKE2S_BLOCKBYTES];
+ size_t buflen;
+
+#ifdef RAR_SMP
+ ThreadPool *ThPool;
+ uint MaxThreads;
+#endif
+};
+
+void blake2sp_init( blake2sp_state *S );
+void blake2sp_update( blake2sp_state *S, const byte *in, size_t inlen );
+void blake2sp_final( blake2sp_state *S, byte *digest );
+
+#endif
+
diff --git a/libunrar/blake2s_sse.cpp b/libunrar/blake2s_sse.cpp
new file mode 100644
index 0000000..1a02f21
--- /dev/null
+++ b/libunrar/blake2s_sse.cpp
@@ -0,0 +1,129 @@
+// Based on public domain code written in 2012 by Samuel Neves
+
+extern const byte blake2s_sigma[10][16];
+
+// Initialization vector.
+static __m128i blake2s_IV_0_3, blake2s_IV_4_7;
+
+#ifdef _WIN_64
+// Constants for cyclic rotation. Used in 64-bit mode in mm_rotr_epi32 macro.
+static __m128i crotr8, crotr16;
+#endif
+
+static void blake2s_init_sse()
+{
+ // We cannot initialize these 128 bit variables in place when declaring
+ // them globally, because global scope initialization is performed before
+ // our SSE check and it would make code incompatible with older non-SSE2
+ // CPUs. Also we cannot initialize them as static inside of function
+ // using these variables, because SSE static initialization is not thread
+ // safe: first thread starts initialization and sets "init done" flag even
+ // if it is not done yet, second thread can attempt to access half-init
+ // SSE data. So we moved init code here.
+
+ blake2s_IV_0_3 = _mm_setr_epi32( 0x6A09E667, 0xBB67AE85, 0x3C6EF372, 0xA54FF53A );
+ blake2s_IV_4_7 = _mm_setr_epi32( 0x510E527F, 0x9B05688C, 0x1F83D9AB, 0x5BE0CD19 );
+
+#ifdef _WIN_64
+ crotr8 = _mm_set_epi8( 12, 15, 14, 13, 8, 11, 10, 9, 4, 7, 6, 5, 0, 3, 2, 1 );
+ crotr16 = _mm_set_epi8( 13, 12, 15, 14, 9, 8, 11, 10, 5, 4, 7, 6, 1, 0, 3, 2 );
+#endif
+}
+
+
+#define LOAD(p) _mm_load_si128( (__m128i *)(p) )
+#define STORE(p,r) _mm_store_si128((__m128i *)(p), r)
+
+#ifdef _WIN_32
+// 32-bit mode has less SSE2 registers and in MSVC2008 it is more efficient
+// to not use _mm_shuffle_epi8 here.
+#define mm_rotr_epi32(r, c) ( \
+ _mm_xor_si128(_mm_srli_epi32( (r), c ),_mm_slli_epi32( (r), 32-c )) )
+#else
+#define mm_rotr_epi32(r, c) ( \
+ c==8 ? _mm_shuffle_epi8(r,crotr8) \
+ : c==16 ? _mm_shuffle_epi8(r,crotr16) \
+ : _mm_xor_si128(_mm_srli_epi32( (r), c ),_mm_slli_epi32( (r), 32-c )) )
+#endif
+
+
+#define G1(row1,row2,row3,row4,buf) \
+ row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \
+ row4 = _mm_xor_si128( row4, row1 ); \
+ row4 = mm_rotr_epi32(row4, 16); \
+ row3 = _mm_add_epi32( row3, row4 ); \
+ row2 = _mm_xor_si128( row2, row3 ); \
+ row2 = mm_rotr_epi32(row2, 12);
+
+#define G2(row1,row2,row3,row4,buf) \
+ row1 = _mm_add_epi32( _mm_add_epi32( row1, buf), row2 ); \
+ row4 = _mm_xor_si128( row4, row1 ); \
+ row4 = mm_rotr_epi32(row4, 8); \
+ row3 = _mm_add_epi32( row3, row4 ); \
+ row2 = _mm_xor_si128( row2, row3 ); \
+ row2 = mm_rotr_epi32(row2, 7);
+
+#define DIAGONALIZE(row1,row2,row3,row4) \
+ row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(2,1,0,3) ); \
+ row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \
+ row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(0,3,2,1) );
+
+#define UNDIAGONALIZE(row1,row2,row3,row4) \
+ row4 = _mm_shuffle_epi32( row4, _MM_SHUFFLE(0,3,2,1) ); \
+ row3 = _mm_shuffle_epi32( row3, _MM_SHUFFLE(1,0,3,2) ); \
+ row2 = _mm_shuffle_epi32( row2, _MM_SHUFFLE(2,1,0,3) );
+
+#ifdef _WIN_64
+ // MSVC 2008 in x64 mode expands _mm_set_epi32 to store to stack and load
+ // from stack operations, which are slower than this code.
+ #define _mm_set_epi32(i3,i2,i1,i0) \
+ _mm_unpacklo_epi32(_mm_unpacklo_epi32(_mm_cvtsi32_si128(i0),_mm_cvtsi32_si128(i2)), \
+ _mm_unpacklo_epi32(_mm_cvtsi32_si128(i1),_mm_cvtsi32_si128(i3)))
+#endif
+
+// Original BLAKE2 SSE4.1 message loading code was a little slower in x86 mode
+// and about the same in x64 mode in our test. Perhaps depends on compiler.
+// We also tried _mm_i32gather_epi32 and _mm256_i32gather_epi32 AVX2 gather
+// instructions here, but they did not show any speed gain on i7-6700K.
+#define SSE_ROUND(m,row,r) \
+{ \
+ __m128i buf; \
+ buf=_mm_set_epi32(m[blake2s_sigma[r][6]],m[blake2s_sigma[r][4]],m[blake2s_sigma[r][2]],m[blake2s_sigma[r][0]]); \
+ G1(row[0],row[1],row[2],row[3],buf); \
+ buf=_mm_set_epi32(m[blake2s_sigma[r][7]],m[blake2s_sigma[r][5]],m[blake2s_sigma[r][3]],m[blake2s_sigma[r][1]]); \
+ G2(row[0],row[1],row[2],row[3],buf); \
+ DIAGONALIZE(row[0],row[1],row[2],row[3]); \
+ buf=_mm_set_epi32(m[blake2s_sigma[r][14]],m[blake2s_sigma[r][12]],m[blake2s_sigma[r][10]],m[blake2s_sigma[r][8]]); \
+ G1(row[0],row[1],row[2],row[3],buf); \
+ buf=_mm_set_epi32(m[blake2s_sigma[r][15]],m[blake2s_sigma[r][13]],m[blake2s_sigma[r][11]],m[blake2s_sigma[r][9]]); \
+ G2(row[0],row[1],row[2],row[3],buf); \
+ UNDIAGONALIZE(row[0],row[1],row[2],row[3]); \
+}
+
+
+static int blake2s_compress_sse( blake2s_state *S, const byte block[BLAKE2S_BLOCKBYTES] )
+{
+ __m128i row[4];
+ __m128i ff0, ff1;
+
+ const uint32 *m = ( uint32 * )block;
+
+ row[0] = ff0 = LOAD( &S->h[0] );
+ row[1] = ff1 = LOAD( &S->h[4] );
+
+ row[2] = blake2s_IV_0_3;
+ row[3] = _mm_xor_si128( blake2s_IV_4_7, LOAD( &S->t[0] ) );
+ SSE_ROUND( m, row, 0 );
+ SSE_ROUND( m, row, 1 );
+ SSE_ROUND( m, row, 2 );
+ SSE_ROUND( m, row, 3 );
+ SSE_ROUND( m, row, 4 );
+ SSE_ROUND( m, row, 5 );
+ SSE_ROUND( m, row, 6 );
+ SSE_ROUND( m, row, 7 );
+ SSE_ROUND( m, row, 8 );
+ SSE_ROUND( m, row, 9 );
+ STORE( &S->h[0], _mm_xor_si128( ff0, _mm_xor_si128( row[0], row[2] ) ) );
+ STORE( &S->h[4], _mm_xor_si128( ff1, _mm_xor_si128( row[1], row[3] ) ) );
+ return 0;
+}
diff --git a/libunrar/blake2sp.cpp b/libunrar/blake2sp.cpp
new file mode 100644
index 0000000..da64588
--- /dev/null
+++ b/libunrar/blake2sp.cpp
@@ -0,0 +1,153 @@
+/*
+ BLAKE2 reference source code package - reference C implementations
+
+ Written in 2012 by Samuel Neves
+
+ To the extent possible under law, the author(s) have dedicated all copyright
+ and related and neighboring rights to this software to the public domain
+ worldwide. This software is distributed without any warranty.
+
+ You should have received a copy of the CC0 Public Domain Dedication along with
+ this software. If not, see .
+*/
+
+#define PARALLELISM_DEGREE 8
+
+void blake2sp_init( blake2sp_state *S )
+{
+ memset( S->buf, 0, sizeof( S->buf ) );
+ S->buflen = 0;
+
+ blake2s_init_param( &S->R, 0, 1 ); // Init root.
+
+ for( uint i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2s_init_param( &S->S[i], i, 0 ); // Init leaf.
+
+ S->R.last_node = 1;
+ S->S[PARALLELISM_DEGREE - 1].last_node = 1;
+}
+
+
+struct Blake2ThreadData
+{
+ void Update();
+ blake2s_state *S;
+ const byte *in;
+ size_t inlen;
+};
+
+
+void Blake2ThreadData::Update()
+{
+ size_t inlen__ = inlen;
+ const byte *in__ = ( const byte * )in;
+
+ while( inlen__ >= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES )
+ {
+#ifdef USE_SSE
+ // We gain 5% in i7 SSE mode by prefetching next data block.
+ if (_SSE_Version>=SSE_SSE && inlen__ >= 2 * PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES)
+ _mm_prefetch((char*)(in__ + PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES), _MM_HINT_T0);
+#endif
+ blake2s_update( S, in__, BLAKE2S_BLOCKBYTES );
+ in__ += PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+ inlen__ -= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+ }
+}
+
+#ifdef RAR_SMP
+THREAD_PROC(Blake2Thread)
+{
+ Blake2ThreadData *td=(Blake2ThreadData *)Data;
+ td->Update();
+}
+#endif
+
+
+void blake2sp_update( blake2sp_state *S, const byte *in, size_t inlen )
+{
+ size_t left = S->buflen;
+ size_t fill = sizeof( S->buf ) - left;
+
+ if( left && inlen >= fill )
+ {
+ memcpy( S->buf + left, in, fill );
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2s_update( &S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, BLAKE2S_BLOCKBYTES );
+
+ in += fill;
+ inlen -= fill;
+ left = 0;
+ }
+
+ Blake2ThreadData btd_array[PARALLELISM_DEGREE];
+
+#ifdef RAR_SMP
+ uint ThreadNumber = inlen < 0x1000 ? 1 : S->MaxThreads;
+
+ if (ThreadNumber==6 || ThreadNumber==7) // 6 and 7 threads work slower than 4 here.
+ ThreadNumber=4;
+#else
+ uint ThreadNumber=1;
+#endif
+
+ for (size_t id__=0;id__inlen = inlen;
+ btd->in = in + id__ * BLAKE2S_BLOCKBYTES;
+ btd->S = &S->S[id__];
+
+#ifdef RAR_SMP
+ if (ThreadNumber>1)
+ S->ThPool->AddTask(Blake2Thread,(void*)btd);
+ else
+ btd->Update();
+#else
+ btd->Update();
+#endif
+ id__++;
+ }
+#ifdef RAR_SMP
+ if (S->ThPool!=NULL) // Can be NULL in -mt1 mode.
+ S->ThPool->WaitDone();
+#endif // RAR_SMP
+ }
+
+ in += inlen - inlen % ( PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES );
+ inlen %= PARALLELISM_DEGREE * BLAKE2S_BLOCKBYTES;
+
+ if( inlen > 0 )
+ memcpy( S->buf + left, in, (size_t)inlen );
+
+ S->buflen = left + (size_t)inlen;
+}
+
+
+void blake2sp_final( blake2sp_state *S, byte *digest )
+{
+ byte hash[PARALLELISM_DEGREE][BLAKE2S_OUTBYTES];
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ {
+ if( S->buflen > i * BLAKE2S_BLOCKBYTES )
+ {
+ size_t left = S->buflen - i * BLAKE2S_BLOCKBYTES;
+
+ if( left > BLAKE2S_BLOCKBYTES ) left = BLAKE2S_BLOCKBYTES;
+
+ blake2s_update( &S->S[i], S->buf + i * BLAKE2S_BLOCKBYTES, left );
+ }
+
+ blake2s_final( &S->S[i], hash[i] );
+ }
+
+ for( size_t i = 0; i < PARALLELISM_DEGREE; ++i )
+ blake2s_update( &S->R, hash[i], BLAKE2S_OUTBYTES );
+
+ blake2s_final( &S->R, digest );
+}
diff --git a/libunrar/cmddata.cpp b/libunrar/cmddata.cpp
index 4f53f44..74e565a 100644
--- a/libunrar/cmddata.cpp
+++ b/libunrar/cmddata.cpp
@@ -1,127 +1,154 @@
#include "rar.hpp"
+#include "cmdfilter.cpp"
+#include "cmdmix.cpp"
+
CommandData::CommandData()
{
- FileArgs=ExclArgs=InclArgs=StoreArgs=ArcNames=NULL;
Init();
}
-CommandData::~CommandData()
-{
- Close();
-}
-
-
void CommandData::Init()
{
- Close();
+ RAROptions::Init();
*Command=0;
*ArcName=0;
- *ArcNameW=0;
FileLists=false;
NoMoreSwitches=false;
- FileArgs=new StringList;
- ExclArgs=new StringList;
- InclArgs=new StringList;
- StoreArgs=new StringList;
- ArcNames=new StringList;
-}
+ ListMode=RCLM_AUTO;
+
+ BareOutput=false;
-void CommandData::Close()
-{
- delete FileArgs;
- delete ExclArgs;
- delete InclArgs;
- delete StoreArgs;
- delete ArcNames;
- FileArgs=ExclArgs=InclArgs=StoreArgs=ArcNames=NULL;
+ FileArgs.Reset();
+ ExclArgs.Reset();
+ InclArgs.Reset();
+ StoreArgs.Reset();
+ ArcNames.Reset();
NextVolSizes.Reset();
}
+// Return the pointer to next position in the string and store dynamically
+// allocated command line parameter in Par.
+static const wchar *AllocCmdParam(const wchar *CmdLine,wchar **Par)
+{
+ const wchar *NextCmd=GetCmdParam(CmdLine,NULL,0);
+ if (NextCmd==NULL)
+ return NULL;
+ size_t ParSize=NextCmd-CmdLine+2; // Parameter size including the trailing zero.
+ *Par=(wchar *)malloc(ParSize*sizeof(wchar));
+ if (*Par==NULL)
+ return NULL;
+ return GetCmdParam(CmdLine,*Par,ParSize);
+}
+
+
#if !defined(SFX_MODULE)
-void CommandData::ParseArg(char *Arg,wchar *ArgW)
+void CommandData::ParseCommandLine(bool Preprocess,int argc, char *argv[])
+{
+ *Command=0;
+ NoMoreSwitches=false;
+#ifdef CUSTOM_CMDLINE_PARSER
+ // In Windows we may prefer to implement our own command line parser
+ // to avoid replacing \" by " in standard parser. Such replacing corrupts
+ // destination paths like "dest path\" in extraction commands.
+ // Also our own parser is Unicode compatible.
+ const wchar *CmdLine=GetCommandLine();
+
+ wchar *Par;
+ for (bool FirstParam=true;;FirstParam=false)
+ {
+ if ((CmdLine=AllocCmdParam(CmdLine,&Par))==NULL)
+ break;
+ if (!FirstParam) // First parameter is the executable name.
+ if (Preprocess)
+ PreprocessArg(Par);
+ else
+ ParseArg(Par);
+ free(Par);
+ }
+#else
+ Array Arg;
+ for (int I=1;IAddString(Arg);
+ if ((Add || CmdChar=='T') && (*Arg!='@' || ListMode==RCLM_REJECT_LISTS))
+ FileArgs.AddString(Arg);
else
{
- struct FindData FileData;
- bool Found=FindFile::FastFind(Arg,NULL,&FileData);
- if (!Found && *Arg=='@' && !IsWildcard(Arg))
+ FindData FileData;
+ bool Found=FindFile::FastFind(Arg,&FileData);
+ if ((!Found || ListMode==RCLM_ACCEPT_LISTS) &&
+ ListMode!=RCLM_REJECT_LISTS && *Arg=='@' && !IsWildcard(Arg+1))
{
FileLists=true;
- RAR_CHARSET Charset=FilelistCharset;
+ ReadTextFile(Arg+1,&FileArgs,false,true,FilelistCharset,true,true,true);
-#if defined(_WIN_32) && !defined(GUI)
- // for compatibility reasons we use OEM encoding
- // in Win32 console version by default
-
- if (Charset==RCH_DEFAULT)
- Charset=RCH_OEM;
-#endif
-
- ReadTextFile(Arg+1,FileArgs,false,true,Charset,true,true,true);
}
- else
- if (Found && FileData.IsDir && Extract && *ExtrPath==0)
+ else // We use 'destpath\' when extracting and reparing.
+ if (Found && FileData.IsDir && (Extract || Repair) && *ExtrPath==0)
{
- strcpy(ExtrPath,Arg);
- AddEndSlash(ExtrPath);
+ wcsncpyz(ExtrPath,Arg,ASIZE(ExtrPath));
+ AddEndSlash(ExtrPath,ASIZE(ExtrPath));
}
else
- FileArgs->AddString(Arg);
+ FileArgs.AddString(Arg);
}
}
}
@@ -130,257 +157,163 @@ void CommandData::ParseArg(char *Arg,wchar *ArgW)
void CommandData::ParseDone()
{
- if (FileArgs->ItemsCount()==0 && !FileLists)
- FileArgs->AddString(MASKALL);
- char CmdChar=etoupper(*Command);
+ if (FileArgs.ItemsCount()==0 && !FileLists)
+ FileArgs.AddString(MASKALL);
+ wchar CmdChar=toupperw(Command[0]);
bool Extract=CmdChar=='X' || CmdChar=='E' || CmdChar=='P';
if (Test && Extract)
Test=false; // Switch '-t' is senseless for 'X', 'E', 'P' commands.
- BareOutput=(CmdChar=='L' || CmdChar=='V') && Command[1]=='B';
+
+ // Suppress the copyright message and final end of line for 'lb' and 'vb'.
+ if ((CmdChar=='L' || CmdChar=='V') && Command[1]=='B')
+ BareOutput=true;
}
-#if !defined(SFX_MODULE) && !defined(_WIN_CE)
+#if !defined(SFX_MODULE)
void CommandData::ParseEnvVar()
{
char *EnvStr=getenv("RAR");
if (EnvStr!=NULL)
- ProcessSwitchesString(EnvStr);
-}
-#endif
-
-
-
-// return 'false' if -cfg- is present and preprocess switches
-// which must be processed before the rest of command line
-
-#ifndef SFX_MODULE
-bool CommandData::IsConfigEnabled(int argc,char *argv[])
-{
- bool ConfigEnabled=true;
- for (int I=1;I EnvStrW(strlen(EnvStr)+1);
+ CharToWide(EnvStr,&EnvStrW[0],EnvStrW.Size());
+ ProcessSwitchesString(&EnvStrW[0]);
}
}
#endif
-#if !defined(SFX_MODULE) && !defined(_WIN_CE)
-void CommandData::ProcessSwitchesString(char *Str)
+
+#if !defined(SFX_MODULE)
+// Preprocess those parameters, which must be processed before the rest of
+// command line. Return 'false' to stop further processing.
+void CommandData::PreprocessArg(const wchar *Arg)
{
- while (*Str)
+ if (IsSwitch(Arg[0]) && !NoMoreSwitches)
{
- while (!IsSwitch(*Str) && *Str!=0)
- Str++;
- if (*Str==0)
- break;
- char *Next=Str;
- while (!(Next[0]==' ' && IsSwitch(Next[1])) && *Next!=0)
- Next++;
- char NextChar=*Next;
- *Next=0;
- ProcessSwitch(Str+1);
- *Next=NextChar;
- Str=Next;
+ Arg++;
+ if (Arg[0]=='-' && Arg[1]==0) // Switch "--".
+ NoMoreSwitches=true;
+ if (wcsicomp(Arg,L"cfg-")==0)
+ ConfigDisabled=true;
+ if (wcsnicomp(Arg,L"ilog",4)==0)
+ {
+ // Ensure that correct log file name is already set
+ // if we need to report an error when processing the command line.
+ ProcessSwitch(Arg);
+ InitLogOptions(LogName,ErrlogCharset);
+ }
+ if (wcsnicomp(Arg,L"sc",2)==0)
+ {
+ // Process -sc before reading any file lists.
+ ProcessSwitch(Arg);
+ if (*LogName!=0)
+ InitLogOptions(LogName,ErrlogCharset);
+ }
+ }
+ else
+ if (*Command==0)
+ wcsncpy(Command,Arg,ASIZE(Command)); // Need for rar.ini.
+}
+#endif
+
+
+#if !defined(SFX_MODULE)
+void CommandData::ReadConfig()
+{
+ StringList List;
+ if (ReadTextFile(DefConfigName,&List,true))
+ {
+ wchar *Str;
+ while ((Str=List.GetString())!=NULL)
+ {
+ while (IsSpace(*Str))
+ Str++;
+ if (wcsnicomp(Str,L"switches=",9)==0)
+ ProcessSwitchesString(Str+9);
+ if (*Command!=0)
+ {
+ wchar Cmd[16];
+ wcsncpyz(Cmd,Command,ASIZE(Cmd));
+ wchar C0=toupperw(Cmd[0]);
+ wchar C1=toupperw(Cmd[1]);
+ if (C0=='I' || C0=='L' || C0=='M' || C0=='S' || C0=='V')
+ Cmd[1]=0;
+ if (C0=='R' && (C1=='R' || C1=='V'))
+ Cmd[2]=0;
+ wchar SwName[16+ASIZE(Cmd)];
+ swprintf(SwName,ASIZE(SwName),L"switches_%ls=",Cmd);
+ size_t Length=wcslen(SwName);
+ if (wcsnicomp(Str,SwName,Length)==0)
+ ProcessSwitchesString(Str+Length);
+ }
+ }
}
}
#endif
#if !defined(SFX_MODULE)
-void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW)
+void CommandData::ProcessSwitchesString(const wchar *Str)
+{
+ wchar *Par;
+ while ((Str=AllocCmdParam(Str,&Par))!=NULL)
+ {
+ if (IsSwitch(*Par))
+ ProcessSwitch(Par+1);
+ free(Par);
+ }
+}
+#endif
+
+
+#if !defined(SFX_MODULE)
+void CommandData::ProcessSwitch(const wchar *Switch)
{
- switch(etoupper(Switch[0]))
+ switch(toupperw(Switch[0]))
{
- case 'I':
- if (strnicomp(&Switch[1],"LOG",3)==0)
- {
- strncpyz(LogName,Switch[4] ? Switch+4:DefLogName,ASIZE(LogName));
- break;
- }
- if (stricomp(&Switch[1],"SND")==0)
- {
- Sound=true;
- break;
- }
- if (stricomp(&Switch[1],"ERR")==0)
- {
- MsgStream=MSG_STDERR;
- break;
- }
- if (strnicomp(&Switch[1],"EML",3)==0)
- {
- strncpyz(EmailTo,Switch[4] ? Switch+4:"@",ASIZE(EmailTo));
- EmailTo[sizeof(EmailTo)-1]=0;
- break;
- }
- if (stricomp(&Switch[1],"NUL")==0)
- {
- MsgStream=MSG_NULL;
- break;
- }
- if (etoupper(Switch[1])=='D')
- {
- for (int I=2;Switch[I]!=0;I++)
- switch(etoupper(Switch[I]))
- {
- case 'Q':
- MsgStream=MSG_ERRONLY;
- break;
- case 'C':
- DisableCopyright=true;
- break;
- case 'D':
- DisableDone=true;
- break;
- case 'P':
- DisablePercentage=true;
- break;
- }
- break;
- }
- if (stricomp(&Switch[1],"OFF")==0)
- {
- Shutdown=true;
- break;
- }
- break;
- case 'T':
- switch(etoupper(Switch[1]))
- {
- case 'K':
- ArcTime=ARCTIME_KEEP;
- break;
- case 'L':
- ArcTime=ARCTIME_LATEST;
- break;
- case 'O':
- FileTimeBefore.SetAgeText(Switch+2);
- break;
- case 'N':
- FileTimeAfter.SetAgeText(Switch+2);
- break;
- case 'B':
- FileTimeBefore.SetIsoText(Switch+2);
- break;
- case 'A':
- FileTimeAfter.SetIsoText(Switch+2);
- break;
- case 'S':
- {
- EXTTIME_MODE Mode=EXTTIME_HIGH3;
- bool CommonMode=Switch[2]>='0' && Switch[2]<='4';
- if (CommonMode)
- Mode=(EXTTIME_MODE)(Switch[2]-'0');
- if (Switch[2]=='-')
- Mode=EXTTIME_NONE;
- if (CommonMode || Switch[2]=='-' || Switch[2]=='+' || Switch[2]==0)
- xmtime=xctime=xatime=Mode;
- else
- {
- if (Switch[3]>='0' && Switch[3]<='4')
- Mode=(EXTTIME_MODE)(Switch[3]-'0');
- if (Switch[3]=='-')
- Mode=EXTTIME_NONE;
- switch(etoupper(Switch[2]))
- {
- case 'M':
- xmtime=Mode;
- break;
- case 'C':
- xctime=Mode;
- break;
- case 'A':
- xatime=Mode;
- break;
- case 'R':
- xarctime=Mode;
- break;
- }
- }
- }
- break;
- case '-':
- Test=false;
- break;
- case 0:
- Test=true;
- break;
- default:
- BadSwitch(Switch);
- break;
- }
+ case '@':
+ ListMode=Switch[1]=='+' ? RCLM_ACCEPT_LISTS:RCLM_REJECT_LISTS;
break;
case 'A':
- switch(etoupper(Switch[1]))
+ switch(toupperw(Switch[1]))
{
case 'C':
ClearArc=true;
break;
case 'D':
- AppendArcNameToPath=true;
+ if (Switch[2]==0)
+ AppendArcNameToPath=APPENDARCNAME_DESTPATH;
+ else
+ if (Switch[2]=='1')
+ AppendArcNameToPath=APPENDARCNAME_OWNDIR;
break;
+#ifndef SFX_MODULE
case 'G':
if (Switch[2]=='-' && Switch[3]==0)
GenerateArcName=0;
else
- {
- GenerateArcName=true;
- strncpyz(GenerateMask,Switch+2,ASIZE(GenerateMask));
- }
+ if (toupperw(Switch[2])=='F')
+ wcsncpyz(DefGenerateMask,Switch+3,ASIZE(DefGenerateMask));
+ else
+ {
+ GenerateArcName=true;
+ wcsncpyz(GenerateMask,Switch+2,ASIZE(GenerateMask));
+ }
break;
+#endif
case 'I':
IgnoreGeneralAttr=true;
break;
- case 'N': //reserved for archive name
+ case 'N': // Reserved for archive name.
break;
case 'O':
AddArcOnly=true;
break;
case 'P':
- strcpy(ArcPath,Switch+2);
- if (SwitchW!=NULL && *SwitchW!=0)
- strcpyw(ArcPathW,SwitchW+2);
+ wcsncpyz(ArcPath,Switch+2,ASIZE(ArcPath));
break;
case 'S':
SyncFiles=true;
@@ -390,9 +323,24 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW)
break;
}
break;
+ case 'C':
+ if (Switch[2]==0)
+ switch(toupperw(Switch[1]))
+ {
+ case '-':
+ DisableComment=true;
+ break;
+ case 'U':
+ ConvertNames=NAMES_UPPERCASE;
+ break;
+ case 'L':
+ ConvertNames=NAMES_LOWERCASE;
+ break;
+ }
+ break;
case 'D':
if (Switch[2]==0)
- switch(etoupper(Switch[1]))
+ switch(toupperw(Switch[1]))
{
case 'S':
DisableSortSolid=true;
@@ -405,98 +353,8 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW)
break;
}
break;
- case 'O':
- switch(etoupper(Switch[1]))
- {
- case '+':
- Overwrite=OVERWRITE_ALL;
- break;
- case '-':
- Overwrite=OVERWRITE_NONE;
- break;
- case 0:
- Overwrite=OVERWRITE_FORCE_ASK;
- break;
- case 'R':
- Overwrite=OVERWRITE_AUTORENAME;
- break;
- case 'W':
- ProcessOwners=true;
- break;
-#ifdef SAVE_LINKS
- case 'L':
- SaveLinks=true;
- break;
-#endif
-#ifdef _WIN_32
- case 'S':
- SaveStreams=true;
- break;
- case 'C':
- SetCompressedAttr=true;
- break;
-#endif
- default :
- BadSwitch(Switch);
- break;
- }
- break;
- case 'R':
- switch(etoupper(Switch[1]))
- {
- case 0:
- Recurse=RECURSE_ALWAYS;
- break;
- case '-':
- Recurse=RECURSE_DISABLE;
- break;
- case '0':
- Recurse=RECURSE_WILDCARDS;
- break;
-#ifndef _WIN_CE
- case 'I':
- {
- Priority=atoi(Switch+2);
- char *ChPtr=strchr(Switch+2,':');
- if (ChPtr!=NULL)
- {
- SleepTime=atoi(ChPtr+1);
- InitSystemOptions(SleepTime);
- }
- SetPriority(Priority);
- }
- break;
-#endif
- }
- break;
- case 'Y':
- AllYes=true;
- break;
- case 'N':
- case 'X':
- if (Switch[1]!=0)
- {
- StringList *Args=etoupper(Switch[0])=='N' ? InclArgs:ExclArgs;
- if (Switch[1]=='@' && !IsWildcard(Switch))
- {
- RAR_CHARSET Charset=FilelistCharset;
-
-#if defined(_WIN_32) && !defined(GUI)
- // for compatibility reasons we use OEM encoding
- // in Win32 console version by default
-
- if (Charset==RCH_DEFAULT)
- Charset=RCH_OEM;
-#endif
-
- ReadTextFile(Switch+2,Args,false,true,Charset,true,true,true);
- }
- else
- Args->AddString(Switch+1);
- }
- break;
case 'E':
- switch(etoupper(Switch[1]))
+ switch(toupperw(Switch[1]))
{
case 'P':
switch(Switch[2])
@@ -515,77 +373,166 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW)
break;
}
break;
- case 'E':
- ProcessEA=false;
- break;
- case 'N':
- NoEndBlock=true;
- break;
default:
if (Switch[1]=='+')
{
- InclFileAttr=GetExclAttr(&Switch[2]);
+ InclFileAttr|=GetExclAttr(Switch+2,InclDir);
InclAttrSet=true;
}
else
- ExclFileAttr=GetExclAttr(&Switch[1]);
+ ExclFileAttr|=GetExclAttr(Switch+1,ExclDir);
break;
}
break;
- case 'P':
+ case 'F':
if (Switch[1]==0)
- {
- GetPassword(PASSWORD_GLOBAL,NULL,Password,sizeof(Password));
- eprintf("\n");
- }
+ FreshFiles=true;
else
- strncpyz(Password,Switch+1,ASIZE(Password));
+ BadSwitch(Switch);
break;
case 'H':
- if (etoupper(Switch[1])=='P')
+ switch (toupperw(Switch[1]))
{
- EncryptHeaders=true;
- if (Switch[2]!=0)
- strncpyz(Password,Switch+2,ASIZE(Password));
- else
- if (*Password==0)
+ case 'P':
+ EncryptHeaders=true;
+ if (Switch[2]!=0)
{
- GetPassword(PASSWORD_GLOBAL,NULL,Password,sizeof(Password));
- eprintf("\n");
+ Password.Set(Switch+2);
+ cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0]));
}
+ else
+ if (!Password.IsSet())
+ {
+ uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password);
+ eprintf(L"\n");
+ }
+ break;
+ default :
+ BadSwitch(Switch);
+ break;
}
break;
- case 'Z':
- strncpyz(CommentFile,Switch[1]!=0 ? Switch+1:"stdin",ASIZE(CommentFile));
+ case 'I':
+ if (wcsnicomp(Switch+1,L"LOG",3)==0)
+ {
+ wcsncpyz(LogName,Switch[4]!=0 ? Switch+4:DefLogName,ASIZE(LogName));
+ break;
+ }
+ if (wcsnicomp(Switch+1,L"SND",3)==0)
+ {
+ Sound=Switch[4]=='-' ? SOUND_NOTIFY_OFF : SOUND_NOTIFY_ON;
+ break;
+ }
+ if (wcsicomp(Switch+1,L"ERR")==0)
+ {
+ MsgStream=MSG_STDERR;
+ // Set it immediately when parsing the command line, so it also
+ // affects messages issued while parsing the command line.
+ SetConsoleMsgStream(MSG_STDERR);
+ break;
+ }
+ if (wcsnicomp(Switch+1,L"EML",3)==0)
+ {
+ wcsncpyz(EmailTo,Switch[4]!=0 ? Switch+4:L"@",ASIZE(EmailTo));
+ break;
+ }
+ if (wcsicomp(Switch+1,L"M")==0)
+ {
+ MoreInfo=true;
+ break;
+ }
+ if (wcsicomp(Switch+1,L"NUL")==0)
+ {
+ MsgStream=MSG_NULL;
+ SetConsoleMsgStream(MSG_NULL);
+ break;
+ }
+ if (toupperw(Switch[1])=='D')
+ {
+ for (uint I=2;Switch[I]!=0;I++)
+ switch(toupperw(Switch[I]))
+ {
+ case 'Q':
+ MsgStream=MSG_ERRONLY;
+ SetConsoleMsgStream(MSG_ERRONLY);
+ break;
+ case 'C':
+ DisableCopyright=true;
+ break;
+ case 'D':
+ DisableDone=true;
+ break;
+ case 'P':
+ DisablePercentage=true;
+ break;
+ }
+ break;
+ }
+ if (wcsnicomp(Switch+1,L"OFF",3)==0)
+ {
+ switch(Switch[4])
+ {
+ case 0:
+ case '1':
+ Shutdown=POWERMODE_OFF;
+ break;
+ case '2':
+ Shutdown=POWERMODE_HIBERNATE;
+ break;
+ case '3':
+ Shutdown=POWERMODE_SLEEP;
+ break;
+ case '4':
+ Shutdown=POWERMODE_RESTART;
+ break;
+ }
+ break;
+ }
+ if (wcsicomp(Switch+1,L"VER")==0)
+ {
+ PrintVersion=true;
+ break;
+ }
+ break;
+ case 'K':
+ switch(toupperw(Switch[1]))
+ {
+ case 'B':
+ KeepBroken=true;
+ break;
+ case 0:
+ Lock=true;
+ break;
+ }
break;
case 'M':
- switch(etoupper(Switch[1]))
+ switch(toupperw(Switch[1]))
{
case 'C':
{
- char *Str=Switch+2;
+ const wchar *Str=Switch+2;
if (*Str=='-')
- for (int I=0;IAddString(Mask);
+ wcsncpyz(Mask,Names,ASIZE(Mask));
+ StoreArgs.AddString(Mask);
if (End==NULL)
break;
Names=End+1;
}
}
break;
-#ifdef PACK_SMP
+#ifdef RAR_SMP
case 'T':
- Threads=atoi(Switch+2);
- if (Threads>16)
+ Threads=atoiw(Switch+2);
+ if (Threads>MaxPoolThreads || Threads<1)
BadSwitch(Switch);
else
{
@@ -659,121 +595,140 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW)
break;
}
break;
- case 'V':
- switch(etoupper(Switch[1]))
+ case 'N':
+ case 'X':
+ if (Switch[1]!=0)
{
-#ifdef _WIN_32
- case 'D':
- EraseDisk=true;
- break;
-#endif
- case 'N':
- OldNumbering=true;
- break;
- case 'P':
- VolumePause=true;
- break;
- case 'E':
- if (etoupper(Switch[2])=='R')
- VersionControl=atoi(Switch+3)+1;
+ StringList *Args=toupperw(Switch[0])=='N' ? &InclArgs:&ExclArgs;
+ if (Switch[1]=='@' && !IsWildcard(Switch))
+ ReadTextFile(Switch+2,Args,false,true,FilelistCharset,true,true,true);
+ else
+ Args->AddString(Switch+1);
+ }
+ break;
+ case 'O':
+ switch(toupperw(Switch[1]))
+ {
+ case '+':
+ Overwrite=OVERWRITE_ALL;
break;
case '-':
- VolSize=0;
+ Overwrite=OVERWRITE_NONE;
+ break;
+ case 0:
+ Overwrite=OVERWRITE_FORCE_ASK;
+ break;
+#ifdef _WIN_ALL
+ case 'C':
+ SetCompressedAttr=true;
+ break;
+#endif
+ case 'H':
+ SaveHardLinks=true;
break;
- default:
- {
- int64 NewVolSize=atoil(&Switch[1]);
- if (NewVolSize==0)
- NewVolSize=INT64NDF; // Autodetecting volume size.
- else
- switch (Switch[strlen(Switch)-1])
- {
- case 'f':
- case 'F':
- switch(NewVolSize)
- {
- case 360:
- NewVolSize=362496;
- break;
- case 720:
- NewVolSize=730112;
- break;
- case 1200:
- NewVolSize=1213952;
- break;
- case 1440:
- NewVolSize=1457664;
- break;
- case 2880:
- NewVolSize=2915328;
- break;
- }
- break;
- case 'k':
- NewVolSize*=1024;
- break;
- case 'm':
- NewVolSize*=1024*1024;
- break;
- case 'M':
- NewVolSize*=1000*1000;
- break;
- case 'g':
- NewVolSize*=1024*1024;
- NewVolSize*=1024;
- break;
- case 'G':
- NewVolSize*=1000*1000;
- NewVolSize*=1000;
- break;
- case 'b':
- case 'B':
- break;
- default:
- NewVolSize*=1000;
- break;
- }
- if (VolSize==0)
- VolSize=NewVolSize;
- else
- NextVolSizes.Push(NewVolSize);
+
+#ifdef SAVE_LINKS
+ case 'L':
+ SaveSymLinks=true;
+ if (toupperw(Switch[2])=='A')
+ AbsoluteLinks=true;
+ break;
+#endif
+#ifdef _WIN_ALL
+ case 'N':
+ if (toupperw(Switch[2])=='I')
+ AllowIncompatNames=true;
+ break;
+#endif
+ case 'R':
+ Overwrite=OVERWRITE_AUTORENAME;
+ break;
+#ifdef _WIN_ALL
+ case 'S':
+ SaveStreams=true;
+ break;
+#endif
+ case 'W':
+ ProcessOwners=true;
+ break;
+ default :
+ BadSwitch(Switch);
+ break;
+ }
+ break;
+ case 'P':
+ if (Switch[1]==0)
+ {
+ uiGetPassword(UIPASSWORD_GLOBAL,NULL,&Password);
+ eprintf(L"\n");
+ }
+ else
+ {
+ Password.Set(Switch+1);
+ cleandata((void *)Switch,wcslen(Switch)*sizeof(Switch[0]));
+ }
+ break;
+#ifndef SFX_MODULE
+ case 'Q':
+ if (toupperw(Switch[1])=='O')
+ switch(toupperw(Switch[2]))
+ {
+ case 0:
+ QOpenMode=QOPEN_AUTO;
+ break;
+ case '-':
+ QOpenMode=QOPEN_NONE;
+ break;
+ case '+':
+ QOpenMode=QOPEN_ALWAYS;
+ break;
+ default:
+ BadSwitch(Switch);
+ break;
+ }
+ else
+ BadSwitch(Switch);
+ break;
+#endif
+ case 'R':
+ switch(toupperw(Switch[1]))
+ {
+ case 0:
+ Recurse=RECURSE_ALWAYS;
+ break;
+ case '-':
+ Recurse=RECURSE_DISABLE;
+ break;
+ case '0':
+ Recurse=RECURSE_WILDCARDS;
+ break;
+ case 'I':
+ {
+ Priority=atoiw(Switch+2);
+ if (Priority<0 || Priority>15)
+ BadSwitch(Switch);
+ const wchar *ChPtr=wcschr(Switch+2,':');
+ if (ChPtr!=NULL)
+ {
+ SleepTime=atoiw(ChPtr+1);
+ if (SleepTime>1000)
+ BadSwitch(Switch);
+ InitSystemOptions(SleepTime);
+ }
+ SetPriority(Priority);
}
break;
}
break;
- case 'F':
- if (Switch[1]==0)
- FreshFiles=true;
- else
- BadSwitch(Switch);
- break;
- case 'U':
- if (Switch[1]==0)
- UpdateFiles=true;
- else
- BadSwitch(Switch);
- break;
- case 'W':
- strncpyz(TempPath,&Switch[1],ASIZE(TempPath));
- AddEndSlash(TempPath);
- break;
case 'S':
- if (strnicomp(Switch,"SFX",3)==0)
- {
- const char *SFXName=Switch[3] ? Switch+3:DefSFXName;
- if (PointToName(SFXName)!=SFXName || FileExist(SFXName))
- strcpy(SFXModule,SFXName);
- else
- GetConfigName(SFXName,SFXModule,true);
- }
if (IsDigit(Switch[1]))
{
Solid|=SOLID_COUNT;
- SolidCount=atoi(&Switch[1]);
+ SolidCount=atoiw(&Switch[1]);
}
else
- switch(etoupper(Switch[1]))
+ switch(toupperw(Switch[1]))
{
case 0:
Solid|=SOLID_NORMAL;
@@ -792,19 +747,18 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW)
break;
case 'L':
if (IsDigit(Switch[2]))
- FileSizeLess=atoil(Switch+2);
+ FileSizeLess=atoilw(Switch+2);
break;
case 'M':
if (IsDigit(Switch[2]))
- FileSizeMore=atoil(Switch+2);
+ FileSizeMore=atoilw(Switch+2);
break;
case 'C':
{
- // Switch is already found bad, avoid reporting it several times.
- bool AlreadyBad=false;
+ bool AlreadyBad=false; // Avoid reporting "bad switch" several times.
RAR_CHARSET rch=RCH_DEFAULT;
- switch(etoupper(Switch[2]))
+ switch(toupperw(Switch[2]))
{
case 'A':
rch=RCH_ANSI;
@@ -815,6 +769,9 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW)
case 'U':
rch=RCH_UNICODE;
break;
+ case 'F':
+ rch=RCH_UTF8;
+ break;
default :
BadSwitch(Switch);
AlreadyBad=true;
@@ -822,10 +779,10 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW)
};
if (!AlreadyBad)
if (Switch[3]==0)
- CommentCharset=FilelistCharset=rch;
+ CommentCharset=FilelistCharset=ErrlogCharset=RedirectCharset=rch;
else
- for (int I=3;Switch[I]!=0 && !AlreadyBad;I++)
- switch(etoupper(Switch[I]))
+ for (uint I=3;Switch[I]!=0 && !AlreadyBad;I++)
+ switch(toupperw(Switch[I]))
{
case 'C':
CommentCharset=rch;
@@ -833,47 +790,100 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW)
case 'L':
FilelistCharset=rch;
break;
+ case 'R':
+ RedirectCharset=rch;
+ break;
default:
BadSwitch(Switch);
AlreadyBad=true;
break;
}
+ // Set it immediately when parsing the command line, so it also
+ // affects messages issued while parsing the command line.
+ SetConsoleRedirectCharset(RedirectCharset);
}
break;
}
break;
- case 'C':
- if (Switch[2]==0)
- switch(etoupper(Switch[1]))
- {
- case '-':
- DisableComment=true;
- break;
- case 'U':
- ConvertNames=NAMES_UPPERCASE;
- break;
- case 'L':
- ConvertNames=NAMES_LOWERCASE;
- break;
- }
- break;
- case 'K':
- switch(etoupper(Switch[1]))
+ case 'T':
+ switch(toupperw(Switch[1]))
{
+ case 'K':
+ ArcTime=ARCTIME_KEEP;
+ break;
+ case 'L':
+ ArcTime=ARCTIME_LATEST;
+ break;
+ case 'O':
+ SetTimeFilters(Switch+2,true,true);
+ break;
+ case 'N':
+ SetTimeFilters(Switch+2,false,true);
+ break;
case 'B':
- KeepBroken=true;
+ SetTimeFilters(Switch+2,true,false);
+ break;
+ case 'A':
+ SetTimeFilters(Switch+2,false,false);
+ break;
+ case 'S':
+ SetStoreTimeMode(Switch+2);
+ break;
+ case '-':
+ Test=false;
break;
case 0:
- Lock=true;
+ Test=true;
+ break;
+ default:
+ BadSwitch(Switch);
break;
}
break;
-#ifndef GUI
- case '?' :
- OutHelp();
+ case 'U':
+ if (Switch[1]==0)
+ UpdateFiles=true;
+ else
+ BadSwitch(Switch);
+ break;
+ case 'V':
+ switch(toupperw(Switch[1]))
+ {
+ case 'P':
+ VolumePause=true;
+ break;
+ case 'E':
+ if (toupperw(Switch[2])=='R')
+ VersionControl=atoiw(Switch+3)+1;
+ break;
+ case '-':
+ VolSize=0;
+ break;
+ default:
+ VolSize=VOLSIZE_AUTO; // UnRAR -v switch for list command.
+ break;
+ }
+ break;
+ case 'W':
+ wcsncpyz(TempPath,Switch+1,ASIZE(TempPath));
+ AddEndSlash(TempPath,ASIZE(TempPath));
+ break;
+ case 'Y':
+ AllYes=true;
+ break;
+ case 'Z':
+ if (Switch[1]==0)
+ {
+ // If comment file is not specified, we read data from stdin.
+ wcsncpyz(CommentFile,L"stdin",ASIZE(CommentFile));
+ }
+ else
+ wcsncpyz(CommentFile,Switch+1,ASIZE(CommentFile));
+ break;
+ case '?' :
+ OutHelp(RARX_SUCCESS);
break;
-#endif
default :
BadSwitch(Switch);
break;
@@ -882,310 +892,59 @@ void CommandData::ProcessSwitch(char *Switch,wchar *SwitchW)
#endif
-#ifndef SFX_MODULE
-void CommandData::BadSwitch(char *Switch)
+#if !defined(SFX_MODULE)
+void CommandData::BadSwitch(const wchar *Switch)
{
mprintf(St(MUnknownOption),Switch);
- ErrHandler.Exit(USER_ERROR);
+ ErrHandler.Exit(RARX_USERERROR);
}
#endif
-#ifndef GUI
-void CommandData::OutTitle()
-{
- if (BareOutput || DisableCopyright)
- return;
-#if defined(__GNUC__) && defined(SFX_MODULE)
- mprintf(St(MCopyrightS));
-#else
-#ifndef SILENT
- static bool TitleShown=false;
- if (TitleShown)
- return;
- TitleShown=true;
- char Version[50];
- int Beta=RARVER_BETA;
- if (Beta!=0)
- sprintf(Version,"%d.%02d %s %d",RARVER_MAJOR,RARVER_MINOR,St(MBeta),RARVER_BETA);
- else
- sprintf(Version,"%d.%02d",RARVER_MAJOR,RARVER_MINOR);
-#ifdef UNRAR
- mprintf(St(MUCopyright),Version,RARVER_YEAR);
-#else
-#endif
-#endif
-#endif
-}
-#endif
-
-
-inline bool CmpMSGID(MSGID i1,MSGID i2)
-{
-#ifdef MSGID_INT
- return(i1==i2);
-#else
- // If MSGID is const char*, we cannot compare pointers only.
- // Pointers to different instances of same strings can differ,
- // so we need to compare complete strings.
- return(strcmp(i1,i2)==0);
-#endif
-}
-
-void CommandData::OutHelp()
-{
-#if !defined(GUI) && !defined(SILENT)
- OutTitle();
- static MSGID Help[]={
-#ifdef SFX_MODULE
- // Console SFX switches definition.
- MCHelpCmd,MSHelpCmdE,MSHelpCmdT,MSHelpCmdV
-#elif defined(UNRAR)
- // UnRAR switches definition.
- MUNRARTitle1,MRARTitle2,MCHelpCmd,MCHelpCmdE,MCHelpCmdL,
- MCHelpCmdP,MCHelpCmdT,MCHelpCmdV,MCHelpCmdX,MCHelpSw,
- MCHelpSwm,MCHelpSwAC,MCHelpSwAD,MCHelpSwAI,MCHelpSwAP,
- MCHelpSwCm,MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU,
- MCHelpSwDH,MCHelpSwEP,MCHelpSwEP3,MCHelpSwF,MCHelpSwIDP,MCHelpSwIERR,
- MCHelpSwINUL,MCHelpSwIOFF,MCHelpSwKB,MCHelpSwN,MCHelpSwNa,MCHelpSwNal,
- MCHelpSwO,MCHelpSwOC,MCHelpSwOR,MCHelpSwOW,MCHelpSwP,
- MCHelpSwPm,MCHelpSwR,MCHelpSwRI,MCHelpSwSL,MCHelpSwSM,MCHelpSwTA,
- MCHelpSwTB,MCHelpSwTN,MCHelpSwTO,MCHelpSwTS,MCHelpSwU,MCHelpSwVUnr,
- MCHelpSwVER,MCHelpSwVP,MCHelpSwX,MCHelpSwXa,MCHelpSwXal,MCHelpSwY
-#else
- // RAR switches definition.
- MRARTitle1,MRARTitle2,MCHelpCmd,MCHelpCmdA,MCHelpCmdC,MCHelpCmdCF,
- MCHelpCmdCH,MCHelpCmdCW,MCHelpCmdD,MCHelpCmdE,MCHelpCmdF,MCHelpCmdI,
- MCHelpCmdK,MCHelpCmdL,MCHelpCmdM,MCHelpCmdP,MCHelpCmdR,MCHelpCmdRC,
- MCHelpCmdRN,MCHelpCmdRR,MCHelpCmdRV,MCHelpCmdS,MCHelpCmdT,MCHelpCmdU,
- MCHelpCmdV,MCHelpCmdX,MCHelpSw,MCHelpSwm,MCHelpSwAC,MCHelpSwAD,MCHelpSwAG,
- MCHelpSwAI,MCHelpSwAO,MCHelpSwAP,MCHelpSwAS,MCHelpSwAV,MCHelpSwAVm,
- MCHelpSwCm,MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU,MCHelpSwDF,MCHelpSwDH,
- MCHelpSwDR,MCHelpSwDS,MCHelpSwDW,MCHelpSwEa,MCHelpSwED,MCHelpSwEE,
- MCHelpSwEN,MCHelpSwEP,MCHelpSwEP1,MCHelpSwEP2,MCHelpSwEP3,MCHelpSwF,
- MCHelpSwHP,MCHelpSwIDP,MCHelpSwIEML,MCHelpSwIERR,MCHelpSwILOG,MCHelpSwINUL,
- MCHelpSwIOFF,MCHelpSwISND,MCHelpSwK,MCHelpSwKB,MCHelpSwMn,MCHelpSwMC,
- MCHelpSwMD,MCHelpSwMS,MCHelpSwMT,MCHelpSwN,MCHelpSwNa,MCHelpSwNal,
- MCHelpSwO,MCHelpSwOC,MCHelpSwOL,MCHelpSwOR,MCHelpSwOS,MCHelpSwOW,
- MCHelpSwP,MCHelpSwPm,MCHelpSwR,MCHelpSwRm,MCHelpSwR0,MCHelpSwRI,
- MCHelpSwRR,MCHelpSwRV,MCHelpSwS,MCHelpSwSm,MCHelpSwSC,MCHelpSwSFX,
- MCHelpSwSI,MCHelpSwSL,MCHelpSwSM,MCHelpSwT,MCHelpSwTA,MCHelpSwTB,
- MCHelpSwTK,MCHelpSwTL,MCHelpSwTN,MCHelpSwTO,MCHelpSwTS,MCHelpSwU,
- MCHelpSwV,MCHelpSwVn,MCHelpSwVD,MCHelpSwVER,MCHelpSwVN,MCHelpSwVP,
- MCHelpSwW,MCHelpSwX,MCHelpSwXa,MCHelpSwXal,MCHelpSwY,MCHelpSwZ
-#endif
- };
-
- for (int I=0;IRewind();
- while ((CurName=Args->GetString())!=NULL)
-#ifndef SFX_MODULE
- if (CheckFullPath && IsFullPath(CurName))
- {
- if (*FullName==0)
- ConvertNameToFull(CheckName,FullName);
- if (CmpName(CurName,FullName,MatchMode))
- return(true);
- }
- else
-#endif
- if (CmpName(ConvertPath(CurName,NULL),Name,MatchMode))
- return(true);
- return(false);
-}
-
-
-// Return 'true' if we need to exclude the file from processing as result
-// of -x switch. If CheckInclList is true, we also check the file against
-// the include list created with -n switch.
-bool CommandData::ExclCheck(char *CheckName,bool CheckFullPath,bool CheckInclList)
-{
- if (ExclCheckArgs(ExclArgs,CheckName,CheckFullPath,MATCH_WILDSUBPATH))
- return(true);
- if (!CheckInclList || InclArgs->ItemsCount()==0)
- return(false);
- if (ExclCheckArgs(InclArgs,CheckName,false,MATCH_WILDSUBPATH))
- return(false);
- return(true);
-}
-
-
-
-
-#ifndef SFX_MODULE
-bool CommandData::TimeCheck(RarTime &ft)
-{
- if (FileTimeBefore.IsSet() && ft>=FileTimeBefore)
- return(true);
- if (FileTimeAfter.IsSet() && ft<=FileTimeAfter)
- return(true);
- return(false);
-}
-#endif
-
-
-#ifndef SFX_MODULE
-bool CommandData::SizeCheck(int64 Size)
-{
- if (FileSizeLess!=INT64NDF && Size>=FileSizeLess)
- return(true);
- if (FileSizeMore!=INT64NDF && Size<=FileSizeMore)
- return(true);
- return(false);
-}
-#endif
-
-
-
-
-int CommandData::IsProcessFile(FileHeader &NewLhd,bool *ExactMatch,int MatchType)
-{
- if (strlen(NewLhd.FileName)>=NM || strlenw(NewLhd.FileNameW)>=NM)
- return(0);
- if (ExclCheck(NewLhd.FileName,false,true))
- return(0);
-#ifndef SFX_MODULE
- if (TimeCheck(NewLhd.mtime))
- return(0);
- if ((NewLhd.FileAttr & ExclFileAttr)!=0 || InclAttrSet && (NewLhd.FileAttr & InclFileAttr)==0)
- return(0);
- if ((NewLhd.Flags & LHD_WINDOWMASK)!=LHD_DIRECTORY && SizeCheck(NewLhd.FullUnpSize))
- return(0);
-#endif
- char *ArgName;
- wchar *ArgNameW;
- FileArgs->Rewind();
- for (int StringCount=1;FileArgs->GetString(&ArgName,&ArgNameW);StringCount++)
- {
-#ifndef SFX_MODULE
- bool Unicode=(NewLhd.Flags & LHD_UNICODE) || ArgNameW!=NULL;
- if (Unicode)
- {
- wchar NameW[NM],ArgW[NM],*NamePtr=NewLhd.FileNameW;
- bool CorrectUnicode=true;
- if (ArgNameW==NULL)
- {
- if (!CharToWide(ArgName,ArgW) || *ArgW==0)
- CorrectUnicode=false;
- ArgNameW=ArgW;
- }
- if ((NewLhd.Flags & LHD_UNICODE)==0)
- {
- if (!CharToWide(NewLhd.FileName,NameW) || *NameW==0)
- CorrectUnicode=false;
- NamePtr=NameW;
- }
- if (CmpName(ArgNameW,NamePtr,MatchType))
- {
- if (ExactMatch!=NULL)
- *ExactMatch=stricompcw(ArgNameW,NamePtr)==0;
- return(StringCount);
- }
- if (CorrectUnicode)
- continue;
- }
-#endif
- if (CmpName(ArgName,NewLhd.FileName,MatchType))
- {
- if (ExactMatch!=NULL)
- *ExactMatch=stricompc(ArgName,NewLhd.FileName)==0;
- return(StringCount);
- }
- }
- return(0);
-}
-
-
-#ifndef GUI
void CommandData::ProcessCommand()
{
#ifndef SFX_MODULE
- const char *SingleCharCommands="FUADPXETK";
- if (Command[1] && strchr(SingleCharCommands,*Command)!=NULL || *ArcName==0)
- OutHelp();
+ const wchar *SingleCharCommands=L"FUADPXETK";
+ if (Command[0]!=0 && Command[1]!=0 && wcschr(SingleCharCommands,Command[0])!=NULL || *ArcName==0)
+ OutHelp(*Command==0 ? RARX_SUCCESS:RARX_USERERROR); // Return 'success' for 'rar' without parameters.
+ const wchar *ArcExt=GetExt(ArcName);
#ifdef _UNIX
- if (GetExt(ArcName)==NULL && (!FileExist(ArcName) || IsDir(GetFileAttr(ArcName))))
- strcat(ArcName,".rar");
+ if (ArcExt==NULL && (!FileExist(ArcName) || IsDir(GetFileAttr(ArcName))))
+ wcsncatz(ArcName,L".rar",ASIZE(ArcName));
#else
- if (GetExt(ArcName)==NULL)
- strcat(ArcName,".rar");
+ if (ArcExt==NULL)
+ wcsncatz(ArcName,L".rar",ASIZE(ArcName));
#endif
-
- if (strchr("AFUMD",*Command)==NULL)
+ // Treat arcname.part1 as arcname.part1.rar.
+ if (ArcExt!=NULL && wcsnicomp(ArcExt,L".part",5)==0 && IsDigit(ArcExt[5]) &&
+ !FileExist(ArcName))
{
+ wchar Name[NM];
+ wcsncpyz(Name,ArcName,ASIZE(Name));
+ wcsncatz(Name,L".rar",ASIZE(Name));
+ if (FileExist(Name))
+ wcsncpyz(ArcName,Name,ASIZE(ArcName));
+ }
+
+ if (wcschr(L"AFUMD",*Command)==NULL)
+ {
+ if (GenerateArcName)
+ {
+ const wchar *Mask=*GenerateMask!=0 ? GenerateMask:DefGenerateMask;
+ GenerateArchiveName(ArcName,ASIZE(ArcName),Mask,false);
+ }
+
StringList ArcMasks;
ArcMasks.AddString(ArcName);
- ScanTree Scan(&ArcMasks,Recurse,SaveLinks,SCAN_SKIPDIRS);
+ ScanTree Scan(&ArcMasks,Recurse,SaveSymLinks,SCAN_SKIPDIRS);
FindData FindData;
while (Scan.GetNext(&FindData)==SCAN_SUCCESS)
- AddArcName(FindData.Name,FindData.NameW);
+ AddArcName(FindData.Name);
}
else
- AddArcName(ArcName,NULL);
+ AddArcName(ArcName);
#endif
switch(Command[0])
@@ -1194,10 +953,9 @@ void CommandData::ProcessCommand()
case 'X':
case 'E':
case 'T':
- case 'I':
{
- CmdExtract Extract;
- Extract.DoExtract(this);
+ CmdExtract Extract(this);
+ Extract.DoExtract();
}
break;
#ifndef SILENT
@@ -1206,77 +964,72 @@ void CommandData::ProcessCommand()
ListArchive(this);
break;
default:
- OutHelp();
+ OutHelp(RARX_USERERROR);
#endif
}
if (!BareOutput)
- mprintf("\n");
-}
-#endif
-
-
-void CommandData::AddArcName(char *Name,wchar *NameW)
-{
- ArcNames->AddString(Name,NameW);
+ mprintf(L"\n");
}
-bool CommandData::GetArcName(char *Name,wchar *NameW,int MaxSize)
+void CommandData::AddArcName(const wchar *Name)
{
- if (!ArcNames->GetString(Name,NameW,NM))
- return(false);
- return(true);
+ ArcNames.AddString(Name);
+}
+
+
+bool CommandData::GetArcName(wchar *Name,int MaxSize)
+{
+ return ArcNames.GetString(Name,MaxSize);
}
bool CommandData::IsSwitch(int Ch)
{
-#if defined(_WIN_32) || defined(_EMX)
- return(Ch=='-' || Ch=='/');
+#if defined(_WIN_ALL) || defined(_EMX)
+ return Ch=='-' || Ch=='/';
#else
- return(Ch=='-');
+ return Ch=='-';
#endif
}
#ifndef SFX_MODULE
-uint CommandData::GetExclAttr(char *Str)
+uint CommandData::GetExclAttr(const wchar *Str,bool &Dir)
{
if (IsDigit(*Str))
- return(strtol(Str,NULL,0));
- else
+ return wcstol(Str,NULL,0);
+
+ uint Attr=0;
+ while (*Str!=0)
{
- uint Attr;
- for (Attr=0;*Str;Str++)
- switch(etoupper(*Str))
- {
+ switch(toupperw(*Str))
+ {
+ case 'D':
+ Dir=true;
+ break;
#ifdef _UNIX
- case 'D':
- Attr|=S_IFDIR;
- break;
- case 'V':
- Attr|=S_IFCHR;
- break;
-#elif defined(_WIN_32) || defined(_EMX)
- case 'R':
- Attr|=0x1;
- break;
- case 'H':
- Attr|=0x2;
- break;
- case 'S':
- Attr|=0x4;
- break;
- case 'D':
- Attr|=0x10;
- break;
- case 'A':
- Attr|=0x20;
- break;
+ case 'V':
+ Attr|=S_IFCHR;
+ break;
+#elif defined(_WIN_ALL) || defined(_EMX)
+ case 'R':
+ Attr|=0x1;
+ break;
+ case 'H':
+ Attr|=0x2;
+ break;
+ case 'S':
+ Attr|=0x4;
+ break;
+ case 'A':
+ Attr|=0x20;
+ break;
#endif
- }
- return(Attr);
+ }
+ Str++;
}
+ return Attr;
}
#endif
@@ -1286,13 +1039,44 @@ uint CommandData::GetExclAttr(char *Str)
#ifndef SFX_MODULE
bool CommandData::CheckWinSize()
{
- static int ValidSize[]={
- 0x10000,0x20000,0x40000,0x80000,0x100000,0x200000,0x400000
- };
- for (int I=0;IRewind();
+ while (Args->GetString(CurMask,ASIZE(CurMask)))
+ {
+ wchar *LastMaskChar=PointToLastChar(CurMask);
+ bool DirMask=IsPathDiv(*LastMaskChar); // Mask for directories only.
+
+ if (Dir)
+ {
+ // CheckName is a directory.
+ if (DirMask)
+ {
+ // We process the directory and have the directory exclusion mask.
+ // So let's convert "mask\" to "mask" and process it normally.
+
+ *LastMaskChar=0;
+ }
+ else
+ {
+ // REMOVED, we want -npath\* to match empty folders too.
+ // If mask has wildcards in name part and does not have the trailing
+ // '\' character, we cannot use it for directories.
+
+ // if (IsWildcard(PointToName(CurMask)))
+ // continue;
+ }
+ }
+ else
+ {
+ // If we process a file inside of directory excluded by "dirmask\".
+ // we want to exclude such file too. So we convert "dirmask\" to
+ // "dirmask\*". It is important for operations other than archiving
+ // with -x. When archiving with -x, directory matched by "dirmask\"
+ // is excluded from further scanning.
+
+ if (DirMask)
+ wcsncatz(CurMask,L"*",ASIZE(CurMask));
+ }
+
+#ifndef SFX_MODULE
+ if (CheckFullPath && IsFullPath(CurMask))
+ {
+ // We do not need to do the special "*\" processing here, because
+ // unlike the "else" part of this "if", now we convert names to full
+ // format, so they all include the path, which is matched by "*\"
+ // correctly. Moreover, removing "*\" from mask would break
+ // the comparison, because now all names have the path.
+
+ if (*FullName==0)
+ ConvertNameToFull(CheckName,FullName,ASIZE(FullName));
+ if (CmpName(CurMask,FullName,MatchMode))
+ return true;
+ }
+ else
+#endif
+ {
+ wchar NewName[NM+2],*CurName=Name;
+
+ // Important to convert before "*\" check below, so masks like
+ // d:*\something are processed properly.
+ wchar *CmpMask=ConvertPath(CurMask,NULL,0);
+
+ if (CmpMask[0]=='*' && IsPathDiv(CmpMask[1]))
+ {
+ // We want "*\name" to match 'name' not only in subdirectories,
+ // but also in the current directory. We convert the name
+ // from 'name' to '.\name' to be matched by "*\" part even if it is
+ // in current directory.
+ NewName[0]='.';
+ NewName[1]=CPATHDIVIDER;
+ wcsncpyz(NewName+2,Name,ASIZE(NewName)-2);
+ CurName=NewName;
+ }
+
+ if (CmpName(CmpMask,CurName,MatchMode))
+ return true;
+ }
+ }
+ return false;
+}
+
+
+
+
+#ifndef SFX_MODULE
+// Now this function performs only one task and only in Windows version:
+// it skips symlinks to directories if -e1024 switch is specified.
+// Symlinks are skipped in ScanTree class, so their entire contents
+// is skipped too. Without this function we would check the attribute
+// only directly before archiving, so we would skip the symlink record,
+// but not the contents of symlinked directory.
+bool CommandData::ExclDirByAttr(uint FileAttr)
+{
+#ifdef _WIN_ALL
+ if ((FileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0 &&
+ (ExclFileAttr & FILE_ATTRIBUTE_REPARSE_POINT)!=0)
+ return true;
+#endif
+ return false;
+}
+#endif
+
+
+
+
+#if !defined(SFX_MODULE)
+void CommandData::SetTimeFilters(const wchar *Mod,bool Before,bool Age)
+{
+ bool ModeOR=false,TimeMods=false;
+ const wchar *S=Mod;
+ // Check if any 'mca' modifiers are present, set OR mode if 'o' is present,
+ // skip modifiers and set S to beginning of time string. Be sure to check
+ // *S!=0, because termination 0 is a part of string for wcschr.
+ for (;*S!=0 && wcschr(L"MCAOmcao",*S)!=NULL;S++)
+ if (*S=='o' || *S=='O')
+ ModeOR=true;
+ else
+ TimeMods=true;
+
+ if (!TimeMods) // Assume 'm' if no modifiers are specified.
+ Mod=L"m";
+
+ // Set the specified time for every modifier. Be sure to check *Mod!=0,
+ // because termination 0 is a part of string for wcschr. This check is
+ // important when we set Mod to "m" above.
+ for (;*Mod!=0 && wcschr(L"MCAOmcao",*Mod)!=NULL;Mod++)
+ switch(toupperw(*Mod))
+ {
+ case 'M':
+ if (Before)
+ {
+ Age ? FileMtimeBefore.SetAgeText(S):FileMtimeBefore.SetIsoText(S);
+ FileMtimeBeforeOR=ModeOR;
+ }
+ else
+ {
+ Age ? FileMtimeAfter.SetAgeText(S):FileMtimeAfter.SetIsoText(S);
+ FileMtimeAfterOR=ModeOR;
+ }
+ break;
+ case 'C':
+ if (Before)
+ {
+ Age ? FileCtimeBefore.SetAgeText(S):FileCtimeBefore.SetIsoText(S);
+ FileCtimeBeforeOR=ModeOR;
+ }
+ else
+ {
+ Age ? FileCtimeAfter.SetAgeText(S):FileCtimeAfter.SetIsoText(S);
+ FileCtimeAfterOR=ModeOR;
+ }
+ break;
+ case 'A':
+ if (Before)
+ {
+ Age ? FileAtimeBefore.SetAgeText(S):FileAtimeBefore.SetIsoText(S);
+ FileAtimeBeforeOR=ModeOR;
+ }
+ else
+ {
+ Age ? FileAtimeAfter.SetAgeText(S):FileAtimeAfter.SetIsoText(S);
+ FileAtimeAfterOR=ModeOR;
+ }
+ break;
+ }
+}
+#endif
+
+
+#ifndef SFX_MODULE
+// Return 'true' if we need to exclude the file from processing.
+bool CommandData::TimeCheck(RarTime &ftm,RarTime &ftc,RarTime &fta)
+{
+ bool FilterOR=false;
+
+ if (FileMtimeBefore.IsSet()) // Filter present.
+ if (ftm>=FileMtimeBefore) // Condition not matched.
+ if (FileMtimeBeforeOR)
+ FilterOR=true; // Not matched OR filter is present.
+ else
+ return true; // Exclude file in AND mode.
+ else // Condition matched.
+ if (FileMtimeBeforeOR)
+ return false; // Include file in OR mode.
+
+ if (FileMtimeAfter.IsSet()) // Filter present.
+ if (ftm=FileCtimeBefore) // Condition not matched.
+ if (FileCtimeBeforeOR)
+ FilterOR=true; // Not matched OR filter is present.
+ else
+ return true; // Exclude file in AND mode.
+ else // Condition matched.
+ if (FileCtimeBeforeOR)
+ return false; // Include file in OR mode.
+
+ if (FileCtimeAfter.IsSet()) // Filter present.
+ if (ftc=FileAtimeBefore) // Condition not matched.
+ if (FileAtimeBeforeOR)
+ FilterOR=true; // Not matched OR filter is present.
+ else
+ return true; // Exclude file in AND mode.
+ else // Condition matched.
+ if (FileAtimeBeforeOR)
+ return false; // Include file in OR mode.
+
+ if (FileAtimeAfter.IsSet()) // Filter present.
+ if (fta=FileSizeLess)
+ return true;
+ if (FileSizeMore!=INT64NDF && Size<=FileSizeMore)
+ return true;
+ return false;
+}
+#endif
+
+
+
+
+// Return 0 if file must not be processed or a number of matched parameter otherwise.
+int CommandData::IsProcessFile(FileHeader &FileHead,bool *ExactMatch,int MatchType,
+ bool Flags,wchar *MatchedArg,uint MatchedArgSize)
+{
+ if (MatchedArg!=NULL && MatchedArgSize>0)
+ *MatchedArg=0;
+ bool Dir=FileHead.Dir;
+ if (ExclCheck(FileHead.FileName,Dir,false,true))
+ return 0;
+#ifndef SFX_MODULE
+ if (TimeCheck(FileHead.mtime,FileHead.ctime,FileHead.atime))
+ return 0;
+ if ((FileHead.FileAttr & ExclFileAttr)!=0 || FileHead.Dir && ExclDir)
+ return 0;
+ if (InclAttrSet && (!FileHead.Dir && (FileHead.FileAttr & InclFileAttr)==0 ||
+ FileHead.Dir && !InclDir))
+ return 0;
+ if (!Dir && SizeCheck(FileHead.UnpSize))
+ return 0;
+#endif
+ wchar *ArgName;
+ FileArgs.Rewind();
+ for (int StringCount=1;(ArgName=FileArgs.GetString())!=NULL;StringCount++)
+ if (CmpName(ArgName,FileHead.FileName,MatchType))
+ {
+ if (ExactMatch!=NULL)
+ *ExactMatch=wcsicompc(ArgName,FileHead.FileName)==0;
+ if (MatchedArg!=NULL)
+ wcsncpyz(MatchedArg,ArgName,MatchedArgSize);
+ return StringCount;
+ }
+ return 0;
+}
+
+
+#if !defined(SFX_MODULE)
+void CommandData::SetStoreTimeMode(const wchar *S)
+{
+ if (*S==0 || IsDigit(*S) || *S=='-' || *S=='+')
+ {
+ // Apply -ts, -ts1, -ts-, -ts+ to all 3 times.
+ // Handle obsolete -ts[2,3,4] as ts+.
+ EXTTIME_MODE Mode=EXTTIME_MAX;
+ if (*S=='-')
+ Mode=EXTTIME_NONE;
+ if (*S=='1')
+ Mode=EXTTIME_1S;
+ xmtime=xctime=xatime=Mode;
+ S++;
+ }
+
+ while (*S!=0)
+ {
+ EXTTIME_MODE Mode=EXTTIME_MAX;
+ if (S[1]=='-')
+ Mode=EXTTIME_NONE;
+ if (S[1]=='1')
+ Mode=EXTTIME_1S;
+ switch(toupperw(*S))
+ {
+ case 'M':
+ xmtime=Mode;
+ break;
+ case 'C':
+ xctime=Mode;
+ break;
+ case 'A':
+ xatime=Mode;
+ break;
+ case 'P':
+ PreserveAtime=true;
+ break;
+ }
+ S++;
+ }
+}
+#endif
diff --git a/libunrar/cmdmix.cpp b/libunrar/cmdmix.cpp
new file mode 100644
index 0000000..3990cc1
--- /dev/null
+++ b/libunrar/cmdmix.cpp
@@ -0,0 +1,118 @@
+void CommandData::OutTitle()
+{
+ if (BareOutput || DisableCopyright)
+ return;
+#if defined(__GNUC__) && defined(SFX_MODULE)
+ mprintf(St(MCopyrightS));
+#else
+#ifndef SILENT
+ static bool TitleShown=false;
+ if (TitleShown)
+ return;
+ TitleShown=true;
+
+ wchar Version[80];
+ if (RARVER_BETA!=0)
+ swprintf(Version,ASIZE(Version),L"%d.%02d %ls %d",RARVER_MAJOR,RARVER_MINOR,St(MBeta),RARVER_BETA);
+ else
+ swprintf(Version,ASIZE(Version),L"%d.%02d",RARVER_MAJOR,RARVER_MINOR);
+#if defined(_WIN_32) || defined(_WIN_64)
+ wcsncatz(Version,L" ",ASIZE(Version));
+#endif
+#ifdef _WIN_32
+ wcsncatz(Version,St(Mx86),ASIZE(Version));
+#endif
+#ifdef _WIN_64
+ wcsncatz(Version,St(Mx64),ASIZE(Version));
+#endif
+ if (PrintVersion)
+ {
+ mprintf(L"%s",Version);
+ exit(0);
+ }
+ mprintf(St(MUCopyright),Version,RARVER_YEAR);
+#endif
+#endif
+}
+
+
+inline bool CmpMSGID(MSGID i1,MSGID i2)
+{
+#ifdef MSGID_INT
+ return i1==i2;
+#else
+ // If MSGID is const char*, we cannot compare pointers only.
+ // Pointers to different instances of same string can differ,
+ // so we need to compare complete strings.
+ return wcscmp(i1,i2)==0;
+#endif
+}
+
+void CommandData::OutHelp(RAR_EXIT ExitCode)
+{
+#if !defined(SILENT)
+ OutTitle();
+ static MSGID Help[]={
+#ifdef SFX_MODULE
+ // Console SFX switches definition.
+ MCHelpCmd,MSHelpCmdE,MSHelpCmdT,MSHelpCmdV
+#else
+ // UnRAR switches definition.
+ MUNRARTitle1,MRARTitle2,MCHelpCmd,MCHelpCmdE,MCHelpCmdL,
+ MCHelpCmdP,MCHelpCmdT,MCHelpCmdV,MCHelpCmdX,MCHelpSw,MCHelpSwm,
+ MCHelpSwAT,MCHelpSwAC,MCHelpSwAD,MCHelpSwAG,MCHelpSwAI,MCHelpSwAP,
+ MCHelpSwCm,MCHelpSwCFGm,MCHelpSwCL,MCHelpSwCU,
+ MCHelpSwDH,MCHelpSwEP,MCHelpSwEP3,MCHelpSwF,MCHelpSwIDP,MCHelpSwIERR,
+ MCHelpSwINUL,MCHelpSwIOFF,MCHelpSwKB,MCHelpSwN,MCHelpSwNa,MCHelpSwNal,
+ MCHelpSwO,MCHelpSwOC,MCHelpSwOL,MCHelpSwOR,MCHelpSwOW,MCHelpSwP,
+ MCHelpSwPm,MCHelpSwR,MCHelpSwRI,MCHelpSwSC,MCHelpSwSL,MCHelpSwSM,
+ MCHelpSwTA,MCHelpSwTB,MCHelpSwTN,MCHelpSwTO,MCHelpSwTS,MCHelpSwU,
+ MCHelpSwVUnr,MCHelpSwVER,MCHelpSwVP,MCHelpSwX,MCHelpSwXa,MCHelpSwXal,
+ MCHelpSwY
+#endif
+ };
+
+ for (uint I=0;ISet(PlainPsw);
+ cleandata(PlainPsw,sizeof(PlainPsw));
+ break;
+ }
+ return true;
+}
#endif
#ifndef SILENT
-bool GetPassword(PASSWORD_TYPE Type,const char *FileName,char *Password,int MaxLength)
+bool getwstr(wchar *str,size_t n)
{
- Alarm();
- while (true)
+ // Print buffered prompt title function before waiting for input.
+ fflush(stderr);
+
+ *str=0;
+#if defined(_WIN_ALL)
+ // fgetws does not work well with non-English text in Windows,
+ // so we do not use it.
+ if (StdinRedirected) // ReadConsole does not work if redirected.
{
- char PromptStr[NM+256];
-#if defined(_EMX) || defined(_BEOS)
- strcpy(PromptStr,St(MAskPswEcho));
-#else
- strcpy(PromptStr,St(MAskPsw));
-#endif
- if (Type!=PASSWORD_GLOBAL)
+ // fgets does not work well with pipes in Windows in our test.
+ // Let's use files.
+ Array StrA(n*4); // Up to 4 UTF-8 characters per wchar_t.
+ File SrcFile;
+ SrcFile.SetHandleType(FILE_HANDLESTD);
+ int ReadSize=SrcFile.Read(&StrA[0],StrA.Size()-1);
+ if (ReadSize<=0)
{
- strcat(PromptStr,St(MFor));
- char *NameOnly=PointToName(FileName);
- if (strlen(PromptStr)+strlen(NameOnly)4 ? "\n":" "):", ");
+ eprintf(I==0 ? (NumItems>4 ? L"\n":L" "):L", ");
int KeyPos=ItemKeyPos[I];
for (int J=0;J[{key};"{string}"p used to redefine
+ // a keyboard key on some terminals.
+ if (Data[J]=='\"')
+ return true;
+ if (!IsDigit(Data[J]) && Data[J]!=';')
break;
}
- RetCode=1;
- }
-#endif
- return(RetCode);
+ return false;
}
-void OutComment(char *Comment,size_t Size)
+void OutComment(const wchar *Comment,size_t Size)
{
-#ifndef GUI
- if (KbdAnsi(Comment,Size)==2)
+ if (IsCommentUnsafe(Comment,Size))
return;
const size_t MaxOutSize=0x400;
for (size_t I=0;I>1)^0xEDB88320L : (C>>1);
+ for (uint J=0;J<8;J++)
+ C=(C & 1) ? (C>>1)^0xEDB88320 : (C>>1);
CRCTab[I]=C;
}
}
-uint CRC(uint StartCRC,const void *Addr,size_t Size)
+static void InitTables()
{
- if (CRCTab[1]==0)
- InitCRC();
- byte *Data=(byte *)Addr;
+ InitCRC32(crc_tables[0]);
-#if defined(LITTLE_ENDIAN) && defined(PRESENT_INT32) && defined(ALLOW_NOT_ALIGNED_INT)
- while (Size>0 && ((long)Data & 7))
+ for (uint I=0;I<256;I++) // Build additional lookup tables.
{
- StartCRC=CRCTab[(byte)(StartCRC^Data[0])]^(StartCRC>>8);
- Size--;
- Data++;
+ uint C=crc_tables[0][I];
+ for (uint J=1;J<8;J++)
+ {
+ C=crc_tables[0][(byte)C]^(C>>8);
+ crc_tables[J][I]=C;
+ }
}
- while (Size>=8)
- {
- StartCRC^=*(uint32 *)Data;
- StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8);
- StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8);
- StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8);
- StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8);
- StartCRC^=*(uint32 *)(Data+4);
- StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8);
- StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8);
- StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8);
- StartCRC=CRCTab[(byte)StartCRC]^(StartCRC>>8);
- Data+=8;
- Size-=8;
- }
-#endif
-
- for (size_t I=0;I>8);
- return(StartCRC);
}
+
+struct CallInitCRC {CallInitCRC() {InitTables();}} static CallInit32;
+
+uint CRC32(uint StartCRC,const void *Addr,size_t Size)
+{
+ byte *Data=(byte *)Addr;
+
+ // Align Data to 8 for better performance.
+ for (;Size>0 && ((size_t)Data & 7);Size--,Data++)
+ StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8);
+
+ for (;Size>=8;Size-=8,Data+=8)
+ {
+#ifdef BIG_ENDIAN
+ StartCRC ^= Data[0]|(Data[1] << 8)|(Data[2] << 16)|(Data[3] << 24);
+ uint NextData = Data[4]|(Data[5] << 8)|(Data[6] << 16)|(Data[7] << 24);
+#else
+ StartCRC ^= *(uint32 *) Data;
+ uint NextData = *(uint32 *) (Data+4);
+#endif
+ StartCRC = crc_tables[7][(byte) StartCRC ] ^
+ crc_tables[6][(byte)(StartCRC >> 8) ] ^
+ crc_tables[5][(byte)(StartCRC >> 16)] ^
+ crc_tables[4][(byte)(StartCRC >> 24)] ^
+ crc_tables[3][(byte) NextData ] ^
+ crc_tables[2][(byte)(NextData >> 8) ] ^
+ crc_tables[1][(byte)(NextData >> 16)] ^
+ crc_tables[0][(byte)(NextData >> 24)];
+ }
+
+ for (;Size>0;Size--,Data++) // Process left data.
+ StartCRC=crc_tables[0][(byte)(StartCRC^Data[0])]^(StartCRC>>8);
+
+ return StartCRC;
+}
+
+
#ifndef SFX_MODULE
-ushort OldCRC(ushort StartCRC,const void *Addr,size_t Size)
+// For RAR 1.4 archives in case somebody still has them.
+ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size)
{
byte *Data=(byte *)Addr;
for (size_t I=0;I>15))&0xffff;
}
- return(StartCRC);
+ return StartCRC;
}
#endif
+
+
diff --git a/libunrar/crc.hpp b/libunrar/crc.hpp
index a632a54..d8fea28 100644
--- a/libunrar/crc.hpp
+++ b/libunrar/crc.hpp
@@ -1,10 +1,15 @@
#ifndef _RAR_CRC_
#define _RAR_CRC_
-extern uint CRCTab[256];
+// This function is only to intialize external CRC tables. We do not need to
+// call it before calculating CRC32.
+void InitCRC32(uint *CRCTab);
+
+uint CRC32(uint StartCRC,const void *Addr,size_t Size);
+
+#ifndef SFX_MODULE
+ushort Checksum14(ushort StartCRC,const void *Addr,size_t Size);
+#endif
-void InitCRC();
-uint CRC(uint StartCRC,const void *Addr,size_t Size);
-ushort OldCRC(ushort StartCRC,const void *Addr,size_t Size);
#endif
diff --git a/libunrar/crypt.cpp b/libunrar/crypt.cpp
index b6a56db..fc2126d 100644
--- a/libunrar/crypt.cpp
+++ b/libunrar/crypt.cpp
@@ -1,381 +1,134 @@
#include "rar.hpp"
#ifndef SFX_MODULE
-extern uint CRCTab[256];
+#include "crypt1.cpp"
+#include "crypt2.cpp"
#endif
-
-#define NROUNDS 32
-
-#define rol(x,n,xsize) (((x)<<(n)) | ((x)>>(xsize-(n))))
-#define ror(x,n,xsize) (((x)>>(n)) | ((x)<<(xsize-(n))))
-
-#define substLong(t) ( (uint)SubstTable[(uint)t&255] | \
- ((uint)SubstTable[(int)(t>> 8)&255]<< 8) | \
- ((uint)SubstTable[(int)(t>>16)&255]<<16) | \
- ((uint)SubstTable[(int)(t>>24)&255]<<24) )
-
-CryptKeyCacheItem CryptData::Cache[4];
-int CryptData::CachePos=0;
+#include "crypt3.cpp"
+#include "crypt5.cpp"
-#ifndef SFX_MODULE
-static byte InitSubstTable[256]={
- 215, 19,149, 35, 73,197,192,205,249, 28, 16,119, 48,221, 2, 42,
- 232, 1,177,233, 14, 88,219, 25,223,195,244, 90, 87,239,153,137,
- 255,199,147, 70, 92, 66,246, 13,216, 40, 62, 29,217,230, 86, 6,
- 71, 24,171,196,101,113,218,123, 93, 91,163,178,202, 67, 44,235,
- 107,250, 75,234, 49,167,125,211, 83,114,157,144, 32,193,143, 36,
- 158,124,247,187, 89,214,141, 47,121,228, 61,130,213,194,174,251,
- 97,110, 54,229,115, 57,152, 94,105,243,212, 55,209,245, 63, 11,
- 164,200, 31,156, 81,176,227, 21, 76, 99,139,188,127, 17,248, 51,
- 207,120,189,210, 8,226, 41, 72,183,203,135,165,166, 60, 98, 7,
- 122, 38,155,170, 69,172,252,238, 39,134, 59,128,236, 27,240, 80,
- 131, 3, 85,206,145, 79,154,142,159,220,201,133, 74, 64, 20,129,
- 224,185,138,103,173,182, 43, 34,254, 82,198,151,231,180, 58, 10,
- 118, 26,102, 12, 50,132, 22,191,136,111,162,179, 45, 4,148,108,
- 161, 56, 78,126,242,222, 15,175,146, 23, 33,241,181,190, 77,225,
- 0, 46,169,186, 68, 95,237, 65, 53,208,253,168, 9, 18,100, 52,
- 116,184,160, 96,109, 37, 30,106,140,104,150, 5,204,117,112, 84
-};
-#endif
+CryptData::CryptData()
+{
+ Method=CRYPT_NONE;
+ memset(KDF3Cache,0,sizeof(KDF3Cache));
+ memset(KDF5Cache,0,sizeof(KDF5Cache));
+ KDF3CachePos=0;
+ KDF5CachePos=0;
+ memset(CRCTab,0,sizeof(CRCTab));
+}
+
+
+CryptData::~CryptData()
+{
+ cleandata(KDF3Cache,sizeof(KDF3Cache));
+ cleandata(KDF5Cache,sizeof(KDF5Cache));
+}
+
void CryptData::DecryptBlock(byte *Buf,size_t Size)
{
- rin.blockDecrypt(Buf,Size,Buf);
-}
-
-
-#ifndef SFX_MODULE
-void CryptData::EncryptBlock20(byte *Buf)
-{
- uint A,B,C,D,T,TA,TB;
-#if defined(BIG_ENDIAN) || !defined(PRESENT_INT32) || !defined(ALLOW_NOT_ALIGNED_INT)
- A=((uint)Buf[0]|((uint)Buf[1]<<8)|((uint)Buf[2]<<16)|((uint)Buf[3]<<24))^Key[0];
- B=((uint)Buf[4]|((uint)Buf[5]<<8)|((uint)Buf[6]<<16)|((uint)Buf[7]<<24))^Key[1];
- C=((uint)Buf[8]|((uint)Buf[9]<<8)|((uint)Buf[10]<<16)|((uint)Buf[11]<<24))^Key[2];
- D=((uint)Buf[12]|((uint)Buf[13]<<8)|((uint)Buf[14]<<16)|((uint)Buf[15]<<24))^Key[3];
-#else
- uint32 *BufPtr=(uint32 *)Buf;
- A=BufPtr[0]^Key[0];
- B=BufPtr[1]^Key[1];
- C=BufPtr[2]^Key[2];
- D=BufPtr[3]^Key[3];
-#endif
- for(int I=0;I>8);
- Buf[2]=(byte)(C>>16);
- Buf[3]=(byte)(C>>24);
- D^=Key[1];
- Buf[4]=(byte)D;
- Buf[5]=(byte)(D>>8);
- Buf[6]=(byte)(D>>16);
- Buf[7]=(byte)(D>>24);
- A^=Key[2];
- Buf[8]=(byte)A;
- Buf[9]=(byte)(A>>8);
- Buf[10]=(byte)(A>>16);
- Buf[11]=(byte)(A>>24);
- B^=Key[3];
- Buf[12]=(byte)B;
- Buf[13]=(byte)(B>>8);
- Buf[14]=(byte)(B>>16);
- Buf[15]=(byte)(B>>24);
-#else
- BufPtr[0]=C^Key[0];
- BufPtr[1]=D^Key[1];
- BufPtr[2]=A^Key[2];
- BufPtr[3]=B^Key[3];
-#endif
- UpdKeys(Buf);
-}
-
-
-void CryptData::DecryptBlock20(byte *Buf)
-{
- byte InBuf[16];
- uint A,B,C,D,T,TA,TB;
-#if defined(BIG_ENDIAN) || !defined(PRESENT_INT32) || !defined(ALLOW_NOT_ALIGNED_INT)
- A=((uint)Buf[0]|((uint)Buf[1]<<8)|((uint)Buf[2]<<16)|((uint)Buf[3]<<24))^Key[0];
- B=((uint)Buf[4]|((uint)Buf[5]<<8)|((uint)Buf[6]<<16)|((uint)Buf[7]<<24))^Key[1];
- C=((uint)Buf[8]|((uint)Buf[9]<<8)|((uint)Buf[10]<<16)|((uint)Buf[11]<<24))^Key[2];
- D=((uint)Buf[12]|((uint)Buf[13]<<8)|((uint)Buf[14]<<16)|((uint)Buf[15]<<24))^Key[3];
-#else
- uint32 *BufPtr=(uint32 *)Buf;
- A=BufPtr[0]^Key[0];
- B=BufPtr[1]^Key[1];
- C=BufPtr[2]^Key[2];
- D=BufPtr[3]^Key[3];
-#endif
- memcpy(InBuf,Buf,sizeof(InBuf));
- for(int I=NROUNDS-1;I>=0;I--)
- {
- T=((C+rol(D,11,32))^Key[I&3]);
- TA=A^substLong(T);
- T=((D^rol(C,17,32))+Key[I&3]);
- TB=B^substLong(T);
- A=C;
- B=D;
- C=TA;
- D=TB;
- }
-#if defined(BIG_ENDIAN) || !defined(PRESENT_INT32) || !defined(ALLOW_NOT_ALIGNED_INT)
- C^=Key[0];
- Buf[0]=(byte)C;
- Buf[1]=(byte)(C>>8);
- Buf[2]=(byte)(C>>16);
- Buf[3]=(byte)(C>>24);
- D^=Key[1];
- Buf[4]=(byte)D;
- Buf[5]=(byte)(D>>8);
- Buf[6]=(byte)(D>>16);
- Buf[7]=(byte)(D>>24);
- A^=Key[2];
- Buf[8]=(byte)A;
- Buf[9]=(byte)(A>>8);
- Buf[10]=(byte)(A>>16);
- Buf[11]=(byte)(A>>24);
- B^=Key[3];
- Buf[12]=(byte)B;
- Buf[13]=(byte)(B>>8);
- Buf[14]=(byte)(B>>16);
- Buf[15]=(byte)(B>>24);
-#else
- BufPtr[0]=C^Key[0];
- BufPtr[1]=D^Key[1];
- BufPtr[2]=A^Key[2];
- BufPtr[3]=B^Key[3];
-#endif
- UpdKeys(InBuf);
-}
-
-
-void CryptData::UpdKeys(byte *Buf)
-{
- for (int I=0;I<16;I+=4)
- {
- Key[0]^=CRCTab[Buf[I]];
- Key[1]^=CRCTab[Buf[I+1]];
- Key[2]^=CRCTab[Buf[I+2]];
- Key[3]^=CRCTab[Buf[I+3]];
- }
-}
-
-
-void CryptData::Swap(byte *Ch1,byte *Ch2)
-{
- byte Ch=*Ch1;
- *Ch1=*Ch2;
- *Ch2=Ch;
-}
-#endif
-
-
-void CryptData::SetCryptKeys(const char *Password,const byte *Salt,bool Encrypt,bool OldOnly,bool HandsOffHash)
-{
- if (*Password==0)
- return;
- if (OldOnly)
+ switch(Method)
{
#ifndef SFX_MODULE
- if (CRCTab[1]==0)
- InitCRC();
- byte Psw[MAXPASSWORD];
- SetOldKeys(Password);
- Key[0]=0xD3A3B879L;
- Key[1]=0x3F6D12F7L;
- Key[2]=0x7515A235L;
- Key[3]=0xA4E7F123L;
- memset(Psw,0,sizeof(Psw));
-#if defined(_WIN_32) && !defined(GUI)
- CharToOemBuff(Password,(char*)Psw,(DWORD)strlen(Password));
-#else
- strncpyz((char *)Psw,Password,ASIZE(Psw));
-#endif
- size_t PswLength=strlen(Password);
- memcpy(SubstTable,InitSubstTable,sizeof(SubstTable));
- for (int J=0;J<256;J++)
- for (size_t I=0;I>8);
- PswNum[2]=(byte)(I>>16);
- hash_process( &c, PswNum, 3, HandsOffHash);
- if (I%(HashRounds/16)==0)
- {
- hash_context tempc=c;
- uint32 digest[5];
- hash_final( &tempc, digest, HandsOffHash);
- AESInit[I/(HashRounds/16)]=(byte)digest[4];
- }
- }
- uint32 digest[5];
- hash_final( &c, digest, HandsOffHash);
- for (int I=0;I<4;I++)
- for (int J=0;J<4;J++)
- AESKey[I*4+J]=(byte)(digest[I]>>(J*8));
-
- strcpy(Cache[CachePos].Password,Password);
- if ((Cache[CachePos].SaltPresent=(Salt!=NULL))==true)
- memcpy(Cache[CachePos].Salt,Salt,SALT_SIZE);
- Cache[CachePos].HandsOffHash=HandsOffHash;
- memcpy(Cache[CachePos].AESKey,AESKey,sizeof(AESKey));
- memcpy(Cache[CachePos].AESInit,AESInit,sizeof(AESInit));
- CachePos=(CachePos+1)%(sizeof(Cache)/sizeof(Cache[0]));
- }
- rin.init(Encrypt ? Rijndael::Encrypt : Rijndael::Decrypt,AESKey,AESInit);
-}
-
-
-#ifndef SFX_MODULE
-void CryptData::SetOldKeys(const char *Password)
-{
- uint PswCRC=CRC(0xffffffff,Password,strlen(Password));
- OldKey[0]=PswCRC&0xffff;
- OldKey[1]=(PswCRC>>16)&0xffff;
- OldKey[2]=OldKey[3]=0;
- PN1=PN2=PN3=0;
- byte Ch;
- while ((Ch=*Password)!=0)
- {
- PN1+=Ch;
- PN2^=Ch;
- PN3+=Ch;
- PN3=(byte)rol(PN3,1,8);
- OldKey[2]^=Ch^CRCTab[Ch];
- OldKey[3]+=Ch+(CRCTab[Ch]>>16);
- Password++;
- }
-}
-
-
-void CryptData::SetAV15Encryption()
-{
- OldKey[0]=0x4765;
- OldKey[1]=0x9021;
- OldKey[2]=0x7382;
- OldKey[3]=0x5215;
-}
-
-
-void CryptData::SetCmt13Encryption()
-{
- PN1=0;
- PN2=7;
- PN3=77;
-}
-
-
-void CryptData::Crypt(byte *Data,uint Count,int Method)
-{
- if (Method==OLD_DECODE)
- Decode13(Data,Count);
- else
- if (Method==OLD_ENCODE)
- Encode13(Data,Count);
- else
- Crypt15(Data,Count);
-}
-
-
-void CryptData::Encode13(byte *Data,uint Count)
-{
- while (Count--)
- {
- PN2+=PN3;
- PN1+=PN2;
- *Data+=PN1;
- Data++;
- }
-}
-
-
-void CryptData::Decode13(byte *Data,uint Count)
-{
- while (Count--)
- {
- PN2+=PN3;
- PN1+=PN2;
- *Data-=PN1;
- Data++;
- }
-}
-
-
-void CryptData::Crypt15(byte *Data,uint Count)
-{
- while (Count--)
- {
- OldKey[0]+=0x1234;
- OldKey[1]^=CRCTab[(OldKey[0] & 0x1fe)>>1];
- OldKey[2]-=CRCTab[(OldKey[0] & 0x1fe)>>1]>>16;
- OldKey[0]^=OldKey[2];
- OldKey[3]=ror(OldKey[3]&0xffff,1,16)^OldKey[1];
- OldKey[3]=ror(OldKey[3]&0xffff,1,16);
- OldKey[0]^=OldKey[3];
- *Data^=(byte)(OldKey[0]>>8);
- Data++;
- }
-}
#endif
+ case CRYPT_RAR30:
+ case CRYPT_RAR50:
+ rin.blockDecrypt(Buf,Size,Buf);
+ break;
+ }
+}
+bool CryptData::SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,
+ SecPassword *Password,const byte *Salt,
+ const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck)
+{
+ if (!Password->IsSet() || Method==CRYPT_NONE)
+ return false;
+
+ CryptData::Method=Method;
+
+ wchar PwdW[MAXPASSWORD];
+ Password->Get(PwdW,ASIZE(PwdW));
+ char PwdA[MAXPASSWORD];
+ WideToChar(PwdW,PwdA,ASIZE(PwdA));
+
+ switch(Method)
+ {
+#ifndef SFX_MODULE
+ case CRYPT_RAR13:
+ SetKey13(PwdA);
+ break;
+ case CRYPT_RAR15:
+ SetKey15(PwdA);
+ break;
+ case CRYPT_RAR20:
+ SetKey20(PwdA);
+ break;
+#endif
+ case CRYPT_RAR30:
+ SetKey30(Encrypt,Password,PwdW,Salt);
+ break;
+ case CRYPT_RAR50:
+ SetKey50(Encrypt,Password,PwdW,Salt,InitV,Lg2Cnt,HashKey,PswCheck);
+ break;
+ }
+ cleandata(PwdA,sizeof(PwdA));
+ cleandata(PwdW,sizeof(PwdW));
+ return true;
+}
+
+
+// Use the current system time to additionally randomize data.
+static void TimeRandomize(byte *RndBuf,size_t BufSize)
+{
+ static uint Count=0;
+ RarTime CurTime;
+ CurTime.SetCurrentTime();
+ uint64 Random=CurTime.GetWin()+clock();
+ for (size_t I=0;I> ( (I & 7) * 8 ));
+ RndBuf[I]=byte( (RndByte ^ I) + Count++);
+ }
+}
+
+
+
+
+// Fill buffer with random data.
+void GetRnd(byte *RndBuf,size_t BufSize)
+{
+ bool Success=false;
+#if defined(_WIN_ALL)
+ HCRYPTPROV hProvider = 0;
+ if (CryptAcquireContext(&hProvider, 0, 0, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT | CRYPT_SILENT))
+ {
+ Success=CryptGenRandom(hProvider, (DWORD)BufSize, RndBuf) == TRUE;
+ CryptReleaseContext(hProvider, 0);
+ }
+#elif defined(_UNIX)
+ FILE *rndf = fopen("/dev/urandom", "r");
+ if (rndf!=NULL)
+ {
+ Success=fread(RndBuf, BufSize, 1, rndf) == BufSize;
+ fclose(rndf);
+ }
+#endif
+ // We use this code only as the last resort if code above failed.
+ if (!Success)
+ TimeRandomize(RndBuf,BufSize);
+}
diff --git a/libunrar/crypt.hpp b/libunrar/crypt.hpp
index b16e650..f6382ef 100644
--- a/libunrar/crypt.hpp
+++ b/libunrar/crypt.hpp
@@ -1,62 +1,101 @@
#ifndef _RAR_CRYPT_
#define _RAR_CRYPT_
-enum { OLD_DECODE=0,OLD_ENCODE=1,NEW_CRYPT=2 };
-
-struct CryptKeyCacheItem
-{
-#ifndef _SFX_RTL_
- CryptKeyCacheItem()
- {
- *Password=0;
- }
-
- ~CryptKeyCacheItem()
- {
- memset(AESKey,0,sizeof(AESKey));
- memset(AESInit,0,sizeof(AESInit));
- memset(Password,0,sizeof(Password));
- }
-#endif
- byte AESKey[16],AESInit[16];
- char Password[MAXPASSWORD];
- bool SaltPresent;
- byte Salt[SALT_SIZE];
- bool HandsOffHash;
+enum CRYPT_METHOD {
+ CRYPT_NONE,CRYPT_RAR13,CRYPT_RAR15,CRYPT_RAR20,CRYPT_RAR30,CRYPT_RAR50
};
+#define SIZE_SALT50 16
+#define SIZE_SALT30 8
+#define SIZE_INITV 16
+#define SIZE_PSWCHECK 8
+#define SIZE_PSWCHECK_CSUM 4
+
+#define CRYPT_BLOCK_SIZE 16
+#define CRYPT_BLOCK_MASK (CRYPT_BLOCK_SIZE-1) // 0xf
+
+#define CRYPT5_KDF_LG2_COUNT 15 // LOG2 of PDKDF2 iteration count.
+#define CRYPT5_KDF_LG2_COUNT_MAX 24 // LOG2 of maximum accepted iteration count.
+#define CRYPT_VERSION 0 // Supported encryption version.
+
+
class CryptData
{
+ struct KDF5CacheItem
+ {
+ SecPassword Pwd;
+ byte Salt[SIZE_SALT50];
+ byte Key[32];
+ uint Lg2Count; // Log2 of PBKDF2 repetition count.
+ byte PswCheckValue[SHA256_DIGEST_SIZE];
+ byte HashKeyValue[SHA256_DIGEST_SIZE];
+ };
+
+ struct KDF3CacheItem
+ {
+ SecPassword Pwd;
+ byte Salt[SIZE_SALT30];
+ byte Key[16];
+ byte Init[16];
+ bool SaltPresent;
+ };
+
+
private:
- void Encode13(byte *Data,uint Count);
- void Decode13(byte *Data,uint Count);
- void Crypt15(byte *Data,uint Count);
- void UpdKeys(byte *Buf);
- void Swap(byte *Ch1,byte *Ch2);
- void SetOldKeys(const char *Password);
+ void SetKey13(const char *Password);
+ void Decrypt13(byte *Data,size_t Count);
- Rijndael rin;
-
- byte SubstTable[256];
- uint Key[4];
- ushort OldKey[4];
- byte PN1,PN2,PN3;
+ void SetKey15(const char *Password);
+ void Crypt15(byte *Data,size_t Count);
- byte AESKey[16],AESInit[16];
-
- static CryptKeyCacheItem Cache[4];
- static int CachePos;
- public:
- void SetCryptKeys(const char *Password,const byte *Salt,bool Encrypt,bool OldOnly,bool HandsOffHash);
- void SetAV15Encryption();
- void SetCmt13Encryption();
+ void SetKey20(const char *Password);
+ void Swap20(byte *Ch1,byte *Ch2);
+ void UpdKeys20(byte *Buf);
void EncryptBlock20(byte *Buf);
void DecryptBlock20(byte *Buf);
+
+ void SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt);
+ void SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,byte *PswCheck);
+
+ KDF3CacheItem KDF3Cache[4];
+ uint KDF3CachePos;
+
+ KDF5CacheItem KDF5Cache[4];
+ uint KDF5CachePos;
+
+ CRYPT_METHOD Method;
+
+ Rijndael rin;
+
+ uint CRCTab[256]; // For RAR 1.5 and RAR 2.0 encryption.
+
+ byte SubstTable20[256];
+ uint Key20[4];
+
+ byte Key13[3];
+ ushort Key15[4];
+ public:
+ CryptData();
+ ~CryptData();
+ bool SetCryptKeys(bool Encrypt,CRYPT_METHOD Method,SecPassword *Password,
+ const byte *Salt,const byte *InitV,uint Lg2Cnt,
+ byte *HashKey,byte *PswCheck);
+ void SetAV15Encryption();
+ void SetCmt13Encryption();
void EncryptBlock(byte *Buf,size_t Size);
void DecryptBlock(byte *Buf,size_t Size);
- void Crypt(byte *Data,uint Count,int Method);
- static void SetSalt(byte *Salt,int SaltSize);
+ static void SetSalt(byte *Salt,size_t SaltSize);
};
+void GetRnd(byte *RndBuf,size_t BufSize);
+
+void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data,
+ size_t DataLength,byte *ResDigest);
+void pbkdf2(const byte *pass, size_t pass_len, const byte *salt,
+ size_t salt_len,byte *key, byte *Value1, byte *Value2,
+ uint rounds);
+
+void ConvertHashToMAC(HashValue *Value,byte *Key);
+
#endif
diff --git a/libunrar/crypt1.cpp b/libunrar/crypt1.cpp
new file mode 100644
index 0000000..1426393
--- /dev/null
+++ b/libunrar/crypt1.cpp
@@ -0,0 +1,79 @@
+extern uint CRCTab[256];
+
+void CryptData::SetKey13(const char *Password)
+{
+ Key13[0]=Key13[1]=Key13[2]=0;
+ for (size_t I=0;Password[I]!=0;I++)
+ {
+ byte P=Password[I];
+ Key13[0]+=P;
+ Key13[1]^=P;
+ Key13[2]+=P;
+ Key13[2]=(byte)rotls(Key13[2],1,8);
+ }
+}
+
+
+void CryptData::SetKey15(const char *Password)
+{
+ InitCRC32(CRCTab);
+ uint PswCRC=CRC32(0xffffffff,Password,strlen(Password));
+ Key15[0]=PswCRC&0xffff;
+ Key15[1]=(PswCRC>>16)&0xffff;
+ Key15[2]=Key15[3]=0;
+ for (size_t I=0;Password[I]!=0;I++)
+ {
+ byte P=Password[I];
+ Key15[2]^=P^CRCTab[P];
+ Key15[3]+=P+(CRCTab[P]>>16);
+ }
+}
+
+
+void CryptData::SetAV15Encryption()
+{
+ InitCRC32(CRCTab);
+ Method=CRYPT_RAR15;
+ Key15[0]=0x4765;
+ Key15[1]=0x9021;
+ Key15[2]=0x7382;
+ Key15[3]=0x5215;
+}
+
+
+void CryptData::SetCmt13Encryption()
+{
+ Method=CRYPT_RAR13;
+ Key13[0]=0;
+ Key13[1]=7;
+ Key13[2]=77;
+}
+
+
+void CryptData::Decrypt13(byte *Data,size_t Count)
+{
+ while (Count--)
+ {
+ Key13[1]+=Key13[2];
+ Key13[0]+=Key13[1];
+ *Data-=Key13[0];
+ Data++;
+ }
+}
+
+
+void CryptData::Crypt15(byte *Data,size_t Count)
+{
+ while (Count--)
+ {
+ Key15[0]+=0x1234;
+ Key15[1]^=CRCTab[(Key15[0] & 0x1fe)>>1];
+ Key15[2]-=CRCTab[(Key15[0] & 0x1fe)>>1]>>16;
+ Key15[0]^=Key15[2];
+ Key15[3]=rotrs(Key15[3]&0xffff,1,16)^Key15[1];
+ Key15[3]=rotrs(Key15[3]&0xffff,1,16);
+ Key15[0]^=Key15[3];
+ *Data^=(byte)(Key15[0]>>8);
+ Data++;
+ }
+}
diff --git a/libunrar/crypt2.cpp b/libunrar/crypt2.cpp
new file mode 100644
index 0000000..5fa4a97
--- /dev/null
+++ b/libunrar/crypt2.cpp
@@ -0,0 +1,133 @@
+#define NROUNDS 32
+
+#define substLong(t) ( (uint)SubstTable20[(uint)t&255] | \
+ ((uint)SubstTable20[(int)(t>> 8)&255]<< 8) | \
+ ((uint)SubstTable20[(int)(t>>16)&255]<<16) | \
+ ((uint)SubstTable20[(int)(t>>24)&255]<<24) )
+
+
+static byte InitSubstTable20[256]={
+ 215, 19,149, 35, 73,197,192,205,249, 28, 16,119, 48,221, 2, 42,
+ 232, 1,177,233, 14, 88,219, 25,223,195,244, 90, 87,239,153,137,
+ 255,199,147, 70, 92, 66,246, 13,216, 40, 62, 29,217,230, 86, 6,
+ 71, 24,171,196,101,113,218,123, 93, 91,163,178,202, 67, 44,235,
+ 107,250, 75,234, 49,167,125,211, 83,114,157,144, 32,193,143, 36,
+ 158,124,247,187, 89,214,141, 47,121,228, 61,130,213,194,174,251,
+ 97,110, 54,229,115, 57,152, 94,105,243,212, 55,209,245, 63, 11,
+ 164,200, 31,156, 81,176,227, 21, 76, 99,139,188,127, 17,248, 51,
+ 207,120,189,210, 8,226, 41, 72,183,203,135,165,166, 60, 98, 7,
+ 122, 38,155,170, 69,172,252,238, 39,134, 59,128,236, 27,240, 80,
+ 131, 3, 85,206,145, 79,154,142,159,220,201,133, 74, 64, 20,129,
+ 224,185,138,103,173,182, 43, 34,254, 82,198,151,231,180, 58, 10,
+ 118, 26,102, 12, 50,132, 22,191,136,111,162,179, 45, 4,148,108,
+ 161, 56, 78,126,242,222, 15,175,146, 23, 33,241,181,190, 77,225,
+ 0, 46,169,186, 68, 95,237, 65, 53,208,253,168, 9, 18,100, 52,
+ 116,184,160, 96,109, 37, 30,106,140,104,150, 5,204,117,112, 84
+};
+
+
+void CryptData::SetKey20(const char *Password)
+{
+ InitCRC32(CRCTab);
+
+ char Psw[MAXPASSWORD];
+ strncpyz(Psw,Password,ASIZE(Psw)); // We'll need to modify it below.
+ size_t PswLength=strlen(Psw);
+
+ Key20[0]=0xD3A3B879L;
+ Key20[1]=0x3F6D12F7L;
+ Key20[2]=0x7515A235L;
+ Key20[3]=0xA4E7F123L;
+
+ memcpy(SubstTable20,InitSubstTable20,sizeof(SubstTable20));
+ for (uint J=0;J<256;J++)
+ for (size_t I=0;I=0;I--)
+ {
+ T=((C+rotls(D,11,32))^Key20[I&3]);
+ TA=A^substLong(T);
+ T=((D^rotls(C,17,32))+Key20[I&3]);
+ TB=B^substLong(T);
+ A=C;
+ B=D;
+ C=TA;
+ D=TB;
+ }
+ RawPut4(C^Key20[0],Buf+0);
+ RawPut4(D^Key20[1],Buf+4);
+ RawPut4(A^Key20[2],Buf+8);
+ RawPut4(B^Key20[3],Buf+12);
+ UpdKeys20(InBuf);
+}
+
+
+void CryptData::UpdKeys20(byte *Buf)
+{
+ for (int I=0;I<16;I+=4)
+ {
+ Key20[0]^=CRCTab[Buf[I]];
+ Key20[1]^=CRCTab[Buf[I+1]];
+ Key20[2]^=CRCTab[Buf[I+2]];
+ Key20[3]^=CRCTab[Buf[I+3]];
+ }
+}
+
+
+void CryptData::Swap20(byte *Ch1,byte *Ch2)
+{
+ byte Ch=*Ch1;
+ *Ch1=*Ch2;
+ *Ch2=Ch;
+}
diff --git a/libunrar/crypt3.cpp b/libunrar/crypt3.cpp
new file mode 100644
index 0000000..fe3bf97
--- /dev/null
+++ b/libunrar/crypt3.cpp
@@ -0,0 +1,68 @@
+void CryptData::SetKey30(bool Encrypt,SecPassword *Password,const wchar *PwdW,const byte *Salt)
+{
+ byte AESKey[16],AESInit[16];
+
+ bool Cached=false;
+ for (uint I=0;I>8);
+ PswNum[2]=(byte)(I>>16);
+ sha1_process(&c, PswNum, 3);
+ if (I%(HashRounds/16)==0)
+ {
+ sha1_context tempc=c;
+ uint32 digest[5];
+ sha1_done( &tempc, digest );
+ AESInit[I/(HashRounds/16)]=(byte)digest[4];
+ }
+ }
+ uint32 digest[5];
+ sha1_done( &c, digest );
+ for (uint I=0;I<4;I++)
+ for (uint J=0;J<4;J++)
+ AESKey[I*4+J]=(byte)(digest[I]>>(J*8));
+
+ KDF3Cache[KDF3CachePos].Pwd=*Password;
+ if ((KDF3Cache[KDF3CachePos].SaltPresent=(Salt!=NULL))==true)
+ memcpy(KDF3Cache[KDF3CachePos].Salt,Salt,SIZE_SALT30);
+ memcpy(KDF3Cache[KDF3CachePos].Key,AESKey,sizeof(AESKey));
+ SecHideData(KDF3Cache[KDF3CachePos].Key,sizeof(KDF3Cache[KDF3CachePos].Key),true,false);
+ memcpy(KDF3Cache[KDF3CachePos].Init,AESInit,sizeof(AESInit));
+ KDF3CachePos=(KDF3CachePos+1)%ASIZE(KDF3Cache);
+
+ cleandata(RawPsw,sizeof(RawPsw));
+ }
+ rin.Init(Encrypt, AESKey, 128, AESInit);
+ cleandata(AESKey,sizeof(AESKey));
+ cleandata(AESInit,sizeof(AESInit));
+}
+
diff --git a/libunrar/crypt5.cpp b/libunrar/crypt5.cpp
new file mode 100644
index 0000000..7562469
--- /dev/null
+++ b/libunrar/crypt5.cpp
@@ -0,0 +1,233 @@
+static void hmac_sha256(const byte *Key,size_t KeyLength,const byte *Data,
+ size_t DataLength,byte *ResDigest,
+ sha256_context *ICtxOpt,bool *SetIOpt,
+ sha256_context *RCtxOpt,bool *SetROpt)
+{
+ const size_t Sha256BlockSize=64; // As defined in RFC 4868.
+
+ byte KeyHash[SHA256_DIGEST_SIZE];
+ if (KeyLength > Sha256BlockSize) // Convert longer keys to key hash.
+ {
+ sha256_context KCtx;
+ sha256_init(&KCtx);
+ sha256_process(&KCtx, Key, KeyLength);
+ sha256_done(&KCtx, KeyHash);
+
+ Key = KeyHash;
+ KeyLength = SHA256_DIGEST_SIZE;
+ }
+
+ byte KeyBuf[Sha256BlockSize]; // Store the padded key here.
+ sha256_context ICtx;
+
+ if (ICtxOpt!=NULL && *SetIOpt)
+ ICtx=*ICtxOpt; // Use already calculated first block context.
+ else
+ {
+ // This calculation is the same for all iterations with same password.
+ // So for PBKDF2 we can calculate it only for first block and then reuse
+ // to improve performance.
+
+ for (size_t I = 0; I < KeyLength; I++) // Use 0x36 padding for inner digest.
+ KeyBuf[I] = Key[I] ^ 0x36;
+ for (size_t I = KeyLength; I < Sha256BlockSize; I++)
+ KeyBuf[I] = 0x36;
+
+ sha256_init(&ICtx);
+ sha256_process(&ICtx, KeyBuf, Sha256BlockSize); // Hash padded key.
+ }
+
+ if (ICtxOpt!=NULL && !*SetIOpt) // Store constant context for further reuse.
+ {
+ *ICtxOpt=ICtx;
+ *SetIOpt=true;
+ }
+
+ sha256_process(&ICtx, Data, DataLength); // Hash data.
+
+ byte IDig[SHA256_DIGEST_SIZE]; // Internal digest for padded key and data.
+ sha256_done(&ICtx, IDig);
+
+ sha256_context RCtx;
+
+ if (RCtxOpt!=NULL && *SetROpt)
+ RCtx=*RCtxOpt; // Use already calculated first block context.
+ else
+ {
+ // This calculation is the same for all iterations with same password.
+ // So for PBKDF2 we can calculate it only for first block and then reuse
+ // to improve performance.
+
+ for (size_t I = 0; I < KeyLength; I++) // Use 0x5c for outer key padding.
+ KeyBuf[I] = Key[I] ^ 0x5c;
+ for (size_t I = KeyLength; I < Sha256BlockSize; I++)
+ KeyBuf[I] = 0x5c;
+
+ sha256_init(&RCtx);
+ sha256_process(&RCtx, KeyBuf, Sha256BlockSize); // Hash padded key.
+ }
+
+ if (RCtxOpt!=NULL && !*SetROpt) // Store constant context for further reuse.
+ {
+ *RCtxOpt=RCtx;
+ *SetROpt=true;
+ }
+
+ sha256_process(&RCtx, IDig, SHA256_DIGEST_SIZE); // Hash internal digest.
+
+ sha256_done(&RCtx, ResDigest);
+}
+
+
+// PBKDF2 for 32 byte key length. We generate the key for specified number
+// of iteration count also as two supplementary values (key for checksums
+// and password verification) for iterations+16 and iterations+32.
+void pbkdf2(const byte *Pwd, size_t PwdLength,
+ const byte *Salt, size_t SaltLength,
+ byte *Key, byte *V1, byte *V2, uint Count)
+{
+ const size_t MaxSalt=64;
+ byte SaltData[MaxSalt+4];
+ memcpy(SaltData, Salt, Min(SaltLength,MaxSalt));
+
+ SaltData[SaltLength + 0] = 0; // Salt concatenated to 1.
+ SaltData[SaltLength + 1] = 0;
+ SaltData[SaltLength + 2] = 0;
+ SaltData[SaltLength + 3] = 1;
+
+ // First iteration: HMAC of password, salt and block index (1).
+ byte U1[SHA256_DIGEST_SIZE];
+ hmac_sha256(Pwd, PwdLength, SaltData, SaltLength + 4, U1, NULL, NULL, NULL, NULL);
+ byte Fn[SHA256_DIGEST_SIZE]; // Current function value.
+ memcpy(Fn, U1, sizeof(Fn)); // Function at first iteration.
+
+ uint CurCount[] = { Count-1, 16, 16 };
+ byte *CurValue[] = { Key , V1, V2 };
+
+ sha256_context ICtxOpt,RCtxOpt;
+ bool SetIOpt=false,SetROpt=false;
+
+ byte U2[SHA256_DIGEST_SIZE];
+ for (uint I = 0; I < 3; I++) // For output key and 2 supplementary values.
+ {
+ for (uint J = 0; J < CurCount[I]; J++)
+ {
+ // U2 = PRF (P, U1).
+ hmac_sha256(Pwd, PwdLength, U1, sizeof(U1), U2, &ICtxOpt, &SetIOpt, &RCtxOpt, &SetROpt);
+ memcpy(U1, U2, sizeof(U1));
+ for (uint K = 0; K < sizeof(Fn); K++) // Function ^= U.
+ Fn[K] ^= U1[K];
+ }
+ memcpy(CurValue[I], Fn, SHA256_DIGEST_SIZE);
+ }
+
+ cleandata(SaltData, sizeof(SaltData));
+ cleandata(Fn, sizeof(Fn));
+ cleandata(U1, sizeof(U1));
+ cleandata(U2, sizeof(U2));
+}
+
+
+void CryptData::SetKey50(bool Encrypt,SecPassword *Password,const wchar *PwdW,
+ const byte *Salt,const byte *InitV,uint Lg2Cnt,byte *HashKey,
+ byte *PswCheck)
+{
+ if (Lg2Cnt>CRYPT5_KDF_LG2_COUNT_MAX)
+ return;
+
+ byte Key[32],PswCheckValue[SHA256_DIGEST_SIZE],HashKeyValue[SHA256_DIGEST_SIZE];
+ bool Found=false;
+ for (uint I=0;ILg2Count==Lg2Cnt && Item->Pwd==*Password &&
+ memcmp(Item->Salt,Salt,SIZE_SALT50)==0)
+ {
+ memcpy(Key,Item->Key,sizeof(Key));
+ SecHideData(Key,sizeof(Key),false,false);
+
+ memcpy(PswCheckValue,Item->PswCheckValue,sizeof(PswCheckValue));
+ memcpy(HashKeyValue,Item->HashKeyValue,sizeof(HashKeyValue));
+ Found=true;
+ break;
+ }
+ }
+
+ if (!Found)
+ {
+ char PwdUtf[MAXPASSWORD*4];
+ WideToUtf(PwdW,PwdUtf,ASIZE(PwdUtf));
+
+ pbkdf2((byte *)PwdUtf,strlen(PwdUtf),Salt,SIZE_SALT50,Key,HashKeyValue,PswCheckValue,(1<Lg2Count=Lg2Cnt;
+ Item->Pwd=*Password;
+ memcpy(Item->Salt,Salt,SIZE_SALT50);
+ memcpy(Item->Key,Key,sizeof(Item->Key));
+ memcpy(Item->PswCheckValue,PswCheckValue,sizeof(PswCheckValue));
+ memcpy(Item->HashKeyValue,HashKeyValue,sizeof(HashKeyValue));
+ SecHideData(Item->Key,sizeof(Item->Key),true,false);
+ }
+ if (HashKey!=NULL)
+ memcpy(HashKey,HashKeyValue,SHA256_DIGEST_SIZE);
+ if (PswCheck!=NULL)
+ {
+ memset(PswCheck,0,SIZE_PSWCHECK);
+ for (uint I=0;IType==HASH_CRC32)
+ {
+ byte RawCRC[4];
+ RawPut4(Value->CRC32,RawCRC);
+ byte Digest[SHA256_DIGEST_SIZE];
+ hmac_sha256(Key,SHA256_DIGEST_SIZE,RawCRC,sizeof(RawCRC),Digest,NULL,NULL,NULL,NULL);
+ Value->CRC32=0;
+ for (uint I=0;ICRC32^=Digest[I] << ((I & 3) * 8);
+ }
+ if (Value->Type==HASH_BLAKE2)
+ {
+ byte Digest[BLAKE2_DIGEST_SIZE];
+ hmac_sha256(Key,BLAKE2_DIGEST_SIZE,Value->Digest,sizeof(Value->Digest),Digest,NULL,NULL,NULL,NULL);
+ memcpy(Value->Digest,Digest,sizeof(Value->Digest));
+ }
+}
+
+
+#if 0
+static void TestPBKDF2();
+struct TestKDF {TestKDF() {TestPBKDF2();exit(0);}} GlobalTestKDF;
+
+void TestPBKDF2() // Test PBKDF2 HMAC-SHA256
+{
+ byte Key[32],V1[32],V2[32];
+
+ pbkdf2((byte *)"password", 8, (byte *)"salt", 4, Key, V1, V2, 1);
+ byte Res1[32]={0x12, 0x0f, 0xb6, 0xcf, 0xfc, 0xf8, 0xb3, 0x2c, 0x43, 0xe7, 0x22, 0x52, 0x56, 0xc4, 0xf8, 0x37, 0xa8, 0x65, 0x48, 0xc9, 0x2c, 0xcc, 0x35, 0x48, 0x08, 0x05, 0x98, 0x7c, 0xb7, 0x0b, 0xe1, 0x7b };
+ mprintf(L"\nPBKDF2 test1: %s", memcmp(Key,Res1,32)==0 ? L"OK":L"Failed");
+
+ pbkdf2((byte *)"password", 8, (byte *)"salt", 4, Key, V1, V2, 4096);
+ byte Res2[32]={0xc5, 0xe4, 0x78, 0xd5, 0x92, 0x88, 0xc8, 0x41, 0xaa, 0x53, 0x0d, 0xb6, 0x84, 0x5c, 0x4c, 0x8d, 0x96, 0x28, 0x93, 0xa0, 0x01, 0xce, 0x4e, 0x11, 0xa4, 0x96, 0x38, 0x73, 0xaa, 0x98, 0x13, 0x4a };
+ mprintf(L"\nPBKDF2 test2: %s", memcmp(Key,Res2,32)==0 ? L"OK":L"Failed");
+
+ pbkdf2((byte *)"just some long string pretending to be a password", 49, (byte *)"salt, salt, salt, a lot of salt", 31, Key, V1, V2, 65536);
+ byte Res3[32]={0x08, 0x0f, 0xa3, 0x1d, 0x42, 0x2d, 0xb0, 0x47, 0x83, 0x9b, 0xce, 0x3a, 0x3b, 0xce, 0x49, 0x51, 0xe2, 0x62, 0xb9, 0xff, 0x76, 0x2f, 0x57, 0xe9, 0xc4, 0x71, 0x96, 0xce, 0x4b, 0x6b, 0x6e, 0xbf};
+ mprintf(L"\nPBKDF2 test3: %s", memcmp(Key,Res3,32)==0 ? L"OK":L"Failed");
+}
+#endif
diff --git a/libunrar/dll.cpp b/libunrar/dll.cpp
index be5120d..61a68b4 100644
--- a/libunrar/dll.cpp
+++ b/libunrar/dll.cpp
@@ -1,17 +1,16 @@
#include "rar.hpp"
-#include "dll.hpp"
-static int RarErrorToDll(int ErrCode);
+static int RarErrorToDll(RAR_EXIT ErrCode);
struct DataSet
{
CommandData Cmd;
- CmdExtract Extract;
Archive Arc;
+ CmdExtract Extract;
int OpenMode;
int HeaderSize;
- DataSet():Arc(&Cmd) {};
+ DataSet():Arc(&Cmd),Extract(&Cmd) {};
};
@@ -27,124 +26,183 @@ HANDLE PASCAL RAROpenArchive(struct RAROpenArchiveData *r)
r->OpenResult=rx.OpenResult;
r->CmtSize=rx.CmtSize;
r->CmtState=rx.CmtState;
- return(hArc);
+ return hArc;
}
HANDLE PASCAL RAROpenArchiveEx(struct RAROpenArchiveDataEx *r)
{
+ DataSet *Data=NULL;
try
{
+ ErrHandler.Clean();
+
r->OpenResult=0;
- DataSet *Data=new DataSet;
+ Data=new DataSet;
Data->Cmd.DllError=0;
Data->OpenMode=r->OpenMode;
- Data->Cmd.FileArgs->AddString("*");
+ Data->Cmd.FileArgs.AddString(L"*");
+ Data->Cmd.KeepBroken=(r->OpFlags&ROADOF_KEEPBROKEN)!=0;
- char an[NM];
- if (r->ArcName==NULL && r->ArcNameW!=NULL)
+ char AnsiArcName[NM];
+ *AnsiArcName=0;
+ if (r->ArcName!=NULL)
{
- WideToChar(r->ArcNameW,an,NM);
- r->ArcName=an;
+ strncpyz(AnsiArcName,r->ArcName,ASIZE(AnsiArcName));
+#ifdef _WIN_ALL
+ if (!AreFileApisANSI())
+ {
+ OemToCharBuffA(r->ArcName,AnsiArcName,ASIZE(AnsiArcName));
+ AnsiArcName[ASIZE(AnsiArcName)-1]=0;
+ }
+#endif
}
- Data->Cmd.AddArcName(r->ArcName,r->ArcNameW);
+ wchar ArcName[NM];
+ GetWideName(AnsiArcName,r->ArcNameW,ArcName,ASIZE(ArcName));
+
+ Data->Cmd.AddArcName(ArcName);
Data->Cmd.Overwrite=OVERWRITE_ALL;
Data->Cmd.VersionControl=1;
- if (!Data->Arc.Open(r->ArcName,r->ArcNameW))
+
+ 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);
+ return NULL;
}
- if (!Data->Arc.IsArchive(false))
+ if (!Data->Arc.IsArchive(true))
{
- r->OpenResult=Data->Cmd.DllError!=0 ? Data->Cmd.DllError:ERAR_BAD_ARCHIVE;
+ 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);
+ return NULL;
}
- r->Flags=Data->Arc.NewMhd.Flags;
- Array CmtData;
- if (r->CmtBufSize!=0 && Data->Arc.GetComment(&CmtData,NULL))
+ r->Flags=0;
+
+ if (Data->Arc.Volume)
+ r->Flags|=ROADF_VOLUME;
+ if (Data->Arc.MainComment)
+ r->Flags|=ROADF_COMMENT;
+ if (Data->Arc.Locked)
+ r->Flags|=ROADF_LOCK;
+ if (Data->Arc.Solid)
+ r->Flags|=ROADF_SOLID;
+ if (Data->Arc.NewNumbering)
+ r->Flags|=ROADF_NEWNUMBERING;
+ if (Data->Arc.Signed)
+ r->Flags|=ROADF_SIGNED;
+ if (Data->Arc.Protected)
+ r->Flags|=ROADF_RECOVERY;
+ if (Data->Arc.Encrypted)
+ r->Flags|=ROADF_ENCHEADERS;
+ if (Data->Arc.FirstVolume)
+ r->Flags|=ROADF_FIRSTVOLUME;
+
+ Array CmtDataW;
+ if (r->CmtBufSize!=0 && Data->Arc.GetComment(&CmtDataW))
{
- r->Flags|=2;
- size_t Size=CmtData.Size()+1;
- 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;
+ if (r->CmtBufW!=NULL)
+ {
+ CmtDataW.Push(0);
+ size_t Size=wcslen(&CmtDataW[0])+1;
+
+ r->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1;
+ r->CmtSize=(uint)Min(Size,r->CmtBufSize);
+ memcpy(r->CmtBufW,&CmtDataW[0],(r->CmtSize-1)*sizeof(*r->CmtBufW));
+ r->CmtBufW[r->CmtSize-1]=0;
+ }
+ else
+ if (r->CmtBuf!=NULL)
+ {
+ Array 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->CmtState=Size>r->CmtBufSize ? ERAR_SMALL_BUF:1;
+ r->CmtSize=(uint)Min(Size,r->CmtBufSize);
+ memcpy(r->CmtBuf,&CmtData[0],r->CmtSize-1);
+ r->CmtBuf[r->CmtSize-1]=0;
+ }
}
else
r->CmtState=r->CmtSize=0;
- if (Data->Arc.Signed)
- r->Flags|=0x20;
- Data->Extract.ExtractArchiveInit(&Data->Cmd,Data->Arc);
- return((HANDLE)Data);
+ Data->Extract.ExtractArchiveInit(Data->Arc);
+ return (HANDLE)Data;
}
- catch (int ErrCode)
+ catch (RAR_EXIT ErrCode)
{
- r->OpenResult=RarErrorToDll(ErrCode);
- return(NULL);
+ 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;
- bool Success=Data==NULL ? false:Data->Arc.Close();
- delete Data;
- return(Success ? 0:ERAR_ECLOSE);
+ 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)
{
- DataSet *Data=(DataSet *)hArcData;
- try
- {
- if ((Data->HeaderSize=(int)Data->Arc.SearchBlock(FILE_HEAD))<=0)
- {
- if (Data->Arc.Volume && Data->Arc.GetHeaderType()==ENDARC_HEAD &&
- (Data->Arc.EndArcHead.Flags & EARC_NEXT_VOLUME))
- if (MergeArchive(Data->Arc,NULL,false,'L'))
- {
- Data->Extract.SignatureFound=false;
- Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET);
- return(RARReadHeader(hArcData,D));
- }
- else
- return(ERAR_EOPEN);
- return(Data->Arc.BrokenFileHeader ? ERAR_BAD_DATA:ERAR_END_ARCHIVE);
- }
- if (Data->OpenMode==RAR_OM_LIST && (Data->Arc.NewLhd.Flags & LHD_SPLIT_BEFORE))
- {
- int Code=RARProcessFile(hArcData,RAR_SKIP,NULL,NULL);
- if (Code==0)
- return(RARReadHeader(hArcData,D));
- else
- return(Code);
- }
- strncpyz(D->ArcName,Data->Arc.FileName,ASIZE(D->ArcName));
- strncpyz(D->FileName,Data->Arc.NewLhd.FileName,ASIZE(D->FileName));
- D->Flags=Data->Arc.NewLhd.Flags;
- D->PackSize=Data->Arc.NewLhd.PackSize;
- D->UnpSize=Data->Arc.NewLhd.UnpSize;
- D->HostOS=Data->Arc.NewLhd.HostOS;
- D->FileCRC=Data->Arc.NewLhd.FileCRC;
- D->FileTime=Data->Arc.NewLhd.FileTime;
- D->UnpVer=Data->Arc.NewLhd.UnpVer;
- D->Method=Data->Arc.NewLhd.Method;
- D->FileAttr=Data->Arc.NewLhd.FileAttr;
- D->CmtSize=0;
- D->CmtState=0;
- }
- catch (int ErrCode)
- {
- return(RarErrorToDll(ErrCode));
- }
- return(0);
+ 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;
}
@@ -153,65 +211,114 @@ int PASCAL RARReadHeaderEx(HANDLE hArcData,struct RARHeaderDataEx *D)
DataSet *Data=(DataSet *)hArcData;
try
{
- if ((Data->HeaderSize=(int)Data->Arc.SearchBlock(FILE_HEAD))<=0)
+ if ((Data->HeaderSize=(int)Data->Arc.SearchBlock(HEAD_FILE))<=0)
{
- if (Data->Arc.Volume && Data->Arc.GetHeaderType()==ENDARC_HEAD &&
- (Data->Arc.EndArcHead.Flags & EARC_NEXT_VOLUME))
+ if (Data->Arc.Volume && Data->Arc.GetHeaderType()==HEAD_ENDARC &&
+ Data->Arc.EndArcHead.NextVolume)
if (MergeArchive(Data->Arc,NULL,false,'L'))
{
- Data->Extract.SignatureFound=false;
Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET);
- return(RARReadHeaderEx(hArcData,D));
+ return RARReadHeaderEx(hArcData,D);
}
else
- return(ERAR_EOPEN);
- return(Data->Arc.BrokenFileHeader ? ERAR_BAD_DATA:ERAR_END_ARCHIVE);
+ 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;
}
- if (Data->OpenMode==RAR_OM_LIST && (Data->Arc.NewLhd.Flags & LHD_SPLIT_BEFORE))
+ 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));
+ return RARReadHeaderEx(hArcData,D);
else
- return(Code);
+ return Code;
}
- strncpyz(D->ArcName,Data->Arc.FileName,ASIZE(D->ArcName));
- if (*Data->Arc.FileNameW)
- strncpyw(D->ArcNameW,Data->Arc.FileNameW,sizeof(D->ArcNameW));
- else
- CharToWide(Data->Arc.FileName,D->ArcNameW);
- strncpyz(D->FileName,Data->Arc.NewLhd.FileName,ASIZE(D->FileName));
- if (*Data->Arc.NewLhd.FileNameW)
- strncpyw(D->FileNameW,Data->Arc.NewLhd.FileNameW,sizeof(D->FileNameW));
- else
- {
-#ifdef _WIN_32
- char AnsiName[NM];
- OemToChar(Data->Arc.NewLhd.FileName,AnsiName);
- CharToWide(AnsiName,D->FileNameW);
-#else
- CharToWide(Data->Arc.NewLhd.FileName,D->FileNameW);
+ 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=Data->Arc.NewLhd.Flags;
- D->PackSize=Data->Arc.NewLhd.PackSize;
- D->PackSizeHigh=Data->Arc.NewLhd.HighPackSize;
- D->UnpSize=Data->Arc.NewLhd.UnpSize;
- D->UnpSizeHigh=Data->Arc.NewLhd.HighUnpSize;
- D->HostOS=Data->Arc.NewLhd.HostOS;
- D->FileCRC=Data->Arc.NewLhd.FileCRC;
- D->FileTime=Data->Arc.NewLhd.FileTime;
- D->UnpVer=Data->Arc.NewLhd.UnpVer;
- D->Method=Data->Arc.NewLhd.Method;
- D->FileAttr=Data->Arc.NewLhd.FileAttr;
+
+ 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;
+ 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 (int ErrCode)
+ catch (RAR_EXIT ErrCode)
{
- return(RarErrorToDll(ErrCode));
+ return Data->Cmd.DllError!=0 ? Data->Cmd.DllError : RarErrorToDll(ErrCode);
}
- return(0);
+ return ERAR_SUCCESS;
}
@@ -224,88 +331,101 @@ int PASCAL ProcessFile(HANDLE hArcData,int Operation,char *DestPath,char *DestNa
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()==FILE_HEAD &&
- (Data->Arc.NewLhd.Flags & LHD_SPLIT_AFTER)!=0)
+ if (Data->Arc.Volume && Data->Arc.GetHeaderType()==HEAD_FILE &&
+ Data->Arc.FileHead.SplitAfter)
if (MergeArchive(Data->Arc,NULL,false,'L'))
{
- Data->Extract.SignatureFound=false;
Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET);
- return(0);
+ return ERAR_SUCCESS;
}
else
- return(ERAR_EOPEN);
+ return ERAR_EOPEN;
Data->Arc.SeekToNext();
}
else
{
Data->Cmd.DllOpMode=Operation;
- if (DestPath!=NULL || DestName!=NULL)
+ *Data->Cmd.ExtrPath=0;
+ *Data->Cmd.DllDestName=0;
+
+ if (DestPath!=NULL)
{
-#ifdef _WIN_32
- OemToChar(NullToEmpty(DestPath),Data->Cmd.ExtrPath);
-#else
- strcpy(Data->Cmd.ExtrPath,NullToEmpty(DestPath));
+ 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
- AddEndSlash(Data->Cmd.ExtrPath);
-#ifdef _WIN_32
- OemToChar(NullToEmpty(DestName),Data->Cmd.DllDestName);
-#else
- strcpy(Data->Cmd.DllDestName,NullToEmpty(DestName));
+ 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
- }
- else
- {
- *Data->Cmd.ExtrPath=0;
- *Data->Cmd.DllDestName=0;
+ CharToWide(DestNameA,Data->Cmd.DllDestName,ASIZE(Data->Cmd.DllDestName));
}
- if (DestPathW!=NULL || DestNameW!=NULL)
+ if (DestPathW!=NULL)
{
- strncpyw(Data->Cmd.ExtrPathW,NullToEmpty(DestPathW),NM-2);
- AddEndSlash(Data->Cmd.ExtrPathW);
- strncpyw(Data->Cmd.DllDestNameW,NullToEmpty(DestNameW),NM-1);
-
- if (*Data->Cmd.DllDestNameW!=0 && *Data->Cmd.DllDestName==0)
- WideToChar(Data->Cmd.DllDestNameW,Data->Cmd.DllDestName);
- }
- else
- {
- *Data->Cmd.ExtrPathW=0;
- *Data->Cmd.DllDestNameW=0;
+ wcsncpy(Data->Cmd.ExtrPath,DestPathW,ASIZE(Data->Cmd.ExtrPath));
+ AddEndSlash(Data->Cmd.ExtrPath,ASIZE(Data->Cmd.ExtrPath));
}
- strcpy(Data->Cmd.Command,Operation==RAR_EXTRACT ? "X":"T");
+ if (DestNameW!=NULL)
+ wcsncpyz(Data->Cmd.DllDestName,DestNameW,ASIZE(Data->Cmd.DllDestName));
+
+ wcsncpyz(Data->Cmd.Command,Operation==RAR_EXTRACT ? L"X":L"T",ASIZE(Data->Cmd.Command));
Data->Cmd.Test=Operation!=RAR_EXTRACT;
bool Repeat=false;
- Data->Extract.ExtractCurrentFile(&Data->Cmd,Data->Arc,Data->HeaderSize,Repeat);
+ Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat);
- while (Data->Arc.ReadHeader()!=0 && Data->Arc.GetHeaderType()==NEWSUB_HEAD)
+ // 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->Cmd,Data->Arc,Data->HeaderSize,Repeat);
+ Data->Extract.ExtractCurrentFile(Data->Arc,Data->HeaderSize,Repeat);
Data->Arc.SeekToNext();
}
Data->Arc.Seek(Data->Arc.CurBlockPos,SEEK_SET);
}
}
- catch (int ErrCode)
+ catch (std::bad_alloc&)
{
- return(RarErrorToDll(ErrCode));
+ return ERAR_NO_MEMORY;
}
- return(Data->Cmd.DllError);
+ 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));
+ 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));
+ return ProcessFile(hArcData,Operation,NULL,NULL,DestPath,DestName);
}
@@ -330,40 +450,46 @@ void PASCAL RARSetProcessDataProc(HANDLE hArcData,PROCESSDATAPROC ProcessDataPro
Data->Cmd.ProcessDataProc=ProcessDataProc;
}
-#ifndef NOCRYPT
+
void PASCAL RARSetPassword(HANDLE hArcData,char *Password)
{
+#ifndef RAR_NOCRYPT
DataSet *Data=(DataSet *)hArcData;
- strncpyz(Data->Cmd.Password,Password,ASIZE(Data->Cmd.Password));
-}
+ 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);
+ return RAR_DLL_VERSION;
}
-static int RarErrorToDll(int ErrCode)
+static int RarErrorToDll(RAR_EXIT ErrCode)
{
switch(ErrCode)
{
- case FATAL_ERROR:
- return(ERAR_EREAD);
- case CRC_ERROR:
- return(ERAR_BAD_DATA);
- case WRITE_ERROR:
- return(ERAR_EWRITE);
- case OPEN_ERROR:
- return(ERAR_EOPEN);
- case CREATE_ERROR:
- return(ERAR_ECREATE);
- case MEMORY_ERROR:
- return(ERAR_NO_MEMORY);
- case SUCCESS:
- return(0);
+ 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);
+ return ERAR_UNKNOWN;
}
}
diff --git a/libunrar/dll.def b/libunrar/dll.def
index 660f69b..3c9a2c8 100644
--- a/libunrar/dll.def
+++ b/libunrar/dll.def
@@ -5,6 +5,7 @@ EXPORTS
RARReadHeader
RARReadHeaderEx
RARProcessFile
+ RARProcessFileW
RARSetCallback
RARSetChangeVolProc
RARSetProcessDataProc
diff --git a/libunrar/dll.hpp b/libunrar/dll.hpp
index 4582f2c..c785ff1 100644
--- a/libunrar/dll.hpp
+++ b/libunrar/dll.hpp
@@ -1,6 +1,9 @@
#ifndef _UNRAR_DLL_
#define _UNRAR_DLL_
+#pragma pack(push, 1)
+
+#define ERAR_SUCCESS 0
#define ERAR_END_ARCHIVE 10
#define ERAR_NO_MEMORY 11
#define ERAR_BAD_DATA 12
@@ -14,6 +17,8 @@
#define ERAR_SMALL_BUF 20
#define ERAR_UNKNOWN 21
#define ERAR_MISSING_PASSWORD 22
+#define ERAR_EREFERENCE 23
+#define ERAR_BAD_PASSWORD 24
#define RAR_OM_LIST 0
#define RAR_OM_EXTRACT 1
@@ -26,7 +31,12 @@
#define RAR_VOL_ASK 0
#define RAR_VOL_NOTIFY 1
-#define RAR_DLL_VERSION 4
+#define RAR_DLL_VERSION 8
+
+#define RAR_HASH_NONE 0
+#define RAR_HASH_CRC32 1
+#define RAR_HASH_BLAKE2 2
+
#ifdef _UNIX
#define CALLBACK
@@ -37,6 +47,13 @@
#define UINT unsigned int
#endif
+#define RHDF_SPLITBEFORE 0x01
+#define RHDF_SPLITAFTER 0x02
+#define RHDF_ENCRYPTED 0x04
+#define RHDF_SOLID 0x10
+#define RHDF_DIRECTORY 0x20
+
+
struct RARHeaderData
{
char ArcName[260];
@@ -78,7 +95,20 @@ struct RARHeaderDataEx
unsigned int CmtBufSize;
unsigned int CmtSize;
unsigned int CmtState;
- unsigned int Reserved[1024];
+ unsigned int DictSize;
+ unsigned int HashType;
+ char Hash[32];
+ unsigned int RedirType;
+ wchar_t *RedirName;
+ unsigned int RedirNameSize;
+ unsigned int DirTarget;
+ unsigned int MtimeLow;
+ unsigned int MtimeHigh;
+ unsigned int CtimeLow;
+ unsigned int CtimeHigh;
+ unsigned int AtimeLow;
+ unsigned int AtimeHigh;
+ unsigned int Reserved[988];
};
@@ -93,26 +123,43 @@ struct RAROpenArchiveData
unsigned int CmtState;
};
+typedef int (CALLBACK *UNRARCALLBACK)(UINT msg,LPARAM UserData,LPARAM P1,LPARAM P2);
+
+#define ROADF_VOLUME 0x0001
+#define ROADF_COMMENT 0x0002
+#define ROADF_LOCK 0x0004
+#define ROADF_SOLID 0x0008
+#define ROADF_NEWNUMBERING 0x0010
+#define ROADF_SIGNED 0x0020
+#define ROADF_RECOVERY 0x0040
+#define ROADF_ENCHEADERS 0x0080
+#define ROADF_FIRSTVOLUME 0x0100
+
+#define ROADOF_KEEPBROKEN 0x0001
+
struct RAROpenArchiveDataEx
{
char *ArcName;
wchar_t *ArcNameW;
- unsigned int OpenMode;
- unsigned int OpenResult;
+ unsigned int OpenMode;
+ unsigned int OpenResult;
char *CmtBuf;
- unsigned int CmtBufSize;
- unsigned int CmtSize;
- unsigned int CmtState;
- unsigned int Flags;
- unsigned int Reserved[32];
+ unsigned int CmtBufSize;
+ unsigned int CmtSize;
+ unsigned int CmtState;
+ unsigned int Flags;
+ UNRARCALLBACK Callback;
+ LPARAM UserData;
+ unsigned int OpFlags;
+ wchar_t *CmtBufW;
+ unsigned int Reserved[25];
};
enum UNRARCALLBACK_MESSAGES {
- UCM_CHANGEVOLUME,UCM_PROCESSDATA,UCM_NEEDPASSWORD
+ UCM_CHANGEVOLUME,UCM_PROCESSDATA,UCM_NEEDPASSWORD,UCM_CHANGEVOLUMEW,
+ UCM_NEEDPASSWORDW
};
-typedef int (CALLBACK *UNRARCALLBACK)(UINT msg,LPARAM UserData,LPARAM P1,LPARAM P2);
-
typedef int (PASCAL *CHANGEVOLPROC)(char *ArcName,int Mode);
typedef int (PASCAL *PROCESSDATAPROC)(unsigned char *Addr,int Size);
@@ -137,4 +184,6 @@ int PASCAL RARGetDllVersion();
}
#endif
+#pragma pack(pop)
+
#endif
diff --git a/libunrar/dll.rc b/libunrar/dll.rc
new file mode 100644
index 0000000..7ff2021
--- /dev/null
+++ b/libunrar/dll.rc
@@ -0,0 +1,28 @@
+#include
+#include
+
+VS_VERSION_INFO VERSIONINFO
+FILEVERSION 5, 91, 100, 3470
+PRODUCTVERSION 5, 91, 100, 3470
+FILEOS VOS__WINDOWS32
+FILETYPE VFT_APP
+{
+ BLOCK "StringFileInfo"
+ {
+ BLOCK "040904E4"
+ {
+ VALUE "CompanyName", "Alexander Roshal\0"
+ VALUE "ProductName", "RAR decompression library\0"
+ VALUE "FileDescription", "RAR decompression library\0"
+ VALUE "FileVersion", "5.91.0\0"
+ VALUE "ProductVersion", "5.91.0\0"
+ VALUE "LegalCopyright", "Copyright © Alexander Roshal 1993-2020\0"
+ VALUE "OriginalFilename", "Unrar.dll\0"
+ }
+ }
+ BLOCK "VarFileInfo"
+ {
+ VALUE "Translation", 0x0409, 0x04E4
+ }
+}
+
diff --git a/libunrar/dll_nocrypt.def b/libunrar/dll_nocrypt.def
new file mode 100644
index 0000000..d473e97
--- /dev/null
+++ b/libunrar/dll_nocrypt.def
@@ -0,0 +1,13 @@
+EXPORTS
+ RAROpenArchive
+ RAROpenArchiveEx
+ RARCloseArchive
+ RARReadHeader
+ RARReadHeaderEx
+ RARProcessFile
+ RARProcessFileW
+ RARSetCallback
+ RARSetChangeVolProc
+ RARSetProcessDataProc
+; RARSetPassword
+ RARGetDllVersion
diff --git a/libunrar/encname.cpp b/libunrar/encname.cpp
index 4e42a75..84731a7 100644
--- a/libunrar/encname.cpp
+++ b/libunrar/encname.cpp
@@ -11,41 +11,53 @@ EncodeFileName::EncodeFileName()
-void EncodeFileName::Decode(char *Name,byte *EncName,size_t EncSize,wchar *NameW,
- size_t MaxDecSize)
+void EncodeFileName::Decode(char *Name,size_t NameSize,byte *EncName,size_t EncSize,
+ wchar *NameW,size_t MaxDecSize)
{
size_t EncPos=0,DecPos=0;
- byte HighByte=EncName[EncPos++];
+ byte HighByte=EncPos=EncSize)
+ break;
Flags=EncName[EncPos++];
FlagBits=8;
}
switch(Flags>>6)
{
case 0:
+ if (EncPos>=EncSize)
+ break;
NameW[DecPos++]=EncName[EncPos++];
break;
case 1:
+ if (EncPos>=EncSize)
+ break;
NameW[DecPos++]=EncName[EncPos++]+(HighByte<<8);
break;
case 2:
+ if (EncPos+1>=EncSize)
+ break;
NameW[DecPos++]=EncName[EncPos]+(EncName[EncPos+1]<<8);
EncPos+=2;
break;
case 3:
{
+ if (EncPos>=EncSize)
+ break;
int Length=EncName[EncPos++];
- if (Length & 0x80)
+ if ((Length & 0x80)!=0)
{
+ if (EncPos>=EncSize)
+ break;
byte Correction=EncName[EncPos++];
- for (Length=(Length&0x7f)+2;Length>0 && DecPos0 && DecPos0 && DecPos0 && DecPosMAX_PATH)
- {
- Log(ArcName && *ArcName ? ArcName:NULL,St(MMaxPathLimit),MAX_PATH);
- }
- }
-#endif
+ uiMsg(UIERROR_FILECREATE,ArcName,FileName);
SysErrMsg();
-#endif
+ SetErrorCode(RARX_CREATE);
}
-void ErrorHandler::ReadErrorMsg(const char *ArcName,const char *FileName)
+void ErrorHandler::ReadErrorMsg(const wchar *FileName)
{
-#ifndef SILENT
- ErrMsg(ArcName,St(MErrRead),FileName);
- SysErrMsg();
-#endif
+ ReadErrorMsg(NULL,FileName);
}
-void ErrorHandler::WriteErrorMsg(const char *ArcName,const char *FileName)
+void ErrorHandler::ReadErrorMsg(const wchar *ArcName,const wchar *FileName)
{
-#ifndef SILENT
- ErrMsg(ArcName,St(MErrWrite),FileName);
+ uiMsg(UIERROR_FILEREAD,ArcName,FileName);
SysErrMsg();
-#endif
+ SetErrorCode(RARX_FATAL);
}
-void ErrorHandler::Exit(int ExitCode)
+void ErrorHandler::WriteErrorMsg(const wchar *ArcName,const wchar *FileName)
{
-#ifndef SFX_MODULE
- Alarm();
-#endif
+ uiMsg(UIERROR_FILEWRITE,ArcName,FileName);
+ SysErrMsg();
+ SetErrorCode(RARX_WRITE);
+}
+
+
+void ErrorHandler::ArcBrokenMsg(const wchar *ArcName)
+{
+ uiMsg(UIERROR_ARCBROKEN,ArcName);
+ SetErrorCode(RARX_CRC);
+}
+
+
+void ErrorHandler::ChecksumFailedMsg(const wchar *ArcName,const wchar *FileName)
+{
+ uiMsg(UIERROR_CHECKSUM,ArcName,FileName);
+ SetErrorCode(RARX_CRC);
+}
+
+
+void ErrorHandler::UnknownMethodMsg(const wchar *ArcName,const wchar *FileName)
+{
+ uiMsg(UIERROR_UNKNOWNMETHOD,ArcName,FileName);
+ ErrHandler.SetErrorCode(RARX_FATAL);
+}
+
+
+void ErrorHandler::Exit(RAR_EXIT ExitCode)
+{
+ uiAlarm(UIALARM_ERROR);
Throw(ExitCode);
}
-#ifndef GUI
-void ErrorHandler::ErrMsg(const char *ArcName,const char *fmt,...)
-{
- safebuf char Msg[NM+1024];
- va_list argptr;
- va_start(argptr,fmt);
- vsprintf(Msg,fmt,argptr);
- va_end(argptr);
-#ifdef _WIN_32
- if (UserBreak)
- Sleep(5000);
-#endif
- Alarm();
- if (*Msg)
- {
- Log(ArcName,"\n%s",Msg);
- mprintf("\n%s\n",St(MProgAborted));
- }
-}
-#endif
-
-
-void ErrorHandler::SetErrorCode(int Code)
+void ErrorHandler::SetErrorCode(RAR_EXIT Code)
{
switch(Code)
{
- case WARNING:
- case USER_BREAK:
- if (ExitCode==SUCCESS)
+ case RARX_WARNING:
+ case RARX_USERBREAK:
+ if (ExitCode==RARX_SUCCESS)
ExitCode=Code;
break;
- case FATAL_ERROR:
- if (ExitCode==SUCCESS || ExitCode==WARNING)
- ExitCode=FATAL_ERROR;
+ case RARX_CRC:
+ if (ExitCode!=RARX_BADPWD)
+ ExitCode=Code;
+ break;
+ case RARX_FATAL:
+ if (ExitCode==RARX_SUCCESS || ExitCode==RARX_WARNING)
+ ExitCode=RARX_FATAL;
break;
default:
ExitCode=Code;
@@ -266,8 +254,7 @@ void ErrorHandler::SetErrorCode(int Code)
}
-#if !defined(GUI) && !defined(_SFX_RTL_)
-#ifdef _WIN_32
+#ifdef _WIN_ALL
BOOL __stdcall ProcessSignal(DWORD SigType)
#else
#if defined(__sun)
@@ -276,102 +263,147 @@ extern "C"
void _stdfunction ProcessSignal(int SigType)
#endif
{
-#ifdef _WIN_32
+#ifdef _WIN_ALL
+ // When a console application is run as a service, this allows the service
+ // to continue running after the user logs off.
if (SigType==CTRL_LOGOFF_EVENT)
- return(TRUE);
+ return TRUE;
#endif
- UserBreak=true;
+
+ ErrHandler.UserBreak=true;
+ ErrHandler.SetDisableShutdown();
mprintf(St(MBreak));
- for (int I=0;!File::RemoveCreated() && I<3;I++)
- {
-#ifdef _WIN_32
+
+#ifdef _WIN_ALL
+ // Let the main thread to handle 'throw' and destroy file objects.
+ for (uint I=0;!ErrHandler.MainExit && I<50;I++)
Sleep(100);
-#endif
- }
-#if defined(USE_RC) && !defined(SFX_MODULE) && !defined(_WIN_CE)
+#if defined(USE_RC) && !defined(SFX_MODULE) && !defined(RARDLL)
ExtRes.UnloadDLL();
#endif
- exit(USER_BREAK);
-#if defined(_WIN_32) && !defined(_MSC_VER)
- // never reached, just to avoid a compiler warning
- return(TRUE);
+ exit(RARX_USERBREAK);
+#endif
+
+#ifdef _UNIX
+ static uint BreakCount=0;
+ // User continues to press Ctrl+C, exit immediately without cleanup.
+ if (++BreakCount>1)
+ exit(RARX_USERBREAK);
+ // Otherwise return from signal handler and let Wait() function to close
+ // files and quit. We cannot use the same approach as in Windows,
+ // because Unix signal handler can block execution of our main code.
+#endif
+
+#if defined(_WIN_ALL) && !defined(_MSC_VER)
+ // Never reached, just to avoid a compiler warning
+ return TRUE;
#endif
}
-#endif
void ErrorHandler::SetSignalHandlers(bool Enable)
{
EnableBreak=Enable;
-#if !defined(GUI) && !defined(_SFX_RTL_)
-#ifdef _WIN_32
+#ifdef _WIN_ALL
SetConsoleCtrlHandler(Enable ? ProcessSignal:NULL,TRUE);
-// signal(SIGBREAK,Enable ? ProcessSignal:SIG_IGN);
#else
signal(SIGINT,Enable ? ProcessSignal:SIG_IGN);
signal(SIGTERM,Enable ? ProcessSignal:SIG_IGN);
#endif
-#endif
}
-void ErrorHandler::Throw(int Code)
+void ErrorHandler::Throw(RAR_EXIT Code)
{
- if (Code==USER_BREAK && !EnableBreak)
+ if (Code==RARX_USERBREAK && !EnableBreak)
return;
- ErrHandler.SetErrorCode(Code);
-#ifdef ALLOW_EXCEPTIONS
- throw Code;
-#else
- File::RemoveCreated();
- exit(Code);
+#if !defined(SILENT)
+ // Do not write "aborted" when just displaying online help.
+ if (Code!=RARX_SUCCESS && Code!=RARX_USERERROR)
+ mprintf(L"\n%s\n",St(MProgAborted));
#endif
+ SetErrorCode(Code);
+ throw Code;
+}
+
+
+bool ErrorHandler::GetSysErrMsg(wchar *Msg,size_t Size)
+{
+#ifndef SILENT
+#ifdef _WIN_ALL
+ int ErrType=GetLastError();
+ if (ErrType!=0)
+ return FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM|FORMAT_MESSAGE_IGNORE_INSERTS,
+ NULL,ErrType,MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT),
+ Msg,(DWORD)Size,NULL)!=0;
+#endif
+
+#if defined(_UNIX) || defined(_EMX)
+ if (errno!=0)
+ {
+ char *err=strerror(errno);
+ if (err!=NULL)
+ {
+ CharToWide(err,Msg,Size);
+ return true;
+ }
+ }
+#endif
+#endif
+ return false;
}
void ErrorHandler::SysErrMsg()
{
#if !defined(SFX_MODULE) && !defined(SILENT)
-#ifdef _WIN_32
- #define STRCHR strchr
- #define ERRCHAR char
- ERRCHAR *lpMsgBuf=NULL;
- int ErrType=GetLastError();
- if (ErrType!=0 && FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER|FORMAT_MESSAGE_FROM_SYSTEM,
- NULL,ErrType,MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
- (LPTSTR)&lpMsgBuf,0,NULL))
+ wchar Msg[1024];
+ if (!GetSysErrMsg(Msg,ASIZE(Msg)))
+ return;
+#ifdef _WIN_ALL
+ wchar *CurMsg=Msg;
+ while (CurMsg!=NULL) // Print string with \r\n as several strings to multiple lines.
{
- ERRCHAR *CurMsg=lpMsgBuf;
- while (CurMsg!=NULL)
+ while (*CurMsg=='\r' || *CurMsg=='\n')
+ CurMsg++;
+ if (*CurMsg==0)
+ break;
+ wchar *EndMsg=wcschr(CurMsg,'\r');
+ if (EndMsg==NULL)
+ EndMsg=wcschr(CurMsg,'\n');
+ if (EndMsg!=NULL)
{
- while (*CurMsg=='\r' || *CurMsg=='\n')
- CurMsg++;
- if (*CurMsg==0)
- break;
- ERRCHAR *EndMsg=STRCHR(CurMsg,'\r');
- if (EndMsg==NULL)
- EndMsg=STRCHR(CurMsg,'\n');
- if (EndMsg!=NULL)
- {
- *EndMsg=0;
- EndMsg++;
- }
- Log(NULL,"\n%s",CurMsg);
- CurMsg=EndMsg;
+ *EndMsg=0;
+ EndMsg++;
}
+ uiMsg(UIERROR_SYSERRMSG,CurMsg);
+ CurMsg=EndMsg;
}
- LocalFree( lpMsgBuf );
#endif
#if defined(_UNIX) || defined(_EMX)
- char *err=strerror(errno);
- if (err!=NULL)
- Log(NULL,"\n%s",err);
+ uiMsg(UIERROR_SYSERRMSG,Msg);
#endif
#endif
}
+int ErrorHandler::GetSystemErrorCode()
+{
+#ifdef _WIN_ALL
+ return GetLastError();
+#else
+ return errno;
+#endif
+}
+void ErrorHandler::SetSystemErrorCode(int Code)
+{
+#ifdef _WIN_ALL
+ SetLastError(Code);
+#else
+ errno=Code;
+#endif
+}
diff --git a/libunrar/errhnd.hpp b/libunrar/errhnd.hpp
index 28e9843..3455dac 100644
--- a/libunrar/errhnd.hpp
+++ b/libunrar/errhnd.hpp
@@ -1,63 +1,72 @@
#ifndef _RAR_ERRHANDLER_
#define _RAR_ERRHANDLER_
-#if (defined(GUI) || !defined(_WIN_32)) && !defined(SFX_MODULE) && !defined(_WIN_CE) || defined(RARDLL)
-#define ALLOW_EXCEPTIONS
-#endif
-
-
-
-#define rarmalloc malloc
-#define rarcalloc calloc
-#define rarrealloc realloc
-#define rarfree free
-#define rarstrdup strdup
-#define rarstrdupw strdupw
-
-
-
-enum { SUCCESS,WARNING,FATAL_ERROR,CRC_ERROR,LOCK_ERROR,WRITE_ERROR,
- OPEN_ERROR,USER_ERROR,MEMORY_ERROR,CREATE_ERROR,USER_BREAK=255};
+enum RAR_EXIT // RAR exit code.
+{
+ RARX_SUCCESS = 0,
+ RARX_WARNING = 1,
+ RARX_FATAL = 2,
+ RARX_CRC = 3,
+ RARX_LOCK = 4,
+ RARX_WRITE = 5,
+ RARX_OPEN = 6,
+ RARX_USERERROR = 7,
+ RARX_MEMORY = 8,
+ RARX_CREATE = 9,
+ RARX_NOFILES = 10,
+ RARX_BADPWD = 11,
+ RARX_USERBREAK = 255
+};
class ErrorHandler
{
private:
- void ErrMsg(const char *ArcName,const char *fmt,...);
-
- int ExitCode;
- int ErrCount;
+ RAR_EXIT ExitCode;
+ uint ErrCount;
bool EnableBreak;
bool Silent;
- bool DoShutdown;
+ bool DisableShutdown; // Shutdown is not suitable after last error.
public:
ErrorHandler();
void Clean();
void MemoryError();
- void OpenError(const char *FileName);
- void CloseError(const char *FileName);
- void ReadError(const char *FileName);
- bool AskRepeatRead(const char *FileName);
- void WriteError(const char *ArcName,const char *FileName);
- void WriteErrorFAT(const char *FileName);
- bool AskRepeatWrite(const char *FileName,bool DiskFull);
- void SeekError(const char *FileName);
- void GeneralErrMsg(const char *Msg);
+ void OpenError(const wchar *FileName);
+ void CloseError(const wchar *FileName);
+ void ReadError(const wchar *FileName);
+ bool AskRepeatRead(const wchar *FileName);
+ void WriteError(const wchar *ArcName,const wchar *FileName);
+ void WriteErrorFAT(const wchar *FileName);
+ bool AskRepeatWrite(const wchar *FileName,bool DiskFull);
+ void SeekError(const wchar *FileName);
+ void GeneralErrMsg(const wchar *fmt,...);
void MemoryErrorMsg();
- void OpenErrorMsg(const char *FileName);
- void OpenErrorMsg(const char *ArcName,const char *FileName);
- void CreateErrorMsg(const char *FileName);
- void CreateErrorMsg(const char *ArcName,const char *FileName);
- void ReadErrorMsg(const char *ArcName,const char *FileName);
- void WriteErrorMsg(const char *ArcName,const char *FileName);
- void Exit(int ExitCode);
- void SetErrorCode(int Code);
- int GetErrorCode() {return(ExitCode);}
- int GetErrorCount() {return(ErrCount);}
+ void OpenErrorMsg(const wchar *FileName);
+ void OpenErrorMsg(const wchar *ArcName,const wchar *FileName);
+ void CreateErrorMsg(const wchar *FileName);
+ void CreateErrorMsg(const wchar *ArcName,const wchar *FileName);
+ void ReadErrorMsg(const wchar *FileName);
+ void ReadErrorMsg(const wchar *ArcName,const wchar *FileName);
+ void WriteErrorMsg(const wchar *ArcName,const wchar *FileName);
+ void ArcBrokenMsg(const wchar *ArcName);
+ void ChecksumFailedMsg(const wchar *ArcName,const wchar *FileName);
+ void UnknownMethodMsg(const wchar *ArcName,const wchar *FileName);
+ void Exit(RAR_EXIT ExitCode);
+ void SetErrorCode(RAR_EXIT Code);
+ RAR_EXIT GetErrorCode() {return ExitCode;}
+ uint GetErrorCount() {return ErrCount;}
void SetSignalHandlers(bool Enable);
- void Throw(int Code);
- void SetSilent(bool Mode) {Silent=Mode;};
- void SetShutdown(bool Mode) {DoShutdown=Mode;};
+ void Throw(RAR_EXIT Code);
+ void SetSilent(bool Mode) {Silent=Mode;}
+ bool GetSysErrMsg(wchar *Msg,size_t Size);
void SysErrMsg();
+ int GetSystemErrorCode();
+ void SetSystemErrorCode(int Code);
+ void SetDisableShutdown() {DisableShutdown=true;}
+ bool IsShutdownEnabled() {return !DisableShutdown;}
+
+ bool UserBreak; // Ctrl+Break is pressed.
+ bool MainExit; // main() is completed.
};
+
#endif
diff --git a/libunrar/extinfo.cpp b/libunrar/extinfo.cpp
index 13239fe..5cb90a4 100644
--- a/libunrar/extinfo.cpp
+++ b/libunrar/extinfo.cpp
@@ -1,51 +1,43 @@
#include "rar.hpp"
-#ifdef _WIN_32
-#include "win32acl.cpp"
+#include "hardlinks.cpp"
#include "win32stm.cpp"
+
+#ifdef _WIN_ALL
+#include "win32acl.cpp"
+#include "win32lnk.cpp"
#endif
-#ifdef _BEOS
-#include "beosea.cpp"
-#endif
-#if defined(_EMX) && !defined(_DJGPP)
-#include "os2ea.cpp"
-#endif
+
#ifdef _UNIX
#include "uowners.cpp"
+#ifdef SAVE_LINKS
+#include "ulinks.cpp"
+#endif
#endif
+// RAR2 service header extra records.
#ifndef SFX_MODULE
-void SetExtraInfo(CommandData *Cmd,Archive &Arc,char *Name,wchar *NameW)
+void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name)
{
+ if (Cmd->Test)
+ return;
switch(Arc.SubBlockHead.SubType)
{
-#if defined(_EMX) && !defined(_DJGPP)
- case EA_HEAD:
- if (Cmd->ProcessEA)
- ExtractOS2EA(Arc,Name);
- break;
-#endif
#ifdef _UNIX
case UO_HEAD:
if (Cmd->ProcessOwners)
- ExtractUnixOwner(Arc,Name);
+ ExtractUnixOwner20(Arc,Name);
break;
#endif
-#ifdef _BEOS
- case BEEA_HEAD:
- if (Cmd->ProcessEA)
- ExtractBeEA(Arc,Name);
- break;
-#endif
-#ifdef _WIN_32
+#ifdef _WIN_ALL
case NTACL_HEAD:
if (Cmd->ProcessOwners)
- ExtractACL(Arc,Name,NameW);
+ ExtractACL20(Arc,Name);
break;
case STREAM_HEAD:
- ExtractStreams(Arc,Name,NameW);
+ ExtractStreams20(Arc,Name);
break;
#endif
}
@@ -53,24 +45,134 @@ void SetExtraInfo(CommandData *Cmd,Archive &Arc,char *Name,wchar *NameW)
#endif
-void SetExtraInfoNew(CommandData *Cmd,Archive &Arc,char *Name,wchar *NameW)
+// RAR3 and RAR5 service header extra records.
+void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name)
{
-#if defined(_EMX) && !defined(_DJGPP)
- if (Cmd->ProcessEA && Arc.SubHead.CmpName(SUBHEAD_TYPE_OS2EA))
- ExtractOS2EANew(Arc,Name);
-#endif
#ifdef _UNIX
- if (Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER))
- ExtractUnixOwnerNew(Arc,Name);
+ if (!Cmd->Test && Cmd->ProcessOwners && Arc.Format==RARFMT15 &&
+ Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER))
+ ExtractUnixOwner30(Arc,Name);
#endif
-#ifdef _BEOS
- if (Cmd->ProcessEA && Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER))
- ExtractUnixOwnerNew(Arc,Name);
-#endif
-#ifdef _WIN_32
- if (Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_ACL))
- ExtractACLNew(Arc,Name,NameW);
+#ifdef _WIN_ALL
+ if (!Cmd->Test && Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_ACL))
+ ExtractACL(Arc,Name);
if (Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM))
- ExtractStreamsNew(Arc,Name,NameW);
+ ExtractStreams(Arc,Name,Cmd->Test);
#endif
}
+
+
+// Extra data stored directly in file header.
+void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name)
+{
+#ifdef _UNIX
+ if (Cmd->ProcessOwners && Arc.Format==RARFMT50 && Arc.FileHead.UnixOwnerSet)
+ SetUnixOwner(Arc,Name);
+#endif
+}
+
+
+
+
+// Calculate a number of path components except \. and \..
+static int CalcAllowedDepth(const wchar *Name)
+{
+ int AllowedDepth=0;
+ while (*Name!=0)
+ {
+ if (IsPathDiv(Name[0]) && Name[1]!=0 && !IsPathDiv(Name[1]))
+ {
+ bool Dot=Name[1]=='.' && (IsPathDiv(Name[2]) || Name[2]==0);
+ bool Dot2=Name[1]=='.' && Name[2]=='.' && (IsPathDiv(Name[3]) || Name[3]==0);
+ if (!Dot && !Dot2)
+ AllowedDepth++;
+ }
+ Name++;
+ }
+ return AllowedDepth;
+}
+
+
+// Check if all existing path components are directories and not links.
+static bool LinkInPath(const wchar *Name)
+{
+ wchar Path[NM];
+ if (wcslen(Name)>=ASIZE(Path))
+ return true; // It should not be that long, skip.
+ wcsncpyz(Path,Name,ASIZE(Path));
+ for (wchar *s=Path+wcslen(Path)-1;s>Path;s--)
+ if (IsPathDiv(*s))
+ {
+ *s=0;
+ FindData FD;
+ if (FindFile::FastFind(Path,&FD,true) && (FD.IsLink || !FD.IsDir))
+ return true;
+ }
+ return false;
+}
+
+
+bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName)
+{
+ // Catch root dir based /path/file paths also as stuff like \\?\.
+ // Do not check PrepSrcName here, it can be root based if destination path
+ // is a root based.
+ if (IsFullRootPath(SrcName) || IsFullRootPath(TargetName))
+ return false;
+
+ // Number of ".." in link target.
+ int UpLevels=0;
+ for (int Pos=0;*TargetName!=0;Pos++)
+ {
+ bool Dot2=TargetName[0]=='.' && TargetName[1]=='.' &&
+ (IsPathDiv(TargetName[2]) || TargetName[2]==0) &&
+ (Pos==0 || IsPathDiv(*(TargetName-1)));
+ if (Dot2)
+ UpLevels++;
+ TargetName++;
+ }
+ // If link target includes "..", it must not have another links
+ // in the path, because they can bypass our safety check. For example,
+ // suppose we extracted "lnk1" -> "." first and "lnk1/lnk2" -> ".." next
+ // or "dir/lnk1" -> ".." first and "dir/lnk1/lnk2" -> ".." next.
+ if (UpLevels>0 && LinkInPath(PrepSrcName))
+ return false;
+
+ // We could check just prepared src name, but for extra safety
+ // we check both original (as from archive header) and prepared
+ // (after applying the destination path and -ep switches) names.
+
+ int AllowedDepth=CalcAllowedDepth(SrcName); // Original name depth.
+
+ // Remove the destination path from prepared name if any. We should not
+ // count the destination path depth, because the link target must point
+ // inside of this path, not outside of it.
+ size_t ExtrPathLength=wcslen(Cmd->ExtrPath);
+ if (ExtrPathLength>0 && wcsncmp(PrepSrcName,Cmd->ExtrPath,ExtrPathLength)==0)
+ {
+ PrepSrcName+=ExtrPathLength;
+ while (IsPathDiv(*PrepSrcName))
+ PrepSrcName++;
+ }
+ int PrepAllowedDepth=CalcAllowedDepth(PrepSrcName);
+
+ return AllowedDepth>=UpLevels && PrepAllowedDepth>=UpLevels;
+}
+
+
+bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName)
+{
+#if defined(SAVE_LINKS) && defined(_UNIX)
+ // For RAR 3.x archives we process links even in test mode to skip link data.
+ if (Arc.Format==RARFMT15)
+ return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName);
+ if (Arc.Format==RARFMT50)
+ return ExtractUnixLink50(Cmd,LinkName,&Arc.FileHead);
+#elif defined _WIN_ALL
+ // RAR 5.0 archives store link information in file header, so there is
+ // no need to additionally test it if we do not create a file.
+ if (Arc.Format==RARFMT50)
+ return CreateReparsePoint(Cmd,LinkName,&Arc.FileHead);
+#endif
+ return false;
+}
diff --git a/libunrar/extinfo.hpp b/libunrar/extinfo.hpp
index db7cea5..2b0005d 100644
--- a/libunrar/extinfo.hpp
+++ b/libunrar/extinfo.hpp
@@ -1,8 +1,23 @@
#ifndef _RAR_EXTINFO_
#define _RAR_EXTINFO_
+bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName);
+bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName);
+#ifdef _UNIX
+void SetUnixOwner(Archive &Arc,const wchar *FileName);
+#endif
+
+bool ExtractHardlink(wchar *NameNew,wchar *NameExisting,size_t NameExistingSize);
+
+void GetStreamNameNTFS(Archive &Arc,wchar *StreamName,size_t MaxSize);
+
+#ifdef _WIN_ALL
+bool SetPrivilege(LPCTSTR PrivName);
+#endif
+
+void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name);
+void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name);
+void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name);
-void SetExtraInfo(CommandData *Cmd,Archive &Arc,char *Name,wchar *NameW);
-void SetExtraInfoNew(CommandData *Cmd,Archive &Arc,char *Name,wchar *NameW);
#endif
diff --git a/libunrar/extract.cpp b/libunrar/extract.cpp
index 992c87d..76ee2d4 100644
--- a/libunrar/extract.cpp
+++ b/libunrar/extract.cpp
@@ -1,75 +1,85 @@
#include "rar.hpp"
-CmdExtract::CmdExtract()
+CmdExtract::CmdExtract(CommandData *Cmd)
{
+ CmdExtract::Cmd=Cmd;
+
+ *ArcName=0;
+
+ *DestFileName=0;
+
TotalFileCount=0;
- *Password=0;
Unp=new Unpack(&DataIO);
- Unp->Init(NULL);
+#ifdef RAR_SMP
+ Unp->SetThreads(Cmd->Threads);
+#endif
}
CmdExtract::~CmdExtract()
{
delete Unp;
- memset(Password,0,sizeof(Password));
}
-void CmdExtract::DoExtract(CommandData *Cmd)
+void CmdExtract::DoExtract()
{
+#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
+ Fat32=NotFat32=false;
+#endif
PasswordCancelled=false;
- DataIO.SetCurrentCommand(*Cmd->Command);
+ DataIO.SetCurrentCommand(Cmd->Command[0]);
- struct FindData FD;
- while (Cmd->GetArcName(ArcName,ArcNameW,sizeof(ArcName)))
- if (FindFile::FastFind(ArcName,ArcNameW,&FD))
+ FindData FD;
+ while (Cmd->GetArcName(ArcName,ASIZE(ArcName)))
+ if (FindFile::FastFind(ArcName,&FD))
DataIO.TotalArcSize+=FD.Size;
- Cmd->ArcNames->Rewind();
- while (Cmd->GetArcName(ArcName,ArcNameW,sizeof(ArcName)))
+ Cmd->ArcNames.Rewind();
+ while (Cmd->GetArcName(ArcName,ASIZE(ArcName)))
{
+ if (Cmd->ManualPassword)
+ Cmd->Password.Clean(); // Clean user entered password before processing next archive.
+
+ ReconstructDone=false; // Must be reset here, not in ExtractArchiveInit().
while (true)
{
- char PrevCmdPassword[MAXPASSWORD];
- strcpy(PrevCmdPassword,Cmd->Password);
-
- EXTRACT_ARC_CODE Code=ExtractArchive(Cmd);
-
- // Restore Cmd->Password, which could be changed in IsArchive() call
- // for next header encrypted archive.
- strcpy(Cmd->Password,PrevCmdPassword);
-
+ EXTRACT_ARC_CODE Code=ExtractArchive();
if (Code!=EXTRACT_ARC_REPEAT)
break;
}
- if (FindFile::FastFind(ArcName,ArcNameW,&FD))
+ if (FindFile::FastFind(ArcName,&FD))
DataIO.ProcessedArcSize+=FD.Size;
}
- if (TotalFileCount==0 && *Cmd->Command!='I')
+ // Clean user entered password. Not really required, just for extra safety.
+ if (Cmd->ManualPassword)
+ Cmd->Password.Clean();
+
+ if (TotalFileCount==0 && Cmd->Command[0]!='I' &&
+ ErrHandler.GetErrorCode()!=RARX_BADPWD) // Not in case of wrong archive password.
{
if (!PasswordCancelled)
- {
- mprintf(St(MExtrNoFiles));
- }
- ErrHandler.SetErrorCode(WARNING);
+ uiMsg(UIERROR_NOFILESTOEXTRACT,ArcName);
+
+ // Other error codes may explain a reason of "no files extracted" clearer,
+ // so set it only if no other errors found (wrong mask set by user).
+ if (ErrHandler.GetErrorCode()==RARX_SUCCESS)
+ ErrHandler.SetErrorCode(RARX_NOFILES);
}
-#ifndef GUI
else
if (!Cmd->DisableDone)
- if (*Cmd->Command=='I')
+ if (Cmd->Command[0]=='I')
mprintf(St(MDone));
else
if (ErrHandler.GetErrorCount()==0)
mprintf(St(MExtrAllOk));
else
mprintf(St(MExtrTotalErr),ErrHandler.GetErrorCount());
-#endif
}
-void CmdExtract::ExtractArchiveInit(CommandData *Cmd,Archive &Arc)
+void CmdExtract::ExtractArchiveInit(Archive &Arc)
{
DataIO.UnpArcSize=Arc.FileLength();
@@ -79,56 +89,68 @@ void CmdExtract::ExtractArchiveInit(CommandData *Cmd,Archive &Arc)
FirstFile=true;
#endif
- if (*Cmd->Password!=0)
- strcpy(Password,Cmd->Password);
- PasswordAll=(*Cmd->Password!=0);
+ GlobalPassword=Cmd->Password.IsSet() || uiIsGlobalPasswordSet();
DataIO.UnpVolume=false;
- PrevExtracted=false;
- SignatureFound=false;
+ PrevProcessed=false;
AllMatchesExact=true;
- ReconstructDone=false;
+ AnySolidDataUnpackedWell=false;
StartTime.SetCurrentTime();
}
-EXTRACT_ARC_CODE CmdExtract::ExtractArchive(CommandData *Cmd)
+EXTRACT_ARC_CODE CmdExtract::ExtractArchive()
{
Archive Arc(Cmd);
- if (!Arc.WOpen(ArcName,ArcNameW))
- {
- ErrHandler.SetErrorCode(OPEN_ERROR);
- return(EXTRACT_ARC_NEXT);
- }
+ if (!Arc.WOpen(ArcName))
+ return EXTRACT_ARC_NEXT;
if (!Arc.IsArchive(true))
{
-#ifndef GUI
- mprintf(St(MNotRAR),ArcName);
-#endif
- if (CmpExt(ArcName,"rar"))
- ErrHandler.SetErrorCode(WARNING);
- return(EXTRACT_ARC_NEXT);
- }
+#if !defined(SFX_MODULE) && !defined(RARDLL)
+ if (CmpExt(ArcName,L"rev"))
+ {
+ wchar FirstVolName[NM];
+ VolNameToFirstName(ArcName,FirstVolName,ASIZE(FirstVolName),true);
- // archive with corrupt encrypted header can be closed in IsArchive() call
- if (!Arc.IsOpened())
- return(EXTRACT_ARC_NEXT);
+ // If several volume names from same volume set are specified
+ // and current volume is not first in set and first volume is present
+ // and specified too, let's skip the current volume.
+ if (wcsicomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) &&
+ Cmd->ArcNames.Search(FirstVolName,false))
+ return EXTRACT_ARC_NEXT;
+ RecVolumesTest(Cmd,NULL,ArcName);
+ TotalFileCount++; // Suppress "No files to extract" message.
+ return EXTRACT_ARC_NEXT;
+ }
+#endif
+
+ mprintf(St(MNotRAR),ArcName);
#ifndef SFX_MODULE
- if (Arc.Volume && Arc.NotFirstVolume)
+ if (CmpExt(ArcName,L"rar"))
+#endif
+ ErrHandler.SetErrorCode(RARX_WARNING);
+ return EXTRACT_ARC_NEXT;
+ }
+
+ if (Arc.FailedHeaderDecryption) // Bad archive password.
+ return EXTRACT_ARC_NEXT;
+
+#ifndef SFX_MODULE
+ if (Arc.Volume && !Arc.FirstVolume)
{
- char FirstVolName[NM];
- VolNameToFirstName(ArcName,FirstVolName,(Arc.NewMhd.Flags & MHD_NEWNUMBERING)!=0);
+ wchar FirstVolName[NM];
+ VolNameToFirstName(ArcName,FirstVolName,ASIZE(FirstVolName),Arc.NewNumbering);
// If several volume names from same volume set are specified
// and current volume is not first in set and first volume is present
// and specified too, let's skip the current volume.
- if (stricomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) &&
- Cmd->ArcNames->Search(FirstVolName,NULL,false))
- return(EXTRACT_ARC_NEXT);
+ if (wcsicomp(ArcName,FirstVolName)!=0 && FileExist(FirstVolName) &&
+ Cmd->ArcNames.Search(FirstVolName,false))
+ return EXTRACT_ARC_NEXT;
}
#endif
@@ -139,20 +161,17 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive(CommandData *Cmd)
// Calculate the total size of all accessible volumes.
// This size is necessary to display the correct total progress indicator.
- char NextName[NM];
- wchar NextNameW[NM];
-
- strcpy(NextName,Arc.FileName);
- strcpyw(NextNameW,Arc.FileNameW);
+ wchar NextName[NM];
+ wcsncpyz(NextName,Arc.FileName,ASIZE(NextName));
while (true)
{
// First volume is already added to DataIO.TotalArcSize
// in initial TotalArcSize calculation in DoExtract.
// So we skip it and start from second volume.
- NextVolumeName(NextName,NextNameW,ASIZE(NextName),(Arc.NewMhd.Flags & MHD_NEWNUMBERING)==0 || Arc.OldFormat);
- struct FindData FD;
- if (FindFile::FastFind(NextName,NextNameW,&FD))
+ NextVolumeName(NextName,ASIZE(NextName),!Arc.NewNumbering);
+ FindData FD;
+ if (FindFile::FastFind(NextName,&FD))
VolumeSetSize+=FD.Size;
else
break;
@@ -160,33 +179,29 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive(CommandData *Cmd)
DataIO.TotalArcSize+=VolumeSetSize;
}
- ExtractArchiveInit(Cmd,Arc);
+ ExtractArchiveInit(Arc);
if (*Cmd->Command=='T' || *Cmd->Command=='I')
Cmd->Test=true;
-#ifndef GUI
+
if (*Cmd->Command=='I')
+ {
Cmd->DisablePercentage=true;
+ }
else
- if (Cmd->Test)
- mprintf(St(MExtrTest),ArcName);
- else
- mprintf(St(MExtracting),ArcName);
-#endif
+ uiStartArchiveExtract(!Cmd->Test,ArcName);
Arc.ViewComment();
- // RAR can close a corrupt encrypted archive
- if (!Arc.IsOpened())
- return(EXTRACT_ARC_NEXT);
-
while (1)
{
size_t Size=Arc.ReadHeader();
+
+
bool Repeat=false;
- if (!ExtractCurrentFile(Cmd,Arc,Size,Repeat))
+ if (!ExtractCurrentFile(Arc,Size,Repeat))
if (Repeat)
{
// If we started extraction from not first volume and need to
@@ -194,373 +209,208 @@ EXTRACT_ARC_CODE CmdExtract::ExtractArchive(CommandData *Cmd)
// for correct total progress display. We subtract the size
// of current volume and all volumes after it and add the size
// of new (first) volume.
- struct FindData OldArc,NewArc;
- if (FindFile::FastFind(Arc.FileName,Arc.FileNameW,&OldArc) &&
- FindFile::FastFind(ArcName,ArcNameW,&NewArc))
+ FindData OldArc,NewArc;
+ if (FindFile::FastFind(Arc.FileName,&OldArc) &&
+ FindFile::FastFind(ArcName,&NewArc))
DataIO.TotalArcSize-=VolumeSetSize+OldArc.Size-NewArc.Size;
- return(EXTRACT_ARC_REPEAT);
+ return EXTRACT_ARC_REPEAT;
}
else
break;
}
- return(EXTRACT_ARC_NEXT);
+
+#if !defined(SFX_MODULE) && !defined(RARDLL)
+ if (Cmd->Test && Arc.Volume)
+ RecVolumesTest(Cmd,&Arc,ArcName);
+#endif
+
+ return EXTRACT_ARC_NEXT;
}
-bool CmdExtract::ExtractCurrentFile(CommandData *Cmd,Archive &Arc,size_t HeaderSize,bool &Repeat)
+bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
{
- char Command=*Cmd->Command;
+ wchar Command=Cmd->Command[0];
if (HeaderSize==0)
if (DataIO.UnpVolume)
{
#ifdef NOVOLUME
- return(false);
+ return false;
#else
+ // Supposing we unpack an old RAR volume without the end of archive
+ // record and last file is not split between volumes.
if (!MergeArchive(Arc,&DataIO,false,Command))
{
- ErrHandler.SetErrorCode(WARNING);
- return(false);
+ ErrHandler.SetErrorCode(RARX_WARNING);
+ return false;
}
- SignatureFound=false;
#endif
}
else
- return(false);
- int HeadType=Arc.GetHeaderType();
- if (HeadType!=FILE_HEAD)
+ return false;
+
+ HEADER_TYPE HeaderType=Arc.GetHeaderType();
+ if (HeaderType!=HEAD_FILE)
{
- if (HeadType==AV_HEAD || HeadType==SIGN_HEAD)
- SignatureFound=true;
-#if !defined(SFX_MODULE) && !defined(_WIN_CE)
- if (HeadType==SUB_HEAD && PrevExtracted)
- SetExtraInfo(Cmd,Arc,DestFileName,*DestFileNameW ? DestFileNameW:NULL);
+#ifndef SFX_MODULE
+ if (Arc.Format==RARFMT15 && HeaderType==HEAD3_OLDSERVICE && PrevProcessed)
+ SetExtraInfo20(Cmd,Arc,DestFileName);
#endif
- if (HeadType==NEWSUB_HEAD)
- {
- if (Arc.SubHead.CmpName(SUBHEAD_TYPE_AV))
- SignatureFound=true;
-#if !defined(NOSUBBLOCKS) && !defined(_WIN_CE)
- if (PrevExtracted)
- SetExtraInfoNew(Cmd,Arc,DestFileName,*DestFileNameW ? DestFileNameW:NULL);
-#endif
- }
- if (HeadType==ENDARC_HEAD)
- if (Arc.EndArcHead.Flags & EARC_NEXT_VOLUME)
+ if (HeaderType==HEAD_SERVICE && PrevProcessed)
+ SetExtraInfo(Cmd,Arc,DestFileName);
+ if (HeaderType==HEAD_ENDARC)
+ if (Arc.EndArcHead.NextVolume)
{
-#ifndef NOVOLUME
+#ifdef NOVOLUME
+ return false;
+#else
if (!MergeArchive(Arc,&DataIO,false,Command))
{
- ErrHandler.SetErrorCode(WARNING);
- return(false);
+ ErrHandler.SetErrorCode(RARX_WARNING);
+ return false;
}
- SignatureFound=false;
-#endif
Arc.Seek(Arc.CurBlockPos,SEEK_SET);
- return(true);
+ return true;
+#endif
}
else
- return(false);
+ return false;
Arc.SeekToNext();
- return(true);
+ return true;
}
- PrevExtracted=false;
+ PrevProcessed=false;
- if (SignatureFound ||
- !Cmd->Recurse && MatchedArgs>=Cmd->FileArgs->ItemsCount() &&
- AllMatchesExact)
- return(false);
+ // We can get negative sizes in corrupt archive and it is unacceptable
+ // for size comparisons in ComprDataIO::UnpRead, where we cast sizes
+ // to size_t and can exceed another read or available size. We could fix it
+ // when reading an archive. But we prefer to do it here, because this
+ // function is called directly in unrar.dll, so we fix bad parameters
+ // passed to dll. Also we want to see real negative sizes in the listing
+ // of corrupt archive. To prevent uninitialized data access perform
+ // these checks after rejecting zero length and non-file headers above.
+ if (Arc.FileHead.PackSize<0)
+ Arc.FileHead.PackSize=0;
+ if (Arc.FileHead.UnpSize<0)
+ Arc.FileHead.UnpSize=0;
- char ArcFileName[NM];
- IntToExt(Arc.NewLhd.FileName,Arc.NewLhd.FileName);
- strcpy(ArcFileName,Arc.NewLhd.FileName);
-
- wchar ArcFileNameW[NM];
- *ArcFileNameW=0;
+ if (!Cmd->Recurse && MatchedArgs>=Cmd->FileArgs.ItemsCount() && AllMatchesExact)
+ return false;
int MatchType=MATCH_WILDSUBPATH;
bool EqualNames=false;
- int MatchNumber=Cmd->IsProcessFile(Arc.NewLhd,&EqualNames,MatchType);
- bool ExactMatch=MatchNumber!=0;
-#if !defined(SFX_MODULE) && !defined(_WIN_CE)
+ wchar MatchedArg[NM];
+ int MatchNumber=Cmd->IsProcessFile(Arc.FileHead,&EqualNames,MatchType,0,MatchedArg,ASIZE(MatchedArg));
+ bool MatchFound=MatchNumber!=0;
+#ifndef SFX_MODULE
if (Cmd->ExclPath==EXCL_BASEPATH)
{
- *Cmd->ArcPath=0;
- if (ExactMatch)
- {
- Cmd->FileArgs->Rewind();
- if (Cmd->FileArgs->GetString(Cmd->ArcPath,NULL,sizeof(Cmd->ArcPath),MatchNumber-1))
- *PointToName(Cmd->ArcPath)=0;
- }
+ wcsncpyz(Cmd->ArcPath,MatchedArg,ASIZE(Cmd->ArcPath));
+ *PointToName(Cmd->ArcPath)=0;
+ if (IsWildcard(Cmd->ArcPath)) // Cannot correctly process path*\* masks here.
+ *Cmd->ArcPath=0;
}
#endif
- if (ExactMatch && !EqualNames)
+ if (MatchFound && !EqualNames)
AllMatchesExact=false;
-#ifdef UNICODE_SUPPORTED
- bool WideName=(Arc.NewLhd.Flags & LHD_UNICODE) && UnicodeEnabled();
-#else
- bool WideName=false;
-#endif
+ Arc.ConvertAttributes();
-#ifdef _APPLE
- if (WideName)
+#if !defined(SFX_MODULE) && !defined(RARDLL)
+ if (Arc.FileHead.SplitBefore && FirstFile)
{
- WideToUtf(Arc.NewLhd.FileNameW,ArcFileName,sizeof(ArcFileName));
- WideName=false;
+ wchar CurVolName[NM];
+ wcsncpyz(CurVolName,ArcName,ASIZE(CurVolName));
+ VolNameToFirstName(ArcName,ArcName,ASIZE(ArcName),Arc.NewNumbering);
+
+ if (wcsicomp(ArcName,CurVolName)!=0 && FileExist(ArcName))
+ {
+ wcsncpyz(Cmd->ArcName,ArcName,ASIZE(ArcName)); // For GUI "Delete archive after extraction".
+ // If first volume name does not match the current name and if such
+ // volume name really exists, let's unpack from this first volume.
+ Repeat=true;
+ return false;
+ }
+#ifndef RARDLL
+ if (!ReconstructDone)
+ {
+ ReconstructDone=true;
+ if (RecVolumesRestore(Cmd,Arc.FileName,true))
+ {
+ Repeat=true;
+ return false;
+ }
+ }
+#endif
+ wcsncpyz(ArcName,CurVolName,ASIZE(ArcName));
}
#endif
- wchar *DestNameW=WideName ? DestFileNameW:NULL;
+ wchar ArcFileName[NM];
+ ConvertPath(Arc.FileHead.FileName,ArcFileName,ASIZE(ArcFileName));
-#ifdef UNICODE_SUPPORTED
- if (WideName)
- {
- ConvertPath(Arc.NewLhd.FileNameW,ArcFileNameW);
- char Name[NM];
- if (WideToChar(ArcFileNameW,Name) && IsNameUsable(Name))
- strcpy(ArcFileName,Name);
- }
-#endif
-
- ConvertPath(ArcFileName,ArcFileName);
-
- if (Arc.IsArcLabel())
- return(true);
-
- if (Arc.NewLhd.Flags & LHD_VERSION)
+ if (Arc.FileHead.Version)
{
if (Cmd->VersionControl!=1 && !EqualNames)
{
if (Cmd->VersionControl==0)
- ExactMatch=false;
- int Version=ParseVersionFileName(ArcFileName,ArcFileNameW,false);
+ MatchFound=false;
+ int Version=ParseVersionFileName(ArcFileName,false);
if (Cmd->VersionControl-1==Version)
- ParseVersionFileName(ArcFileName,ArcFileNameW,true);
+ ParseVersionFileName(ArcFileName,true);
else
- ExactMatch=false;
+ MatchFound=false;
}
}
else
if (!Arc.IsArcDir() && Cmd->VersionControl>1)
- ExactMatch=false;
+ MatchFound=false;
- Arc.ConvertAttributes();
-
-#ifndef SFX_MODULE
- if ((Arc.NewLhd.Flags & (LHD_SPLIT_BEFORE/*|LHD_SOLID*/)) && FirstFile)
- {
- char CurVolName[NM];
- strcpy(CurVolName,ArcName);
-
- VolNameToFirstName(ArcName,ArcName,(Arc.NewMhd.Flags & MHD_NEWNUMBERING)!=0);
- if (stricomp(ArcName,CurVolName)!=0 && FileExist(ArcName))
- {
- *ArcNameW=0;
- Repeat=true;
- return(false);
- }
-#if !defined(RARDLL) && !defined(_WIN_CE)
- if (!ReconstructDone)
- {
- ReconstructDone=true;
-
- RecVolumes RecVol;
- if (RecVol.Restore(Cmd,Arc.FileName,Arc.FileNameW,true))
- {
- Repeat=true;
- return(false);
- }
- }
-#endif
- strcpy(ArcName,CurVolName);
- }
-#endif
- DataIO.UnpVolume=(Arc.NewLhd.Flags & LHD_SPLIT_AFTER)!=0;
+ DataIO.UnpVolume=Arc.FileHead.SplitAfter;
DataIO.NextVolumeMissing=false;
- Arc.Seek(Arc.NextBlockPos-Arc.NewLhd.FullPackSize,SEEK_SET);
+ Arc.Seek(Arc.NextBlockPos-Arc.FileHead.PackSize,SEEK_SET);
- bool TestMode=false;
bool ExtrFile=false;
bool SkipSolid=false;
#ifndef SFX_MODULE
- if (FirstFile && (ExactMatch || Arc.Solid) && (Arc.NewLhd.Flags & (LHD_SPLIT_BEFORE/*|LHD_SOLID*/))!=0)
+ if (FirstFile && (MatchFound || Arc.Solid) && Arc.FileHead.SplitBefore)
{
- if (ExactMatch)
+ if (MatchFound)
{
- Log(Arc.FileName,St(MUnpCannotMerge),ArcFileName);
+ uiMsg(UIERROR_NEEDPREVVOL,Arc.FileName,ArcFileName);
#ifdef RARDLL
Cmd->DllError=ERAR_BAD_DATA;
#endif
- ErrHandler.SetErrorCode(OPEN_ERROR);
+ ErrHandler.SetErrorCode(RARX_OPEN);
}
- ExactMatch=false;
+ MatchFound=false;
}
FirstFile=false;
#endif
- if (ExactMatch || (SkipSolid=Arc.Solid)!=0)
+ if (MatchFound || (SkipSolid=Arc.Solid)!=0)
{
- if ((Arc.NewLhd.Flags & LHD_PASSWORD)!=0)
-#ifndef RARDLL
- if (*Password==0)
-#endif
- {
-#ifdef RARDLL
- if (*Cmd->Password==0)
- if (Cmd->Callback==NULL ||
- Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)Cmd->Password,sizeof(Cmd->Password))==-1)
- return(false);
- strcpy(Password,Cmd->Password);
+ // First common call of uiStartFileExtract. It is done before overwrite
+ // prompts, so if SkipSolid state is changed below, we'll need to make
+ // additional uiStartFileExtract calls with updated parameters.
+ if (!uiStartFileExtract(ArcFileName,!Cmd->Test,Cmd->Test && Command!='I',SkipSolid))
+ return false;
-#else
- if (!GetPassword(PASSWORD_FILE,ArcFileName,Password,sizeof(Password)))
- {
- PasswordCancelled=true;
- return(false);
- }
-#endif
- }
-#if !defined(GUI) && !defined(SILENT)
- else
- if (!PasswordAll && (!Arc.Solid || Arc.NewLhd.UnpVer>=20 && (Arc.NewLhd.Flags & LHD_SOLID)==0))
- {
- eprintf(St(MUseCurPsw),ArcFileName);
- switch(Cmd->AllYes ? 1:Ask(St(MYesNoAll)))
- {
- case -1:
- ErrHandler.Exit(USER_BREAK);
- case 2:
- if (!GetPassword(PASSWORD_FILE,ArcFileName,Password,sizeof(Password)))
- {
- return(false);
- }
- break;
- case 3:
- PasswordAll=true;
- break;
- }
- }
-#endif
+ ExtrPrepareName(Arc,ArcFileName,DestFileName,ASIZE(DestFileName));
-#ifndef SFX_MODULE
- if (*Cmd->ExtrPath==0 && *Cmd->ExtrPathW!=0)
- WideToChar(Cmd->ExtrPathW,DestFileName);
- else
-#endif
- strcpy(DestFileName,Cmd->ExtrPath);
-
-
-#ifndef SFX_MODULE
- if (Cmd->AppendArcNameToPath)
- {
- strcat(DestFileName,PointToName(Arc.FirstVolumeName));
- SetExt(DestFileName,NULL);
- AddEndSlash(DestFileName);
- }
-#endif
-
- char *ExtrName=ArcFileName;
-
- bool EmptyName=false;
-#ifndef SFX_MODULE
- size_t Length=strlen(Cmd->ArcPath);
- if (Length>1 && IsPathDiv(Cmd->ArcPath[Length-1]) &&
- strlen(ArcFileName)==Length-1)
- Length--;
- if (Length>0 && strnicomp(Cmd->ArcPath,ArcFileName,Length)==0)
- {
- ExtrName+=Length;
- while (*ExtrName==CPATHDIVIDER)
- ExtrName++;
- if (*ExtrName==0)
- EmptyName=true;
- }
-#endif
-
- bool AbsPaths=Cmd->ExclPath==EXCL_ABSPATH && Command=='X' && IsDriveDiv(':');
- if (AbsPaths)
- *DestFileName=0;
-
- if (Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH)
- strcat(DestFileName,PointToName(ExtrName));
- else
- strcat(DestFileName,ExtrName);
-
- char DiskLetter=etoupper(DestFileName[0]);
-
- if (AbsPaths && DestFileName[1]=='_' && IsPathDiv(DestFileName[2]) &&
- DiskLetter>='A' && DiskLetter<='Z')
- DestFileName[1]=':';
-
-#ifndef SFX_MODULE
- if (!WideName && *Cmd->ExtrPathW!=0)
- {
- DestNameW=DestFileNameW;
- WideName=true;
- CharToWide(ArcFileName,ArcFileNameW);
- }
-#endif
-
- if (WideName)
- {
- if (*Cmd->ExtrPathW!=0)
- strcpyw(DestFileNameW,Cmd->ExtrPathW);
- else
- CharToWide(Cmd->ExtrPath,DestFileNameW);
-
-#ifndef SFX_MODULE
- if (Cmd->AppendArcNameToPath)
- {
- wchar FileNameW[NM];
- if (*Arc.FirstVolumeNameW!=0)
- strcpyw(FileNameW,Arc.FirstVolumeNameW);
- else
- CharToWide(Arc.FirstVolumeName,FileNameW);
- strcatw(DestFileNameW,PointToName(FileNameW));
- SetExt(DestFileNameW,NULL);
- AddEndSlash(DestFileNameW);
- }
-#endif
- wchar *ExtrNameW=ArcFileNameW;
-#ifndef SFX_MODULE
- if (Length>0)
- {
- wchar ArcPathW[NM];
- GetWideName(Cmd->ArcPath,Cmd->ArcPathW,ArcPathW);
- Length=strlenw(ArcPathW);
- }
- ExtrNameW+=Length;
- while (*ExtrNameW==CPATHDIVIDER)
- ExtrNameW++;
-#endif
-
- if (AbsPaths)
- *DestFileNameW=0;
-
- if (Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH)
- strcatw(DestFileNameW,PointToName(ExtrNameW));
- else
- strcatw(DestFileNameW,ExtrNameW);
-
- if (AbsPaths && DestFileNameW[1]=='_' && IsPathDiv(DestFileNameW[2]))
- DestFileNameW[1]=':';
- }
- else
- *DestFileNameW=0;
-
- ExtrFile=!SkipSolid && !EmptyName && (Arc.NewLhd.Flags & LHD_SPLIT_BEFORE)==0;
+ // DestFileName can be set empty in case of excessive -ap switch.
+ ExtrFile=!SkipSolid && *DestFileName!=0 && !Arc.FileHead.SplitBefore;
if ((Cmd->FreshFiles || Cmd->UpdateFiles) && (Command=='E' || Command=='X'))
{
- struct FindData FD;
- if (FindFile::FastFind(DestFileName,DestNameW,&FD))
+ FindData FD;
+ if (FindFile::FastFind(DestFileName,&FD))
{
- if (FD.mtime >= Arc.NewLhd.mtime)
+ if (FD.mtime >= Arc.FileHead.mtime)
{
// If directory already exists and its modification time is newer
// than start of extraction, it is likely it was created
@@ -577,197 +427,159 @@ bool CmdExtract::ExtractCurrentFile(CommandData *Cmd,Archive &Arc,size_t HeaderS
ExtrFile=false;
}
- // Skip encrypted file if no password is specified.
- if ((Arc.NewLhd.Flags & LHD_PASSWORD)!=0 && *Password==0)
+ if (!CheckUnpVer(Arc,ArcFileName))
{
- ErrHandler.SetErrorCode(WARNING);
-#ifdef RARDLL
- Cmd->DllError=ERAR_MISSING_PASSWORD;
-#endif
- ExtrFile=false;
- }
-
-#ifdef RARDLL
- if (*Cmd->DllDestName)
- {
- strncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName));
- *DestFileNameW=0;
- if (Cmd->DllOpMode!=RAR_EXTRACT)
- ExtrFile=false;
- }
- if (*Cmd->DllDestNameW)
- {
- strncpyzw(DestFileNameW,Cmd->DllDestNameW,ASIZE(DestFileNameW));
- DestNameW=DestFileNameW;
- if (Cmd->DllOpMode!=RAR_EXTRACT)
- ExtrFile=false;
- }
-#endif
-
-#ifdef SFX_MODULE
- if ((Arc.NewLhd.UnpVer!=UNP_VER && Arc.NewLhd.UnpVer!=29) &&
- Arc.NewLhd.Method!=0x30)
-#else
- if (Arc.NewLhd.UnpVer<13 || Arc.NewLhd.UnpVer>UNP_VER)
-#endif
- {
-#ifndef SILENT
- Log(Arc.FileName,St(MUnknownMeth),ArcFileName);
-#ifndef SFX_MODULE
- Log(Arc.FileName,St(MVerRequired),Arc.NewLhd.UnpVer/10,Arc.NewLhd.UnpVer%10);
-#endif
-#endif
- ExtrFile=false;
- ErrHandler.SetErrorCode(WARNING);
+ ErrHandler.SetErrorCode(RARX_FATAL);
#ifdef RARDLL
Cmd->DllError=ERAR_UNKNOWN_FORMAT;
#endif
+ Arc.SeekToNext();
+ return !Arc.Solid; // Can try extracting next file only in non-solid archive.
}
+ while (true) // Repeat the password prompt for wrong and empty passwords.
+ {
+ if (Arc.FileHead.Encrypted)
+ {
+ // Stop archive extracting if user cancelled a password prompt.
+#ifdef RARDLL
+ if (!ExtrDllGetPassword())
+ {
+ Cmd->DllError=ERAR_MISSING_PASSWORD;
+ return false;
+ }
+#else
+ if (!ExtrGetPassword(Arc,ArcFileName))
+ {
+ PasswordCancelled=true;
+ return false;
+ }
+#endif
+ }
+
+ // Set a password before creating the file, so we can skip creating
+ // in case of wrong password.
+ SecPassword FilePassword=Cmd->Password;
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+ ConvertDosPassword(Arc,FilePassword);
+#endif
+
+ byte PswCheck[SIZE_PSWCHECK];
+ DataIO.SetEncryption(false,Arc.FileHead.CryptMethod,&FilePassword,
+ Arc.FileHead.SaltSet ? Arc.FileHead.Salt:NULL,
+ Arc.FileHead.InitV,Arc.FileHead.Lg2Count,
+ Arc.FileHead.HashKey,PswCheck);
+
+ // If header is damaged, we cannot rely on password check value,
+ // because it can be damaged too.
+ if (Arc.FileHead.Encrypted && Arc.FileHead.UsePswCheck &&
+ memcmp(Arc.FileHead.PswCheck,PswCheck,SIZE_PSWCHECK)!=0 &&
+ !Arc.BrokenHeader)
+ {
+ if (GlobalPassword) // For -p or Ctrl+P to avoid the infinite loop.
+ {
+ // This message is used by Android GUI to reset cached passwords.
+ // Update appropriate code if changed.
+ uiMsg(UIERROR_BADPSW,Arc.FileName,ArcFileName);
+ }
+ else // For passwords entered manually.
+ {
+ // This message is used by Android GUI and Windows GUI and SFX to
+ // reset cached passwords. Update appropriate code if changed.
+ uiMsg(UIWAIT_BADPSW,Arc.FileName,ArcFileName);
+ Cmd->Password.Clean();
+
+ // Avoid new requests for unrar.dll to prevent the infinite loop
+ // if app always returns the same password.
+#ifndef RARDLL
+ continue; // Request a password again.
+#endif
+ }
+#ifdef RARDLL
+ // If we already have ERAR_EOPEN as result of missing volume,
+ // we should not replace it with less precise ERAR_BAD_PASSWORD.
+ if (Cmd->DllError!=ERAR_EOPEN)
+ Cmd->DllError=ERAR_BAD_PASSWORD;
+#endif
+ ErrHandler.SetErrorCode(RARX_BADPWD);
+ ExtrFile=false;
+ }
+ break;
+ }
+
+#ifdef RARDLL
+ if (*Cmd->DllDestName!=0)
+ wcsncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName));
+#endif
+
File CurFile;
- if (!IsLink(Arc.NewLhd.FileAttr))
+ bool LinkEntry=Arc.FileHead.RedirType!=FSREDIR_NONE;
+ if (LinkEntry && Arc.FileHead.RedirType!=FSREDIR_FILECOPY)
+ {
+ if (ExtrFile && Command!='P' && !Cmd->Test)
+ {
+ // Overwrite prompt for symbolic and hard links.
+ bool UserReject=false;
+ if (FileExist(DestFileName) && !UserReject)
+ FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime);
+ if (UserReject)
+ ExtrFile=false;
+ }
+ }
+ else
if (Arc.IsArcDir())
{
- if (!ExtrFile || Command=='P' || Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH)
- return(true);
- if (SkipSolid)
- {
-#ifndef GUI
- mprintf(St(MExtrSkipFile),ArcFileName);
-#endif
- return(true);
- }
+ if (!ExtrFile || Command=='P' || Command=='I' || Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH)
+ return true;
TotalFileCount++;
- if (Cmd->Test)
- {
-#ifndef GUI
- mprintf(St(MExtrTestFile),ArcFileName);
- mprintf(" %s",St(MOk));
-#endif
- return(true);
- }
- MKDIR_CODE MDCode=MakeDir(DestFileName,DestNameW,!Cmd->IgnoreGeneralAttr,Arc.NewLhd.FileAttr);
- bool DirExist=false;
- if (MDCode!=MKDIR_SUCCESS)
- {
- DirExist=FileExist(DestFileName,DestNameW);
- if (DirExist && !IsDir(GetFileAttr(DestFileName,DestNameW)))
- {
- bool UserReject;
- FileCreate(Cmd,NULL,DestFileName,DestNameW,Cmd->Overwrite,&UserReject,Arc.NewLhd.FullUnpSize,Arc.NewLhd.FileTime);
- DirExist=false;
- }
- CreatePath(DestFileName,DestNameW,true);
- MDCode=MakeDir(DestFileName,DestNameW,!Cmd->IgnoreGeneralAttr,Arc.NewLhd.FileAttr);
- }
- if (MDCode==MKDIR_SUCCESS)
- {
-#ifndef GUI
- mprintf(St(MCreatDir),DestFileName);
- mprintf(" %s",St(MOk));
-#endif
- PrevExtracted=true;
- }
- else
- if (DirExist)
- {
- if (!Cmd->IgnoreGeneralAttr)
- SetFileAttr(DestFileName,DestNameW,Arc.NewLhd.FileAttr);
- PrevExtracted=true;
- }
- else
- {
- Log(Arc.FileName,St(MExtrErrMkDir),DestFileName);
- ErrHandler.SysErrMsg();
-#ifdef RARDLL
- Cmd->DllError=ERAR_ECREATE;
-#endif
- ErrHandler.SetErrorCode(CREATE_ERROR);
- }
- if (PrevExtracted)
- {
-#if defined(_WIN_32) && !defined(_WIN_CE) && !defined(SFX_MODULE)
- if (Cmd->SetCompressedAttr &&
- (Arc.NewLhd.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0 && WinNT())
- SetFileCompression(DestFileName,DestNameW,true);
-#endif
- SetDirTime(DestFileName,DestNameW,
- Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.NewLhd.mtime,
- Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.NewLhd.ctime,
- Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.NewLhd.atime);
- }
- return(true);
+ ExtrCreateDir(Arc,ArcFileName);
+ // It is important to not increment MatchedArgs here, so we extract
+ // dir with its entire contents and not dir record only even if
+ // dir record precedes files.
+ return true;
}
else
- {
- if (Cmd->Test && ExtrFile)
- TestMode=true;
-#if !defined(GUI) && !defined(SFX_MODULE)
- if (Command=='P' && ExtrFile)
- CurFile.SetHandleType(FILE_HANDLESTD);
-#endif
- if ((Command=='E' || Command=='X') && ExtrFile && !Cmd->Test)
- {
- bool UserReject;
- if (!FileCreate(Cmd,&CurFile,DestFileName,DestNameW,Cmd->Overwrite,&UserReject,Arc.NewLhd.FullUnpSize,Arc.NewLhd.FileTime))
- {
- ExtrFile=false;
- if (!UserReject)
- {
- ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName);
- ErrHandler.SetErrorCode(CREATE_ERROR);
-#ifdef RARDLL
- Cmd->DllError=ERAR_ECREATE;
-#endif
- if (!IsNameUsable(DestFileName))
- {
- Log(Arc.FileName,St(MCorrectingName));
- char OrigName[sizeof(DestFileName)];
- strncpyz(OrigName,DestFileName,ASIZE(OrigName));
-
- MakeNameUsable(DestFileName,true);
- CreatePath(DestFileName,NULL,true);
- if (FileCreate(Cmd,&CurFile,DestFileName,NULL,Cmd->Overwrite,&UserReject,Arc.NewLhd.FullUnpSize,Arc.NewLhd.FileTime))
- {
-#ifndef SFX_MODULE
- Log(Arc.FileName,St(MRenaming),OrigName,DestFileName);
-#endif
- ExtrFile=true;
- }
- else
- ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName);
- }
- }
- }
- }
- }
+ if (ExtrFile) // Create files and file copies (FSREDIR_FILECOPY).
+ ExtrFile=ExtrCreateFile(Arc,CurFile);
if (!ExtrFile && Arc.Solid)
{
SkipSolid=true;
- TestMode=true;
ExtrFile=true;
+ // We changed SkipSolid, so we need to call uiStartFileExtract
+ // with "Skip" parameter to change the operation status
+ // from "extracting" to "skipping". For example, it can be necessary
+ // if user answered "No" to overwrite prompt when unpacking
+ // a solid archive.
+ if (!uiStartFileExtract(ArcFileName,false,false,true))
+ return false;
}
if (ExtrFile)
{
+ // Set it in test mode, so we also test subheaders such as NTFS streams
+ // after tested file.
+ if (Cmd->Test)
+ PrevProcessed=true;
+
+ bool TestMode=Cmd->Test || SkipSolid; // Unpack to memory, not to disk.
+
if (!SkipSolid)
{
if (!TestMode && Command!='P' && CurFile.IsDevice())
{
- Log(Arc.FileName,St(MInvalidName),DestFileName);
+ uiMsg(UIERROR_INVALIDNAME,Arc.FileName,DestFileName);
ErrHandler.WriteError(Arc.FileName,DestFileName);
}
TotalFileCount++;
}
FileCount++;
-#ifndef GUI
if (Command!='I')
if (SkipSolid)
mprintf(St(MExtrSkipFile),ArcFileName);
else
- switch(Cmd->Test ? 'T':Command)
+ switch(Cmd->Test ? 'T':Command) // "Test" can be also enabled by -t switch.
{
case 'T':
mprintf(St(MExtrTestFile),ArcFileName);
@@ -783,146 +595,604 @@ bool CmdExtract::ExtractCurrentFile(CommandData *Cmd,Archive &Arc,size_t HeaderS
break;
}
if (!Cmd->DisablePercentage)
- mprintf(" ");
-#endif
+ mprintf(L" ");
+
DataIO.CurUnpRead=0;
DataIO.CurUnpWrite=0;
- DataIO.UnpFileCRC=Arc.OldFormat ? 0 : 0xffffffff;
- DataIO.PackedCRC=0xffffffff;
- DataIO.SetEncryption(
- (Arc.NewLhd.Flags & LHD_PASSWORD) ? Arc.NewLhd.UnpVer:0,Password,
- (Arc.NewLhd.Flags & LHD_SALT) ? Arc.NewLhd.Salt:NULL,false,
- Arc.NewLhd.UnpVer>=36);
- DataIO.SetPackedSizeToRead(Arc.NewLhd.FullPackSize);
+ DataIO.UnpHash.Init(Arc.FileHead.FileHash.Type,Cmd->Threads);
+ DataIO.PackedDataHash.Init(Arc.FileHead.FileHash.Type,Cmd->Threads);
+ DataIO.SetPackedSizeToRead(Arc.FileHead.PackSize);
DataIO.SetFiles(&Arc,&CurFile);
DataIO.SetTestMode(TestMode);
DataIO.SetSkipUnpCRC(SkipSolid);
-#ifndef _WIN_CE
- if (!TestMode && !Arc.BrokenFileHeader &&
- (Arc.NewLhd.FullPackSize<<11)>Arc.NewLhd.FullUnpSize &&
- (Arc.NewLhd.FullUnpSize<100000000 || Arc.FileLength()>Arc.NewLhd.FullPackSize))
- CurFile.Prealloc(Arc.NewLhd.FullUnpSize);
+
+#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
+ if (!TestMode && !Arc.BrokenHeader &&
+ Arc.FileHead.UnpSize>0xffffffff && (Fat32 || !NotFat32))
+ {
+ if (!Fat32) // Not detected yet.
+ NotFat32=!(Fat32=IsFAT(Cmd->ExtrPath));
+ if (Fat32)
+ uiMsg(UIMSG_FAT32SIZE); // Inform user about FAT32 size limit.
+ }
#endif
+ uint64 Preallocated=0;
+ if (!TestMode && !Arc.BrokenHeader && Arc.FileHead.UnpSize>1000000 &&
+ Arc.FileHead.PackSize*1024>Arc.FileHead.UnpSize &&
+ (Arc.FileHead.UnpSize<100000000 || Arc.FileLength()>Arc.FileHead.PackSize))
+ {
+ CurFile.Prealloc(Arc.FileHead.UnpSize);
+ Preallocated=Arc.FileHead.UnpSize;
+ }
CurFile.SetAllowDelete(!Cmd->KeepBroken);
- bool LinkCreateMode=!Cmd->Test && !SkipSolid;
- if (ExtractLink(DataIO,Arc,DestFileName,DataIO.UnpFileCRC,LinkCreateMode))
- PrevExtracted=LinkCreateMode;
- else
- if ((Arc.NewLhd.Flags & LHD_SPLIT_BEFORE)==0)
- if (Arc.NewLhd.Method==0x30)
- UnstoreFile(DataIO,Arc.NewLhd.FullUnpSize);
+ bool FileCreateMode=!TestMode && !SkipSolid && Command!='P';
+ bool ShowChecksum=true; // Display checksum verification result.
+
+ bool LinkSuccess=true; // Assume success for test mode.
+ if (LinkEntry)
+ {
+ FILE_SYSTEM_REDIRECT Type=Arc.FileHead.RedirType;
+
+ if (Type==FSREDIR_HARDLINK || Type==FSREDIR_FILECOPY)
+ {
+ wchar NameExisting[NM];
+ ExtrPrepareName(Arc,Arc.FileHead.RedirName,NameExisting,ASIZE(NameExisting));
+ if (FileCreateMode && *NameExisting!=0) // *NameExisting can be 0 in case of excessive -ap switch.
+ if (Type==FSREDIR_HARDLINK)
+ LinkSuccess=ExtractHardlink(DestFileName,NameExisting,ASIZE(NameExisting));
+ else
+ LinkSuccess=ExtractFileCopy(CurFile,Arc.FileName,DestFileName,NameExisting,ASIZE(NameExisting));
+ }
+ else
+ if (Type==FSREDIR_UNIXSYMLINK || Type==FSREDIR_WINSYMLINK || Type==FSREDIR_JUNCTION)
+ {
+ if (FileCreateMode)
+ LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName);
+ }
else
{
- Unp->SetDestSize(Arc.NewLhd.FullUnpSize);
+ uiMsg(UIERROR_UNKNOWNEXTRA, Arc.FileName, DestFileName);
+ LinkSuccess=false;
+ }
+
+ if (!LinkSuccess || Arc.Format==RARFMT15 && !FileCreateMode)
+ {
+ // RAR 5.x links have a valid data checksum even in case of
+ // failure, because they do not store any data.
+ // We do not want to display "OK" in this case.
+ // For 4.x symlinks we verify the checksum only when extracting,
+ // but not when testing an archive.
+ ShowChecksum=false;
+ }
+ PrevProcessed=FileCreateMode && LinkSuccess;
+ }
+ else
+ if (!Arc.FileHead.SplitBefore)
+ if (Arc.FileHead.Method==0)
+ UnstoreFile(DataIO,Arc.FileHead.UnpSize);
+ else
+ {
+ Unp->Init(Arc.FileHead.WinSize,Arc.FileHead.Solid);
+ Unp->SetDestSize(Arc.FileHead.UnpSize);
#ifndef SFX_MODULE
- if (Arc.NewLhd.UnpVer<=15)
+ if (Arc.Format!=RARFMT50 && Arc.FileHead.UnpVer<=15)
Unp->DoUnpack(15,FileCount>1 && Arc.Solid);
else
#endif
- Unp->DoUnpack(Arc.NewLhd.UnpVer,(Arc.NewLhd.Flags & LHD_SOLID)!=0);
+ Unp->DoUnpack(Arc.FileHead.UnpVer,Arc.FileHead.Solid);
}
- if (Arc.IsOpened())
- Arc.SeekToNext();
+ Arc.SeekToNext();
+ // We check for "split after" flag to detect partially extracted files
+ // from incomplete volume sets. For them file header contains packed
+ // data hash, which must not be compared against unpacked data hash
+ // to prevent accidental match. Moreover, for -m0 volumes packed data
+ // hash would match truncated unpacked data hash and lead to fake "OK"
+ // in incomplete volume set.
+ bool ValidCRC=!Arc.FileHead.SplitAfter && DataIO.UnpHash.Cmp(&Arc.FileHead.FileHash,Arc.FileHead.UseHashKey ? Arc.FileHead.HashKey:NULL);
+
+ // We set AnySolidDataUnpackedWell to true if we found at least one
+ // valid non-zero solid file in preceding solid stream. If it is true
+ // and if current encrypted file is broken, we do not need to hint
+ // about a wrong password and can report CRC error only.
+ if (!Arc.FileHead.Solid)
+ AnySolidDataUnpackedWell=false; // Reset the flag, because non-solid file is found.
+ else
+ if (Arc.FileHead.Method!=0 && Arc.FileHead.UnpSize>0 && ValidCRC)
+ AnySolidDataUnpackedWell=true;
+
bool BrokenFile=false;
- if (!SkipSolid)
+
+ // Checksum is not calculated in skip solid mode for performance reason.
+ if (!SkipSolid && ShowChecksum)
{
- if (Arc.OldFormat && UINT32(DataIO.UnpFileCRC)==UINT32(Arc.NewLhd.FileCRC) ||
- !Arc.OldFormat && UINT32(DataIO.UnpFileCRC)==UINT32(Arc.NewLhd.FileCRC^0xffffffff))
+ if (ValidCRC)
{
-#ifndef GUI
if (Command!='P' && Command!='I')
- mprintf("%s%s ",Cmd->DisablePercentage ? " ":"\b\b\b\b\b ",St(MOk));
-#endif
+ mprintf(L"%s%s ",Cmd->DisablePercentage ? L" ":L"\b\b\b\b\b ",
+ Arc.FileHead.FileHash.Type==HASH_NONE ? L" ?":St(MOk));
}
else
{
- char *BadArcName=/*(Arc.NewLhd.Flags & LHD_SPLIT_BEFORE) ? NULL:*/Arc.FileName;
- if (Arc.NewLhd.Flags & LHD_PASSWORD)
- {
- Log(BadArcName,St(MEncrBadCRC),ArcFileName);
- }
+ if (Arc.FileHead.Encrypted && (!Arc.FileHead.UsePswCheck ||
+ Arc.BrokenHeader) && !AnySolidDataUnpackedWell)
+ uiMsg(UIERROR_CHECKSUMENC,Arc.FileName,ArcFileName);
else
- {
- Log(BadArcName,St(MCRCFailed),ArcFileName);
- }
+ uiMsg(UIERROR_CHECKSUM,Arc.FileName,ArcFileName);
BrokenFile=true;
- ErrHandler.SetErrorCode(CRC_ERROR);
+ ErrHandler.SetErrorCode(RARX_CRC);
#ifdef RARDLL
- Cmd->DllError=ERAR_BAD_DATA;
+ // If we already have ERAR_EOPEN as result of missing volume
+ // or ERAR_BAD_PASSWORD for RAR5 wrong password,
+ // we should not replace it with less precise ERAR_BAD_DATA.
+ if (Cmd->DllError!=ERAR_EOPEN && Cmd->DllError!=ERAR_BAD_PASSWORD)
+ Cmd->DllError=ERAR_BAD_DATA;
#endif
- Alarm();
}
}
-#ifndef GUI
else
- mprintf("\b\b\b\b\b ");
-#endif
+ mprintf(L"\b\b\b\b\b ");
+
+ // If we successfully unpacked a hard link, we wish to set its file
+ // attributes. Hard link shares file metadata with link target,
+ // so we do not need to set link time or owner. But when we overwrite
+ // an existing link, we can call PrepareToDelete(), which affects
+ // link target attributes as well. So we set link attributes to restore
+ // both target and link attributes if PrepareToDelete() changed them.
+ bool SetAttrOnly=LinkEntry && Arc.FileHead.RedirType==FSREDIR_HARDLINK && LinkSuccess;
if (!TestMode && (Command=='X' || Command=='E') &&
- !IsLink(Arc.NewLhd.FileAttr))
+ (!LinkEntry || SetAttrOnly || Arc.FileHead.RedirType==FSREDIR_FILECOPY && LinkSuccess) &&
+ (!BrokenFile || Cmd->KeepBroken))
{
-#if defined(_WIN_32) || defined(_EMX)
- if (Cmd->ClearArc)
- Arc.NewLhd.FileAttr&=~FA_ARCH;
-/*
- else
- Arc.NewLhd.FileAttr|=FA_ARCH; //set archive bit for unpacked files (file is not backed up)
-*/
-#endif
- if (!BrokenFile || Cmd->KeepBroken)
+ // Below we use DestFileName instead of CurFile.FileName,
+ // so we can set file attributes also for hard links, which do not
+ // have the open CurFile. These strings are the same for other items.
+
+ if (!SetAttrOnly)
{
- if (BrokenFile)
+ // We could preallocate more space that really written to broken file
+ // or file with crafted header.
+ if (Preallocated>0 && (BrokenFile || DataIO.CurUnpWrite!=Preallocated))
CurFile.Truncate();
+
+
CurFile.SetOpenFileTime(
- Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.NewLhd.mtime,
- Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.NewLhd.ctime,
- Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.NewLhd.atime);
+ Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime,
+ Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.FileHead.ctime,
+ Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime);
CurFile.Close();
-#if defined(_WIN_32) && !defined(_WIN_CE) && !defined(SFX_MODULE)
- if (Cmd->SetCompressedAttr &&
- (Arc.NewLhd.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0 && WinNT())
- SetFileCompression(CurFile.FileName,CurFile.FileNameW,true);
-#endif
+
+ SetFileHeaderExtra(Cmd,Arc,DestFileName);
+
CurFile.SetCloseFileTime(
- Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.NewLhd.mtime,
- Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.NewLhd.atime);
- if (!Cmd->IgnoreGeneralAttr)
- SetFileAttr(CurFile.FileName,CurFile.FileNameW,Arc.NewLhd.FileAttr);
- PrevExtracted=true;
+ Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime,
+ Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime);
}
+
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+ if (Cmd->SetCompressedAttr &&
+ (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0)
+ SetFileCompression(DestFileName,true);
+ if (Cmd->ClearArc)
+ Arc.FileHead.FileAttr&=~FILE_ATTRIBUTE_ARCHIVE;
+#endif
+ if (!Cmd->IgnoreGeneralAttr && !SetFileAttr(DestFileName,Arc.FileHead.FileAttr))
+ uiMsg(UIERROR_FILEATTR,Arc.FileName,DestFileName);
+
+ PrevProcessed=true;
}
}
}
- if (ExactMatch)
+ // It is important to increment it for files, but not dirs. So we extract
+ // dir with its entire contents, not just dir record only even if dir
+ // record precedes files.
+ if (MatchFound)
MatchedArgs++;
- if (DataIO.NextVolumeMissing || !Arc.IsOpened())
- return(false);
+ if (DataIO.NextVolumeMissing)
+ return false;
if (!ExtrFile)
if (!Arc.Solid)
Arc.SeekToNext();
else
if (!SkipSolid)
- return(false);
- return(true);
+ return false;
+ return true;
}
void CmdExtract::UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize)
{
- Array Buffer(0x10000);
- while (1)
+ Array Buffer(File::CopyBufferSize());
+ while (true)
{
- uint Code=DataIO.UnpRead(&Buffer[0],Buffer.Size());
- if (Code==0 || (int)Code==-1)
+ int ReadSize=DataIO.UnpRead(&Buffer[0],Buffer.Size());
+ if (ReadSize<=0)
break;
- Code=Code=0)
- DestUnpSize-=Code;
+ int WriteSize=ReadSize0)
+ {
+ DataIO.UnpWrite(&Buffer[0],WriteSize);
+ DestUnpSize-=WriteSize;
+ }
}
}
+
+bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize)
+{
+ SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives.
+
+ File Existing;
+ if (!Existing.WOpen(NameExisting))
+ {
+ uiMsg(UIERROR_FILECOPY,ArcName,NameExisting,NameNew);
+ uiMsg(UIERROR_FILECOPYHINT,ArcName);
+#ifdef RARDLL
+ Cmd->DllError=ERAR_EREFERENCE;
+#endif
+ return false;
+ }
+
+ Array Buffer(0x100000);
+ int64 CopySize=0;
+
+ while (true)
+ {
+ Wait();
+ int ReadSize=Existing.Read(&Buffer[0],Buffer.Size());
+ if (ReadSize==0)
+ break;
+ New.Write(&Buffer[0],ReadSize);
+ CopySize+=ReadSize;
+ }
+
+ return true;
+}
+
+
+void CmdExtract::ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize)
+{
+ wcsncpyz(DestName,Cmd->ExtrPath,DestSize);
+
+ if (*Cmd->ExtrPath!=0)
+ {
+ wchar LastChar=*PointToLastChar(Cmd->ExtrPath);
+ // We need IsPathDiv check here to correctly handle Unix forward slash
+ // in the end of destination path in Windows: rar x arc dest/
+ // IsDriveDiv is needed for current drive dir: rar x arc d:
+ if (!IsPathDiv(LastChar) && !IsDriveDiv(LastChar))
+ {
+ // Destination path can be without trailing slash if it come from GUI shell.
+ AddEndSlash(DestName,DestSize);
+ }
+ }
+
+#ifndef SFX_MODULE
+ if (Cmd->AppendArcNameToPath!=APPENDARCNAME_NONE)
+ {
+ if (Cmd->AppendArcNameToPath==APPENDARCNAME_DESTPATH)
+ wcsncatz(DestName,PointToName(Arc.FirstVolumeName),DestSize);
+ else
+ wcsncpyz(DestName,Arc.FirstVolumeName,DestSize); // To archive own dir.
+ SetExt(DestName,NULL,DestSize);
+ AddEndSlash(DestName,DestSize);
+ }
+#endif
+
+#ifndef SFX_MODULE
+ size_t ArcPathLength=wcslen(Cmd->ArcPath);
+ if (ArcPathLength>0)
+ {
+ size_t NameLength=wcslen(ArcFileName);
+
+ // Earlier we compared lengths only here, but then noticed a cosmetic bug
+ // in WinRAR. When extracting a file reference from subfolder with
+ // "Extract relative paths", so WinRAR sets ArcPath, if reference target
+ // is missing, error message removed ArcPath both from reference and target
+ // names. If target was stored in another folder, its name looked wrong.
+ if (NameLength>=ArcPathLength &&
+ wcsnicompc(Cmd->ArcPath,ArcFileName,ArcPathLength)==0 &&
+ (IsPathDiv(Cmd->ArcPath[ArcPathLength-1]) ||
+ IsPathDiv(ArcFileName[ArcPathLength]) || ArcFileName[ArcPathLength]==0))
+ {
+ ArcFileName+=Min(ArcPathLength,NameLength);
+ while (IsPathDiv(*ArcFileName))
+ ArcFileName++;
+ if (*ArcFileName==0) // Excessive -ap switch.
+ {
+ *DestName=0;
+ return;
+ }
+ }
+ }
+#endif
+
+ wchar Command=Cmd->Command[0];
+ // Use -ep3 only in systems, where disk letters are exist, not in Unix.
+ bool AbsPaths=Cmd->ExclPath==EXCL_ABSPATH && Command=='X' && IsDriveDiv(':');
+
+ // We do not use any user specified destination paths when extracting
+ // absolute paths in -ep3 mode.
+ if (AbsPaths)
+ *DestName=0;
+
+ if (Command=='E' || Cmd->ExclPath==EXCL_SKIPWHOLEPATH)
+ wcsncatz(DestName,PointToName(ArcFileName),DestSize);
+ else
+ wcsncatz(DestName,ArcFileName,DestSize);
+
+#ifdef _WIN_ALL
+ // Must do after Cmd->ArcPath processing above, so file name and arc path
+ // trailing spaces are in sync.
+ if (!Cmd->AllowIncompatNames)
+ MakeNameCompatible(DestName);
+#endif
+
+ wchar DiskLetter=toupperw(DestName[0]);
+
+ if (AbsPaths)
+ {
+ if (DestName[1]=='_' && IsPathDiv(DestName[2]) &&
+ DiskLetter>='A' && DiskLetter<='Z')
+ DestName[1]=':';
+ else
+ if (DestName[0]=='_' && DestName[1]=='_')
+ {
+ // Convert __server\share to \\server\share.
+ DestName[0]=CPATHDIVIDER;
+ DestName[1]=CPATHDIVIDER;
+ }
+ }
+}
+
+
+#ifdef RARDLL
+bool CmdExtract::ExtrDllGetPassword()
+{
+ if (!Cmd->Password.IsSet())
+ {
+ if (Cmd->Callback!=NULL)
+ {
+ wchar PasswordW[MAXPASSWORD];
+ *PasswordW=0;
+ if (Cmd->Callback(UCM_NEEDPASSWORDW,Cmd->UserData,(LPARAM)PasswordW,ASIZE(PasswordW))==-1)
+ *PasswordW=0;
+ if (*PasswordW==0)
+ {
+ char PasswordA[MAXPASSWORD];
+ *PasswordA=0;
+ if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1)
+ *PasswordA=0;
+ GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW));
+ cleandata(PasswordA,sizeof(PasswordA));
+ }
+ Cmd->Password.Set(PasswordW);
+ cleandata(PasswordW,sizeof(PasswordW));
+ Cmd->ManualPassword=true;
+ }
+ if (!Cmd->Password.IsSet())
+ return false;
+ }
+ return true;
+}
+#endif
+
+
+#ifndef RARDLL
+bool CmdExtract::ExtrGetPassword(Archive &Arc,const wchar *ArcFileName)
+{
+ if (!Cmd->Password.IsSet())
+ {
+ if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password)/* || !Cmd->Password.IsSet()*/)
+ {
+ // Suppress "test is ok" message if user cancelled the password prompt.
+// 2019.03.23: If some archives are tested ok and prompt is cancelled for others,
+// do we really need to suppress "test is ok"? Also if we set an empty password
+// and "Use for all archives" in WinRAR Ctrl+P and skip some encrypted archives.
+// We commented out this UIERROR_INCERRCOUNT for now.
+// uiMsg(UIERROR_INCERRCOUNT);
+ return false;
+ }
+ Cmd->ManualPassword=true;
+ }
+#if !defined(SILENT)
+ else
+ if (!GlobalPassword && !Arc.FileHead.Solid)
+ {
+ eprintf(St(MUseCurPsw),ArcFileName);
+ switch(Cmd->AllYes ? 1 : Ask(St(MYesNoAll)))
+ {
+ case -1:
+ ErrHandler.Exit(RARX_USERBREAK);
+ case 2:
+ if (!uiGetPassword(UIPASSWORD_FILE,ArcFileName,&Cmd->Password))
+ return false;
+ break;
+ case 3:
+ GlobalPassword=true;
+ break;
+ }
+ }
+#endif
+ return true;
+}
+#endif
+
+
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+void CmdExtract::ConvertDosPassword(Archive &Arc,SecPassword &DestPwd)
+{
+ if (Arc.Format==RARFMT15 && Arc.FileHead.HostOS==HOST_MSDOS)
+ {
+ // We need the password in OEM encoding if file was encrypted by
+ // native RAR/DOS (not extender based). Let's make the conversion.
+ wchar PlainPsw[MAXPASSWORD];
+ Cmd->Password.Get(PlainPsw,ASIZE(PlainPsw));
+ char PswA[MAXPASSWORD];
+ CharToOemBuffW(PlainPsw,PswA,ASIZE(PswA));
+ PswA[ASIZE(PswA)-1]=0;
+ CharToWide(PswA,PlainPsw,ASIZE(PlainPsw));
+ DestPwd.Set(PlainPsw);
+ cleandata(PlainPsw,sizeof(PlainPsw));
+ cleandata(PswA,sizeof(PswA));
+ }
+}
+#endif
+
+
+void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName)
+{
+ if (Cmd->Test)
+ {
+ mprintf(St(MExtrTestFile),ArcFileName);
+ mprintf(L" %s",St(MOk));
+ return;
+ }
+
+ MKDIR_CODE MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr);
+ bool DirExist=false;
+ if (MDCode!=MKDIR_SUCCESS)
+ {
+ DirExist=FileExist(DestFileName);
+ if (DirExist && !IsDir(GetFileAttr(DestFileName)))
+ {
+ // File with name same as this directory exists. Propose user
+ // to overwrite it.
+ bool UserReject;
+ FileCreate(Cmd,NULL,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime);
+ DirExist=false;
+ }
+ if (!DirExist)
+ {
+ CreatePath(DestFileName,true);
+ MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr);
+ if (MDCode!=MKDIR_SUCCESS)
+ {
+ wchar OrigName[ASIZE(DestFileName)];
+ wcsncpyz(OrigName,DestFileName,ASIZE(OrigName));
+ MakeNameUsable(DestFileName,true);
+ CreatePath(DestFileName,true);
+ MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr);
+#ifndef SFX_MODULE
+ if (MDCode==MKDIR_SUCCESS)
+ uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName);
+#endif
+ }
+ }
+ }
+ if (MDCode==MKDIR_SUCCESS)
+ {
+ mprintf(St(MCreatDir),DestFileName);
+ mprintf(L" %s",St(MOk));
+ PrevProcessed=true;
+ }
+ else
+ if (DirExist)
+ {
+ if (!Cmd->IgnoreGeneralAttr)
+ SetFileAttr(DestFileName,Arc.FileHead.FileAttr);
+ PrevProcessed=true;
+ }
+ else
+ {
+ uiMsg(UIERROR_DIRCREATE,Arc.FileName,DestFileName);
+ ErrHandler.SysErrMsg();
+#ifdef RARDLL
+ Cmd->DllError=ERAR_ECREATE;
+#endif
+ ErrHandler.SetErrorCode(RARX_CREATE);
+ }
+ if (PrevProcessed)
+ {
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+ if (Cmd->SetCompressedAttr &&
+ (Arc.FileHead.FileAttr & FILE_ATTRIBUTE_COMPRESSED)!=0 && WinNT()!=WNT_NONE)
+ SetFileCompression(DestFileName,true);
+#endif
+ SetFileHeaderExtra(Cmd,Arc,DestFileName);
+ SetDirTime(DestFileName,
+ Cmd->xmtime==EXTTIME_NONE ? NULL:&Arc.FileHead.mtime,
+ Cmd->xctime==EXTTIME_NONE ? NULL:&Arc.FileHead.ctime,
+ Cmd->xatime==EXTTIME_NONE ? NULL:&Arc.FileHead.atime);
+ }
+}
+
+
+bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile)
+{
+ bool Success=true;
+ wchar Command=Cmd->Command[0];
+#if !defined(SFX_MODULE)
+ if (Command=='P')
+ CurFile.SetHandleType(FILE_HANDLESTD);
+#endif
+ if ((Command=='E' || Command=='X') && !Cmd->Test)
+ {
+ bool UserReject;
+ // Specify "write only" mode to avoid OpenIndiana NAS problems
+ // with SetFileTime and read+write files.
+ if (!FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true))
+ {
+ Success=false;
+ if (!UserReject)
+ {
+ ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName);
+#ifdef RARDLL
+ Cmd->DllError=ERAR_ECREATE;
+#endif
+ if (!IsNameUsable(DestFileName))
+ {
+ uiMsg(UIMSG_CORRECTINGNAME,Arc.FileName);
+
+ wchar OrigName[ASIZE(DestFileName)];
+ wcsncpyz(OrigName,DestFileName,ASIZE(OrigName));
+
+ MakeNameUsable(DestFileName,true);
+
+ CreatePath(DestFileName,true);
+ if (FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true))
+ {
+#ifndef SFX_MODULE
+ uiMsg(UIERROR_RENAMING,Arc.FileName,OrigName,DestFileName);
+#endif
+ Success=true;
+ }
+ else
+ ErrHandler.CreateErrorMsg(Arc.FileName,DestFileName);
+ }
+ }
+ }
+ }
+ return Success;
+}
+
+
+bool CmdExtract::CheckUnpVer(Archive &Arc,const wchar *ArcFileName)
+{
+ bool WrongVer;
+ if (Arc.Format==RARFMT50) // Both SFX and RAR can unpack RAR 5.0 archives.
+ WrongVer=Arc.FileHead.UnpVer>VER_UNPACK5;
+ else
+ {
+#ifdef SFX_MODULE // SFX can unpack only RAR 2.9 archives.
+ WrongVer=Arc.FileHead.UnpVer!=VER_UNPACK;
+#else // All formats since 1.3 for RAR.
+ WrongVer=Arc.FileHead.UnpVer<13 || Arc.FileHead.UnpVer>VER_UNPACK;
+#endif
+ }
+
+ // We can unpack stored files regardless of compression version field.
+ if (Arc.FileHead.Method==0)
+ WrongVer=false;
+
+ if (WrongVer)
+ {
+ ErrHandler.UnknownMethodMsg(Arc.FileName,ArcFileName);
+ uiMsg(UIERROR_NEWERRAR,Arc.FileName);
+ }
+ return !WrongVer;
+}
diff --git a/libunrar/extract.hpp b/libunrar/extract.hpp
index 40f9cc0..325928d 100644
--- a/libunrar/extract.hpp
+++ b/libunrar/extract.hpp
@@ -6,9 +6,25 @@ enum EXTRACT_ARC_CODE {EXTRACT_ARC_NEXT,EXTRACT_ARC_REPEAT};
class CmdExtract
{
private:
- EXTRACT_ARC_CODE ExtractArchive(CommandData *Cmd);
+ EXTRACT_ARC_CODE ExtractArchive();
+ bool ExtractFileCopy(File &New,wchar *ArcName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize);
+ void ExtrPrepareName(Archive &Arc,const wchar *ArcFileName,wchar *DestName,size_t DestSize);
+#ifdef RARDLL
+ bool ExtrDllGetPassword();
+#else
+ bool ExtrGetPassword(Archive &Arc,const wchar *ArcFileName);
+#endif
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+ void ConvertDosPassword(Archive &Arc,SecPassword &DestPwd);
+#endif
+ void ExtrCreateDir(Archive &Arc,const wchar *ArcFileName);
+ bool ExtrCreateFile(Archive &Arc,File &CurFile);
+ bool CheckUnpVer(Archive &Arc,const wchar *ArcFileName);
+
RarTime StartTime; // time when extraction started
+ CommandData *Cmd;
+
ComprDataIO DataIO;
Unpack *Unp;
unsigned long TotalFileCount;
@@ -19,25 +35,28 @@ class CmdExtract
bool AllMatchesExact;
bool ReconstructDone;
- char ArcName[NM];
- wchar ArcNameW[NM];
+ // If any non-zero solid file was successfully unpacked before current.
+ // If true and if current encrypted file is broken, obviously
+ // the password is correct and we can report broken CRC without
+ // any wrong password hints.
+ bool AnySolidDataUnpackedWell;
- char Password[MAXPASSWORD];
- bool PasswordAll;
- bool PrevExtracted;
- char DestFileName[NM];
- wchar DestFileNameW[NM];
+ wchar ArcName[NM];
+
+ bool GlobalPassword;
+ bool PrevProcessed; // If previous file was successfully extracted or tested.
+ wchar DestFileName[NM];
bool PasswordCancelled;
+#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
+ bool Fat32,NotFat32;
+#endif
public:
- CmdExtract();
+ CmdExtract(CommandData *Cmd);
~CmdExtract();
- void DoExtract(CommandData *Cmd);
- void ExtractArchiveInit(CommandData *Cmd,Archive &Arc);
- bool ExtractCurrentFile(CommandData *Cmd,Archive &Arc,size_t HeaderSize,
- bool &Repeat);
+ void DoExtract();
+ void ExtractArchiveInit(Archive &Arc);
+ bool ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat);
static void UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize);
-
- bool SignatureFound;
};
#endif
diff --git a/libunrar/filcreat.cpp b/libunrar/filcreat.cpp
index c570cd3..a64a7d4 100644
--- a/libunrar/filcreat.cpp
+++ b/libunrar/filcreat.cpp
@@ -1,245 +1,163 @@
#include "rar.hpp"
-bool FileCreate(RAROptions *Cmd,File *NewFile,char *Name,wchar *NameW,
- OVERWRITE_MODE Mode,bool *UserReject,int64 FileSize,
- uint FileTime)
+// If NewFile==NULL, we delete created file after user confirmation.
+// It is useful we we need to overwrite an existing folder or file,
+// but need user confirmation for that.
+bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize,
+ bool *UserReject,int64 FileSize,RarTime *FileTime,bool WriteOnly)
{
if (UserReject!=NULL)
*UserReject=false;
-#if defined(_WIN_32) && !defined(_WIN_CE)
+#ifdef _WIN_ALL
bool ShortNameChanged=false;
#endif
- while (FileExist(Name,NameW))
+ while (FileExist(Name))
{
-#if defined(_WIN_32) && !defined(_WIN_CE)
+#if defined(_WIN_ALL)
if (!ShortNameChanged)
{
+ // Avoid the infinite loop if UpdateExistingShortName returns
+ // the same name.
ShortNameChanged=true;
- if (UpdateExistingShortName(Name,NameW))
+
+ // Maybe our long name matches the short name of existing file.
+ // Let's check if we can change the short name.
+ if (UpdateExistingShortName(Name))
continue;
}
+ // Allow short name check again. It is necessary, because rename and
+ // autorename below can change the name, so we need to check it again.
+ ShortNameChanged=false;
#endif
- if (Mode==OVERWRITE_NONE)
+ UIASKREP_RESULT Choice=uiAskReplaceEx(Cmd,Name,MaxNameSize,FileSize,FileTime,(NewFile==NULL ? UIASKREP_F_NORENAME:0));
+
+ if (Choice==UIASKREP_R_REPLACE)
+ break;
+ if (Choice==UIASKREP_R_SKIP)
{
if (UserReject!=NULL)
*UserReject=true;
- return(false);
- }
-#ifdef SILENT
- Mode=OVERWRITE_ALL;
-#endif
- if (Cmd->AllYes || Mode==OVERWRITE_ALL)
- break;
- if (Mode==OVERWRITE_DEFAULT || Mode==OVERWRITE_FORCE_ASK)
- {
- eprintf(St(MFileExists),Name);
- int Choice=Ask(St(MYesNoAllRenQ));
- if (Choice==1)
- break;
- if (Choice==2)
- {
- if (UserReject!=NULL)
- *UserReject=true;
- return(false);
- }
- if (Choice==3)
- {
- Cmd->Overwrite=OVERWRITE_ALL;
- break;
- }
- if (Choice==4)
- {
- if (UserReject!=NULL)
- *UserReject=true;
- Cmd->Overwrite=OVERWRITE_NONE;
- return(false);
- }
- if (Choice==5)
- {
- mprintf(St(MAskNewName));
-
- char NewName[NM];
-#ifdef _WIN_32
- File SrcFile;
- SrcFile.SetHandleType(FILE_HANDLESTD);
- int Size=SrcFile.Read(NewName,sizeof(NewName)-1);
- NewName[Size]=0;
- OemToChar(NewName,NewName);
-#else
- if (fgets(NewName,sizeof(NewName),stdin)==NULL)
- {
- // Process fgets failure as if user answered 'No'.
- if (UserReject!=NULL)
- *UserReject=true;
- return(false);
- }
-#endif
- RemoveLF(NewName);
- if (PointToName(NewName)==NewName)
- strcpy(PointToName(Name),NewName);
- else
- strcpy(Name,NewName);
- if (NameW!=NULL)
- *NameW=0;
- continue;
- }
- if (Choice==6)
- ErrHandler.Exit(USER_BREAK);
- }
- if (Mode==OVERWRITE_AUTORENAME)
- {
- if (GetAutoRenamedName(Name))
- {
- if (NameW!=NULL)
- *NameW=0;
- }
- else
- Mode=OVERWRITE_DEFAULT;
- continue;
+ return false;
}
+ if (Choice==UIASKREP_R_CANCEL)
+ ErrHandler.Exit(RARX_USERBREAK);
}
- if (NewFile!=NULL && NewFile->Create(Name,NameW))
- return(true);
- PrepareToDelete(Name,NameW);
- CreatePath(Name,NameW,true);
- return(NewFile!=NULL ? NewFile->Create(Name,NameW):DelFile(Name,NameW));
+
+ // Try to truncate the existing file first instead of delete,
+ // so we preserve existing file permissions such as NTFS permissions.
+ uint FileMode=WriteOnly ? FMF_WRITE|FMF_SHAREREAD:FMF_UPDATE|FMF_SHAREREAD;
+ if (NewFile!=NULL && NewFile->Create(Name,FileMode))
+ return true;
+
+ CreatePath(Name,true);
+ return NewFile!=NULL ? NewFile->Create(Name,FileMode):DelFile(Name);
}
-bool GetAutoRenamedName(char *Name)
+bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize)
{
- char NewName[NM];
-
- if (strlen(Name)>sizeof(NewName)-10)
- return(false);
- char *Ext=GetExt(Name);
+ wchar NewName[NM];
+ size_t NameLength=wcslen(Name);
+ wchar *Ext=GetExt(Name);
if (Ext==NULL)
- Ext=Name+strlen(Name);
- for (int FileVer=1;;FileVer++)
+ Ext=Name+NameLength;
+ for (uint FileVer=1;;FileVer++)
{
- sprintf(NewName,"%.*s(%d)%s",int(Ext-Name),Name,FileVer,Ext);
+ swprintf(NewName,ASIZE(NewName),L"%.*ls(%u)%ls",uint(Ext-Name),Name,FileVer,Ext);
if (!FileExist(NewName))
{
- strcpy(Name,NewName);
+ wcsncpyz(Name,NewName,MaxNameSize);
break;
}
if (FileVer>=1000000)
- return(false);
+ return false;
}
- return(true);
+ return true;
}
-#if defined(_WIN_32) && !defined(_WIN_CE)
-bool UpdateExistingShortName(char *Name,wchar *NameW)
+#if defined(_WIN_ALL)
+// If we find a file, which short name is equal to 'Name', we try to change
+// its short name, while preserving the long name. It helps when unpacking
+// an archived file, which long name is equal to short name of already
+// existing file. Otherwise we would overwrite the already existing file,
+// even though its long name does not match the name of unpacking file.
+bool UpdateExistingShortName(const wchar *Name)
{
- FindData fd;
- if (!FindFile::FastFind(Name,NameW,&fd))
- return(false);
- if (*fd.Name==0 || *fd.ShortName==0)
- return(false);
- if (stricomp(PointToName(fd.Name),fd.ShortName)==0 ||
- stricomp(PointToName(Name),fd.ShortName)!=0)
- return(false);
+ wchar LongPathName[NM];
+ DWORD Res=GetLongPathName(Name,LongPathName,ASIZE(LongPathName));
+ if (Res==0 || Res>=ASIZE(LongPathName))
+ return false;
+ wchar ShortPathName[NM];
+ Res=GetShortPathName(Name,ShortPathName,ASIZE(ShortPathName));
+ if (Res==0 || Res>=ASIZE(ShortPathName))
+ return false;
+ wchar *LongName=PointToName(LongPathName);
+ wchar *ShortName=PointToName(ShortPathName);
- char NewName[NM];
- for (int I=0;I<10000;I+=123)
+ // We continue only if file has a short name, which does not match its
+ // long name, and this short name is equal to name of file which we need
+ // to create.
+ if (*ShortName==0 || wcsicomp(LongName,ShortName)==0 ||
+ wcsicomp(PointToName(Name),ShortName)!=0)
+ return false;
+
+ // Generate the temporary new name for existing file.
+ wchar NewName[NM];
+ *NewName=0;
+ for (int I=0;I<10000 && *NewName==0;I+=123)
{
- strncpyz(NewName,Name,ASIZE(NewName));
- sprintf(PointToName(NewName),"rtmp%d",I);
- if (!FileExist(NewName))
- break;
+ // Here we copy the path part of file to create. We'll make the temporary
+ // file in the same folder.
+ wcsncpyz(NewName,Name,ASIZE(NewName));
+
+ // Here we set the random name part.
+ swprintf(PointToName(NewName),ASIZE(NewName),L"rtmp%d",I);
+
+ // If such file is already exist, try next random name.
+ if (FileExist(NewName))
+ *NewName=0;
}
- if (FileExist(NewName))
- return(false);
- char FullName[NM];
- strncpyz(FullName,Name,ASIZE(FullName));
- strcpy(PointToName(FullName),PointToName(fd.Name));
+
+ // If we could not generate the name not used by any other file, we return.
+ if (*NewName==0)
+ return false;
+
+ // FastFind returns the name without path, but we need the fully qualified
+ // name for renaming, so we use the path from file to create and long name
+ // from existing file.
+ wchar FullName[NM];
+ wcsncpyz(FullName,Name,ASIZE(FullName));
+ SetName(FullName,LongName,ASIZE(FullName));
+
+ // Rename the existing file to randomly generated name. Normally it changes
+ // the short name too.
if (!MoveFile(FullName,NewName))
- return(false);
+ return false;
+
+ // Now we need to create the temporary empty file with same name as
+ // short name of our already existing file. We do it to occupy its previous
+ // short name and not allow to use it again when renaming the file back to
+ // its original long name.
File KeepShortFile;
bool Created=false;
if (!FileExist(Name))
- Created=KeepShortFile.Create(Name);
+ Created=KeepShortFile.Create(Name,FMF_WRITE|FMF_SHAREREAD);
+
+ // Now we rename the existing file from temporary name to original long name.
+ // Since its previous short name is occupied by another file, it should
+ // get another short name.
MoveFile(NewName,FullName);
+
if (Created)
{
+ // Delete the temporary zero length file occupying the short name,
KeepShortFile.Close();
KeepShortFile.Delete();
}
- return(true);
+ // We successfully changed the short name. Maybe sometimes we'll simplify
+ // this function by use of SetFileShortName Windows API call.
+ // But SetFileShortName is not available in older Windows.
+ return true;
}
-
-/*
-bool UpdateExistingShortName(char *Name,wchar *NameW)
-{
- if (WinNT()<5)
- return(false);
- FindData fd;
- if (!FindFile::FastFind(Name,NameW,&fd))
- return(false);
- if (*fd.Name==0 || *fd.ShortName==0)
- return(false);
- if (stricomp(PointToName(fd.Name),fd.ShortName)==0 ||
- stricomp(PointToName(Name),fd.ShortName)!=0)
- return(false);
-
- typedef BOOL (WINAPI *SETFILESHORTNAME)(HANDLE,LPCSTR);
- static SETFILESHORTNAME pSetFileShortName=NULL;
- if (pSetFileShortName==NULL)
- {
- HMODULE hKernel=GetModuleHandle("kernel32.dll");
- if (hKernel!=NULL)
- pSetFileShortName=(SETFILESHORTNAME)GetProcAddress(hKernel,"SetFileShortNameA");
- if (pSetFileShortName==NULL)
- return(false);
- }
- static bool RestoreEnabled=false;
- if (!RestoreEnabled)
- {
- HANDLE hToken;
- if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES, &hToken))
- return(false);
-
- TOKEN_PRIVILEGES tp;
- tp.PrivilegeCount = 1;
- tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
-
- if (LookupPrivilegeValue(NULL,SE_RESTORE_NAME,&tp.Privileges[0].Luid))
- AdjustTokenPrivileges(hToken, FALSE, &tp, 0, NULL, NULL);
-
- CloseHandle(hToken);
- RestoreEnabled=true;
- }
-
- wchar FileNameW[NM];
- GetWideName(Name,NameW,FileNameW);
- HANDLE hFile=CreateFileW(FileNameW,GENERIC_WRITE|DELETE,FILE_SHARE_READ|FILE_SHARE_WRITE,
- NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
- if (hFile==INVALID_HANDLE_VALUE)
- return(false);
-
- bool RetCode=false;
-
- char FullName[NM];
- wchar FullNameW[NM];
- strcpy(FullName,Name);
- strcpyw(FullNameW,NullToEmpty(NameW));
- for (int I=1;I<1000000;I++)
- {
- char NewName[NM];
- sprintf(NewName,"NAME~%d.%d",I%1000,I/1000+1);
- strcpy(PointToName(FullName),NewName);
- if (*FullNameW)
- CharToWide(NewName,PointToName(FullNameW));
- if (!FileExist(FullName,FullNameW))
- {
- RetCode=pSetFileShortName(hFile,NewName);
- break;
- }
- }
- CloseHandle(hFile);
- return(RetCode);
-}
-*/
#endif
diff --git a/libunrar/filcreat.hpp b/libunrar/filcreat.hpp
index 5c8e8f5..44f801d 100644
--- a/libunrar/filcreat.hpp
+++ b/libunrar/filcreat.hpp
@@ -1,13 +1,14 @@
#ifndef _RAR_FILECREATE_
#define _RAR_FILECREATE_
-bool FileCreate(RAROptions *Cmd,File *NewFile,char *Name,wchar *NameW,
- OVERWRITE_MODE Mode,bool *UserReject,int64 FileSize=INT64NDF,
- uint FileTime=0);
-bool GetAutoRenamedName(char *Name);
+bool FileCreate(RAROptions *Cmd,File *NewFile,wchar *Name,size_t MaxNameSize,
+ bool *UserReject,int64 FileSize=INT64NDF,
+ RarTime *FileTime=NULL,bool WriteOnly=false);
-#if defined(_WIN_32) && !defined(_WIN_CE)
-bool UpdateExistingShortName(char *Name,wchar *NameW);
+bool GetAutoRenamedName(wchar *Name,size_t MaxNameSize);
+
+#if defined(_WIN_ALL)
+bool UpdateExistingShortName(const wchar *Name);
#endif
#endif
diff --git a/libunrar/file.cpp b/libunrar/file.cpp
index bd54c06..e506fde 100644
--- a/libunrar/file.cpp
+++ b/libunrar/file.cpp
@@ -1,13 +1,9 @@
#include "rar.hpp"
-static File *CreatedFiles[256];
-static int RemoveCreatedActive=0;
-
File::File()
{
- hFile=BAD_HANDLE;
+ hFile=FILE_BAD_HANDLE;
*FileName=0;
- *FileNameW=0;
NewFile=false;
LastWrite=false;
HandleType=FILE_HANDLENORMAL;
@@ -16,17 +12,18 @@ File::File()
ErrorType=FILE_SUCCESS;
OpenShared=false;
AllowDelete=true;
- CloseCount=0;
AllowExceptions=true;
-#ifdef _WIN_32
+ PreserveAtime=false;
+#ifdef _WIN_ALL
NoSequentialRead=false;
+ CreateMode=FMF_UNDEFINED;
#endif
}
File::~File()
{
- if (hFile!=BAD_HANDLE && !SkipClose)
+ if (hFile!=FILE_BAD_HANDLE && !SkipClose)
if (NewFile)
Delete();
else
@@ -37,287 +34,314 @@ File::~File()
void File::operator = (File &SrcFile)
{
hFile=SrcFile.hFile;
- strcpy(FileName,SrcFile.FileName);
NewFile=SrcFile.NewFile;
LastWrite=SrcFile.LastWrite;
HandleType=SrcFile.HandleType;
+ wcsncpyz(FileName,SrcFile.FileName,ASIZE(FileName));
SrcFile.SkipClose=true;
}
-bool File::Open(const char *Name,const wchar *NameW,bool OpenShared,bool Update)
+bool File::Open(const wchar *Name,uint Mode)
{
ErrorType=FILE_SUCCESS;
FileHandle hNewFile;
- if (File::OpenShared)
- OpenShared=true;
-#ifdef _WIN_32
- uint Access=GENERIC_READ;
- if (Update)
+ bool OpenShared=File::OpenShared || (Mode & FMF_OPENSHARED)!=0;
+ bool UpdateMode=(Mode & FMF_UPDATE)!=0;
+ bool WriteMode=(Mode & FMF_WRITE)!=0;
+#ifdef _WIN_ALL
+ uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ;
+ if (UpdateMode)
Access|=GENERIC_WRITE;
- uint ShareMode=FILE_SHARE_READ;
+ uint ShareMode=(Mode & FMF_OPENEXCLUSIVE) ? 0 : FILE_SHARE_READ;
if (OpenShared)
ShareMode|=FILE_SHARE_WRITE;
uint Flags=NoSequentialRead ? 0:FILE_FLAG_SEQUENTIAL_SCAN;
- if (WinNT() && NameW!=NULL && *NameW!=0)
- hNewFile=CreateFileW(NameW,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
- else
- hNewFile=CreateFile(Name,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
+ FindData FD;
+ if (PreserveAtime)
+ Access|=FILE_WRITE_ATTRIBUTES; // Needed to preserve atime.
+ hNewFile=CreateFile(Name,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
- if (hNewFile==BAD_HANDLE && GetLastError()==ERROR_FILE_NOT_FOUND)
+ DWORD LastError;
+ if (hNewFile==FILE_BAD_HANDLE)
+ {
+ LastError=GetLastError();
+
+ wchar LongName[NM];
+ if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
+ {
+ hNewFile=CreateFile(LongName,Access,ShareMode,NULL,OPEN_EXISTING,Flags,NULL);
+
+ // For archive names longer than 260 characters first CreateFile
+ // (without \\?\) fails and sets LastError to 3 (access denied).
+ // We need the correct "file not found" error code to decide
+ // if we create a new archive or quit with "cannot create" error.
+ // So we need to check the error code after \\?\ CreateFile again,
+ // otherwise we'll fail to create new archives with long names.
+ // But we cannot simply assign the new code to LastError,
+ // because it would break "..\arcname.rar" relative names processing.
+ // First CreateFile returns the correct "file not found" code for such
+ // names, but "\\?\" CreateFile returns ERROR_INVALID_NAME treating
+ // dots as a directory name. So we check only for "file not found"
+ // error here and for other errors use the first CreateFile result.
+ if (GetLastError()==ERROR_FILE_NOT_FOUND)
+ LastError=ERROR_FILE_NOT_FOUND;
+ }
+ }
+ if (hNewFile==FILE_BAD_HANDLE && LastError==ERROR_FILE_NOT_FOUND)
ErrorType=FILE_NOTFOUND;
+ if (PreserveAtime && hNewFile!=FILE_BAD_HANDLE)
+ {
+ FILETIME ft={0xffffffff,0xffffffff}; // This value prevents atime modification.
+ SetFileTime(hNewFile,NULL,&ft,NULL);
+ }
+
#else
- int flags=Update ? O_RDWR:O_RDONLY;
+ int flags=UpdateMode ? O_RDWR:(WriteMode ? O_WRONLY:O_RDONLY);
#ifdef O_BINARY
flags|=O_BINARY;
#if defined(_AIX) && defined(_LARGE_FILE_API)
flags|=O_LARGEFILE;
#endif
#endif
-#if defined(_EMX) && !defined(_DJGPP)
- int sflags=OpenShared ? SH_DENYNO:SH_DENYWR;
- int handle=sopen(Name,flags,sflags);
-#else
- int handle=open(Name,flags);
+ // NDK r20 has O_NOATIME, but fails to create files with it in Android 7+.
+#if defined(O_NOATIME)
+ if (PreserveAtime)
+ flags|=O_NOATIME;
+#endif
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
+
+ int handle=open(NameA,flags);
#ifdef LOCK_EX
#ifdef _OSF_SOURCE
extern "C" int flock(int, int);
#endif
- if (!OpenShared && Update && handle>=0 && flock(handle,LOCK_EX|LOCK_NB)==-1)
+ if (!OpenShared && UpdateMode && handle>=0 && flock(handle,LOCK_EX|LOCK_NB)==-1)
{
close(handle);
- return(false);
+ return false;
}
#endif
+ if (handle==-1)
+ hNewFile=FILE_BAD_HANDLE;
+ else
+ {
+#ifdef FILE_USE_OPEN
+ hNewFile=handle;
+#else
+ hNewFile=fdopen(handle,UpdateMode ? UPDATEBINARY:READBINARY);
#endif
- hNewFile=handle==-1 ? BAD_HANDLE:fdopen(handle,Update ? UPDATEBINARY:READBINARY);
- if (hNewFile==BAD_HANDLE && errno==ENOENT)
+ }
+ if (hNewFile==FILE_BAD_HANDLE && errno==ENOENT)
ErrorType=FILE_NOTFOUND;
#endif
NewFile=false;
HandleType=FILE_HANDLENORMAL;
SkipClose=false;
- bool Success=hNewFile!=BAD_HANDLE;
+ bool Success=hNewFile!=FILE_BAD_HANDLE;
if (Success)
{
hFile=hNewFile;
- if (NameW!=NULL)
- strcpyw(FileNameW,NameW);
- else
- *FileNameW=0;
- if (Name!=NULL)
- strcpy(FileName,Name);
- else
- WideToChar(NameW,FileName);
- AddFileToList(hFile);
+ wcsncpyz(FileName,Name,ASIZE(FileName));
}
- return(Success);
+ return Success;
}
-#if !defined(SHELL_EXT) && !defined(SFX_MODULE)
-void File::TOpen(const char *Name,const wchar *NameW)
+#if !defined(SFX_MODULE)
+void File::TOpen(const wchar *Name)
{
- if (!WOpen(Name,NameW))
- ErrHandler.Exit(OPEN_ERROR);
+ if (!WOpen(Name))
+ ErrHandler.Exit(RARX_OPEN);
}
#endif
-bool File::WOpen(const char *Name,const wchar *NameW)
+bool File::WOpen(const wchar *Name)
{
- if (Open(Name,NameW))
- return(true);
+ if (Open(Name))
+ return true;
ErrHandler.OpenErrorMsg(Name);
- return(false);
+ return false;
}
-bool File::Create(const char *Name,const wchar *NameW,bool ShareRead)
+bool File::Create(const wchar *Name,uint Mode)
{
-#ifdef _WIN_32
- DWORD ShareMode=(ShareRead || File::OpenShared) ? FILE_SHARE_READ:0;
- if (WinNT() && NameW!=NULL && *NameW!=0)
- hFile=CreateFileW(NameW,GENERIC_READ|GENERIC_WRITE,ShareMode,NULL,
- CREATE_ALWAYS,0,NULL);
+ // OpenIndiana based NAS and CIFS shares fail to set the file time if file
+ // was created in read+write mode and some data was written and not flushed
+ // before SetFileTime call. So we should use the write only mode if we plan
+ // SetFileTime call and do not need to read from file.
+ bool WriteMode=(Mode & FMF_WRITE)!=0;
+ bool ShareRead=(Mode & FMF_SHAREREAD)!=0 || File::OpenShared;
+#ifdef _WIN_ALL
+ CreateMode=Mode;
+ uint Access=WriteMode ? GENERIC_WRITE:GENERIC_READ|GENERIC_WRITE;
+ DWORD ShareMode=ShareRead ? FILE_SHARE_READ:0;
+
+ // Windows automatically removes dots and spaces in the end of file name,
+ // So we detect such names and process them with \\?\ prefix.
+ wchar *LastChar=PointToLastChar(Name);
+ bool Special=*LastChar=='.' || *LastChar==' ';
+
+ if (Special && (Mode & FMF_STANDARDNAMES)==0)
+ hFile=FILE_BAD_HANDLE;
else
- hFile=CreateFile(Name,GENERIC_READ|GENERIC_WRITE,ShareMode,NULL,
- CREATE_ALWAYS,0,NULL);
+ hFile=CreateFile(Name,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL);
+
+ if (hFile==FILE_BAD_HANDLE)
+ {
+ wchar LongName[NM];
+ if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
+ hFile=CreateFile(LongName,Access,ShareMode,NULL,CREATE_ALWAYS,0,NULL);
+ }
+
#else
- hFile=fopen(Name,CREATEBINARY);
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
+#ifdef FILE_USE_OPEN
+ hFile=open(NameA,(O_CREAT|O_TRUNC) | (WriteMode ? O_WRONLY : O_RDWR),0666);
+#else
+ hFile=fopen(NameA,WriteMode ? WRITEBINARY:CREATEBINARY);
+#endif
#endif
NewFile=true;
HandleType=FILE_HANDLENORMAL;
SkipClose=false;
- if (NameW!=NULL)
- strcpyw(FileNameW,NameW);
- else
- *FileNameW=0;
- if (Name!=NULL)
- strcpy(FileName,Name);
- else
- WideToChar(NameW,FileName);
- AddFileToList(hFile);
- return(hFile!=BAD_HANDLE);
+ wcsncpyz(FileName,Name,ASIZE(FileName));
+ return hFile!=FILE_BAD_HANDLE;
}
-void File::AddFileToList(FileHandle hFile)
+#if !defined(SFX_MODULE)
+void File::TCreate(const wchar *Name,uint Mode)
{
- if (hFile!=BAD_HANDLE)
- for (int I=0;I0)
@@ -339,12 +363,13 @@ void File::Write(const void *Data,size_t Size)
break;
}
LastWrite=true;
+ return Success; // It can return false only if AllowExceptions is disabled.
}
int File::Read(void *Data,size_t Size)
{
- int64 FilePos=0; //initialized only to suppress some compilers warning
+ int64 FilePos=0; // Initialized only to suppress some compilers warning.
if (IgnoreReadErrors)
FilePos=Tell();
@@ -376,39 +401,61 @@ int File::Read(void *Data,size_t Size)
}
break;
}
- return(ReadSize);
+ return ReadSize; // It can return -1 only if AllowExceptions is disabled.
}
// Returns -1 in case of error.
int File::DirectRead(void *Data,size_t Size)
{
-#ifdef _WIN_32
+#ifdef _WIN_ALL
const size_t MaxDeviceRead=20000;
+ const size_t MaxLockedRead=32768;
#endif
-#ifndef _WIN_CE
if (HandleType==FILE_HANDLESTD)
{
-#ifdef _WIN_32
- if (Size>MaxDeviceRead)
- Size=MaxDeviceRead;
+#ifdef _WIN_ALL
+// if (Size>MaxDeviceRead)
+// Size=MaxDeviceRead;
hFile=GetStdHandle(STD_INPUT_HANDLE);
+#else
+#ifdef FILE_USE_OPEN
+ hFile=STDIN_FILENO;
#else
hFile=stdin;
#endif
- }
#endif
-#ifdef _WIN_32
+ }
+#ifdef _WIN_ALL
+ // For pipes like 'type file.txt | rar -si arcname' ReadFile may return
+ // data in small ~4KB blocks. It may slightly reduce the compression ratio.
DWORD Read;
if (!ReadFile(hFile,Data,(DWORD)Size,&Read,NULL))
{
if (IsDevice() && Size>MaxDeviceRead)
- return(DirectRead(Data,MaxDeviceRead));
+ return DirectRead(Data,MaxDeviceRead);
if (HandleType==FILE_HANDLESTD && GetLastError()==ERROR_BROKEN_PIPE)
- return(0);
- return(-1);
+ return 0;
+
+ // We had a bug report about failure to archive 1C database lock file
+ // 1Cv8tmp.1CL, which is a zero length file with a region above 200 KB
+ // permanently locked. If our first read request uses too large buffer
+ // and if we are in -dh mode, so we were able to open the file,
+ // we'll fail with "Read error". So now we use try a smaller buffer size
+ // in case of lock error.
+ if (HandleType==FILE_HANDLENORMAL && Size>MaxLockedRead &&
+ GetLastError()==ERROR_LOCK_VIOLATION)
+ return DirectRead(Data,MaxLockedRead);
+
+ return -1;
}
- return(Read);
+ return Read;
+#else
+#ifdef FILE_USE_OPEN
+ ssize_t ReadSize=read(hFile,Data,Size);
+ if (ReadSize==-1)
+ return -1;
+ return (int)ReadSize;
#else
if (LastWrite)
{
@@ -418,8 +465,9 @@ int File::DirectRead(void *Data,size_t Size)
clearerr(hFile);
size_t ReadSize=fread(Data,1,Size,hFile);
if (ferror(hFile))
- return(-1);
- return((int)ReadSize);
+ return -1;
+ return (int)ReadSize;
+#endif
#endif
}
@@ -433,47 +481,58 @@ void File::Seek(int64 Offset,int Method)
bool File::RawSeek(int64 Offset,int Method)
{
- if (hFile==BAD_HANDLE)
- return(true);
+ if (hFile==FILE_BAD_HANDLE)
+ return true;
if (Offset<0 && Method!=SEEK_SET)
{
Offset=(Method==SEEK_CUR ? Tell():FileLength())+Offset;
Method=SEEK_SET;
}
-#ifdef _WIN_32
+#ifdef _WIN_ALL
LONG HighDist=(LONG)(Offset>>32);
if (SetFilePointer(hFile,(LONG)Offset,&HighDist,Method)==0xffffffff &&
GetLastError()!=NO_ERROR)
- return(false);
+ return false;
#else
LastWrite=false;
-#if defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) && !defined(__VMS)
+#ifdef FILE_USE_OPEN
+ if (lseek(hFile,(off_t)Offset,Method)==-1)
+ return false;
+#elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE) && !defined(__VMS)
if (fseeko(hFile,Offset,Method)!=0)
+ return false;
#else
if (fseek(hFile,(long)Offset,Method)!=0)
+ return false;
#endif
- return(false);
#endif
- return(true);
+ return true;
}
int64 File::Tell()
{
-#ifdef _WIN_32
+ if (hFile==FILE_BAD_HANDLE)
+ if (AllowExceptions)
+ ErrHandler.SeekError(FileName);
+ else
+ return -1;
+#ifdef _WIN_ALL
LONG HighDist=0;
uint LowDist=SetFilePointer(hFile,0,&HighDist,FILE_CURRENT);
if (LowDist==0xffffffff && GetLastError()!=NO_ERROR)
if (AllowExceptions)
ErrHandler.SeekError(FileName);
else
- return(-1);
- return(INT32TO64(HighDist,LowDist));
+ return -1;
+ return INT32TO64(HighDist,LowDist);
#else
-#if defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE)
- return(ftello(hFile));
+#ifdef FILE_USE_OPEN
+ return lseek(hFile,0,SEEK_CUR);
+#elif defined(_LARGEFILE_SOURCE) && !defined(_OSF_SOURCE)
+ return ftello(hFile);
#else
- return(ftell(hFile));
+ return ftell(hFile);
#endif
#endif
}
@@ -481,13 +540,21 @@ int64 File::Tell()
void File::Prealloc(int64 Size)
{
-#ifdef _WIN_32
+#ifdef _WIN_ALL
if (RawSeek(Size,SEEK_SET))
{
Truncate();
Seek(0,SEEK_SET);
}
#endif
+
+#if defined(_UNIX) && defined(USE_FALLOCATE)
+ // fallocate is rather new call. Only latest kernels support it.
+ // So we are not using it by default yet.
+ int fd = GetFD();
+ if (fd >= 0)
+ fallocate(fd, 0, 0, Size);
+#endif
}
@@ -495,7 +562,7 @@ byte File::GetByte()
{
byte Byte=0;
Read(&Byte,1);
- return(Byte);
+ return Byte;
}
@@ -507,27 +574,46 @@ void File::PutByte(byte Byte)
bool File::Truncate()
{
-#ifdef _WIN_32
- return(SetEndOfFile(hFile)==TRUE);
+#ifdef _WIN_ALL
+ return SetEndOfFile(hFile)==TRUE;
#else
- return(false);
+ return ftruncate(GetFD(),(off_t)Tell())==0;
+#endif
+}
+
+
+void File::Flush()
+{
+#ifdef _WIN_ALL
+ FlushFileBuffers(hFile);
+#else
+#ifndef FILE_USE_OPEN
+ fflush(hFile);
+#endif
+ fsync(GetFD());
#endif
}
void File::SetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta)
{
-#ifdef _WIN_32
+#ifdef _WIN_ALL
+ // Workaround for OpenIndiana NAS time bug. If we cannot create a file
+ // in write only mode, we need to flush the write buffer before calling
+ // SetFileTime or file time will not be changed.
+ if (CreateMode!=FMF_UNDEFINED && (CreateMode & FMF_WRITE)==0)
+ FlushFileBuffers(hFile);
+
bool sm=ftm!=NULL && ftm->IsSet();
bool sc=ftc!=NULL && ftc->IsSet();
bool sa=fta!=NULL && fta->IsSet();
FILETIME fm,fc,fa;
if (sm)
- ftm->GetWin32(&fm);
+ ftm->GetWinFT(&fm);
if (sc)
- ftc->GetWin32(&fc);
+ ftc->GetWinFT(&fc);
if (sa)
- fta->GetWin32(&fa);
+ fta->GetWinFT(&fa);
SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL);
#endif
}
@@ -535,29 +621,47 @@ void File::SetOpenFileTime(RarTime *ftm,RarTime *ftc,RarTime *fta)
void File::SetCloseFileTime(RarTime *ftm,RarTime *fta)
{
-#if defined(_UNIX) || defined(_EMX)
+// Android APP_PLATFORM := android-14 does not support futimens and futimes.
+// Newer platforms support futimens, but fail on Android 4.2.
+// We have to use utime for Android.
+// Also we noticed futimens fail to set timestamps on NTFS partition
+// mounted to virtual Linux x86 machine, but utimensat worked correctly.
+// So we set timestamps for already closed files in Unix.
+#ifdef _UNIX
SetCloseFileTimeByName(FileName,ftm,fta);
#endif
}
-void File::SetCloseFileTimeByName(const char *Name,RarTime *ftm,RarTime *fta)
+void File::SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta)
{
-#if defined(_UNIX) || defined(_EMX)
+#ifdef _UNIX
bool setm=ftm!=NULL && ftm->IsSet();
bool seta=fta!=NULL && fta->IsSet();
if (setm || seta)
{
- struct utimbuf ut;
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
+
+#ifdef UNIX_TIME_NS
+ timespec times[2];
+ times[0].tv_sec=seta ? fta->GetUnix() : 0;
+ times[0].tv_nsec=seta ? long(fta->GetUnixNS()%1000000000) : UTIME_NOW;
+ times[1].tv_sec=setm ? ftm->GetUnix() : 0;
+ times[1].tv_nsec=setm ? long(ftm->GetUnixNS()%1000000000) : UTIME_NOW;
+ utimensat(AT_FDCWD,NameA,times,0);
+#else
+ utimbuf ut;
if (setm)
ut.modtime=ftm->GetUnix();
else
- ut.modtime=fta->GetUnix();
+ ut.modtime=fta->GetUnix(); // Need to set something, cannot left it 0.
if (seta)
ut.actime=fta->GetUnix();
else
- ut.actime=ut.modtime;
- utime(Name,&ut);
+ ut.actime=ut.modtime; // Need to set something, cannot left it 0.
+ utime(NameA,&ut);
+#endif
}
#endif
}
@@ -565,99 +669,46 @@ void File::SetCloseFileTimeByName(const char *Name,RarTime *ftm,RarTime *fta)
void File::GetOpenFileTime(RarTime *ft)
{
-#ifdef _WIN_32
+#ifdef _WIN_ALL
FILETIME FileTime;
GetFileTime(hFile,NULL,NULL,&FileTime);
- *ft=FileTime;
+ ft->SetWinFT(&FileTime);
#endif
#if defined(_UNIX) || defined(_EMX)
struct stat st;
- fstat(fileno(hFile),&st);
- *ft=st.st_mtime;
+ fstat(GetFD(),&st);
+ ft->SetUnix(st.st_mtime);
#endif
}
int64 File::FileLength()
{
- SaveFilePos SavePos(*this);
+ int64 SavePos=Tell();
Seek(0,SEEK_END);
- return(Tell());
-}
-
-
-void File::SetHandleType(FILE_HANDLETYPE Type)
-{
- HandleType=Type;
+ int64 Length=Tell();
+ Seek(SavePos,SEEK_SET);
+ return Length;
}
bool File::IsDevice()
{
- if (hFile==BAD_HANDLE)
- return(false);
-#ifdef _WIN_32
+ if (hFile==FILE_BAD_HANDLE)
+ return false;
+#ifdef _WIN_ALL
uint Type=GetFileType(hFile);
- return(Type==FILE_TYPE_CHAR || Type==FILE_TYPE_PIPE);
+ return Type==FILE_TYPE_CHAR || Type==FILE_TYPE_PIPE;
#else
- return(isatty(fileno(hFile)));
+ return isatty(GetFD());
#endif
}
-#ifndef SFX_MODULE
-void File::fprintf(const char *fmt,...)
-{
- va_list argptr;
- va_start(argptr,fmt);
- safebuf char Msg[2*NM+1024],OutMsg[2*NM+1024];
- vsprintf(Msg,fmt,argptr);
-#ifdef _WIN_32
- for (int Src=0,Dest=0;;Src++)
- {
- char CurChar=Msg[Src];
- if (CurChar=='\n')
- OutMsg[Dest++]='\r';
- OutMsg[Dest++]=CurChar;
- if (CurChar==0)
- break;
- }
-#else
- strcpy(OutMsg,Msg);
-#endif
- Write(OutMsg,strlen(OutMsg));
- va_end(argptr);
-}
-#endif
-
-
-bool File::RemoveCreated()
-{
- RemoveCreatedActive++;
- bool RetCode=true;
- for (int I=0;ISetExceptions(false);
- bool Success;
- if (CreatedFiles[I]->NewFile)
- Success=CreatedFiles[I]->Delete();
- else
- Success=CreatedFiles[I]->Close();
- if (Success)
- CreatedFiles[I]=NULL;
- else
- RetCode=false;
- }
- RemoveCreatedActive--;
- return(RetCode);
-}
-
-
#ifndef SFX_MODULE
int64 File::Copy(File &Dest,int64 Length)
{
- Array Buffer(0x10000);
+ Array Buffer(File::CopyBufferSize());
int64 CopySize=0;
bool CopyAll=(Length==INT64NDF);
@@ -665,14 +716,30 @@ int64 File::Copy(File &Dest,int64 Length)
{
Wait();
size_t SizeToRead=(!CopyAll && Length<(int64)Buffer.Size()) ? (size_t)Length:Buffer.Size();
- int ReadSize=Read(&Buffer[0],SizeToRead);
+ byte *Buf=&Buffer[0];
+ int ReadSize=Read(Buf,SizeToRead);
if (ReadSize==0)
break;
- Dest.Write(&Buffer[0],ReadSize);
+ size_t WriteSize=ReadSize;
+#ifdef _WIN_ALL
+ // For FAT32 USB flash drives in Windows if first write is 4 KB or more,
+ // write caching is disabled and "write through" is enabled, resulting
+ // in bad performance, especially for many small files. It happens when
+ // we create SFX archive on USB drive, because SFX module is written first.
+ // So we split the first write to small 1 KB followed by rest of data.
+ if (CopySize==0 && WriteSize>=4096)
+ {
+ const size_t FirstWrite=1024;
+ Dest.Write(Buf,FirstWrite);
+ Buf+=FirstWrite;
+ WriteSize-=FirstWrite;
+ }
+#endif
+ Dest.Write(Buf,WriteSize);
CopySize+=ReadSize;
if (!CopyAll)
Length-=ReadSize;
}
- return(CopySize);
+ return CopySize;
}
#endif
diff --git a/libunrar/file.hpp b/libunrar/file.hpp
index 3e17ae9..a343ce6 100644
--- a/libunrar/file.hpp
+++ b/libunrar/file.hpp
@@ -1,34 +1,55 @@
#ifndef _RAR_FILE_
#define _RAR_FILE_
-#ifdef _WIN_32
-typedef HANDLE FileHandle;
-#define BAD_HANDLE INVALID_HANDLE_VALUE
+#define FILE_USE_OPEN
+
+#ifdef _WIN_ALL
+ typedef HANDLE FileHandle;
+ #define FILE_BAD_HANDLE INVALID_HANDLE_VALUE
+#elif defined(FILE_USE_OPEN)
+ typedef off_t FileHandle;
+ #define FILE_BAD_HANDLE -1
#else
-typedef FILE* FileHandle;
-#define BAD_HANDLE NULL
+ typedef FILE* FileHandle;
+ #define FILE_BAD_HANDLE NULL
#endif
class RAROptions;
-enum FILE_HANDLETYPE {FILE_HANDLENORMAL,FILE_HANDLESTD,FILE_HANDLEERR};
+enum FILE_HANDLETYPE {FILE_HANDLENORMAL,FILE_HANDLESTD};
enum FILE_ERRORTYPE {FILE_SUCCESS,FILE_NOTFOUND,FILE_READERROR};
-struct FileStat
-{
- uint FileAttr;
- uint FileTime;
- int64 FileSize;
- bool IsDir;
+enum FILE_MODE_FLAGS {
+ // Request read only access to file. Default for Open.
+ FMF_READ=0,
+
+ // Request both read and write access to file. Default for Create.
+ FMF_UPDATE=1,
+
+ // Request write only access to file.
+ FMF_WRITE=2,
+
+ // Open files which are already opened for write by other programs.
+ FMF_OPENSHARED=4,
+
+ // Open files only if no other program is opened it even in shared mode.
+ FMF_OPENEXCLUSIVE=8,
+
+ // Provide read access to created file for other programs.
+ FMF_SHAREREAD=16,
+
+ // Use standard NTFS names without trailing dots and spaces.
+ FMF_STANDARDNAMES=32,
+
+ // Mode flags are not defined yet.
+ FMF_UNDEFINED=256
};
class File
{
private:
- void AddFileToList(FileHandle hFile);
-
FileHandle hFile;
bool LastWrite;
FILE_HANDLETYPE HandleType;
@@ -37,62 +58,85 @@ class File
bool NewFile;
bool AllowDelete;
bool AllowExceptions;
-#ifdef _WIN_32
+#ifdef _WIN_ALL
bool NoSequentialRead;
+ uint CreateMode;
#endif
+ bool PreserveAtime;
protected:
- bool OpenShared;
+ bool OpenShared; // Set by 'Archive' class.
public:
- char FileName[NM];
- wchar FileNameW[NM];
+ wchar FileName[NM];
FILE_ERRORTYPE ErrorType;
-
- uint CloseCount;
public:
File();
virtual ~File();
void operator = (File &SrcFile);
- bool Open(const char *Name,const wchar *NameW=NULL,bool OpenShared=false,bool Update=false);
- void TOpen(const char *Name,const wchar *NameW=NULL);
- bool WOpen(const char *Name,const wchar *NameW=NULL);
- bool Create(const char *Name,const wchar *NameW=NULL,bool ShareRead=true);
- void TCreate(const char *Name,const wchar *NameW=NULL,bool ShareRead=true);
- bool WCreate(const char *Name,const wchar *NameW=NULL,bool ShareRead=true);
- bool Close();
- void Flush();
+
+ // Several functions below are 'virtual', because they are redefined
+ // by Archive for QOpen and by MultiFile for split files in WinRAR.
+ virtual bool Open(const wchar *Name,uint Mode=FMF_READ);
+ void TOpen(const wchar *Name);
+ bool WOpen(const wchar *Name);
+ bool Create(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
+ void TCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
+ bool WCreate(const wchar *Name,uint Mode=FMF_UPDATE|FMF_SHAREREAD);
+ virtual bool Close(); // 'virtual' for MultiFile class.
bool Delete();
- bool Rename(const char *NewName,const wchar *NewNameW=NULL);
- void Write(const void *Data,size_t Size);
- int Read(void *Data,size_t Size);
+ bool Rename(const wchar *NewName);
+ bool Write(const void *Data,size_t Size);
+ virtual int Read(void *Data,size_t Size);
int DirectRead(void *Data,size_t Size);
- void Seek(int64 Offset,int Method);
+ virtual void Seek(int64 Offset,int Method);
bool RawSeek(int64 Offset,int Method);
- int64 Tell();
+ virtual int64 Tell();
void Prealloc(int64 Size);
byte GetByte();
void PutByte(byte Byte);
bool Truncate();
+ void Flush();
void SetOpenFileTime(RarTime *ftm,RarTime *ftc=NULL,RarTime *fta=NULL);
void SetCloseFileTime(RarTime *ftm,RarTime *fta=NULL);
- static void SetCloseFileTimeByName(const char *Name,RarTime *ftm,RarTime *fta);
+ static void SetCloseFileTimeByName(const wchar *Name,RarTime *ftm,RarTime *fta);
void GetOpenFileTime(RarTime *ft);
- bool IsOpened() {return(hFile!=BAD_HANDLE);};
+ virtual bool IsOpened() {return hFile!=FILE_BAD_HANDLE;} // 'virtual' for MultiFile class.
int64 FileLength();
- void SetHandleType(FILE_HANDLETYPE Type);
- FILE_HANDLETYPE GetHandleType() {return(HandleType);};
+ void SetHandleType(FILE_HANDLETYPE Type) {HandleType=Type;}
+ FILE_HANDLETYPE GetHandleType() {return HandleType;}
bool IsDevice();
- void fprintf(const char *fmt,...);
static bool RemoveCreated();
- FileHandle GetHandle() {return(hFile);};
- void SetIgnoreReadErrors(bool Mode) {IgnoreReadErrors=Mode;};
- char *GetName() {return(FileName);}
+ FileHandle GetHandle() {return hFile;}
+ void SetHandle(FileHandle Handle) {Close();hFile=Handle;}
+ void SetIgnoreReadErrors(bool Mode) {IgnoreReadErrors=Mode;}
int64 Copy(File &Dest,int64 Length=INT64NDF);
void SetAllowDelete(bool Allow) {AllowDelete=Allow;}
void SetExceptions(bool Allow) {AllowExceptions=Allow;}
-#ifdef _WIN_32
+#ifdef _WIN_ALL
void RemoveSequentialFlag() {NoSequentialRead=true;}
#endif
+ void SetPreserveAtime(bool Preserve) {PreserveAtime=Preserve;}
+#ifdef _UNIX
+ int GetFD()
+ {
+#ifdef FILE_USE_OPEN
+ return hFile;
+#else
+ return fileno(hFile);
+#endif
+ }
+#endif
+ static size_t CopyBufferSize()
+ {
+#ifdef _WIN_ALL
+ // USB flash performance is poor with 64 KB buffer, 256+ KB resolved it.
+ // For copying from HDD to same HDD the best performance was with 256 KB
+ // buffer in XP and with 1 MB buffer in Win10.
+ return WinNT()==WNT_WXP ? 0x40000:0x100000;
+#else
+ return 0x100000;
+#endif
+ }
};
#endif
diff --git a/libunrar/filefn.cpp b/libunrar/filefn.cpp
index 215f797..57a334d 100644
--- a/libunrar/filefn.cpp
+++ b/libunrar/filefn.cpp
@@ -1,147 +1,126 @@
#include "rar.hpp"
-MKDIR_CODE MakeDir(const char *Name,const wchar *NameW,bool SetAttr,uint Attr)
+MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr)
{
-#ifdef _WIN_32
- int Success;
- if (WinNT() && NameW!=NULL && *NameW!=0)
- Success=CreateDirectoryW(NameW,NULL);
- else
- Success=CreateDirectory(Name,NULL);
- if (Success)
+#ifdef _WIN_ALL
+ // Windows automatically removes dots and spaces in the end of directory
+ // name. So we detect such names and process them with \\?\ prefix.
+ wchar *LastChar=PointToLastChar(Name);
+ bool Special=*LastChar=='.' || *LastChar==' ';
+ BOOL RetCode=Special ? FALSE : CreateDirectory(Name,NULL);
+ if (RetCode==0 && !FileExist(Name))
+ {
+ wchar LongName[NM];
+ if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
+ RetCode=CreateDirectory(LongName,NULL);
+ }
+ if (RetCode!=0) // Non-zero return code means success for CreateDirectory.
{
if (SetAttr)
- SetFileAttr(Name,NameW,Attr);
- return(MKDIR_SUCCESS);
+ SetFileAttr(Name,Attr);
+ return MKDIR_SUCCESS;
}
int ErrCode=GetLastError();
if (ErrCode==ERROR_FILE_NOT_FOUND || ErrCode==ERROR_PATH_NOT_FOUND)
- return(MKDIR_BADPATH);
- return(MKDIR_ERROR);
-#endif
-#ifdef _EMX
-#ifdef _DJGPP
- if (mkdir(Name,(Attr & FA_RDONLY) ? 0:S_IWUSR)==0)
-#else
- if (__mkdir(Name)==0)
-#endif
- {
- if (SetAttr)
- SetFileAttr(Name,NameW,Attr);
- return(MKDIR_SUCCESS);
- }
- return(errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR);
-#endif
-#ifdef _UNIX
+ return MKDIR_BADPATH;
+ return MKDIR_ERROR;
+#elif defined(_UNIX)
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
mode_t uattr=SetAttr ? (mode_t)Attr:0777;
- int ErrCode=Name==NULL ? -1:mkdir(Name,uattr);
+ int ErrCode=mkdir(NameA,uattr);
if (ErrCode==-1)
- return(errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR);
- return(MKDIR_SUCCESS);
+ return errno==ENOENT ? MKDIR_BADPATH:MKDIR_ERROR;
+ return MKDIR_SUCCESS;
+#else
+ return MKDIR_ERROR;
#endif
}
-bool CreatePath(const char *Path,const wchar *PathW,bool SkipLastName)
+bool CreatePath(const wchar *Path,bool SkipLastName)
{
-#if defined(_WIN_32) || defined(_EMX)
+ if (Path==NULL || *Path==0)
+ return false;
+
+#if defined(_WIN_ALL) || defined(_EMX)
uint DirAttr=0;
#else
uint DirAttr=0777;
#endif
-#ifdef UNICODE_SUPPORTED
- bool Wide=PathW!=NULL && *PathW!=0 && UnicodeEnabled();
-#else
- bool Wide=false;
-#endif
- bool IgnoreAscii=false;
+
bool Success=true;
- const char *s=Path;
- for (int PosW=0;;PosW++)
+ for (const wchar *s=Path;*s!=0;s++)
{
- if (s==NULL || s-Path>=NM || *s==0)
- IgnoreAscii=true;
- if (Wide && (PosW>=NM || PathW[PosW]==0) || !Wide && IgnoreAscii)
+ wchar DirName[NM];
+ if (s-Path>=ASIZE(DirName))
break;
- if (Wide && PathW[PosW]==CPATHDIVIDER || !Wide && *s==CPATHDIVIDER)
+
+ // Process all kinds of path separators, so user can enter Unix style
+ // path in Windows or Windows in Unix. s>Path check avoids attempting
+ // creating an empty directory for paths starting from path separator.
+ if (IsPathDiv(*s) && s>Path)
{
- wchar *DirPtrW=NULL,DirNameW[NM];
- if (Wide)
- {
- strncpyw(DirNameW,PathW,PosW);
- DirNameW[PosW]=0;
- DirPtrW=DirNameW;
- }
- char DirName[NM];
- if (IgnoreAscii)
- WideToChar(DirPtrW,DirName);
- else
- {
-#ifndef DBCS_SUPPORTED
- if (*s!=CPATHDIVIDER)
- for (const char *n=s;*n!=0 && n-PathIsSet();
bool sc=ftc!=NULL && ftc->IsSet();
bool sa=fta!=NULL && fta->IsSet();
- unsigned int DirAttr=GetFileAttr(Name,NameW);
- bool ResetAttr=(DirAttr!=0xffffffff && (DirAttr & FA_RDONLY)!=0);
+ uint DirAttr=GetFileAttr(Name);
+ bool ResetAttr=(DirAttr!=0xffffffff && (DirAttr & FILE_ATTRIBUTE_READONLY)!=0);
if (ResetAttr)
- SetFileAttr(Name,NameW,0);
+ SetFileAttr(Name,0);
- wchar DirNameW[NM];
- GetWideName(Name,NameW,DirNameW);
- HANDLE hFile=CreateFileW(DirNameW,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
+ HANDLE hFile=CreateFile(Name,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
+ if (hFile==INVALID_HANDLE_VALUE)
+ {
+ wchar LongName[NM];
+ if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
+ hFile=CreateFile(LongName,GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,
+ NULL,OPEN_EXISTING,FILE_FLAG_BACKUP_SEMANTICS,NULL);
+ }
+
if (hFile==INVALID_HANDLE_VALUE)
return;
FILETIME fm,fc,fa;
if (sm)
- ftm->GetWin32(&fm);
+ ftm->GetWinFT(&fm);
if (sc)
- ftc->GetWin32(&fc);
+ ftc->GetWinFT(&fc);
if (sa)
- fta->GetWin32(&fa);
+ fta->GetWinFT(&fa);
SetFileTime(hFile,sc ? &fc:NULL,sa ? &fa:NULL,sm ? &fm:NULL);
CloseHandle(hFile);
if (ResetAttr)
- SetFileAttr(Name,NameW,DirAttr);
+ SetFileAttr(Name,DirAttr);
#endif
#if defined(_UNIX) || defined(_EMX)
File::SetCloseFileTimeByName(Name,ftm,fta);
@@ -149,146 +128,100 @@ void SetDirTime(const char *Name,const wchar *NameW,RarTime *ftm,RarTime *ftc,Ra
}
-bool IsRemovable(const char *Name)
+bool IsRemovable(const wchar *Name)
{
-#ifdef _WIN_32
- char Root[NM];
- GetPathRoot(Name,Root);
- int Type=GetDriveType(*Root ? Root:NULL);
- return(Type==DRIVE_REMOVABLE || Type==DRIVE_CDROM);
-#elif defined(_EMX)
- char Drive=etoupper(Name[0]);
- return((Drive=='A' || Drive=='B') && Name[1]==':');
+#if defined(_WIN_ALL)
+ wchar Root[NM];
+ GetPathRoot(Name,Root,ASIZE(Root));
+ int Type=GetDriveType(*Root!=0 ? Root:NULL);
+ return Type==DRIVE_REMOVABLE || Type==DRIVE_CDROM;
#else
- return(false);
+ return false;
#endif
}
#ifndef SFX_MODULE
-int64 GetFreeDisk(const char *Name)
+int64 GetFreeDisk(const wchar *Name)
{
-#ifdef _WIN_32
- char Root[NM];
- GetPathRoot(Name,Root);
-
- typedef BOOL (WINAPI *GETDISKFREESPACEEX)(
- LPCTSTR,PULARGE_INTEGER,PULARGE_INTEGER,PULARGE_INTEGER
- );
- static GETDISKFREESPACEEX pGetDiskFreeSpaceEx=NULL;
-
- if (pGetDiskFreeSpaceEx==NULL)
- {
- HMODULE hKernel=GetModuleHandle("kernel32.dll");
- if (hKernel!=NULL)
- pGetDiskFreeSpaceEx=(GETDISKFREESPACEEX)GetProcAddress(hKernel,"GetDiskFreeSpaceExA");
- }
- if (pGetDiskFreeSpaceEx!=NULL)
- {
- GetFilePath(Name,Root,ASIZE(Root));
- ULARGE_INTEGER uiTotalSize,uiTotalFree,uiUserFree;
- uiUserFree.u.LowPart=uiUserFree.u.HighPart=0;
- if (pGetDiskFreeSpaceEx(*Root ? Root:NULL,&uiUserFree,&uiTotalSize,&uiTotalFree) &&
- uiUserFree.u.HighPart<=uiTotalFree.u.HighPart)
- return(INT32TO64(uiUserFree.u.HighPart,uiUserFree.u.LowPart));
- }
-
- // We are here if we failed to load GetDiskFreeSpaceExA.
- DWORD SectorsPerCluster,BytesPerSector,FreeClusters,TotalClusters;
- if (!GetDiskFreeSpace(*Root ? Root:NULL,&SectorsPerCluster,&BytesPerSector,&FreeClusters,&TotalClusters))
- return(1457664);
- int64 FreeSize=SectorsPerCluster*BytesPerSector;
- FreeSize=FreeSize*FreeClusters;
- return(FreeSize);
-#elif defined(_BEOS)
- char Root[NM];
+#ifdef _WIN_ALL
+ wchar Root[NM];
GetFilePath(Name,Root,ASIZE(Root));
- dev_t Dev=dev_for_path(*Root ? Root:".");
- if (Dev<0)
- return(1457664);
- fs_info Info;
- if (fs_stat_dev(Dev,&Info)!=0)
- return(1457664);
- int64 FreeSize=Info.block_size;
- FreeSize=FreeSize*Info.free_blocks;
- return(FreeSize);
+
+ ULARGE_INTEGER uiTotalSize,uiTotalFree,uiUserFree;
+ uiUserFree.u.LowPart=uiUserFree.u.HighPart=0;
+ if (GetDiskFreeSpaceEx(*Root!=0 ? Root:NULL,&uiUserFree,&uiTotalSize,&uiTotalFree) &&
+ uiUserFree.u.HighPart<=uiTotalFree.u.HighPart)
+ return INT32TO64(uiUserFree.u.HighPart,uiUserFree.u.LowPart);
+ return 0;
#elif defined(_UNIX)
- return(1457664);
-#elif defined(_EMX)
- int Drive=IsDiskLetter(Name) ? etoupper(Name[0])-'A'+1:0;
-#ifndef _DJGPP
- if (_osmode == OS2_MODE)
- {
- FSALLOCATE fsa;
- if (DosQueryFSInfo(Drive,1,&fsa,sizeof(fsa))!=0)
- return(1457664);
- int64 FreeSize=fsa.cSectorUnit*fsa.cbSector;
- FreeSize=FreeSize*fsa.cUnitAvail;
- return(FreeSize);
- }
- else
-#endif
- {
- union REGS regs,outregs;
- memset(®s,0,sizeof(regs));
- regs.h.ah=0x36;
- regs.h.dl=Drive;
-#ifdef _DJGPP
- int86 (0x21,®s,&outregs);
+ wchar Root[NM];
+ GetFilePath(Name,Root,ASIZE(Root));
+ char RootA[NM];
+ WideToChar(Root,RootA,ASIZE(RootA));
+ struct statvfs sfs;
+ if (statvfs(*RootA!=0 ? RootA:".",&sfs)!=0)
+ return 0;
+ int64 FreeSize=sfs.f_bsize;
+ FreeSize=FreeSize*sfs.f_bavail;
+ return FreeSize;
#else
- _int86 (0x21,®s,&outregs);
-#endif
- if (outregs.x.ax==0xffff)
- return(1457664);
- int64 FreeSize=outregs.x.ax*outregs.x.cx;
- FreeSize=FreeSize*outregs.x.bx;
- return(FreeSize);
- }
-#else
- #define DISABLEAUTODETECT
- return(1457664);
+ return 0;
#endif
}
#endif
-bool FileExist(const char *Name,const wchar *NameW)
+#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
+// Return 'true' for FAT and FAT32, so we can adjust the maximum supported
+// file size to 4 GB for these file systems.
+bool IsFAT(const wchar *Name)
{
-#ifdef _WIN_32
- if (WinNT() && NameW!=NULL && *NameW!=0)
- return(GetFileAttributesW(NameW)!=0xffffffff);
- else
- return(GetFileAttributes(Name)!=0xffffffff);
+ wchar Root[NM];
+ GetPathRoot(Name,Root,ASIZE(Root));
+ wchar FileSystem[MAX_PATH+1];
+ if (GetVolumeInformation(Root,NULL,0,NULL,NULL,NULL,FileSystem,ASIZE(FileSystem)))
+ return wcscmp(FileSystem,L"FAT")==0 || wcscmp(FileSystem,L"FAT32")==0;
+ return false;
+}
+#endif
+
+
+bool FileExist(const wchar *Name)
+{
+#ifdef _WIN_ALL
+ return GetFileAttr(Name)!=0xffffffff;
#elif defined(ENABLE_ACCESS)
- return(access(Name,0)==0);
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
+ return access(NameA,0)==0;
#else
- struct FindData FD;
- return(FindFile::FastFind(Name,NameW,&FD));
+ FindData FD;
+ return FindFile::FastFind(Name,&FD);
#endif
}
+
-
-bool WildFileExist(const char *Name,const wchar *NameW)
+bool WildFileExist(const wchar *Name)
{
- if (IsWildcard(Name,NameW))
+ if (IsWildcard(Name))
{
FindFile Find;
Find.SetMask(Name);
- Find.SetMaskW(NameW);
- struct FindData fd;
- return(Find.Next(&fd));
+ FindData fd;
+ return Find.Next(&fd);
}
- return(FileExist(Name,NameW));
+ return FileExist(Name);
}
bool IsDir(uint Attr)
{
-#if defined (_WIN_32) || defined(_EMX)
- return(Attr!=0xffffffff && (Attr & 0x10)!=0);
+#ifdef _WIN_ALL
+ return Attr!=0xffffffff && (Attr & FILE_ATTRIBUTE_DIRECTORY)!=0;
#endif
#if defined(_UNIX)
- return((Attr & 0xF000)==0x4000);
+ return (Attr & 0xF000)==0x4000;
#endif
}
@@ -296,28 +229,20 @@ bool IsDir(uint Attr)
bool IsUnreadable(uint Attr)
{
#if defined(_UNIX) && defined(S_ISFIFO) && defined(S_ISSOCK) && defined(S_ISCHR)
- return(S_ISFIFO(Attr) || S_ISSOCK(Attr) || S_ISCHR(Attr));
-#endif
- return(false);
-}
-
-
-bool IsLabel(uint Attr)
-{
-#if defined (_WIN_32) || defined(_EMX)
- return((Attr & 8)!=0);
-#else
- return(false);
+ return S_ISFIFO(Attr) || S_ISSOCK(Attr) || S_ISCHR(Attr);
#endif
+ return false;
}
bool IsLink(uint Attr)
{
#ifdef _UNIX
- return((Attr & 0xF000)==0xA000);
+ return (Attr & 0xF000)==0xA000;
+#elif defined(_WIN_ALL)
+ return (Attr & FILE_ATTRIBUTE_REPARSE_POINT)!=0;
#else
- return(false);
+ return false;
#endif
}
@@ -328,180 +253,134 @@ bool IsLink(uint Attr)
bool IsDeleteAllowed(uint FileAttr)
{
-#if defined(_WIN_32) || defined(_EMX)
- return((FileAttr & (FA_RDONLY|FA_SYSTEM|FA_HIDDEN))==0);
+#ifdef _WIN_ALL
+ return (FileAttr & (FILE_ATTRIBUTE_READONLY|FILE_ATTRIBUTE_SYSTEM|FILE_ATTRIBUTE_HIDDEN))==0;
#else
- return((FileAttr & (S_IRUSR|S_IWUSR))==(S_IRUSR|S_IWUSR));
+ return (FileAttr & (S_IRUSR|S_IWUSR))==(S_IRUSR|S_IWUSR);
#endif
}
-void PrepareToDelete(const char *Name,const wchar *NameW)
+void PrepareToDelete(const wchar *Name)
{
-#if defined(_WIN_32) || defined(_EMX)
- SetFileAttr(Name,NameW,0);
+#if defined(_WIN_ALL) || defined(_EMX)
+ SetFileAttr(Name,0);
#endif
#ifdef _UNIX
- chmod(Name,S_IRUSR|S_IWUSR|S_IXUSR);
+ if (Name!=NULL)
+ {
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
+ chmod(NameA,S_IRUSR|S_IWUSR|S_IXUSR);
+ }
#endif
}
-uint GetFileAttr(const char *Name,const wchar *NameW)
+uint GetFileAttr(const wchar *Name)
{
-#ifdef _WIN_32
- if (WinNT() && NameW!=NULL && *NameW!=0)
- return(GetFileAttributesW(NameW));
- else
- return(GetFileAttributes(Name));
-#elif defined(_DJGPP)
- return(_chmod(Name,0));
+#ifdef _WIN_ALL
+ DWORD Attr=GetFileAttributes(Name);
+ if (Attr==0xffffffff)
+ {
+ wchar LongName[NM];
+ if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
+ Attr=GetFileAttributes(LongName);
+ }
+ return Attr;
#else
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
struct stat st;
- if (stat(Name,&st)!=0)
- return(0);
-#ifdef _EMX
- return(st.st_attr);
-#else
- return(st.st_mode);
-#endif
+ if (stat(NameA,&st)!=0)
+ return 0;
+ return st.st_mode;
#endif
}
-bool SetFileAttr(const char *Name,const wchar *NameW,uint Attr)
+bool SetFileAttr(const wchar *Name,uint Attr)
{
- bool Success;
-#ifdef _WIN_32
- if (WinNT() && NameW!=NULL && *NameW!=0)
- Success=SetFileAttributesW(NameW,Attr)!=0;
- else
- Success=SetFileAttributes(Name,Attr)!=0;
-#elif defined(_DJGPP)
- Success=_chmod(Name,1,Attr)!=-1;
-#elif defined(_EMX)
- Success=__chmod(Name,1,Attr)!=-1;
+#ifdef _WIN_ALL
+ bool Success=SetFileAttributes(Name,Attr)!=0;
+ if (!Success)
+ {
+ wchar LongName[NM];
+ if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
+ Success=SetFileAttributes(LongName,Attr)!=0;
+ }
+ return Success;
#elif defined(_UNIX)
- Success=chmod(Name,(mode_t)Attr)==0;
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
+ return chmod(NameA,(mode_t)Attr)==0;
#else
- Success=false;
-#endif
- return(Success);
-}
-
-
-void ConvertNameToFull(const char *Src,char *Dest)
-{
-#ifdef _WIN_32
-#ifndef _WIN_CE
- char FullName[NM],*NamePtr;
- if (GetFullPathName(Src,sizeof(FullName),FullName,&NamePtr))
- strcpy(Dest,FullName);
- else
-#endif
- if (Src!=Dest)
- strcpy(Dest,Src);
-#else
- char FullName[NM];
- if (IsPathDiv(*Src) || IsDiskLetter(Src))
- strcpy(FullName,Src);
- else
- {
- if (getcwd(FullName,sizeof(FullName))==NULL)
- *FullName=0;
- else
- AddEndSlash(FullName);
- strcat(FullName,Src);
- }
- strcpy(Dest,FullName);
+ return false;
#endif
}
-#ifndef SFX_MODULE
-void ConvertNameToFull(const wchar *Src,wchar *Dest)
+#if 0
+wchar *MkTemp(wchar *Name,size_t MaxSize)
{
- if (Src==NULL || *Src==0)
- {
- *Dest=0;
- return;
- }
-#ifdef _WIN_32
-#ifndef _WIN_CE
- if (WinNT())
-#endif
- {
-#ifndef _WIN_CE
- wchar FullName[NM],*NamePtr;
- if (GetFullPathNameW(Src,sizeof(FullName)/sizeof(FullName[0]),FullName,&NamePtr))
- strcpyw(Dest,FullName);
- else
-#endif
- if (Src!=Dest)
- strcpyw(Dest,Src);
- }
-#ifndef _WIN_CE
- else
- {
- char AnsiName[NM];
- WideToChar(Src,AnsiName);
- ConvertNameToFull(AnsiName,AnsiName);
- CharToWide(AnsiName,Dest);
- }
-#endif
-#else
- char AnsiName[NM];
- WideToChar(Src,AnsiName);
- ConvertNameToFull(AnsiName,AnsiName);
- CharToWide(AnsiName,Dest);
-#endif
-}
+ size_t Length=wcslen(Name);
+
+ RarTime CurTime;
+ CurTime.SetCurrentTime();
+
+ // We cannot use CurTime.GetWin() as is, because its lowest bits can
+ // have low informational value, like being a zero or few fixed numbers.
+ uint Random=(uint)(CurTime.GetWin()/100000);
+
+ // Using PID we guarantee that different RAR copies use different temp names
+ // even if started in exactly the same time.
+ uint PID=0;
+#ifdef _WIN_ALL
+ PID=(uint)GetCurrentProcessId();
+#elif defined(_UNIX)
+ PID=(uint)getpid();
#endif
-
-#ifndef SFX_MODULE
-char *MkTemp(char *Name)
-{
- size_t Length=strlen(Name);
- if (Length<=6)
- return(NULL);
- int Random=clock();
- for (int Attempt=0;;Attempt++)
+ for (uint Attempt=0;;Attempt++)
{
- sprintf(Name+Length-6,"%06u",Random+Attempt);
- Name[Length-4]='.';
+ uint Ext=Random%50000+Attempt;
+ wchar RndText[50];
+ swprintf(RndText,ASIZE(RndText),L"%u.%03u",PID,Ext);
+ if (Length+wcslen(RndText)>=MaxSize || Attempt==1000)
+ return NULL;
+ wcsncpyz(Name+Length,RndText,MaxSize-Length);
if (!FileExist(Name))
break;
- if (Attempt==1000)
- return(NULL);
}
- return(Name);
+ return Name;
}
#endif
-
-
-#ifndef SFX_MODULE
-uint CalcFileCRC(File *SrcFile,int64 Size,CALCCRC_SHOWMODE ShowMode)
+#if !defined(SFX_MODULE)
+void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size,uint Flags)
{
- SaveFilePos SavePos(*SrcFile);
- const size_t BufSize=0x10000;
- Array Data(BufSize);
- int64 BlockCount=0;
- uint DataCRC=0xffffffff;
-
-#if !defined(SILENT) && !defined(_WIN_CE)
- int64 FileLength=SrcFile->FileLength();
- if (ShowMode!=CALCCRC_SHOWNONE)
- {
- mprintf(St(MCalcCRC));
- mprintf(" ");
- }
-
+ int64 SavePos=SrcFile->Tell();
+#ifndef SILENT
+ int64 FileLength=Size==INT64NDF ? SrcFile->FileLength() : Size;
#endif
- SrcFile->Seek(0,SEEK_SET);
+ if ((Flags & (CALCFSUM_SHOWTEXT|CALCFSUM_SHOWPERCENT))!=0)
+ uiMsg(UIEVENT_FILESUMSTART);
+
+ if ((Flags & CALCFSUM_CURPOS)==0)
+ SrcFile->Seek(0,SEEK_SET);
+
+ const size_t BufSize=0x100000;
+ Array Data(BufSize);
+
+
+ DataHash HashCRC,HashBlake2;
+ HashCRC.Init(HASH_CRC32,Threads);
+ HashBlake2.Init(HASH_BLAKE2,Threads);
+
+ int64 BlockCount=0;
+ int64 TotalRead=0;
while (true)
{
size_t SizeToRead;
@@ -512,67 +391,113 @@ uint CalcFileCRC(File *SrcFile,int64 Size,CALCCRC_SHOWMODE ShowMode)
int ReadSize=SrcFile->Read(&Data[0],SizeToRead);
if (ReadSize==0)
break;
+ TotalRead+=ReadSize;
- ++BlockCount;
- if ((BlockCount & 15)==0)
+ if ((++BlockCount & 0xf)==0)
{
-#if !defined(SILENT) && !defined(_WIN_CE)
- if (ShowMode==CALCCRC_SHOWALL)
- mprintf("\b\b\b\b%3d%%",ToPercent(BlockCount*int64(BufSize),FileLength));
+#ifndef SILENT
+ if ((Flags & CALCFSUM_SHOWPROGRESS)!=0)
+ uiExtractProgress(TotalRead,FileLength,TotalRead,FileLength);
+ else
+ {
+ if ((Flags & CALCFSUM_SHOWPERCENT)!=0)
+ uiMsg(UIEVENT_FILESUMPROGRESS,ToPercent(TotalRead,FileLength));
+ }
#endif
Wait();
}
- DataCRC=CRC(DataCRC,&Data[0],ReadSize);
+
+ if (CRC32!=NULL)
+ HashCRC.Update(&Data[0],ReadSize);
+ if (Blake2!=NULL)
+ HashBlake2.Update(&Data[0],ReadSize);
+
if (Size!=INT64NDF)
Size-=ReadSize;
}
-#if !defined(SILENT) && !defined(_WIN_CE)
- if (ShowMode==CALCCRC_SHOWALL)
- mprintf("\b\b\b\b ");
-#endif
- return(DataCRC^0xffffffff);
+ SrcFile->Seek(SavePos,SEEK_SET);
+
+ if ((Flags & CALCFSUM_SHOWPERCENT)!=0)
+ uiMsg(UIEVENT_FILESUMEND);
+
+ if (CRC32!=NULL)
+ *CRC32=HashCRC.GetCRC32();
+ if (Blake2!=NULL)
+ {
+ HashValue Result;
+ HashBlake2.Result(&Result);
+ memcpy(Blake2,Result.Digest,sizeof(Result.Digest));
+ }
}
#endif
-bool RenameFile(const char *SrcName,const wchar *SrcNameW,const char *DestName,const wchar *DestNameW)
+bool RenameFile(const wchar *SrcName,const wchar *DestName)
{
- return(rename(SrcName,DestName)==0);
+#ifdef _WIN_ALL
+ bool Success=MoveFile(SrcName,DestName)!=0;
+ if (!Success)
+ {
+ wchar LongName1[NM],LongName2[NM];
+ if (GetWinLongPath(SrcName,LongName1,ASIZE(LongName1)) &&
+ GetWinLongPath(DestName,LongName2,ASIZE(LongName2)))
+ Success=MoveFile(LongName1,LongName2)!=0;
+ }
+ return Success;
+#else
+ char SrcNameA[NM],DestNameA[NM];
+ WideToChar(SrcName,SrcNameA,ASIZE(SrcNameA));
+ WideToChar(DestName,DestNameA,ASIZE(DestNameA));
+ bool Success=rename(SrcNameA,DestNameA)==0;
+ return Success;
+#endif
}
-bool DelFile(const char *Name)
+bool DelFile(const wchar *Name)
{
- return(DelFile(Name,NULL));
-}
-
-
-bool DelFile(const char *Name,const wchar *NameW)
-{
- return(remove(Name)==0);
+#ifdef _WIN_ALL
+ bool Success=DeleteFile(Name)!=0;
+ if (!Success)
+ {
+ wchar LongName[NM];
+ if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
+ Success=DeleteFile(LongName)!=0;
+ }
+ return Success;
+#else
+ char NameA[NM];
+ WideToChar(Name,NameA,ASIZE(NameA));
+ bool Success=remove(NameA)==0;
+ return Success;
+#endif
}
-
-
-#if defined(_WIN_32) && !defined(_WIN_CE) && !defined(SFX_MODULE)
-bool SetFileCompression(char *Name,wchar *NameW,bool State)
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+bool SetFileCompression(const wchar *Name,bool State)
{
- wchar FileNameW[NM];
- GetWideName(Name,NameW,FileNameW);
- HANDLE hFile=CreateFileW(FileNameW,FILE_READ_DATA|FILE_WRITE_DATA,
+ HANDLE hFile=CreateFile(Name,FILE_READ_DATA|FILE_WRITE_DATA,
FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL);
if (hFile==INVALID_HANDLE_VALUE)
- return(false);
+ {
+ wchar LongName[NM];
+ if (GetWinLongPath(Name,LongName,ASIZE(LongName)))
+ hFile=CreateFile(LongName,FILE_READ_DATA|FILE_WRITE_DATA,
+ FILE_SHARE_READ|FILE_SHARE_WRITE,NULL,OPEN_EXISTING,
+ FILE_FLAG_BACKUP_SEMANTICS|FILE_FLAG_SEQUENTIAL_SCAN,NULL);
+ }
+ if (hFile==INVALID_HANDLE_VALUE)
+ return false;
SHORT NewState=State ? COMPRESSION_FORMAT_DEFAULT:COMPRESSION_FORMAT_NONE;
DWORD Result;
int RetCode=DeviceIoControl(hFile,FSCTL_SET_COMPRESSION,&NewState,
sizeof(NewState),NULL,0,&Result,NULL);
CloseHandle(hFile);
- return(RetCode!=0);
+ return RetCode!=0;
}
#endif
@@ -583,3 +508,5 @@ bool SetFileCompression(char *Name,wchar *NameW,bool State)
+
+
diff --git a/libunrar/filefn.hpp b/libunrar/filefn.hpp
index 77df847..1bda9b5 100644
--- a/libunrar/filefn.hpp
+++ b/libunrar/filefn.hpp
@@ -3,42 +3,48 @@
enum MKDIR_CODE {MKDIR_SUCCESS,MKDIR_ERROR,MKDIR_BADPATH};
-MKDIR_CODE MakeDir(const char *Name,const wchar *NameW,bool SetAttr,uint Attr);
-bool CreatePath(const char *Path,const wchar *PathW,bool SkipLastName);
-void SetDirTime(const char *Name,const wchar *NameW,RarTime *ftm,RarTime *ftc,RarTime *fta);
-bool IsRemovable(const char *Name);
-int64 GetFreeDisk(const char *Name);
-bool FileExist(const char *Name,const wchar *NameW=NULL);
-bool WildFileExist(const char *Name,const wchar *NameW=NULL);
+MKDIR_CODE MakeDir(const wchar *Name,bool SetAttr,uint Attr);
+bool CreatePath(const wchar *Path,bool SkipLastName);
+void SetDirTime(const wchar *Name,RarTime *ftm,RarTime *ftc,RarTime *fta);
+bool IsRemovable(const wchar *Name);
+
+#ifndef SFX_MODULE
+int64 GetFreeDisk(const wchar *Name);
+#endif
+
+#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
+bool IsFAT(const wchar *Root);
+#endif
+
+bool FileExist(const wchar *Name);
+bool WildFileExist(const wchar *Name);
bool IsDir(uint Attr);
bool IsUnreadable(uint Attr);
-bool IsLabel(uint Attr);
bool IsLink(uint Attr);
-void SetSFXMode(const char *FileName);
-void EraseDiskContents(const char *FileName);
+void SetSFXMode(const wchar *FileName);
+void EraseDiskContents(const wchar *FileName);
bool IsDeleteAllowed(uint FileAttr);
-void PrepareToDelete(const char *Name,const wchar *NameW=NULL);
-uint GetFileAttr(const char *Name,const wchar *NameW=NULL);
-bool SetFileAttr(const char *Name,const wchar *NameW,uint Attr);
-void ConvertNameToFull(const char *Src,char *Dest);
-void ConvertNameToFull(const wchar *Src,wchar *Dest);
-char* MkTemp(char *Name);
+void PrepareToDelete(const wchar *Name);
+uint GetFileAttr(const wchar *Name);
+bool SetFileAttr(const wchar *Name,uint Attr);
+#if 0
+wchar* MkTemp(wchar *Name,size_t MaxSize);
+#endif
+enum CALCFSUM_FLAGS {CALCFSUM_SHOWTEXT=1,CALCFSUM_SHOWPERCENT=2,CALCFSUM_SHOWPROGRESS=4,CALCFSUM_CURPOS=8};
-enum CALCCRC_SHOWMODE {CALCCRC_SHOWNONE,CALCCRC_SHOWTEXT,CALCCRC_SHOWALL};
-uint CalcFileCRC(File *SrcFile,int64 Size=INT64NDF,CALCCRC_SHOWMODE ShowMode=CALCCRC_SHOWNONE);
+void CalcFileSum(File *SrcFile,uint *CRC32,byte *Blake2,uint Threads,int64 Size=INT64NDF,uint Flags=0);
-bool RenameFile(const char *SrcName,const wchar *SrcNameW,const char *DestName,const wchar *DestNameW);
-bool DelFile(const char *Name);
-bool DelFile(const char *Name,const wchar *NameW);
-bool DelDir(const char *Name);
-bool DelDir(const char *Name,const wchar *NameW);
+bool RenameFile(const wchar *SrcName,const wchar *DestName);
+bool DelFile(const wchar *Name);
+bool DelDir(const wchar *Name);
-#if defined(_WIN_32) && !defined(_WIN_CE)
-bool SetFileCompression(char *Name,wchar *NameW,bool State);
+#if defined(_WIN_ALL) && !defined(SFX_MODULE)
+bool SetFileCompression(const wchar *Name,bool State);
#endif
+
#endif
diff --git a/libunrar/filestr.cpp b/libunrar/filestr.cpp
index c5fba51..a5d29d7 100644
--- a/libunrar/filestr.cpp
+++ b/libunrar/filestr.cpp
@@ -1,189 +1,166 @@
#include "rar.hpp"
-static bool IsUnicode(byte *Data,int Size);
-
-bool ReadTextFile(const char *Name,StringList *List,bool Config,
- bool AbortOnError,RAR_CHARSET SrcCharset,bool Unquote,
- bool SkipComments,bool ExpandEnvStr)
+bool ReadTextFile(
+ const wchar *Name,
+ StringList *List,
+ bool Config,
+ bool AbortOnError,
+ RAR_CHARSET SrcCharset,
+ bool Unquote,
+ bool SkipComments,
+ bool ExpandEnvStr)
{
- char FileName[NM];
- if (Config)
- GetConfigName(Name,FileName,true);
- else
- strcpy(FileName,Name);
+ wchar FileName[NM];
+ *FileName=0;
+
+ if (Name!=NULL)
+ if (Config)
+ GetConfigName(Name,FileName,ASIZE(FileName),true,false);
+ else
+ wcsncpyz(FileName,Name,ASIZE(FileName));
File SrcFile;
- if (*FileName)
+ if (*FileName!=0)
{
- bool OpenCode=AbortOnError ? SrcFile.WOpen(FileName):SrcFile.Open(FileName);
+ bool OpenCode=AbortOnError ? SrcFile.WOpen(FileName):SrcFile.Open(FileName,0);
if (!OpenCode)
{
if (AbortOnError)
- ErrHandler.Exit(OPEN_ERROR);
- return(false);
+ ErrHandler.Exit(RARX_OPEN);
+ return false;
}
}
else
SrcFile.SetHandleType(FILE_HANDLESTD);
- unsigned int DataSize=0,ReadSize;
- const int ReadBlock=1024;
- Array Data(ReadBlock+5);
+ uint DataSize=0,ReadSize;
+ const int ReadBlock=4096;
+
+ Array Data(ReadBlock);
while ((ReadSize=SrcFile.Read(&Data[DataSize],ReadBlock))!=0)
{
DataSize+=ReadSize;
- Data.Add(ReadSize);
+ Data.Add(ReadSize); // Always have ReadBlock available for next data.
}
+ // Set to really read size, so we can zero terminate it correctly.
+ Data.Alloc(DataSize);
- memset(&Data[DataSize],0,5);
+ int LittleEndian=DataSize>=2 && Data[0]==255 && Data[1]==254 ? 1:0;
+ int BigEndian=DataSize>=2 && Data[0]==254 && Data[1]==255 ? 1:0;
+ bool Utf8=DataSize>=3 && Data[0]==0xef && Data[1]==0xbb && Data[2]==0xbf;
- if (SrcCharset==RCH_UNICODE ||
- SrcCharset==RCH_DEFAULT && IsUnicode((byte *)&Data[0],DataSize))
+ if (SrcCharset==RCH_DEFAULT)
+ SrcCharset=DetectTextEncoding(&Data[0],DataSize);
+
+ Array DataW;
+
+ if (SrcCharset==RCH_DEFAULT || SrcCharset==RCH_OEM || SrcCharset==RCH_ANSI)
{
- // Unicode in native system format, can be more than 2 bytes per character.
- Array DataW(Data.Size()/2+1);
- for (size_t I=2;I AnsiName;
-
- while (*CurStr!=0)
- {
- wchar *NextStr=CurStr,*CmtPtr=NULL;
- while (*NextStr!='\r' && *NextStr!='\n' && *NextStr!=0)
- {
- if (SkipComments && NextStr[0]=='/' && NextStr[1]=='/')
- {
- *NextStr=0;
- CmtPtr=NextStr;
- }
- NextStr++;
- }
- *NextStr=0;
- for (wchar *SpacePtr=(CmtPtr ? CmtPtr:NextStr)-1;SpacePtr>=CurStr;SpacePtr--)
- {
- if (*SpacePtr!=' ' && *SpacePtr!='\t')
- break;
- *SpacePtr=0;
- }
- if (*CurStr)
- {
- // Length and AddSize must be defined as signed, because AddSize
- // can be negative.
- int Length=(int)strlenw(CurStr);
- int AddSize=4*(Length-(int)AnsiName.Size()+1);
-
- if (AddSize>0)
- AnsiName.Add(AddSize);
- if (Unquote && *CurStr=='\"' && CurStr[Length-1]=='\"')
- {
- CurStr[Length-1]=0;
- CurStr++;
- }
- WideToChar(CurStr,&AnsiName[0],AnsiName.Size());
-
- bool Expanded=false;
-#if defined(_WIN_32) && !defined(_WIN_CE)
- if (ExpandEnvStr && *CurStr=='%')
- {
- // expanding environment variables in Windows version
-
- char ExpName[NM];
- wchar ExpNameW[NM];
- *ExpNameW=0;
- int ret,retw=1;
- ret=ExpandEnvironmentStrings(&AnsiName[0],ExpName,ASIZE(ExpName));
- if (ret!=0 && WinNT())
- retw=ExpandEnvironmentStringsW(CurStr,ExpNameW,ASIZE(ExpNameW));
- Expanded=ret!=0 && retAddString(ExpName,ExpNameW);
- }
+ Data.Push(0); // Zero terminate.
+#if defined(_WIN_ALL)
+ if (SrcCharset==RCH_OEM)
+ OemToCharA((char *)&Data[0],(char *)&Data[0]);
#endif
- if (!Expanded)
- List->AddString(&AnsiName[0],CurStr);
- }
- CurStr=NextStr+1;
- while (*CurStr=='\r' || *CurStr=='\n')
- CurStr++;
- }
+ DataW.Alloc(Data.Size());
+ CharToWide((char *)&Data[0],&DataW[0],DataW.Size());
}
- else
+
+ if (SrcCharset==RCH_UNICODE)
{
- char *CurStr=&Data[0];
- while (*CurStr!=0)
+ size_t Start=2; // Skip byte order mark.
+ if (!LittleEndian && !BigEndian) // No byte order mask.
{
- char *NextStr=CurStr,*CmtPtr=NULL;
- while (*NextStr!='\r' && *NextStr!='\n' && *NextStr!=0)
- {
- if (SkipComments && NextStr[0]=='/' && NextStr[1]=='/')
- {
- *NextStr=0;
- CmtPtr=NextStr;
- }
- NextStr++;
- }
- *NextStr=0;
- for (char *SpacePtr=(CmtPtr ? CmtPtr:NextStr)-1;SpacePtr>=CurStr;SpacePtr--)
- {
- if (*SpacePtr!=' ' && *SpacePtr!='\t')
- break;
- *SpacePtr=0;
- }
- if (*CurStr)
- {
- if (Unquote && *CurStr=='\"')
- {
- size_t Length=strlen(CurStr);
- if (CurStr[Length-1]=='\"')
- {
- CurStr[Length-1]=0;
- CurStr++;
- }
- }
-#if defined(_WIN_32)
- if (SrcCharset==RCH_OEM)
- OemToChar(CurStr,CurStr);
-#endif
-
- bool Expanded=false;
-#if defined(_WIN_32) && !defined(_WIN_CE)
- if (ExpandEnvStr && *CurStr=='%')
- {
- // expanding environment variables in Windows version
-
- char ExpName[NM];
- int ret=ExpandEnvironmentStrings(CurStr,ExpName,ASIZE(ExpName));
- Expanded=ret!=0 && retAddString(ExpName);
- }
-#endif
- if (!Expanded)
- List->AddString(CurStr);
- }
- CurStr=NextStr+1;
- while (*CurStr=='\r' || *CurStr=='\n')
- CurStr++;
+ Start=0;
+ LittleEndian=1;
}
+
+ DataW.Alloc(Data.Size()/2+1);
+ size_t End=Data.Size() & ~1; // We need even bytes number for UTF-16.
+ for (size_t I=Start;I=CurStr;SpacePtr--)
+ {
+ if (*SpacePtr!=' ' && *SpacePtr!='\t')
+ break;
+ *SpacePtr=0;
+ }
+
+ if (Unquote && *CurStr=='\"')
+ {
+ size_t Length=wcslen(CurStr);
+ if (CurStr[Length-1]=='\"')
+ {
+ CurStr[Length-1]=0;
+ CurStr++;
+ }
+ }
+
+ bool Expanded=false;
+#if defined(_WIN_ALL)
+ if (ExpandEnvStr && *CurStr=='%') // Expand environment variables in Windows.
+ {
+ wchar ExpName[NM];
+ *ExpName=0;
+ DWORD Result=ExpandEnvironmentStrings(CurStr,ExpName,ASIZE(ExpName));
+ Expanded=Result!=0 && ResultAddString(ExpName);
+ }
+#endif
+ if (!Expanded && *CurStr!=0)
+ List->AddString(CurStr);
+
+ if (Done)
+ break;
+ CurStr=NextStr+1;
+ while (*CurStr=='\r' || *CurStr=='\n')
+ CurStr++;
+ }
+ return true;
}
-bool IsUnicode(byte *Data,int Size)
+RAR_CHARSET DetectTextEncoding(const byte *Data,size_t DataSize)
{
- if (Size<4 || Data[0]!=0xff || Data[1]!=0xfe)
- return(false);
- for (int I=2;I3 && Data[0]==0xef && Data[1]==0xbb && Data[2]==0xbf &&
+ IsTextUtf8(Data+3,DataSize-3))
+ return RCH_UTF8;
+
+ bool LittleEndian=DataSize>2 && Data[0]==255 && Data[1]==254;
+ bool BigEndian=DataSize>2 && Data[0]==254 && Data[1]==255;
+
+ if (LittleEndian || BigEndian)
+ for (size_t I=LittleEndian ? 3 : 2;IError=false;
if (*FindMask==0)
- return(false);
-#ifdef _WIN_32
+ return false;
+#ifdef _WIN_ALL
if (FirstCall)
{
- if ((hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,FindMaskW,fd))==INVALID_HANDLE_VALUE)
- return(false);
+ if ((hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,fd))==INVALID_HANDLE_VALUE)
+ return false;
}
else
- if (Win32Find(hFind,FindMask,FindMaskW,fd)==INVALID_HANDLE_VALUE)
- return(false);
+ if (Win32Find(hFind,FindMask,fd)==INVALID_HANDLE_VALUE)
+ return false;
#else
if (FirstCall)
{
- char DirName[NM];
- strcpy(DirName,FindMask);
+ wchar DirName[NM];
+ wcsncpyz(DirName,FindMask,ASIZE(DirName));
RemoveNameFromPath(DirName);
if (*DirName==0)
- strcpy(DirName,".");
-/*
- else
- {
- int Length=strlen(DirName);
- if (Length>1 && DirName[Length-1]==CPATHDIVIDER && (Length!=3 || !IsDriveDiv(DirName[1])))
- DirName[Length-1]=0;
- }
-*/
- if ((dirp=opendir(DirName))==NULL)
+ wcsncpyz(DirName,L".",ASIZE(DirName));
+ char DirNameA[NM];
+ WideToChar(DirName,DirNameA,ASIZE(DirNameA));
+ if ((dirp=opendir(DirNameA))==NULL)
{
fd->Error=(errno!=ENOENT);
- return(false);
+ return false;
}
}
while (1)
{
+ wchar Name[NM];
struct dirent *ent=readdir(dirp);
if (ent==NULL)
- return(false);
+ return false;
if (strcmp(ent->d_name,".")==0 || strcmp(ent->d_name,"..")==0)
continue;
- if (CmpName(FindMask,ent->d_name,MATCH_NAMES))
+ if (!CharToWide(ent->d_name,Name,ASIZE(Name)))
+ uiMsg(UIERROR_INVALIDNAME,UINULL,Name);
+
+ if (CmpName(FindMask,Name,MATCH_NAMES))
{
- char FullName[NM];
- strcpy(FullName,FindMask);
+ wchar FullName[NM];
+ wcsncpyz(FullName,FindMask,ASIZE(FullName));
*PointToName(FullName)=0;
- if (strlen(FullName)+strlen(ent->d_name)>=ASIZE(FullName)-1)
+ if (wcslen(FullName)+wcslen(Name)>=ASIZE(FullName)-1)
{
-#ifndef SILENT
- Log(NULL,"\n%s%s",FullName,ent->d_name);
- Log(NULL,St(MPathTooLong));
-#endif
- return(false);
+ uiMsg(UIERROR_PATHTOOLONG,FullName,L"",Name);
+ return false;
}
- strcat(FullName,ent->d_name);
- if (!FastFind(FullName,NULL,fd,GetSymLink))
+ wcsncatz(FullName,Name,ASIZE(FullName));
+ if (!FastFind(FullName,fd,GetSymLink))
{
ErrHandler.OpenErrorMsg(FullName);
continue;
}
- strcpy(fd->Name,FullName);
+ wcsncpyz(fd->Name,FullName,ASIZE(fd->Name));
break;
}
}
- *fd->NameW=0;
-#ifdef _APPLE
- if (!LowAscii(fd->Name))
- UtfToWide(fd->Name,fd->NameW,sizeof(fd->NameW));
-#elif defined(UNICODE_SUPPORTED)
- if (!LowAscii(fd->Name) && UnicodeEnabled())
- CharToWide(fd->Name,fd->NameW);
-#endif
#endif
fd->Flags=0;
fd->IsDir=IsDir(fd->FileAttr);
+ fd->IsLink=IsLink(fd->FileAttr);
+
FirstCall=false;
- char *Name=PointToName(fd->Name);
- if (strcmp(Name,".")==0 || strcmp(Name,"..")==0)
- return(Next(fd));
- return(true);
+ wchar *NameOnly=PointToName(fd->Name);
+ if (wcscmp(NameOnly,L".")==0 || wcscmp(NameOnly,L"..")==0)
+ return Next(fd);
+ return true;
}
-bool FindFile::FastFind(const char *FindMask,const wchar *FindMaskW,struct FindData *fd,bool GetSymLink)
+bool FindFile::FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink)
{
fd->Error=false;
#ifndef _UNIX
- if (IsWildcard(FindMask,FindMaskW))
- return(false);
+ if (IsWildcard(FindMask))
+ return false;
#endif
-#ifdef _WIN_32
- HANDLE hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,FindMaskW,fd);
+#ifdef _WIN_ALL
+ HANDLE hFind=Win32Find(INVALID_HANDLE_VALUE,FindMask,fd);
if (hFind==INVALID_HANDLE_VALUE)
- return(false);
+ return false;
FindClose(hFind);
#else
+ char FindMaskA[NM];
+ WideToChar(FindMask,FindMaskA,ASIZE(FindMaskA));
+
struct stat st;
if (GetSymLink)
{
#ifdef SAVE_LINKS
- if (lstat(FindMask,&st)!=0)
+ if (lstat(FindMaskA,&st)!=0)
#else
- if (stat(FindMask,&st)!=0)
+ if (stat(FindMaskA,&st)!=0)
#endif
{
fd->Error=(errno!=ENOENT);
- return(false);
+ return false;
}
}
else
- if (stat(FindMask,&st)!=0)
+ if (stat(FindMaskA,&st)!=0)
{
fd->Error=(errno!=ENOENT);
- return(false);
+ return false;
}
-#ifdef _DJGPP
- fd->FileAttr=_chmod(FindMask,0);
-#elif defined(_EMX)
- fd->FileAttr=st.st_attr;
-#else
fd->FileAttr=st.st_mode;
-#endif
- fd->IsDir=IsDir(st.st_mode);
fd->Size=st.st_size;
- fd->mtime=st.st_mtime;
- fd->atime=st.st_atime;
- fd->ctime=st.st_ctime;
- fd->FileTime=fd->mtime.GetDos();
- strcpy(fd->Name,FindMask);
- *fd->NameW=0;
-#ifdef _APPLE
- if (!LowAscii(fd->Name))
- UtfToWide(fd->Name,fd->NameW,sizeof(fd->NameW));
-#elif defined(UNICODE_SUPPORTED)
- if (!LowAscii(fd->Name) && UnicodeEnabled())
- CharToWide(fd->Name,fd->NameW);
+#ifdef UNIX_TIME_NS
+ fd->mtime.SetUnixNS(st.st_mtim.tv_sec*(uint64)1000000000+st.st_mtim.tv_nsec);
+ fd->atime.SetUnixNS(st.st_atim.tv_sec*(uint64)1000000000+st.st_atim.tv_nsec);
+ fd->ctime.SetUnixNS(st.st_ctim.tv_sec*(uint64)1000000000+st.st_ctim.tv_nsec);
+#else
+ fd->mtime.SetUnix(st.st_mtime);
+ fd->atime.SetUnix(st.st_atime);
+ fd->ctime.SetUnix(st.st_ctime);
#endif
+
+ wcsncpyz(fd->Name,FindMask,ASIZE(fd->Name));
#endif
fd->Flags=0;
fd->IsDir=IsDir(fd->FileAttr);
- return(true);
+ fd->IsLink=IsLink(fd->FileAttr);
+
+ return true;
}
-#ifdef _WIN_32
-HANDLE FindFile::Win32Find(HANDLE hFind,const char *Mask,const wchar *MaskW,struct FindData *fd)
+#ifdef _WIN_ALL
+HANDLE FindFile::Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd)
{
-#ifndef _WIN_CE
- if (WinNT())
-#endif
+ WIN32_FIND_DATA FindData;
+ if (hFind==INVALID_HANDLE_VALUE)
{
- wchar WideMask[NM];
- if (MaskW!=NULL && *MaskW!=0)
- strcpyw(WideMask,MaskW);
- else
- CharToWide(Mask,WideMask);
-
- WIN32_FIND_DATAW FindData;
+ hFind=FindFirstFile(Mask,&FindData);
if (hFind==INVALID_HANDLE_VALUE)
{
- hFind=FindFirstFileW(WideMask,&FindData);
- if (hFind==INVALID_HANDLE_VALUE)
- {
- int SysErr=GetLastError();
- fd->Error=(SysErr!=ERROR_FILE_NOT_FOUND &&
- SysErr!=ERROR_PATH_NOT_FOUND &&
- SysErr!=ERROR_NO_MORE_FILES);
- }
+ wchar LongMask[NM];
+ if (GetWinLongPath(Mask,LongMask,ASIZE(LongMask)))
+ hFind=FindFirstFile(LongMask,&FindData);
}
- else
- if (!FindNextFileW(hFind,&FindData))
- {
- hFind=INVALID_HANDLE_VALUE;
- fd->Error=GetLastError()!=ERROR_NO_MORE_FILES;
- }
-
- if (hFind!=INVALID_HANDLE_VALUE)
+ if (hFind==INVALID_HANDLE_VALUE)
{
- strcpyw(fd->NameW,WideMask);
- strcpyw(PointToName(fd->NameW),FindData.cFileName);
- WideToChar(fd->NameW,fd->Name);
- fd->Size=INT32TO64(FindData.nFileSizeHigh,FindData.nFileSizeLow);
- fd->FileAttr=FindData.dwFileAttributes;
- WideToChar(FindData.cAlternateFileName,fd->ShortName);
- fd->ftCreationTime=FindData.ftCreationTime;
- fd->ftLastAccessTime=FindData.ftLastAccessTime;
- fd->ftLastWriteTime=FindData.ftLastWriteTime;
- fd->mtime=FindData.ftLastWriteTime;
- fd->ctime=FindData.ftCreationTime;
- fd->atime=FindData.ftLastAccessTime;
- fd->FileTime=fd->mtime.GetDos();
-
-#ifndef _WIN_CE
- if (LowAscii(fd->NameW))
- *fd->NameW=0;
-#endif
+ int SysErr=GetLastError();
+ // We must not issue an error for "file not found" and "path not found",
+ // because it is normal to not find anything for wildcard mask when
+ // archiving. Also searching for non-existent file is normal in some
+ // other modules, like WinRAR scanning for winrar_theme_description.txt
+ // to check if any themes are available.
+ fd->Error=SysErr!=ERROR_FILE_NOT_FOUND &&
+ SysErr!=ERROR_PATH_NOT_FOUND &&
+ SysErr!=ERROR_NO_MORE_FILES;
}
}
-#ifndef _WIN_CE
else
+ if (!FindNextFile(hFind,&FindData))
+ {
+ hFind=INVALID_HANDLE_VALUE;
+ fd->Error=GetLastError()!=ERROR_NO_MORE_FILES;
+ }
+
+ if (hFind!=INVALID_HANDLE_VALUE)
{
- char CharMask[NM];
- if (Mask!=NULL && *Mask!=0)
- strcpy(CharMask,Mask);
- else
- WideToChar(MaskW,CharMask);
+ wcsncpyz(fd->Name,Mask,ASIZE(fd->Name));
+ SetName(fd->Name,FindData.cFileName,ASIZE(fd->Name));
+ fd->Size=INT32TO64(FindData.nFileSizeHigh,FindData.nFileSizeLow);
+ fd->FileAttr=FindData.dwFileAttributes;
+ fd->ftCreationTime=FindData.ftCreationTime;
+ fd->ftLastAccessTime=FindData.ftLastAccessTime;
+ fd->ftLastWriteTime=FindData.ftLastWriteTime;
+ fd->mtime.SetWinFT(&FindData.ftLastWriteTime);
+ fd->ctime.SetWinFT(&FindData.ftCreationTime);
+ fd->atime.SetWinFT(&FindData.ftLastAccessTime);
- WIN32_FIND_DATA FindData;
- if (hFind==INVALID_HANDLE_VALUE)
- {
- hFind=FindFirstFile(CharMask,&FindData);
- if (hFind==INVALID_HANDLE_VALUE)
- {
- int SysErr=GetLastError();
- fd->Error=SysErr!=ERROR_FILE_NOT_FOUND && SysErr!=ERROR_PATH_NOT_FOUND;
- }
- }
- else
- if (!FindNextFile(hFind,&FindData))
- {
- hFind=INVALID_HANDLE_VALUE;
- fd->Error=GetLastError()!=ERROR_NO_MORE_FILES;
- }
- if (hFind!=INVALID_HANDLE_VALUE)
- {
- strcpy(fd->Name,CharMask);
- strcpy(PointToName(fd->Name),FindData.cFileName);
- CharToWide(fd->Name,fd->NameW);
- fd->Size=INT32TO64(FindData.nFileSizeHigh,FindData.nFileSizeLow);
- fd->FileAttr=FindData.dwFileAttributes;
- strcpy(fd->ShortName,FindData.cAlternateFileName);
- fd->ftCreationTime=FindData.ftCreationTime;
- fd->ftLastAccessTime=FindData.ftLastAccessTime;
- fd->ftLastWriteTime=FindData.ftLastWriteTime;
- fd->mtime=FindData.ftLastWriteTime;
- fd->ctime=FindData.ftCreationTime;
- fd->atime=FindData.ftLastAccessTime;
- fd->FileTime=fd->mtime.GetDos();
- if (LowAscii(fd->Name))
- *fd->NameW=0;
- }
}
-#endif
fd->Flags=0;
- return(hFind);
+ return hFind;
}
#endif
diff --git a/libunrar/find.hpp b/libunrar/find.hpp
index 99e804a..250637f 100644
--- a/libunrar/find.hpp
+++ b/libunrar/find.hpp
@@ -2,22 +2,20 @@
#define _RAR_FINDDATA_
enum FINDDATA_FLAGS {
- FDDF_SECONDDIR=1 // Second encounter of same directory in SCAN_GETDIRSTWICE ScanTree mode
+ FDDF_SECONDDIR=1 // Second encounter of same directory in SCAN_GETDIRSTWICE ScanTree mode.
};
struct FindData
{
- char Name[NM];
- wchar NameW[NM];
- int64 Size;
+ wchar Name[NM];
+ uint64 Size;
uint FileAttr;
- uint FileTime;
bool IsDir;
+ bool IsLink;
RarTime mtime;
RarTime ctime;
RarTime atime;
-#ifdef _WIN_32
- char ShortName[NM];
+#ifdef _WIN_ALL
FILETIME ftCreationTime;
FILETIME ftLastAccessTime;
FILETIME ftLastWriteTime;
@@ -29,14 +27,13 @@ struct FindData
class FindFile
{
private:
-#ifdef _WIN_32
- static HANDLE Win32Find(HANDLE hFind,const char *Mask,const wchar *MaskW,struct FindData *fd);
+#ifdef _WIN_ALL
+ static HANDLE Win32Find(HANDLE hFind,const wchar *Mask,FindData *fd);
#endif
- char FindMask[NM];
- wchar FindMaskW[NM];
+ wchar FindMask[NM];
bool FirstCall;
-#ifdef _WIN_32
+#ifdef _WIN_ALL
HANDLE hFind;
#else
DIR *dirp;
@@ -44,10 +41,9 @@ class FindFile
public:
FindFile();
~FindFile();
- void SetMask(const char *FindMask);
- void SetMaskW(const wchar *FindMaskW);
- bool Next(struct FindData *fd,bool GetSymLink=false);
- static bool FastFind(const char *FindMask,const wchar *FindMaskW,struct FindData *fd,bool GetSymLink=false);
+ void SetMask(const wchar *Mask);
+ bool Next(FindData *fd,bool GetSymLink=false);
+ static bool FastFind(const wchar *FindMask,FindData *fd,bool GetSymLink=false);
};
#endif
diff --git a/libunrar/getbits.cpp b/libunrar/getbits.cpp
index e5d590a..e4db269 100644
--- a/libunrar/getbits.cpp
+++ b/libunrar/getbits.cpp
@@ -1,18 +1,30 @@
#include "rar.hpp"
-BitInput::BitInput()
+BitInput::BitInput(bool AllocBuffer)
{
- // getbits attempts to read data from InAddr, InAddr+1, InAddr+2 positions.
- // So let's allocate two additional bytes for situation, when we need to
- // read only 1 byte from the last position of buffer and avoid a crash
- // from access to next 2 bytes, which contents we do not need.
- InBuf=new byte[MAX_SIZE+2];
+ ExternalBuffer=false;
+ if (AllocBuffer)
+ {
+ // getbits32 attempts to read data from InAddr, ... InAddr+3 positions.
+ // So let's allocate 3 additional bytes for situation, when we need to
+ // read only 1 byte from the last position of buffer and avoid a crash
+ // from access to next 3 bytes, which contents we do not need.
+ size_t BufSize=MAX_SIZE+3;
+ InBuf=new byte[BufSize];
+
+ // Ensure that we get predictable results when accessing bytes in area
+ // not filled with read data.
+ memset(InBuf,0,BufSize);
+ }
+ else
+ InBuf=NULL;
}
BitInput::~BitInput()
{
- delete[] InBuf;
+ if (!ExternalBuffer)
+ delete[] InBuf;
}
@@ -26,5 +38,15 @@ void BitInput::faddbits(uint Bits)
uint BitInput::fgetbits()
{
// Function wrapped version of inline getbits to save code size.
- return(getbits());
+ return getbits();
}
+
+
+void BitInput::SetExternalBuffer(byte *Buf)
+{
+ if (InBuf!=NULL && !ExternalBuffer)
+ delete[] InBuf;
+ InBuf=Buf;
+ ExternalBuffer=true;
+}
+
diff --git a/libunrar/getbits.hpp b/libunrar/getbits.hpp
index d44fb9f..2e151da 100644
--- a/libunrar/getbits.hpp
+++ b/libunrar/getbits.hpp
@@ -5,11 +5,13 @@ class BitInput
{
public:
enum BufferSize {MAX_SIZE=0x8000}; // Size of input buffer.
- protected:
+
int InAddr; // Curent byte position in the buffer.
int InBit; // Current bit position in the current byte.
+
+ bool ExternalBuffer;
public:
- BitInput();
+ BitInput(bool AllocBuffer);
~BitInput();
byte *InBuf; // Dynamically allocated input buffer.
@@ -35,7 +37,20 @@ class BitInput
BitField|=(uint)InBuf[InAddr+1] << 8;
BitField|=(uint)InBuf[InAddr+2];
BitField >>= (8-InBit);
- return(BitField & 0xffff);
+ return BitField & 0xffff;
+ }
+
+ // Return 32 bits from current position in the buffer.
+ // Bit at (InAddr,InBit) has the highest position in returning data.
+ uint getbits32()
+ {
+ uint BitField=(uint)InBuf[InAddr] << 24;
+ BitField|=(uint)InBuf[InAddr+1] << 16;
+ BitField|=(uint)InBuf[InAddr+2] << 8;
+ BitField|=(uint)InBuf[InAddr+3];
+ BitField <<= InBit;
+ BitField|=(uint)InBuf[InAddr+4] >> (8-InBit);
+ return BitField & 0xffffffff;
}
void faddbits(uint Bits);
@@ -45,7 +60,9 @@ class BitInput
// if buffer will be overflown.
bool Overflow(uint IncPtr)
{
- return(InAddr+IncPtr>=MAX_SIZE);
+ return InAddr+IncPtr>=MAX_SIZE;
}
+
+ void SetExternalBuffer(byte *Buf);
};
#endif
diff --git a/libunrar/global.cpp b/libunrar/global.cpp
index 593a057..3975813 100644
--- a/libunrar/global.cpp
+++ b/libunrar/global.cpp
@@ -1,4 +1,7 @@
#define INCLUDEGLOBAL
+#if defined(__BORLANDC__) || defined(_MSC_VER)
+#pragma hdrstop
+#endif
#include "rar.hpp"
diff --git a/libunrar/hardlinks.cpp b/libunrar/hardlinks.cpp
new file mode 100644
index 0000000..946a395
--- /dev/null
+++ b/libunrar/hardlinks.cpp
@@ -0,0 +1,39 @@
+bool ExtractHardlink(wchar *NameNew,wchar *NameExisting,size_t NameExistingSize)
+{
+ SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives.
+
+ if (!FileExist(NameExisting))
+ {
+ uiMsg(UIERROR_HLINKCREATE,NameNew);
+ uiMsg(UIERROR_NOLINKTARGET);
+ ErrHandler.SetErrorCode(RARX_CREATE);
+ return false;
+ }
+ CreatePath(NameNew,true);
+
+#ifdef _WIN_ALL
+ bool Success=CreateHardLink(NameNew,NameExisting,NULL)!=0;
+ if (!Success)
+ {
+ uiMsg(UIERROR_HLINKCREATE,NameNew);
+ ErrHandler.SysErrMsg();
+ ErrHandler.SetErrorCode(RARX_CREATE);
+ }
+ return Success;
+#elif defined(_UNIX)
+ char NameExistingA[NM],NameNewA[NM];
+ WideToChar(NameExisting,NameExistingA,ASIZE(NameExistingA));
+ WideToChar(NameNew,NameNewA,ASIZE(NameNewA));
+ bool Success=link(NameExistingA,NameNewA)==0;
+ if (!Success)
+ {
+ uiMsg(UIERROR_HLINKCREATE,NameNew);
+ ErrHandler.SysErrMsg();
+ ErrHandler.SetErrorCode(RARX_CREATE);
+ }
+ return Success;
+#else
+ return false;
+#endif
+}
+
diff --git a/libunrar/hash.cpp b/libunrar/hash.cpp
new file mode 100644
index 0000000..a4559e0
--- /dev/null
+++ b/libunrar/hash.cpp
@@ -0,0 +1,135 @@
+#include "rar.hpp"
+
+void HashValue::Init(HASH_TYPE Type)
+{
+ HashValue::Type=Type;
+
+ // Zero length data CRC32 is 0. It is important to set it when creating
+ // headers with no following data like directories or symlinks.
+ if (Type==HASH_RAR14 || Type==HASH_CRC32)
+ CRC32=0;
+ if (Type==HASH_BLAKE2)
+ {
+ // dd0e891776933f43c7d032b08a917e25741f8aa9a12c12e1cac8801500f2ca4f
+ // is BLAKE2sp hash of empty data. We init the structure to this value,
+ // so if we create a file or service header with no following data like
+ // "file copy" or "symlink", we set the checksum to proper value avoiding
+ // additional header type or size checks when extracting.
+ static byte EmptyHash[32]={
+ 0xdd, 0x0e, 0x89, 0x17, 0x76, 0x93, 0x3f, 0x43,
+ 0xc7, 0xd0, 0x32, 0xb0, 0x8a, 0x91, 0x7e, 0x25,
+ 0x74, 0x1f, 0x8a, 0xa9, 0xa1, 0x2c, 0x12, 0xe1,
+ 0xca, 0xc8, 0x80, 0x15, 0x00, 0xf2, 0xca, 0x4f
+ };
+ memcpy(Digest,EmptyHash,sizeof(Digest));
+ }
+}
+
+
+bool HashValue::operator == (const HashValue &cmp)
+{
+ if (Type==HASH_NONE || cmp.Type==HASH_NONE)
+ return true;
+ if (Type==HASH_RAR14 && cmp.Type==HASH_RAR14 ||
+ Type==HASH_CRC32 && cmp.Type==HASH_CRC32)
+ return CRC32==cmp.CRC32;
+ if (Type==HASH_BLAKE2 && cmp.Type==HASH_BLAKE2)
+ return memcmp(Digest,cmp.Digest,sizeof(Digest))==0;
+ return false;
+}
+
+
+DataHash::DataHash()
+{
+ blake2ctx=NULL;
+ HashType=HASH_NONE;
+#ifdef RAR_SMP
+ ThPool=NULL;
+ MaxThreads=0;
+#endif
+}
+
+
+DataHash::~DataHash()
+{
+#ifdef RAR_SMP
+ delete ThPool;
+#endif
+ cleandata(&CurCRC32, sizeof(CurCRC32));
+ if (blake2ctx!=NULL)
+ {
+ cleandata(blake2ctx, sizeof(blake2sp_state));
+ delete blake2ctx;
+ }
+}
+
+
+void DataHash::Init(HASH_TYPE Type,uint MaxThreads)
+{
+ if (blake2ctx==NULL)
+ blake2ctx=new blake2sp_state;
+ HashType=Type;
+ if (Type==HASH_RAR14)
+ CurCRC32=0;
+ if (Type==HASH_CRC32)
+ CurCRC32=0xffffffff; // Initial CRC32 value.
+ if (Type==HASH_BLAKE2)
+ blake2sp_init(blake2ctx);
+#ifdef RAR_SMP
+ DataHash::MaxThreads=Min(MaxThreads,MaxHashThreads);
+#endif
+}
+
+
+void DataHash::Update(const void *Data,size_t DataSize)
+{
+#ifndef SFX_MODULE
+ if (HashType==HASH_RAR14)
+ CurCRC32=Checksum14((ushort)CurCRC32,Data,DataSize);
+#endif
+ if (HashType==HASH_CRC32)
+ CurCRC32=CRC32(CurCRC32,Data,DataSize);
+
+ if (HashType==HASH_BLAKE2)
+ {
+#ifdef RAR_SMP
+ if (MaxThreads>1 && ThPool==NULL)
+ ThPool=new ThreadPool(BLAKE2_THREADS_NUMBER);
+ blake2ctx->ThPool=ThPool;
+ blake2ctx->MaxThreads=MaxThreads;
+#endif
+ blake2sp_update( blake2ctx, (byte *)Data, DataSize);
+ }
+}
+
+
+void DataHash::Result(HashValue *Result)
+{
+ Result->Type=HashType;
+ if (HashType==HASH_RAR14)
+ Result->CRC32=CurCRC32;
+ if (HashType==HASH_CRC32)
+ Result->CRC32=CurCRC32^0xffffffff;
+ if (HashType==HASH_BLAKE2)
+ {
+ // Preserve the original context, so we can continue hashing if necessary.
+ blake2sp_state res=*blake2ctx;
+ blake2sp_final(&res,Result->Digest);
+ }
+}
+
+
+uint DataHash::GetCRC32()
+{
+ return HashType==HASH_CRC32 ? CurCRC32^0xffffffff : 0;
+}
+
+
+bool DataHash::Cmp(HashValue *CmpValue,byte *Key)
+{
+ HashValue Final;
+ Result(&Final);
+ if (Key!=NULL)
+ ConvertHashToMAC(&Final,Key);
+ return Final==*CmpValue;
+}
diff --git a/libunrar/hash.hpp b/libunrar/hash.hpp
new file mode 100644
index 0000000..b7d879f
--- /dev/null
+++ b/libunrar/hash.hpp
@@ -0,0 +1,52 @@
+#ifndef _RAR_DATAHASH_
+#define _RAR_DATAHASH_
+
+enum HASH_TYPE {HASH_NONE,HASH_RAR14,HASH_CRC32,HASH_BLAKE2};
+
+struct HashValue
+{
+ void Init(HASH_TYPE Type);
+ bool operator == (const HashValue &cmp);
+ bool operator != (const HashValue &cmp) {return !(*this==cmp);}
+
+ HASH_TYPE Type;
+ union
+ {
+ uint CRC32;
+ byte Digest[SHA256_DIGEST_SIZE];
+ };
+};
+
+
+#ifdef RAR_SMP
+class ThreadPool;
+class DataHash;
+#endif
+
+
+class DataHash
+{
+ private:
+ HASH_TYPE HashType;
+ uint CurCRC32;
+ blake2sp_state *blake2ctx;
+
+#ifdef RAR_SMP
+ ThreadPool *ThPool;
+
+ uint MaxThreads;
+ // Upper limit for maximum threads to prevent wasting threads in pool.
+ static const uint MaxHashThreads=8;
+#endif
+ public:
+ DataHash();
+ ~DataHash();
+ void Init(HASH_TYPE Type,uint MaxThreads);
+ void Update(const void *Data,size_t DataSize);
+ void Result(HashValue *Result);
+ uint GetCRC32();
+ bool Cmp(HashValue *CmpValue,byte *Key);
+ HASH_TYPE Type() {return HashType;}
+};
+
+#endif
diff --git a/libunrar/headers.cpp b/libunrar/headers.cpp
new file mode 100644
index 0000000..b042dc3
--- /dev/null
+++ b/libunrar/headers.cpp
@@ -0,0 +1,61 @@
+#include "rar.hpp"
+
+void FileHeader::Reset(size_t SubDataSize)
+{
+ SubData.Alloc(SubDataSize);
+ BaseBlock::Reset();
+ FileHash.Init(HASH_NONE);
+ mtime.Reset();
+ atime.Reset();
+ ctime.Reset();
+ SplitBefore=false;
+ SplitAfter=false;
+
+ UnknownUnpSize=0;
+
+ SubFlags=0; // Important for RAR 3.0 subhead.
+
+ CryptMethod=CRYPT_NONE;
+ Encrypted=false;
+ SaltSet=false;
+ UsePswCheck=false;
+ UseHashKey=false;
+ Lg2Count=0;
+
+ Solid=false;
+ Dir=false;
+ WinSize=0;
+ Inherited=false;
+ SubBlock=false;
+ CommentInHeader=false;
+ Version=false;
+ LargeFile=false;
+
+ RedirType=FSREDIR_NONE;
+ DirTarget=false;
+ UnixOwnerSet=false;
+}
+
+
+FileHeader& FileHeader::operator = (FileHeader &hd)
+{
+ SubData.Reset();
+ memcpy(this,&hd,sizeof(*this));
+ SubData.CleanData();
+ SubData=hd.SubData;
+ return *this;
+}
+
+
+void MainHeader::Reset()
+{
+ HighPosAV=0;
+ PosAV=0;
+ CommentInHeader=false;
+ PackComment=false;
+ Locator=false;
+ QOpenOffset=0;
+ QOpenMaxSize=0;
+ RROffset=0;
+ RRMaxSize=0;
+}
diff --git a/libunrar/headers.hpp b/libunrar/headers.hpp
index 9e632f4..6af453a 100644
--- a/libunrar/headers.hpp
+++ b/libunrar/headers.hpp
@@ -1,33 +1,31 @@
#ifndef _RAR_HEADERS_
#define _RAR_HEADERS_
-#define SIZEOF_MARKHEAD 7
-#define SIZEOF_OLDMHD 7
-#define SIZEOF_NEWMHD 13
-#define SIZEOF_OLDLHD 21
-#define SIZEOF_NEWLHD 32
+#define SIZEOF_MARKHEAD3 7 // Size of RAR 4.x archive mark header.
+#define SIZEOF_MAINHEAD14 7 // Size of RAR 1.4 main archive header.
+#define SIZEOF_MAINHEAD3 13 // Size of RAR 4.x main archive header.
+#define SIZEOF_FILEHEAD14 21 // Size of RAR 1.4 file header.
+#define SIZEOF_FILEHEAD3 32 // Size of RAR 3.0 file header.
#define SIZEOF_SHORTBLOCKHEAD 7
#define SIZEOF_LONGBLOCKHEAD 11
#define SIZEOF_SUBBLOCKHEAD 14
#define SIZEOF_COMMHEAD 13
#define SIZEOF_PROTECTHEAD 26
-#define SIZEOF_AVHEAD 14
-#define SIZEOF_SIGNHEAD 15
#define SIZEOF_UOHEAD 18
-#define SIZEOF_MACHEAD 22
-#define SIZEOF_EAHEAD 24
-#define SIZEOF_BEEAHEAD 24
#define SIZEOF_STREAMHEAD 26
-#define PACK_VER 29
-#define PACK_CRYPT_VER 29
-#define UNP_VER 36
-#define CRYPT_VER 29
-#define AV_VER 20
-#define PROTECT_VER 20
+#define VER_PACK 29U
+#define VER_PACK5 50U // It is stored as 0, but we subtract 50 when saving an archive.
+#define VER_UNPACK 29U
+#define VER_UNPACK5 50U // It is stored as 0, but we add 50 when reading an archive.
+#define VER_UNKNOWN 9999U // Just some large value.
#define MHD_VOLUME 0x0001U
+
+// Old style main archive comment embed into main archive header. Must not
+// be used in new archives anymore.
#define MHD_COMMENT 0x0002U
+
#define MHD_LOCK 0x0004U
#define MHD_SOLID 0x0008U
#define MHD_PACK_COMMENT 0x0010U
@@ -36,14 +34,19 @@
#define MHD_PROTECT 0x0040U
#define MHD_PASSWORD 0x0080U
#define MHD_FIRSTVOLUME 0x0100U
-#define MHD_ENCRYPTVER 0x0200U
#define LHD_SPLIT_BEFORE 0x0001U
#define LHD_SPLIT_AFTER 0x0002U
#define LHD_PASSWORD 0x0004U
+
+// Old style file comment embed into file header. Must not be used
+// in new archives anymore.
#define LHD_COMMENT 0x0008U
+
+// For non-file subheaders it denotes 'subblock having a parent file' flag.
#define LHD_SOLID 0x0010U
+
#define LHD_WINDOWMASK 0x00e0U
#define LHD_WINDOW64 0x0000U
#define LHD_WINDOW128 0x0020U
@@ -59,96 +62,98 @@
#define LHD_SALT 0x0400U
#define LHD_VERSION 0x0800U
#define LHD_EXTTIME 0x1000U
-#define LHD_EXTFLAGS 0x2000U
#define SKIP_IF_UNKNOWN 0x4000U
#define LONG_BLOCK 0x8000U
-#define EARC_NEXT_VOLUME 0x0001U // not last volume
-#define EARC_DATACRC 0x0002U // store CRC32 of RAR archive (now used only in volumes)
-#define EARC_REVSPACE 0x0004U // reserve space for end of REV file 7 byte record
-#define EARC_VOLNUMBER 0x0008U // store a number of current volume
+#define EARC_NEXT_VOLUME 0x0001U // Not last volume.
+#define EARC_DATACRC 0x0002U // Store CRC32 of RAR archive (now is used only in volumes).
+#define EARC_REVSPACE 0x0004U // Reserve space for end of REV file 7 byte record.
+#define EARC_VOLNUMBER 0x0008U // Store a number of current volume.
enum HEADER_TYPE {
- MARK_HEAD=0x72,MAIN_HEAD=0x73,FILE_HEAD=0x74,COMM_HEAD=0x75,AV_HEAD=0x76,
- SUB_HEAD=0x77,PROTECT_HEAD=0x78,SIGN_HEAD=0x79,NEWSUB_HEAD=0x7a,
- ENDARC_HEAD=0x7b
+ // RAR 5.0 header types.
+ HEAD_MARK=0x00, HEAD_MAIN=0x01, HEAD_FILE=0x02, HEAD_SERVICE=0x03,
+ HEAD_CRYPT=0x04, HEAD_ENDARC=0x05, HEAD_UNKNOWN=0xff,
+
+ // RAR 1.5 - 4.x header types.
+ HEAD3_MARK=0x72,HEAD3_MAIN=0x73,HEAD3_FILE=0x74,HEAD3_CMT=0x75,
+ HEAD3_AV=0x76,HEAD3_OLDSERVICE=0x77,HEAD3_PROTECT=0x78,HEAD3_SIGN=0x79,
+ HEAD3_SERVICE=0x7a,HEAD3_ENDARC=0x7b
};
+
+// RAR 2.9 and earlier.
enum { EA_HEAD=0x100,UO_HEAD=0x101,MAC_HEAD=0x102,BEEA_HEAD=0x103,
NTACL_HEAD=0x104,STREAM_HEAD=0x105 };
+
+// Internal implementation, depends on archive format version.
enum HOST_SYSTEM {
+ // RAR 5.0 host OS
+ HOST5_WINDOWS=0,HOST5_UNIX=1,
+
+ // RAR 3.0 host OS.
HOST_MSDOS=0,HOST_OS2=1,HOST_WIN32=2,HOST_UNIX=3,HOST_MACOS=4,
HOST_BEOS=5,HOST_MAX
};
-#define SUBHEAD_TYPE_CMT "CMT"
-#define SUBHEAD_TYPE_ACL "ACL"
-#define SUBHEAD_TYPE_STREAM "STM"
-#define SUBHEAD_TYPE_UOWNER "UOW"
-#define SUBHEAD_TYPE_AV "AV"
-#define SUBHEAD_TYPE_RR "RR"
-#define SUBHEAD_TYPE_OS2EA "EA2"
-#define SUBHEAD_TYPE_BEOSEA "EABE"
+// Unified archive format independent implementation.
+enum HOST_SYSTEM_TYPE {
+ HSYS_WINDOWS, HSYS_UNIX, HSYS_UNKNOWN
+};
+
+
+// We also use these values in extra field, so do not modify them.
+enum FILE_SYSTEM_REDIRECT {
+ FSREDIR_NONE=0, FSREDIR_UNIXSYMLINK, FSREDIR_WINSYMLINK, FSREDIR_JUNCTION,
+ FSREDIR_HARDLINK, FSREDIR_FILECOPY
+};
+
+
+#define SUBHEAD_TYPE_CMT L"CMT"
+#define SUBHEAD_TYPE_QOPEN L"QO"
+#define SUBHEAD_TYPE_ACL L"ACL"
+#define SUBHEAD_TYPE_STREAM L"STM"
+#define SUBHEAD_TYPE_UOWNER L"UOW"
+#define SUBHEAD_TYPE_AV L"AV"
+#define SUBHEAD_TYPE_RR L"RR"
+#define SUBHEAD_TYPE_OS2EA L"EA2"
/* new file inherits a subblock when updating a host file */
#define SUBHEAD_FLAGS_INHERITED 0x80000000
#define SUBHEAD_FLAGS_CMT_UNICODE 0x00000001
-struct OldMainHeader
-{
- byte Mark[4];
- ushort HeadSize;
- byte Flags;
-};
-
-
-struct OldFileHeader
-{
- uint PackSize;
- uint UnpSize;
- ushort FileCRC;
- ushort HeadSize;
- uint FileTime;
- byte FileAttr;
- byte Flags;
- byte UnpVer;
- byte NameSize;
- byte Method;
-};
-
struct MarkHeader
{
- byte Mark[7];
+ byte Mark[8];
+
+ // Following fields are virtual and not present in real blocks.
+ uint HeadSize;
};
struct BaseBlock
{
- ushort HeadCRC;
- HEADER_TYPE HeadType;//byte
- ushort Flags;
- ushort HeadSize;
+ uint HeadCRC; // 'ushort' for RAR 1.5.
+ HEADER_TYPE HeaderType; // 1 byte for RAR 1.5.
+ uint Flags; // 'ushort' for RAR 1.5.
+ uint HeadSize; // 'ushort' for RAR 1.5, up to 2 MB for RAR 5.0.
- bool IsSubBlock()
+ bool SkipIfUnknown;
+
+ void Reset()
{
- if (HeadType==SUB_HEAD)
- return(true);
- if (HeadType==NEWSUB_HEAD && (Flags & LHD_SOLID)!=0)
- return(true);
- return(false);
+ SkipIfUnknown=false;
}
};
+
struct BlockHeader:BaseBlock
{
- union {
- uint DataSize;
- uint PackSize;
- };
+ uint DataSize;
};
@@ -156,75 +161,141 @@ struct MainHeader:BaseBlock
{
ushort HighPosAV;
uint PosAV;
- byte EncryptVer;
+ bool CommentInHeader;
+ bool PackComment; // For RAR 1.4 archive format only.
+ bool Locator;
+ uint64 QOpenOffset; // Offset of quick list record.
+ uint64 QOpenMaxSize; // Maximum size of QOpen offset in locator extra field.
+ uint64 RROffset; // Offset of recovery record.
+ uint64 RRMaxSize; // Maximum size of RR offset in locator extra field.
+ void Reset();
};
-#define SALT_SIZE 8
-
struct FileHeader:BlockHeader
{
- uint UnpSize;
byte HostOS;
- uint FileCRC;
- uint FileTime;
- byte UnpVer;
+ uint UnpVer; // It is 1 byte in RAR29 and bit field in RAR5.
byte Method;
- ushort NameSize;
union {
uint FileAttr;
uint SubFlags;
};
-/* optional */
- uint HighPackSize;
- uint HighUnpSize;
-/* names */
- char FileName[NM];
- wchar FileNameW[NM];
-/* optional */
+ wchar FileName[NM];
+
Array SubData;
- byte Salt[SALT_SIZE];
RarTime mtime;
RarTime ctime;
RarTime atime;
- RarTime arctime;
-/* dummy */
- int64 FullPackSize;
- int64 FullUnpSize;
- void Clear(size_t SubDataSize)
+ int64 PackSize;
+ int64 UnpSize;
+ int64 MaxSize; // Reserve packed and unpacked size bytes for vint of this size.
+
+ HashValue FileHash;
+
+ uint FileFlags;
+
+ bool SplitBefore;
+ bool SplitAfter;
+
+ bool UnknownUnpSize;
+
+ bool Encrypted;
+ CRYPT_METHOD CryptMethod;
+ bool SaltSet;
+ byte Salt[SIZE_SALT50];
+ byte InitV[SIZE_INITV];
+ bool UsePswCheck;
+ byte PswCheck[SIZE_PSWCHECK];
+
+ // Use HMAC calculated from HashKey and checksum instead of plain checksum.
+ bool UseHashKey;
+
+ // Key to convert checksum to HMAC. Derived from password with PBKDF2
+ // using additional iterations.
+ byte HashKey[SHA256_DIGEST_SIZE];
+
+ uint Lg2Count; // Log2 of PBKDF2 repetition count.
+
+ bool Solid;
+ bool Dir;
+ bool CommentInHeader; // RAR 2.0 file comment.
+ bool Version; // name.ext;ver file name containing the version number.
+ size_t WinSize;
+ bool Inherited; // New file inherits a subblock when updating a host file (for subblocks only).
+
+ // 'true' if file sizes use 8 bytes instead of 4. Not used in RAR 5.0.
+ bool LargeFile;
+
+ // 'true' for HEAD_SERVICE block, which is a child of preceding file block.
+ // RAR 4.x uses 'solid' flag to indicate child subheader blocks in archives.
+ bool SubBlock;
+
+ HOST_SYSTEM_TYPE HSType;
+
+ FILE_SYSTEM_REDIRECT RedirType;
+ wchar RedirName[NM];
+ bool DirTarget;
+
+ bool UnixOwnerSet,UnixOwnerNumeric,UnixGroupNumeric;
+ char UnixOwnerName[256],UnixGroupName[256];
+#ifdef _UNIX
+ uid_t UnixOwnerID;
+ gid_t UnixGroupID;
+#else // Need these Unix fields in Windows too for 'list' command.
+ uint UnixOwnerID;
+ uint UnixGroupID;
+#endif
+
+ void Reset(size_t SubDataSize=0);
+
+ bool CmpName(const wchar *Name)
{
- SubData.Alloc(SubDataSize);
- Flags=LONG_BLOCK;
- SubFlags=0;
+ return(wcscmp(FileName,Name)==0);
}
- bool CmpName(const char *Name)
- {
- return(strcmp(FileName,Name)==0);
- }
-
- FileHeader& operator = (FileHeader &hd)
- {
- SubData.Reset();
- memcpy(this,&hd,sizeof(*this));
- SubData.CleanData();
- SubData=hd.SubData;
- return(*this);
- }
+ FileHeader& operator = (FileHeader &hd);
};
struct EndArcHeader:BaseBlock
{
- uint ArcDataCRC; // optional archive CRC32
- ushort VolNumber; // optional current volume number
+ // Optional CRC32 of entire archive up to start of EndArcHeader block.
+ // Present in RAR 4.x archives if EARC_DATACRC flag is set.
+ uint ArcDataCRC;
+
+ uint VolNumber; // Optional number of current volume.
+
+ // 7 additional zero bytes can be stored here if EARC_REVSPACE is set.
+
+ bool NextVolume; // Not last volume.
+ bool DataCRC;
+ bool RevSpace;
+ bool StoreVolNumber;
+ void Reset()
+ {
+ BaseBlock::Reset();
+ NextVolume=false;
+ DataCRC=false;
+ RevSpace=false;
+ StoreVolNumber=false;
+ }
+};
+
+
+struct CryptHeader:BaseBlock
+{
+ bool UsePswCheck;
+ uint Lg2Count; // Log2 of PBKDF2 repetition count.
+ byte Salt[SIZE_SALT50];
+ byte PswCheck[SIZE_PSWCHECK];
};
// SubBlockHeader and its successors were used in RAR 2.x format.
-// RAR 3.x uses FileHeader with NEWSUB_HEAD HeadType for subblocks.
+// RAR 4.x uses FileHeader with HEAD_SERVICE HeaderType for subblocks.
struct SubBlockHeader:BlockHeader
{
ushort SubType;
@@ -250,30 +321,13 @@ struct ProtectHeader:BlockHeader
};
-struct AVHeader:BaseBlock
-{
- byte UnpVer;
- byte Method;
- byte AVVer;
- uint AVInfoCRC;
-};
-
-
-struct SignHeader:BaseBlock
-{
- uint CreationTime;
- ushort ArcNameSize;
- ushort UserNameSize;
-};
-
-
struct UnixOwnersHeader:SubBlockHeader
{
ushort OwnerNameSize;
ushort GroupNameSize;
/* dummy */
- char OwnerName[NM];
- char GroupName[NM];
+ char OwnerName[256];
+ char GroupName[256];
};
@@ -293,15 +347,7 @@ struct StreamHeader:SubBlockHeader
byte Method;
uint StreamCRC;
ushort StreamNameSize;
-/* dummy */
- byte StreamName[NM];
-};
-
-
-struct MacFInfoHeader:SubBlockHeader
-{
- uint fileType;
- uint fileCreator;
+ char StreamName[260];
};
diff --git a/libunrar/headers5.hpp b/libunrar/headers5.hpp
new file mode 100644
index 0000000..9ea8d97
--- /dev/null
+++ b/libunrar/headers5.hpp
@@ -0,0 +1,100 @@
+#ifndef _RAR_HEADERS5_
+#define _RAR_HEADERS5_
+
+#define SIZEOF_MARKHEAD5 8 // RAR 5.0 signature length.
+#define SIZEOF_SHORTBLOCKHEAD5 7 // Smallest RAR 5.0 block size.
+
+// RAR 5.0 block flags common for all blocks.
+
+// Additional extra area is present in the end of block header.
+#define HFL_EXTRA 0x0001
+// Additional data area is present in the end of block header.
+#define HFL_DATA 0x0002
+// Unknown blocks with this flag must be skipped when updating an archive.
+#define HFL_SKIPIFUNKNOWN 0x0004
+// Data area of this block is continuing from previous volume.
+#define HFL_SPLITBEFORE 0x0008
+// Data area of this block is continuing in next volume.
+#define HFL_SPLITAFTER 0x0010
+// Block depends on preceding file block.
+#define HFL_CHILD 0x0020
+// Preserve a child block if host is modified.
+#define HFL_INHERITED 0x0040
+
+// RAR 5.0 main archive header specific flags.
+#define MHFL_VOLUME 0x0001 // Volume.
+#define MHFL_VOLNUMBER 0x0002 // Volume number field is present. True for all volumes except first.
+#define MHFL_SOLID 0x0004 // Solid archive.
+#define MHFL_PROTECT 0x0008 // Recovery record is present.
+#define MHFL_LOCK 0x0010 // Locked archive.
+
+// RAR 5.0 file header specific flags.
+#define FHFL_DIRECTORY 0x0001 // Directory.
+#define FHFL_UTIME 0x0002 // Time field in Unix format is present.
+#define FHFL_CRC32 0x0004 // CRC32 field is present.
+#define FHFL_UNPUNKNOWN 0x0008 // Unknown unpacked size.
+
+// RAR 5.0 end of archive header specific flags.
+#define EHFL_NEXTVOLUME 0x0001 // Not last volume.
+
+// RAR 5.0 archive encryption header specific flags.
+#define CHFL_CRYPT_PSWCHECK 0x0001 // Password check data is present.
+
+
+// RAR 5.0 file compression flags.
+#define FCI_ALGO_BIT0 0x0001 // Version of compression algorithm.
+#define FCI_ALGO_BIT1 0x0002 // 0 .. 63.
+#define FCI_ALGO_BIT2 0x0004
+#define FCI_ALGO_BIT3 0x0008
+#define FCI_ALGO_BIT4 0x0010
+#define FCI_ALGO_BIT5 0x0020
+#define FCI_SOLID 0x0040 // Solid flag.
+#define FCI_METHOD_BIT0 0x0080 // Compression method.
+#define FCI_METHOD_BIT1 0x0100 // 0 .. 5 (6 and 7 are not used).
+#define FCI_METHOD_BIT2 0x0200
+#define FCI_DICT_BIT0 0x0400 // Dictionary size.
+#define FCI_DICT_BIT1 0x0800 // 128 KB .. 4 GB.
+#define FCI_DICT_BIT2 0x1000
+#define FCI_DICT_BIT3 0x2000
+
+// Main header extra field values.
+#define MHEXTRA_LOCATOR 0x01 // Position of quick list and other blocks.
+
+// Flags for MHEXTRA_LOCATOR.
+#define MHEXTRA_LOCATOR_QLIST 0x01 // Quick open offset is present.
+#define MHEXTRA_LOCATOR_RR 0x02 // Recovery record offset is present.
+
+// File and service header extra field values.
+#define FHEXTRA_CRYPT 0x01 // Encryption parameters.
+#define FHEXTRA_HASH 0x02 // File hash.
+#define FHEXTRA_HTIME 0x03 // High precision file time.
+#define FHEXTRA_VERSION 0x04 // File version information.
+#define FHEXTRA_REDIR 0x05 // File system redirection (links, etc.).
+#define FHEXTRA_UOWNER 0x06 // Unix owner and group information.
+#define FHEXTRA_SUBDATA 0x07 // Service header subdata array.
+
+
+// Hash type values for FHEXTRA_HASH.
+#define FHEXTRA_HASH_BLAKE2 0x00
+
+// Flags for FHEXTRA_HTIME.
+#define FHEXTRA_HTIME_UNIXTIME 0x01 // Use Unix time_t format.
+#define FHEXTRA_HTIME_MTIME 0x02 // mtime is present.
+#define FHEXTRA_HTIME_CTIME 0x04 // ctime is present.
+#define FHEXTRA_HTIME_ATIME 0x08 // atime is present.
+#define FHEXTRA_HTIME_UNIX_NS 0x10 // Unix format with nanosecond precision.
+
+// Flags for FHEXTRA_CRYPT.
+#define FHEXTRA_CRYPT_PSWCHECK 0x01 // Store password check data.
+#define FHEXTRA_CRYPT_HASHMAC 0x02 // Use MAC for unpacked data checksums.
+
+// Flags for FHEXTRA_REDIR.
+#define FHEXTRA_REDIR_DIR 0x01 // Link target is directory.
+
+// Flags for FHEXTRA_UOWNER.
+#define FHEXTRA_UOWNER_UNAME 0x01 // User name string is present.
+#define FHEXTRA_UOWNER_GNAME 0x02 // Group name string is present.
+#define FHEXTRA_UOWNER_NUMUID 0x04 // Numeric user ID is present.
+#define FHEXTRA_UOWNER_NUMGID 0x08 // Numeric group ID is present.
+
+#endif
diff --git a/libunrar/isnt.cpp b/libunrar/isnt.cpp
index 493a9eb..6fadec0 100644
--- a/libunrar/isnt.cpp
+++ b/libunrar/isnt.cpp
@@ -1,9 +1,10 @@
#include "rar.hpp"
-#ifdef _WIN_32
-int WinNT()
+#ifdef _WIN_ALL
+DWORD WinNT()
{
- static int dwPlatformId=-1,dwMajorVersion;
+ static int dwPlatformId=-1;
+ static DWORD dwMajorVersion,dwMinorVersion;
if (dwPlatformId==-1)
{
OSVERSIONINFO WinVer;
@@ -11,7 +12,13 @@ int WinNT()
GetVersionEx(&WinVer);
dwPlatformId=WinVer.dwPlatformId;
dwMajorVersion=WinVer.dwMajorVersion;
+ dwMinorVersion=WinVer.dwMinorVersion;
}
- return(dwPlatformId==VER_PLATFORM_WIN32_NT ? dwMajorVersion:0);
+ DWORD Result=0;
+ if (dwPlatformId==VER_PLATFORM_WIN32_NT)
+ Result=dwMajorVersion*0x100+dwMinorVersion;
+
+
+ return Result;
}
#endif
diff --git a/libunrar/isnt.hpp b/libunrar/isnt.hpp
index 0265236..85790da 100644
--- a/libunrar/isnt.hpp
+++ b/libunrar/isnt.hpp
@@ -1,6 +1,13 @@
#ifndef _RAR_ISNT_
#define _RAR_ISNT_
-int WinNT();
+enum WINNT_VERSION {
+ WNT_NONE=0,WNT_NT351=0x0333,WNT_NT4=0x0400,WNT_W2000=0x0500,
+ WNT_WXP=0x0501,WNT_W2003=0x0502,WNT_VISTA=0x0600,WNT_W7=0x0601,
+ WNT_W8=0x0602,WNT_W81=0x0603,WNT_W10=0x0a00
+};
+
+DWORD WinNT();
+
#endif
diff --git a/libunrar/libunrar.xcodeproj/project.pbxproj b/libunrar/libunrar.xcodeproj/project.pbxproj
new file mode 100644
index 0000000..ada8774
--- /dev/null
+++ b/libunrar/libunrar.xcodeproj/project.pbxproj
@@ -0,0 +1,551 @@
+// !$*UTF8*$!
+{
+ archiveVersion = 1;
+ classes = {
+ };
+ objectVersion = 50;
+ objects = {
+
+/* Begin PBXFileReference section */
+ E296814A24BE5E1200974229 /* UnRARDll.vcxproj */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = UnRARDll.vcxproj; sourceTree = ""; };
+ E296814B24BE5E1200974229 /* scantree.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = scantree.cpp; sourceTree = ""; };
+ E296814C24BE5E1200974229 /* rarlang.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = rarlang.hpp; sourceTree = ""; };
+ E296814D24BE5E1200974229 /* threadpool.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = threadpool.hpp; sourceTree = ""; };
+ E296814E24BE5E1200974229 /* unpack30.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = unpack30.cpp; sourceTree = ""; };
+ E296814F24BE5E1200974229 /* crypt.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = crypt.cpp; sourceTree = ""; };
+ E296815024BE5E1200974229 /* qopen.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = qopen.cpp; sourceTree = ""; };
+ E296815124BE5E1200974229 /* filcreat.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = filcreat.cpp; sourceTree = ""; };
+ E296815224BE5E1200974229 /* crypt1.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = crypt1.cpp; sourceTree = ""; };
+ E296815324BE5E1200974229 /* filestr.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = filestr.hpp; sourceTree = ""; };
+ E296815424BE5E1200974229 /* suballoc.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = suballoc.hpp; sourceTree = ""; };
+ E296815524BE5E1200974229 /* ui.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = ui.cpp; sourceTree = ""; };
+ E296815624BE5E1200974229 /* blake2sp.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = blake2sp.cpp; sourceTree = ""; };
+ E296815724BE5E1200974229 /* filefn.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = filefn.o; sourceTree = ""; };
+ E296815824BE5E1200974229 /* archive.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = archive.o; sourceTree = ""; };
+ E296815924BE5E1200974229 /* crypt3.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = crypt3.cpp; sourceTree = ""; };
+ E296815A24BE5E1200974229 /* crc.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = crc.cpp; sourceTree = ""; };
+ E296815B24BE5E1200974229 /* strlist.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = strlist.hpp; sourceTree = ""; };
+ E296815C24BE5E1200974229 /* hash.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = hash.hpp; sourceTree = ""; };
+ E296815D24BE5E1200974229 /* headers.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = headers.hpp; sourceTree = ""; };
+ E296815E24BE5E1200974229 /* uicommon.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = uicommon.cpp; sourceTree = ""; };
+ E296815F24BE5E1200974229 /* global.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = global.hpp; sourceTree = ""; };
+ E296816024BE5E1200974229 /* win32lnk.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = win32lnk.cpp; sourceTree = ""; };
+ E296816124BE5E1200974229 /* log.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = log.cpp; sourceTree = ""; };
+ E296816224BE5E1200974229 /* crypt2.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = crypt2.cpp; sourceTree = ""; };
+ E296816324BE5E1200974229 /* errhnd.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = errhnd.cpp; sourceTree = ""; };
+ E296816424BE5E1200974229 /* raros.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = raros.hpp; sourceTree = ""; };
+ E296816524BE5E1200974229 /* acknow.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = acknow.txt; sourceTree = ""; };
+ E296816624BE5E1200974229 /* strfn.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = strfn.cpp; sourceTree = ""; };
+ E296816724BE5E1200974229 /* .DS_Store */ = {isa = PBXFileReference; lastKnownFileType = file; path = .DS_Store; sourceTree = ""; };
+ E296816824BE5E1200974229 /* dll.rc */ = {isa = PBXFileReference; lastKnownFileType = text; path = dll.rc; sourceTree = ""; };
+ E296816924BE5E1200974229 /* cmddata.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = cmddata.hpp; sourceTree = ""; };
+ E296816A24BE5E1200974229 /* unpack50frag.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = unpack50frag.cpp; sourceTree = ""; };
+ E296816B24BE5E1200974229 /* ui.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = ui.o; sourceTree = ""; };
+ E296816C24BE5E1200974229 /* rdwrfn.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = rdwrfn.cpp; sourceTree = ""; };
+ E296816D24BE5E1200974229 /* unicode.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = unicode.hpp; sourceTree = ""; };
+ E296816E24BE5E1200974229 /* crypt5.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = crypt5.cpp; sourceTree = ""; };
+ E296816F24BE5E1200974229 /* uowners.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = uowners.cpp; sourceTree = ""; };
+ E296817024BE5E1200974229 /* dll.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = dll.hpp; sourceTree = ""; };
+ E296817124BE5E1200974229 /* extract.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = extract.hpp; sourceTree = ""; };
+ E296817224BE5E1200974229 /* cmdfilter.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = cmdfilter.cpp; sourceTree = ""; };
+ E296817324BE5E1200974229 /* threadpool.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = threadpool.o; sourceTree = ""; };
+ E296817424BE5E1200974229 /* unpack20.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = unpack20.cpp; sourceTree = ""; };
+ E296817524BE5E1200974229 /* compress.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = compress.hpp; sourceTree = ""; };
+ E296817624BE5E1200974229 /* libunrar.so */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.dylib"; path = libunrar.so; sourceTree = ""; };
+ E296817724BE5E1200974229 /* archive.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = archive.hpp; sourceTree = ""; };
+ E296817824BE5E1200974229 /* pathfn.o */ = {isa = PBXFileReference; lastKnownFileType = "compiled.mach-o.objfile"; path = pathfn.o; sourceTree = ""; };
+ E296817924BE5E1200974229 /* blake2s_sse.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = blake2s_sse.cpp; sourceTree = ""; };
+ E296817A24BE5E1200974229 /* loclang.hpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.h; path = loclang.hpp; sourceTree = ""; };
+ E296817B24BE5E1200974229 /* resource.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = resource.cpp; sourceTree = ""; };
+ E296817C24BE5E1200974229 /* libunrar.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libunrar.a; sourceTree = ""; };
+ E296817D24BE5E1200974229 /* rarvm.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = rarvm.cpp; sourceTree = "