2 * cabextract interface for acquiration installation utility
3 * Copyright (C) 2003 Jan Kratochvil <project-captive@jankratochvil.net>
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; exactly version 2 of June 1991 is required
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 #include "cabinet.h" /* self */
23 #include <glib/gmessages.h>
24 #include <libgnomevfs/gnome-vfs-file-size.h>
25 #include <libgnomevfs/gnome-vfs-ops.h>
26 #include "cabextract/cabextract.h"
27 #include "captivemodid.h"
28 #include "moduriload.h"
35 #include "cabinet-memory.h"
37 #include <captive/macros.h>
41 #define ACQUIRE_CABINET_READ_RAW_READ_TRY_MAX 5
42 #define ACQUIRE_CABINET_READ_RAW_READ_TIMEOUT 20
43 #define ACQUIRE_CABINET_READ_RAW_READ_ITER_SEC 0
44 #define ACQUIRE_CABINET_READ_RAW_READ_ITER_USEC 100000
47 #define ACQUIRE_CABINET_READ_RAW_READ_TIMEOUT_ITERS \
48 ((ACQUIRE_CABINET_READ_RAW_READ_TIMEOUT *1000000LL) \
49 /(ACQUIRE_CABINET_READ_RAW_READ_ITER_SEC*1000000LL+ACQUIRE_CABINET_READ_RAW_READ_ITER_USEC))
52 void acquire_cabinet_seek(struct acquire_cabinet *acquire_cabinet,GnomeVFSFileOffset offset)
54 g_return_if_fail(acquire_cabinet!=NULL);
56 /* Do not: (*ui_progress)(acquire_cabinet->uri);
57 * as we currently extract some specific file out of it.
61 acquire_cabinet->offset=offset;
64 void acquire_cabinet_seek_skip(struct acquire_cabinet *acquire_cabinet,GnomeVFSFileOffset offset)
66 g_return_if_fail(acquire_cabinet!=NULL);
70 acquire_cabinet->offset+=offset;
73 GnomeVFSFileOffset acquire_cabinet_tell(struct acquire_cabinet *acquire_cabinet)
75 g_return_val_if_fail(acquire_cabinet!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
79 return acquire_cabinet->offset;
82 static guint handler_SIGALRM_hits;
83 static sigjmp_buf handler_SIGALRM_sigjmp_buf;
85 static void handler_SIGALRM(int signo)
87 g_return_if_fail(signo==SIGALRM);
89 /* Try to abort the read(2) call first.
90 * If it already read something it will return the partially read data.
91 * Otherwise gnome_vfs_inet_connection_read() will loop back to retry read(2)
92 * and we will abort it after 1 second. OK, some data may be read that time
95 if (handler_SIGALRM_hits++<ACQUIRE_CABINET_READ_RAW_READ_TIMEOUT_ITERS
96 && !(*ui_progress)(NULL))
99 siglongjmp(handler_SIGALRM_sigjmp_buf,1); /* 1; meaning: !=0 */
102 /* FIXME: This is hack.
103 * Correct way would be to use 'GnomeVFSCancellation'
104 * to abort 'GnomeVFSInetConnection' acting as 'GnomeVFSSocket'.
105 * This abort should be handled from 'http'/'httpcaptive' handler
106 * but gnome_vfs_cancellation_cancel() cannot be invoked from
107 * the asynchronous slave thread.
109 static GnomeVFSResult acquire_cabinet_read_raw
110 (struct acquire_cabinet *acquire_cabinet,gpointer buffer,GnomeVFSFileSize bytes,GnomeVFSFileSize *bytes_read,
111 GnomeVFSFileOffset offset)
115 g_return_val_if_fail(acquire_cabinet!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
116 g_return_val_if_fail(buffer!=NULL || bytes==0,GNOME_VFS_ERROR_BAD_PARAMETERS);
117 g_return_val_if_fail(bytes_read!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
122 return GNOME_VFS_ERROR_EOF;
124 while (try++<=ACQUIRE_CABINET_READ_RAW_READ_TRY_MAX) {
125 GnomeVFSResult errvfsresult;
126 GnomeVFSHandle *handle_new;
127 struct sigaction oldact;
129 struct itimerval itimerval;
131 if ((*ui_progress)(NULL))
132 return GNOME_VFS_ERROR_INTERRUPTED;
135 handler_SIGALRM_sigjmp_buf, /* env */
136 TRUE)) { /* savesigs */
137 handler_SIGALRM_hits=0;
139 SIGALRM, /* signum */
141 &oldact); /* oldact */
143 signal(SIGALRM,handler_SIGALRM);
144 itimerval.it_interval.tv_sec=ACQUIRE_CABINET_READ_RAW_READ_ITER_SEC;
145 itimerval.it_interval.tv_usec=ACQUIRE_CABINET_READ_RAW_READ_ITER_USEC;
146 itimerval.it_value=itimerval.it_interval;
148 ITIMER_REAL, /* which */
149 &itimerval, /* value */
152 errvfsresult=gnome_vfs_seek(*acquire_cabinet->handlep,GNOME_VFS_SEEK_START,offset);
153 if (GNOME_VFS_OK==errvfsresult)
154 errvfsresult=gnome_vfs_read(*acquire_cabinet->handlep,buffer,bytes,bytes_read);
157 errvfsresult=GNOME_VFS_ERROR_INTERRUPTED;
158 itimerval.it_interval.tv_sec=0;
159 itimerval.it_interval.tv_usec=0;
160 itimerval.it_value=itimerval.it_interval;
162 ITIMER_REAL, /* which */
163 &itimerval, /* value */
167 SIGALRM, /* signum */
171 if (errvfsresult==GNOME_VFS_OK) {
172 g_assert(*bytes_read>0);
176 /* Reopen '*acquire_cabinet->handlep' */
177 g_assert(acquire_cabinet->handle_uri!=NULL);
178 if (GNOME_VFS_OK==(errvfsresult=gnome_vfs_open_uri(&handle_new,acquire_cabinet->handle_uri,GNOME_VFS_OPEN_READ))) {
179 gnome_vfs_close(*acquire_cabinet->handlep); /* errors ignored */
180 *acquire_cabinet->handlep=handle_new;
184 return GNOME_VFS_ERROR_IO;
187 #define ACQUIRE_CABINET_BYTE_CACHED(acquire_cabinet,pos) (!(acquire_cabinet)->base_cached \
188 || (acquire_cabinet)->base_cached[(pos)/8] & 1<<((pos)&7))
189 #define ACQUIRE_CABINET_SET_BYTE_CACHED(acquire_cabinet,pos) ((acquire_cabinet)->base_cached[(pos)/8] |= 1<<((pos)&7))
191 GnomeVFSResult acquire_cabinet_read
192 (struct acquire_cabinet *acquire_cabinet,gpointer buffer,GnomeVFSFileSize bytes,GnomeVFSFileSize *bytes_read)
194 GnomeVFSFileOffset offset_start,offset_end,read_behind;
195 GnomeVFSResult errvfsresult;
196 GnomeVFSFileSize bytes_read_now;
198 g_return_val_if_fail(acquire_cabinet!=NULL,GNOME_VFS_ERROR_BAD_PARAMETERS);
199 g_return_val_if_fail(buffer!=NULL || bytes==0,GNOME_VFS_ERROR_BAD_PARAMETERS);
203 if ((*ui_progress)(NULL))
204 return GNOME_VFS_ERROR_INTERRUPTED;
206 bytes=MAX(0,MIN(bytes,acquire_cabinet->size-acquire_cabinet->offset));
208 return GNOME_VFS_ERROR_EOF;
211 read_behind =acquire_cabinet->offset+bytes;
213 /* GnomeVFS block transfer: */
214 offset_start=acquire_cabinet->offset;
215 offset_end =acquire_cabinet->offset;
216 while (offset_end<read_behind && !ACQUIRE_CABINET_BYTE_CACHED(acquire_cabinet,offset_end))
218 if (offset_end>offset_start) {
219 errvfsresult=acquire_cabinet_read_raw(acquire_cabinet,
220 acquire_cabinet->base+offset_start,offset_end-offset_start,&bytes_read_now,offset_start);
221 if (errvfsresult!=GNOME_VFS_OK)
223 g_assert(bytes_read_now>0);
224 acquire_cabinet->cabinet_done+=bytes_read_now;
226 (*ui_progress_bar)(acquire_cabinet->cabinet_done,acquire_cabinet->cabinet_used);
227 while (bytes_read_now) {
228 ACQUIRE_CABINET_SET_BYTE_CACHED(acquire_cabinet,offset_start);
234 /* Memory block transfer: */
235 offset_start=acquire_cabinet->offset;
236 offset_end =acquire_cabinet->offset;
237 while (offset_end<read_behind && ACQUIRE_CABINET_BYTE_CACHED(acquire_cabinet,offset_end))
239 memcpy(buffer,acquire_cabinet->base+offset_start,offset_end-offset_start);
241 *bytes_read+=offset_end-offset_start;
242 buffer+=offset_end-offset_start;
243 bytes-=offset_end-offset_start;
244 acquire_cabinet->offset=offset_end;
250 static void acquire_cabinet_set_uri(struct acquire_cabinet *acquire_cabinet,GnomeVFSURI *uri)
252 GnomeVFSURI *uri_cabextract;
254 g_return_if_fail(acquire_cabinet!=NULL);
255 g_return_if_fail(uri!=NULL);
257 /* FIXME: HACK: Use proper 'cabextract' scheme after it gets implemented.
258 * GnomeVFS will return NULL on gnome_vfs_uri_new() with scheme not available.
260 uri_cabextract=gnome_vfs_uri_new("file://");
261 g_assert(uri_cabextract->parent==NULL);
262 /* Do not: g_assert(!strcmp(uri_cabextract->method_string,"file"));
263 * uri_cabextract->method_string=g_strdup("cabextract");
264 * as it will just strip such anchor. FIXME: Why?
267 uri_cabextract->parent=gnome_vfs_uri_dup(uri);
269 acquire_cabinet->uri=uri_cabextract;
270 acquire_cabinet->handle_uri=gnome_vfs_uri_ref(uri);
271 acquire_cabinet->filename=gnome_vfs_uri_to_string(acquire_cabinet->uri,GNOME_VFS_URI_HIDE_PASSWORD);
274 struct acquire_cabinet *acquire_cabinet_new_from_memory
275 (gconstpointer file_base,size_t file_length,GnomeVFSURI *uri,gint cabinet_used)
277 struct acquire_cabinet *r;
279 g_return_val_if_fail(file_base!=NULL,NULL);
280 g_return_val_if_fail(uri!=NULL,NULL);
283 r->base=(/* de-const */ gpointer)file_base;
288 acquire_cabinet_set_uri(r,uri);
290 r->cabinet_used=cabinet_used;
291 r->memory=acquire_cabinet_memory_object_new();
296 struct acquire_cabinet *acquire_cabinet_new_from_handle
297 (GnomeVFSHandle **handlep,GnomeVFSFileInfo *file_info,GnomeVFSURI *uri,gint cabinet_used)
299 struct acquire_cabinet *r;
301 g_return_val_if_fail(handlep!=NULL,NULL);
302 g_return_val_if_fail(*handlep!=NULL,NULL);
303 g_return_val_if_fail(file_info!=NULL,NULL);
304 g_return_val_if_fail(uri!=NULL,NULL);
307 if (MAP_FAILED==(r->base=mmap(
309 CAPTIVE_ROUND_UP64(file_info->size,getpagesize()), /* length */
310 PROT_READ|PROT_WRITE,
311 MAP_ANONYMOUS|MAP_PRIVATE /* flags */
312 |MAP_NORESERVE, /* We will not probably not read the whole cabinet. */
313 -1, /* fd; ignored due to MAP_ANONYMOUS */
314 0))) { /* offset; ignored due to MAP_ANONYMOUS */
316 g_return_val_if_reached(NULL);
318 captive_new0n(r->base_cached,CAPTIVE_ROUND_UP64(file_info->size,8)/8);
321 r->size=file_info->size;
323 r->cabinet_used=cabinet_used;
325 /* Replace 'http://' by 'httpcaptive://' if system 'http' does not support seek(). */
326 gnome_vfs_uri_ref(uri);
327 if (GNOME_VFS_ERROR_NOT_SUPPORTED==gnome_vfs_seek(
328 *handlep, /* handle */
329 GNOME_VFS_SEEK_START, /* whence */
335 href=gnome_vfs_uri_to_string(uri,GNOME_VFS_URI_HIDE_NONE);
336 if (strncmp(href,"http://",strlen("http://"))) {
337 g_warning(_("Destination file URL not valid: %s"),href);
340 href2=captive_printf_alloca("httpcaptive://%s",href+strlen("http://"));
341 if (!(uri2=gnome_vfs_uri_new(href2))) {
342 g_warning(_("'httpcaptive' GnomeVFS method not supported; install package 'gnomevfs-httpcaptive'; URL: %s"),href2);
345 gnome_vfs_uri_unref(uri);
350 acquire_cabinet_set_uri(r,uri);
351 gnome_vfs_uri_unref(uri);
352 r->memory=acquire_cabinet_memory_object_new();
357 void acquire_cabinet_free(struct acquire_cabinet *acquire_cabinet)
359 g_return_if_fail(acquire_cabinet!=NULL);
361 if (acquire_cabinet->base_cached) {
362 munmap(acquire_cabinet->base,CAPTIVE_ROUND_UP64(acquire_cabinet->size,getpagesize())); /* errors ignored */
363 g_free(acquire_cabinet->base_cached);
365 g_free((/* de-const */ gchar *)acquire_cabinet->filename);
366 gnome_vfs_uri_unref(acquire_cabinet->uri);
367 gnome_vfs_uri_unref(acquire_cabinet->handle_uri);
368 g_object_unref(acquire_cabinet->memory);
369 g_free(acquire_cabinet);
372 static struct file *file_write_fi_assertion;
373 static GByteArray *file_write_bytearray;
375 int file_write(struct file *fi, UBYTE *buf, size_t length)
377 g_return_val_if_fail(fi!=NULL,0);
378 g_return_val_if_fail(buf!=NULL || length==0,0);
380 g_return_val_if_fail(fi==file_write_fi_assertion,0);
381 g_return_val_if_fail(file_write_bytearray!=NULL,0);
383 if ((*ui_progress)(NULL))
386 g_byte_array_append(file_write_bytearray,buf,length);
388 return 1; /* success */
391 void acquire_cabinet_load(struct acquire_cabinet *acquire_cabinet)
393 struct cabinet *basecab;
394 struct file *filelist,*fi;
396 g_return_if_fail(acquire_cabinet!=NULL);
399 (*ui_progress_bar)(acquire_cabinet->cabinet_done,acquire_cabinet->cabinet_used);
401 if ((*ui_progress)(acquire_cabinet->uri))
404 acquire_cabinet_memory_object_push(acquire_cabinet->memory);
406 basecab=find_cabs_in_file(acquire_cabinet);
408 goto fail_memory_pop;
410 goto fail_memory_pop;
411 if (basecab->prevcab || basecab->nextcab)
412 goto fail_memory_pop;
414 filelist=process_files(basecab);
416 for (fi=filelist;fi;fi=fi->next) {
417 gpointer file_buffer;
421 if (!captivemodid_module_length_is_valid(fi->length))
424 uri_fi=gnome_vfs_uri_append_file_name(acquire_cabinet->uri,fi->filename);
425 if ((*ui_progress)(uri_fi)) {
426 gnome_vfs_uri_unref(uri_fi);
427 goto fail_memory_pop;
430 file_write_fi_assertion=fi;
431 file_write_bytearray=g_byte_array_new();
432 /* extract_file() returns 1 for success. */
433 errint=extract_file(fi,
434 0, /* lower; ignored now */
436 NULL); /* dir; ignored now */
437 if (!errint || fi->length!=file_write_bytearray->len) {
438 g_byte_array_free(file_write_bytearray,
439 TRUE); /* free_segment */
440 gnome_vfs_uri_unref(uri_fi);
442 goto fail_memory_pop;
445 file_buffer=g_byte_array_free(file_write_bytearray,
446 FALSE); /* free_segment */
447 mod_uri_load_file_from_memory(file_buffer,fi->length,uri_fi);
448 gnome_vfs_uri_unref(uri_fi);
455 acquire_cabinet_memory_object_pop(acquire_cabinet->memory);