added uninstaller dialogs
[swftools.git] / installer / archive.c
1 /* archive.c
2
3    Part of the swftools installer.
4    
5    Copyright (c) 2004 Matthias Kramm <kramm@quiss.org> 
6  
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
20
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <stdarg.h>
24 #ifdef WIN32
25 #include <windows.h>
26 #include <shlwapi.h>
27 #else
28 #include <sys/stat.h>
29 #endif
30 #include "archive.h"
31 #include "utils.h"
32
33 #ifdef ZLIB
34 #include "../z/zlib.h"
35 #define ZLIB_BUFFER_SIZE 16384
36 #else
37 #include "lzma/LzmaDecode.h"
38 #endif
39
40 static int verbose = 0;
41 static void msg(char*format, ...)
42 {
43     char buf[1024];
44     int l;
45     va_list arglist;
46     if(!verbose)
47         return;
48     va_start(arglist, format);
49     vsprintf(buf, format, arglist);
50     va_end(arglist);
51     l = strlen(buf);
52     while(l && buf[l-1]=='\n') {
53         buf[l-1] = 0;
54         l--;
55     }
56     printf("(archive) %s\n", buf);
57     fflush(stdout);
58 }
59
60
61 typedef struct _reader
62 {
63     int (*read)(struct _reader*, void*data, int len);
64     void (*dealloc)(struct _reader*);
65     void *internal;
66     int pos;
67 } reader_t;
68
69 /* ---------------------------- mem reader ------------------------------- */
70
71 struct memread_t
72 {
73     unsigned char*data;
74     int length;
75 };
76 static int reader_memread(reader_t*reader, void* data, int _len) 
77 {
78     struct memread_t*mr = (struct memread_t*)reader->internal;
79
80     int len = _len;
81     if(mr->length - reader->pos < len) {
82         len = mr->length - reader->pos;
83     }
84     memcpy(data, &mr->data[reader->pos], len);
85     msg("at pos %d, asked to read %d bytes, did read %d bytes\n", reader->pos, _len, len);
86     reader->pos += len;
87     return len;
88 }
89 static void reader_memread_dealloc(reader_t*reader)
90 {
91     if(reader->internal)
92         free(reader->internal);
93     memset(reader, 0, sizeof(reader_t));
94 }
95 reader_t*reader_init_memreader(void*newdata, int newlength)
96 {
97     reader_t*r = malloc(sizeof(reader_t));
98     struct memread_t*mr = (struct memread_t*)malloc(sizeof(struct memread_t));
99     mr->data = (unsigned char*)newdata;
100     mr->length = newlength;
101     r->read = reader_memread;
102     r->dealloc = reader_memread_dealloc;
103     r->internal = (void*)mr;
104     r->pos = 0;
105     return r;
106
107 /* ---------------------------- lzma reader -------------------------- */
108 typedef struct
109 {
110     reader_t*input;
111     CLzmaDecoderState state;
112     unsigned char*mem;
113     int pos;
114     int len;
115     int lzmapos;
116     int available;
117 } lzma_t;
118
119 static void reader_lzma_dealloc(reader_t*reader)
120 {
121     lzma_t*i = (lzma_t*)reader->internal;
122     free(i->state.Probs);i->state.Probs = 0;
123     free(i->state.Dictionary);i->state.Dictionary = 0;
124     free(reader->internal);reader->internal=0;
125 }
126
127 static int reader_lzma_read(reader_t*reader, void*data, int len)
128 {
129     lzma_t*i = (lzma_t*)reader->internal;
130
131     SizeT processed = 0;
132     if(len>i->available)
133         len = i->available;
134     int ret = LzmaDecode(&i->state, 
135                          &i->mem[i->pos], i->len-i->pos, &i->lzmapos, 
136                          data, len, &processed);
137     i->available -= processed;
138     i->pos += i->lzmapos;
139     return processed;
140 }
141
142 reader_t* reader_init_lzma(void*mem, int len)
143 {
144     reader_t*r = malloc(sizeof(reader_t));
145     memset(r, 0, sizeof(reader_t));
146
147     lzma_t*i = (lzma_t*)malloc(sizeof(lzma_t));
148     memset(i, 0, sizeof(lzma_t));
149     r->internal = i;
150     r->read = reader_lzma_read;
151     r->dealloc = reader_lzma_dealloc;
152     r->pos = 0;
153
154     i->mem = mem;
155     i->len = len;
156     i->lzmapos = 0;
157
158     if(LzmaDecodeProperties(&i->state.Properties, mem, LZMA_PROPERTIES_SIZE)) {
159         printf("Couldn't decode properties\n");
160         return 0;
161     }
162     i->pos += LZMA_PROPERTIES_SIZE;
163
164     unsigned char*l = &i->mem[i->pos];
165     i->available = (long long)l[0]     | (long long)l[1]<<8  | (long long)l[2]<<16 | (long long)l[3]<<24 | 
166                   (long long)l[4]<<32 | (long long)l[5]<<40 | (long long)l[6]<<48 | (long long)l[7]<<56;
167     i->pos += 8; //uncompressed size
168
169     i->state.Probs = (CProb *)malloc(LzmaGetNumProbs(&i->state.Properties) * sizeof(CProb));
170     i->state.Dictionary = (unsigned char *)malloc(i->state.Properties.DictionarySize);
171     LzmaDecoderInit(&i->state);
172
173     return r;
174 }
175
176 #ifdef ZLIB
177 /* ---------------------------- zlibinflate reader -------------------------- */
178 struct zlibinflate_t
179 {
180     z_stream zs;
181     reader_t*input;
182     unsigned char readbuffer[ZLIB_BUFFER_SIZE];
183 };
184
185 static void zlib_error(int ret, char* msg, z_stream*zs)
186 {
187     fprintf(stderr, "%s: zlib error (%d): last zlib error: %s\n", msg, ret, zs->msg?zs->msg:"unknown");
188     perror("errno:");
189     exit(1);
190 }
191
192 static int reader_zlibinflate(reader_t*reader, void* data, int len) 
193 {
194     struct zlibinflate_t*z = (struct zlibinflate_t*)reader->internal;
195     int ret;
196     if(!z) {
197         return 0;
198     }
199     if(!len)
200         return 0;
201     
202     z->zs.next_out = (Bytef *)data;
203     z->zs.avail_out = len;
204
205     while(1) {
206         if(!z->zs.avail_in) {
207             z->zs.avail_in = z->input->read(z->input, z->readbuffer, ZLIB_BUFFER_SIZE);
208             z->zs.next_in = z->readbuffer;
209         }
210         if(z->zs.avail_in)
211             ret = inflate(&z->zs, Z_NO_FLUSH);
212         else
213             ret = inflate(&z->zs, Z_FINISH);
214     
215         if (ret != Z_OK &&
216             ret != Z_STREAM_END) zlib_error(ret, "bitio:inflate_inflate", &z->zs);
217
218         if (ret == Z_STREAM_END) {
219                 int pos = z->zs.next_out - (Bytef*)data;
220                 ret = inflateEnd(&z->zs);
221                 if (ret != Z_OK) zlib_error(ret, "bitio:inflate_end", &z->zs);
222                 free(reader->internal);
223                 reader->internal = 0;
224                 reader->pos += pos;
225                 return pos;
226         }
227         if(!z->zs.avail_out) {
228             break;
229         }
230     }
231     reader->pos += len;
232     return len;
233 }
234 static void reader_zlibinflate_dealloc(reader_t*reader)
235 {
236     struct zlibinflate_t*z = (struct zlibinflate_t*)reader->internal;
237     if(z) {
238         if(z->input) {
239             z->input->dealloc(z->input);z->input = 0;
240         }
241         inflateEnd(&z->zs);
242         free(reader->internal);
243     }
244     memset(reader, 0, sizeof(reader_t));
245 }
246 reader_t* reader_init_zlibinflate(reader_t*input)
247 {
248     reader_t*r = malloc(sizeof(reader_t));
249     struct zlibinflate_t*z;
250     int ret;
251     memset(r, 0, sizeof(reader_t));
252     z = (struct zlibinflate_t*)malloc(sizeof(struct zlibinflate_t));
253     memset(z, 0, sizeof(struct zlibinflate_t));
254     r->internal = z;
255     r->read = reader_zlibinflate;
256     r->dealloc = reader_zlibinflate_dealloc;
257     r->pos = 0;
258     z->input = input;
259     memset(&z->zs,0,sizeof(z_stream));
260     z->zs.zalloc = Z_NULL;
261     z->zs.zfree  = Z_NULL;
262     z->zs.opaque = Z_NULL;
263     ret = inflateInit(&z->zs);
264     if (ret != Z_OK) zlib_error(ret, "bitio:inflate_init", &z->zs);
265     return r;
266 }
267
268 /* -------------------------------------------------------------------------- */
269 #endif
270
271
272 static int create_directory(char*path,status_t* f)
273 {
274     if(!path || !*path || (*path=='.' && (!path[1] || path[1]=='.')))
275         return 1; //nothing to do
276     while(path[0]=='.' && (path[1]=='/' || path[1]=='\\'))
277         path+=2;
278
279 #ifdef WIN32
280     if(PathIsDirectoryA(path))
281         return 1;
282 #else
283     struct stat st;
284     if(stat(path, &st)>=0) {
285         if(S_ISDIR(st.st_mode)) {
286             return 1; /* already exists */
287         }
288     }
289 #endif
290
291     if(mkdir(path,0755)<0) {
292         perror("mkdir");
293         char buf[1024];
294         sprintf(buf, "create directory \"%s\" FAILED", path);
295         f->error(buf);
296         return 0;
297     }
298     return 1;
299 }
300 static int goto_directory(char*path,status_t* f)
301 {
302     if(chdir(path)<0) {
303         char buf[1024];
304         sprintf(buf, "changing to directory \"%s\" FAILED", path);
305         f->error(buf);
306         return 0;
307     }
308     return 1;
309 }
310 static char basenamebuf[256];
311 static char*get_directory(char*filename)
312 {
313     char*r1 = strrchr(filename, '\\');
314     char*r2 = strrchr(filename, '/');
315     char*r = r1>r2?r1:r2;
316     if(!r)
317         return "";
318     memcpy(basenamebuf, filename, r-filename);
319     basenamebuf[r-filename] = 0;
320     //msg("directory name of \"%s\" is \"%s\"", filename, basenamebuf);
321     return basenamebuf;
322 }
323 static int write_file(char*filename, reader_t*r, int len,status_t* f)
324 {
325     while(filename[0]=='.' && (filename[1]=='/' || filename[1]=='\\'))
326         filename+=2;
327
328     filename=strdup(filename);
329
330     char*p = filename;
331     while(*p) {
332         if(*p=='/') *p='\\';
333         p++;
334     }
335
336     f->new_file(filename);
337
338     msg("create file \"%s\" (%d bytes)", filename, len);
339     FILE*fo = fopen(filename, "wb");
340
341     if(!fo) {
342         char buf[1024];
343         sprintf(buf, "Couldn't create file %s", filename);
344         f->error(buf);
345         free(filename);
346         return 0;
347     }
348     int pos=0;
349     char buf[4096];
350     while(pos<len) {
351         int l = 4096;
352         if(pos+l>len)
353             l = len-pos;
354         int n = r->read(r, buf, l);
355         if(n < l) {
356             char buf[1024];
357             sprintf(buf, "Couldn't read byte %d (pos+%d) from input buffer for file %s", pos+n, n, filename);
358             f->error(buf);
359             return 0;
360         }
361         fwrite(buf, l, 1, fo);
362         pos+=l;
363     }
364     fclose(fo);
365     free(filename);
366     return 1;
367 }
368
369 int unpack_archive(void*data, int len, char*destdir, status_t* f)
370 {
371     reader_t*m = reader_init_memreader(data, len);
372 #ifdef ZLIB
373     reader_t*z = reader_init_zlibinflate(m);
374 #else
375     reader_t*z = reader_init_lzma(data, len);
376 #endif
377     if(!z) {
378         f->error("Couldn't decompress installation files");
379         return 0;
380     }
381
382     f->message("Creating installation directory");
383     if(!create_directory(destdir,f)) return 0;
384
385     printf("%s\n", destdir);
386         
387     unsigned b1=0,b2=0,b3=0,b4=0;
388     int l = 0;
389     l+=z->read(z, &b1, 1);
390     l+=z->read(z, &b2, 1);
391     l+=z->read(z, &b3, 1);
392     l+=z->read(z, &b4, 1);
393     if(l<4)
394         return 0;
395     /* read size */
396     int num = b1|b2<<8|b3<<16|b4<<24;
397
398     f->status(0, num);
399     
400     f->message("Uncompressing files...");
401     int pos = 0;
402     while(1) {
403         /* read id */
404         unsigned char id[4];
405         id[3] = 0;
406         if(z->read(z, id, 3)<3) {
407             f->error("Unexpected end of archive");
408             return 0;
409         }
410         if(!strcmp(id, "END"))
411             break;
412
413         unsigned b1=0,b2=0,b3=0,b4=0;
414         int l = 0;
415         l+=z->read(z, &b1, 1);
416         l+=z->read(z, &b2, 1);
417         l+=z->read(z, &b3, 1);
418         l+=z->read(z, &b4, 1);
419         if(l<4)
420             return 0;
421
422         /* read size */
423         int len = b1|b2<<8|b3<<16|b4<<24;
424         
425         /* read filename */
426         unsigned char filename_len;
427         z->read(z, &filename_len, 1);
428         char*filename = malloc(filename_len+1);
429         z->read(z, filename, filename_len);
430         filename[(int)filename_len] = 0;
431     
432         while(filename[0]=='.' && (filename[1]=='/' || filename[1]=='\\'))
433             filename+=2;
434         filename = concatPaths(destdir, filename);
435     
436         f->status(++pos, num);
437
438         if(verbose) printf("[%s] %s %d\n", id, filename, len);
439         char buf[2048];
440         sprintf(buf, "[%s] %s (%d bytes)", id, filename, len);
441         f->message(buf);
442         if(!strcmp(id, "DIR")) {
443             f->new_directory(filename);
444             if(!create_directory(filename,f)) return 0;
445         } else {
446             if(!create_directory(get_directory(filename),f)) return 0;
447             if(!write_file(filename,z,len,f)) return 0;
448         }
449     }
450     f->message("Finishing Installation");
451     return 1;
452 }
453