Handle nested TOC items.
[dorian] / model / extractzip.cpp
1 #include <QFile>
2 #include <QDir>
3 #include <QDebug>
4
5 #include "extractzip.h"
6 #include "unzip/unzip.h"
7
8 #define WRITEBUFFERSIZE (8192)
9 #define MAXFILENAME (256)
10
11 int doExtractCurrentFile(unzFile uf, const QStringList &excludedExtensions)
12 {
13     char fileNameInZip[MAXFILENAME];
14     char *fileNameWithoutPath;
15     char *p;
16     int err = UNZ_OK;
17     QFile *f = 0;
18     QDir dir;
19     void *buf;
20     uInt bufSize;
21     bool ret = true;
22     unz_file_info64 fileInfo;
23
24     err = unzGetCurrentFileInfo64(uf, &fileInfo, fileNameInZip,
25                                   sizeof(fileNameInZip), NULL, 0, NULL, 0);
26     if (err != UNZ_OK) {
27         qDebug() << "doExtractCurrentFile: Error" << err
28                 << "in unzGetCurrentFileInfo";
29         return err;
30     }
31
32     bufSize = WRITEBUFFERSIZE;
33     buf = (void *)malloc(bufSize);
34     if (buf == NULL) {
35         qDebug() << "doExtractCurrentFile: Error allocating memory";
36         return UNZ_INTERNALERROR;
37     }
38
39     p = fileNameWithoutPath = fileNameInZip;
40     while ((*p) != '\0') {
41         if (((*p) == '/') || ((*p) == '\\')) {
42             fileNameWithoutPath = p + 1;
43         }
44         p++;
45     }
46
47     if ((*fileNameWithoutPath) == '\0') {
48         dir.mkdir(fileNameInZip);
49     }
50     else {
51         QString name(fileNameInZip);
52         for (int i = 0; i < excludedExtensions.length(); i++) {
53             if (name.endsWith(excludedExtensions[i], Qt::CaseInsensitive)) {
54                 qDebug() << "Skipping" << name;
55                 free(buf);
56                 return UNZ_OK;
57             }
58         }
59
60         const char *writeFileName;
61         int skip = 0;
62
63         writeFileName = fileNameInZip;
64
65         err = unzOpenCurrentFilePassword(uf, 0);
66         if (err != UNZ_OK) {
67             qDebug() << "doExtractCurrentFile: Error" << err
68                     << "in unzOpenCurrentFilePassword";
69             ret = false;
70         }
71
72         if ((skip == 0) && (err == UNZ_OK)) {
73             f = new QFile(writeFileName);
74             ret = f->open(QIODevice::WriteOnly);
75
76             /* some zipfile don't contain directory alone before file */
77             if (!ret && (fileNameWithoutPath != (char *)fileNameInZip))
78             {
79                 char c = *(fileNameWithoutPath-1);
80                 *(fileNameWithoutPath - 1) = '\0';
81                 dir.mkpath(writeFileName);
82                 *(fileNameWithoutPath - 1) = c;
83                 delete f;
84                 f = new QFile(writeFileName);
85                 ret = f->open(QIODevice::WriteOnly);
86             }
87
88             if (!ret) {
89                 qDebug() << "doExtractCurrentFile: Error opening"
90                         << writeFileName;
91             }
92         }
93
94         if (ret) {
95             do {
96                 err = unzReadCurrentFile(uf, buf, bufSize);
97                 if (err < 0) {
98                     qDebug() << "doExtractCurrentFile: Error" << err
99                             << "in unzReadCurrentFile";
100                     break;
101                 }
102                 if (err > 0) {
103                     if (f->write((char *)buf, err) != err) {
104                         qDebug() << "doExtractCurrentFile:"
105                                 << "Error in writing extracted file";
106                         err = UNZ_ERRNO;
107                         break;
108                     }
109                 }
110             }
111             while (err > 0);
112
113             f->close();
114         }
115
116         if (err == UNZ_OK) {
117             err = unzCloseCurrentFile(uf);
118             if (err != UNZ_OK) {
119                 qDebug() << "doExtractCurrentFile: Error" << err
120                        << "with zipfile in unzCloseCurrentFile";
121             }
122         }
123         else {
124             unzCloseCurrentFile(uf); /* don't lose the error */
125         }
126     }
127
128     delete f;
129     free(buf);
130     return err;
131 }
132
133 bool doExtract(unzFile uf, const QStringList &excludedExtensions)
134 {
135     uLong i;
136     unz_global_info64 gi;
137     int err;
138
139     err = unzGetGlobalInfo64(uf, &gi);
140     if (err != UNZ_OK) {
141         qDebug() << "doExtract: Error" << err << "in unzGetGlobalInfo";
142         return false;
143     }
144
145     for (i = 0; i < gi.number_entry; i++) {
146         if (doExtractCurrentFile(uf, excludedExtensions) != UNZ_OK) {
147             return false;
148         }
149         if ((i + 1) < gi.number_entry) {
150             err = unzGoToNextFile(uf);
151             if (err != UNZ_OK) {
152                 qDebug() << "doExtract: Error" << err << "in unzGoToNextFile";
153                 return false;
154             }
155         }
156     }
157
158     return true;
159 }
160
161 bool extractZip(const QString &zipFile, const QStringList &excludedExtensions)
162 {
163     unzFile uf;
164     bool ret = false;
165
166     uf = unzOpen64(zipFile.toUtf8().constData());
167     if (uf) {
168         ret = doExtract(uf, excludedExtensions);
169         unzClose(uf);
170     }
171     return ret;
172 }