Application de zlib pour la décompression d'archives ZIP

Le code source de la démo se trouve dans le répertoire zlib-1.2.11\contrib\minizip. Voici une analyse simplifiée des fonctions clés :

/* Extrait de miniunz.c - Démonstration de la bibliothèque MiniZip */
/* Modification des noms et structure pour la clarté */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <fcntl.h>
#include "unzip.h"

#define SENSIBILITE_CAS (0)
#define TAILLE_TAMPON_ECRITURE (8192)
#define NOM_FICHIER_MAX (256)

/* Met à jour l'horodatage d'un fichier */
void modifier_horodatage_fichier(const char *nom_fichier, uLong date_dos, tm_unz date_tm) {
#ifdef _WIN32
    HANDLE hFichier;
    FILETIME ftm, ftLocal, ftCreation, ftDernierAcces, ftDerniereEcriture;
    hFichier = CreateFileA(nom_fichier, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL);
    GetFileTime(hFichier, &ftCreation, &ftDernierAcces, &ftDerniereEcriture);
    DosDateTimeToFileTime((WORD)(date_dos >> 16), (WORD)date_dos, &ftLocal);
    LocalFileTimeToFileTime(&ftLocal, &ftm);
    SetFileTime(hFichier, &ftm, &ftDernierAcces, &ftm);
    CloseHandle(hFichier);
#else
    struct utimbuf ut;
    struct tm nouvelle_date;
    nouvelle_date.tm_sec = date_tm.tm_sec;
    nouvelle_date.tm_min = date_tm.tm_min;
    nouvelle_date.tm_hour = date_tm.tm_hour;
    nouvelle_date.tm_mday = date_tm.tm_mday;
    nouvelle_date.tm_mon = date_tm.tm_mon;
    if (date_tm.tm_year > 1900) {
        nouvelle_date.tm_year = date_tm.tm_year - 1900;
    } else {
        nouvelle_date.tm_year = date_tm.tm_year;
    }
    nouvelle_date.tm_isdst = -1;
    ut.actime = ut.modtime = mktime(&nouvelle_date);
    utime(nom_fichier, &ut);
#endif
}

/* Crée un répertoire */
int creer_repertoire(const char *nom_rep) {
    int resultat = 0;
#ifdef _WIN32
    resultat = _mkdir(nom_rep);
#elif unix || __APPLE__
    resultat = mkdir(nom_rep, 0775);
#endif
    return resultat;
}

/* Crée des répertoires imbriqués si nécessaire */
int creer_chemin_repertoire(char *nouveau_chemin) {
    char *tampon;
    char *pointeur;
    int longueur = (int)strlen(nouveau_chemin);
    if (longueur <= 0) return 0;
    tampon = (char*)malloc(longueur + 1);
    if (tampon == NULL) {
        printf("Erreur d'allocation mémoire\n");
        return UNZ_INTERNALERROR;
    }
    strcpy(tampon, nouveau_chemin);
    if (tampon[longueur - 1] == '/') {
        tampon[longueur - 1] = '\0';
    }
    if (creer_repertoire(tampon) == 0) {
        free(tampon);
        return 1;
    }
    pointeur = tampon + 1;
    while (1) {
        char temporaire;
        while (*pointeur && *pointeur != '\\' && *pointeur != '/') pointeur++;
        temporaire = *pointeur;
        *pointeur = 0;
        if ((creer_repertoire(tampon) == -1) && (errno == ENOENT)) {
            printf("Impossible de créer le répertoire %s\n", tampon);
            free(tampon);
            return 0;
        }
        if (temporaire == 0) break;
        *pointeur++ = temporaire;
    }
    free(tampon);
    return 1;
}

/* Liste les fichiers dans une archive ZIP */
int lister_fichiers(unzFile handle_archive) {
    uLong compteur;
    unz_global_info64 info_globale;
    int erreur;
    erreur = unzGetGlobalInfo64(handle_archive, &info_globale);
    if (erreur != UNZ_OK) {
        printf("Erreur %d avec l'archive dans unzGetGlobalInfo\n", erreur);
    }
    printf("  Longueur  Méthode     Taille Ratio   Date    Heure   CRC-32     Nom\n");
    for (compteur = 0; compteur < info_globale.number_entry; compteur++) {
        char nom_fichier_archive[256];
        unz_file_info64 info_fichier;
        uLong taux = 0;
        const char *methode_texte;
        char indicateur_chiffrement = ' ';
        erreur = unzGetCurrentFileInfo64(handle_archive, &info_fichier, nom_fichier_archive, sizeof(nom_fichier_archive), NULL, 0, NULL, 0);
        if (erreur != UNZ_OK) {
            printf("Erreur %d avec l'archive dans unzGetCurrentFileInfo\n", erreur);
            break;
        }
        if (info_fichier.uncompressed_size > 0) {
            taux = (uLong)((info_fichier.compressed_size * 100) / info_fichier.uncompressed_size);
        }
        if ((info_fichier.flag & 1) != 0) {
            indicateur_chiffrement = '*';
        }
        if (info_fichier.compression_method == 0) {
            methode_texte = "Stocké";
        } else if (info_fichier.compression_method == Z_DEFLATED) {
            uInt niveau = (uInt)((info_fichier.flag & 0x6) / 2);
            if (niveau == 0) methode_texte = "Defl:N";
            else if (niveau == 1) methode_texte = "Defl:X";
            else methode_texte = "Defl:F";
        } else if (info_fichier.compression_method == Z_BZIP2ED) {
            methode_texte = "BZip2 ";
        } else {
            methode_texte = "Inconnu";
        }
        // Affichage des détails - code simplifié pour la démonstration
        if ((compteur + 1) < info_globale.number_entry) {
            erreur = unzGoToNextFile(handle_archive);
            if (erreur != UNZ_OK) {
                printf("Erreur %d avec l'archive dans unzGoToNextFile\n", erreur);
                break;
            }
        }
    }
    return 0;
}

Une implémentation personnalisée en C++ peut encapsuler ces fonctionnalités. Voici une classe réécrite avec des noms et une logique modifiés :

// DecompressionOutil.h - En-tête de la classe de décompression
#pragma once
#include <direct.h>
#include "unzip.h"

#define TAILLE_TAMPON 8192

#if (!defined(_WIN32)) && (!defined(WIN32)) && (!defined(__APPLE__))
#ifndef __USE_FILE_OFFSET64
#define __USE_FILE_OFFSET64
#endif
#ifndef __USE_LARGEFILE64
#define __USE_LARGEFILE64
#endif
#ifndef _LARGEFILE64_SOURCE
#define _LARGEFILE64_SOURCE
#endif
#ifndef _FILE_OFFSET_BIT
#define _FILE_OFFSET_BIT 64
#endif
#endif

#ifdef __APPLE__
#define FONCTION_OUVRIR(nom, mode) fopen(nom, mode)
#define FONCTION_POSITION(stream) ftello(stream)
#define FONCTION_DEPLACER(stream, offset, origine) fseeko(stream, offset, origine)
#else
#define FONCTION_OUVRIR(nom, mode) fopen(nom, mode)
#define FONCTION_POSITION(stream) ftello(stream)
#define FONCTION_DEPLACER(stream, offset, origine) fseeko(stream, offset, origine)
#endif

class DecompressionOutil {
public:
    DecompressionOutil();
    ~DecompressionOutil();
    int Extraire(char* chemin_archive, char* mot_de_passe);
    int Extraire(char* chemin_archive, char* mot_de_passe, char* dossier_sortie);
    int Compresser(); // Fonctionnalité à implémenter
private:
    unzFile handle_archive = NULL;
    int executer_extraction(unzFile handle, int ignorer_chemin, int ecraser, const char *mot_de_passe);
    int extraire_fichier_courant(unzFile handle, const int *ignorer_chemin, int *ecraser, const char *mot_de_passe);
    void modifier_horodatage(const char *nom_fichier, uLong date_dos, tm_unz date_tm);
    int creer_repertoire(const char *nom_rep);
    int creer_chemin_repertoire(char *nouveau_chemin);
    char* dossier_extrait;
};

// DecompressionOutil.cpp - Implémentation des méthodes
#include "stdafx.h"
#include "DecompressionOutil.h"

DecompressionOutil::DecompressionOutil() {
    dossier_extrait = NULL;
}

DecompressionOutil::~DecompressionOutil() {
}

int DecompressionOutil::Extraire(char *chemin_archive, char *mot_de_passe) {
    dossier_extrait = NULL;
    if (chemin_archive != NULL) {
        handle_archive = unzOpen64(chemin_archive);
    }
    if (handle_archive == NULL) {
        return 1;
    }
    int resultat = executer_extraction(handle_archive, 0, 1, mot_de_passe);
    unzClose(handle_archive);
    return resultat;
}

int DecompressionOutil::Extraire(char *chemin_archive, char *mot_de_passe, char *dossier_sortie) {
    if (dossier_sortie != NULL && !PathFileExists(CString(dossier_sortie))) {
        creer_chemin_repertoire(dossier_sortie);
    }
    if (chemin_archive != NULL) {
        handle_archive = unzOpen64(chemin_archive);
    }
    if (handle_archive == NULL) {
        return 1;
    }
    dossier_extrait = dossier_sortie;
    int resultat = executer_extraction(handle_archive, 0, 1, mot_de_passe);
    unzClose(handle_archive);
    dossier_extrait = NULL;
    return resultat;
}

int DecompressionOutil::executer_extraction(unzFile handle, int ignorer_chemin, int ecraser, const char *mot_de_passe) {
    uLong compteur;
    unz_global_info64 info_globale;
    int erreur;
    FILE* fichier_sortie = NULL;
    erreur = unzGetGlobalInfo64(handle, &info_globale);
    if (erreur != UNZ_OK) {
        printf("Erreur %d avec l'archive dans unzGetGlobalInfo\n", erreur);
    }
    for (compteur = 0; compteur < info_globale.number_entry; compteur++) {
        if (extraire_fichier_courant(handle, &ignorer_chemin, &ecraser, mot_de_passe) != UNZ_OK) {
            break;
        }
        if ((compteur + 1) < info_globale.number_entry) {
            erreur = unzGoToNextFile(handle);
            if (erreur != UNZ_OK) {
                printf("Erreur %d avec l'archive dans unzGoToNextFile\n", erreur);
                break;
            }
        }
    }
    return 0;
}

int DecompressionOutil::extraire_fichier_courant(unzFile handle, const int *ignorer_chemin, int *ecraser, const char *mot_de_passe) {
    char nom_fichier_archive[256];
    char *nom_sans_chemin;
    char *pointeur;
    int erreur = UNZ_OK;
    FILE *fichier_sortie = NULL;
    void *tampon;
    uInt taille_tampon;
    unz_file_info64 info_fichier;
    erreur = unzGetCurrentFileInfo64(handle, &info_fichier, nom_fichier_archive, sizeof(nom_fichier_archive), NULL, 0, NULL, 0);
    if (erreur != UNZ_OK) {
        printf("Erreur %d avec l'archive dans unzGetCurrentFileInfo\n", erreur);
        return erreur;
    }
    if (dossier_extrait) {
        char chemin_temporaire[256];
        sprintf(chemin_temporaire, "%s%s", dossier_extrait, nom_fichier_archive);
        strncpy(nom_fichier_archive, chemin_temporaire, 256);
    }
    taille_tampon = TAILLE_TAMPON;
    tampon = (void*)malloc(taille_tampon);
    if (tampon == NULL) {
        printf("Erreur d'allocation mémoire\n");
        return UNZ_INTERNALERROR;
    }
    pointeur = nom_sans_chemin = nom_fichier_archive;
    while ((*pointeur) != '\0') {
        if (((*pointeur) == '/') || ((*pointeur) == '\\')) {
            nom_sans_chemin = pointeur + 1;
        }
        pointeur++;
    }
    if ((*nom_sans_chemin) == '\0') {
        if ((*ignorer_chemin) == 0) {
            printf("Création du répertoire: %s\n", nom_fichier_archive);
            creer_repertoire(nom_fichier_archive);
        }
    } else {
        const char* nom_ecriture;
        int ignorer = 0;
        if ((*ignorer_chemin) == 0) {
            nom_ecriture = nom_fichier_archive;
        } else {
            nom_ecriture = nom_sans_chemin;
        }
        erreur = unzOpenCurrentFilePassword(handle, mot_de_passe);
        if (erreur != UNZ_OK) {
            printf("Erreur %d avec l'archive dans unzOpenCurrentFilePassword\n", erreur);
        }
        if (((*ecraser) == 0) && (erreur == UNZ_OK)) {
            char reponse = 0;
            FILE* test_existence;
            test_existence = FONCTION_OUVRIR(nom_ecriture, "rb");
            if (test_existence != NULL) {
                fclose(test_existence);
                do {
                    char saisie[128];
                    int lecture;
                    printf("Le fichier %s existe. Écraser ? [o]ui, [n]on, [T]ous: ", nom_ecriture);
                    lecture = scanf("%1s", saisie);
                    if (lecture != 1) {
                        exit(EXIT_FAILURE);
                    }
                    reponse = saisie[0];
                    if ((reponse >= 'a') && (reponse <= 'z')) {
                        reponse -= 0x20;
                    }
                } while ((reponse != 'O') && (reponse != 'N') && (reponse != 'T'));
            }
            if (reponse == 'N') {
                ignorer = 1;
            }
            if (reponse == 'T') {
                *ecraser = 1;
            }
        }
        if ((ignorer == 0) && (erreur == UNZ_OK)) {
            fichier_sortie = FONCTION_OUVRIR(nom_ecriture, "wb");
            if ((fichier_sortie == NULL) && ((*ignorer_chemin) == 0) && (nom_sans_chemin != (char*)nom_fichier_archive)) {
                char caractere = *(nom_sans_chemin - 1);
                *(nom_sans_chemin - 1) = '\0';
                creer_chemin_repertoire((char*)nom_ecriture);
                *(nom_sans_chemin - 1) = caractere;
                fichier_sortie = FONCTION_OUVRIR(nom_ecriture, "wb");
            }
            if (fichier_sortie == NULL) {
                printf("Erreur lors de l'ouverture de %s\n", nom_ecriture);
            }
        }
        if (fichier_sortie != NULL) {
            printf(" Extraction: %s\n", nom_ecriture);
            do {
                erreur = unzReadCurrentFile(handle, tampon, taille_tampon);
                if (erreur < 0) {
                    printf("Erreur %d avec l'archive dans unzReadCurrentFile\n", erreur);
                    break;
                }
                if (erreur > 0) {
                    if (fwrite(tampon, erreur, 1, fichier_sortie) != 1) {
                        printf("Erreur lors de l'écriture du fichier extrait\n");
                        erreur = UNZ_ERRNO;
                        break;
                    }
                }
            } while (erreur > 0);
            if (fichier_sortie) {
                fclose(fichier_sortie);
            }
            if (erreur == 0) {
                modifier_horodatage(nom_ecriture, info_fichier.dosDate, info_fichier.tmu_date);
            }
        }
        if (erreur == UNZ_OK) {
            erreur = unzCloseCurrentFile(handle);
            if (erreur != UNZ_OK) {
                printf("Erreur %d avec l'archive dans unzCloseCurrentFile\n", erreur);
            }
        } else {
            unzCloseCurrentFile(handle);
        }
    }
    free(tampon);
    return erreur;
}

void DecompressionOutil::modifier_horodatage(const char *nom_fichier, uLong date_dos, tm_unz date_tm) {
    // Implémentation similaire à celle montrée précédemment, avec adaptations
}

int DecompressionOutil::creer_repertoire(const char *nom_rep) {
    // Implémentation adaptée
    return 0;
}

int DecompressionOutil::creer_chemin_repertoire(char *nouveau_chemin) {
    // Implémentation adaptée
    return 1;
}

int DecompressionOutil::Compresser() {
    return 0;
}

Pour utiliser cette classe, voici un exemple :

// Exemple d'utilisation
DecompressionOutil* outil = new DecompressionOutil();

// Extraction avec le répertoire par défaut
// outil->Extraire("chemin/vers/archive.zip", NULL);

// Extraction vers un dossier spécifique
outil->Extraire("chemin/vers/archive.zip", NULL, "./dossier_sortie/");

Notez que la bibliothèque zlibwapi.dll fournie est en 32 bits, ce qui peut causer des problèmes. Pour une compatibilité 64 bits, il est nécessaire de compiler la bibliothèque à partir des sources, disponibles dans le répertoire zlib-1.2.11\contrib\vstudio. Lors de la décompression de fichiers protégés par mot de passe, des erreurs comme UNZ_PARAMERROR ou UNZ_BADZIPFILE peuvent survenir, indiquant des problèmes avec l'archive ou le mot de passe.

Étiquettes: Zlib minizip zip decompression compression

Publié le 25 juillet à 05h20