Implement zip_fdopen and ZIP_AFL_RDONLY.
This allows to open a zip archive from an already open file descriptor;
archives opened this way are marked read-only.

--HG--
branch : HEAD
diff --git a/lib/Makefile.am b/lib/Makefile.am
index f52984c..b6d7b13 100644
--- a/lib/Makefile.am
+++ b/lib/Makefile.am
@@ -25,6 +25,7 @@
 	zip_error_strerror.c \
 	zip_error_to_str.c \
 	zip_fclose.c \
+	zip_fdopen.c \
 	zip_file_error_clear.c \
 	zip_file_error_get.c \
 	zip_file_get_offset.c \
diff --git a/lib/zip.h b/lib/zip.h
index 0c9a7f6..03b5640 100644
--- a/lib/zip.h
+++ b/lib/zip.h
@@ -68,10 +68,13 @@
 #define ZIP_FL_COMPRESSED	4 /* read compressed data */
 #define ZIP_FL_UNCHANGED	8 /* use original data, ignoring changes */
 #define ZIP_FL_RECOMPRESS      16 /* force recompression of data */
+#define ZIP_FL_ENCRYPTED       32 /* read encrypted data
+				     (implies ZIP_FL_COMPRESSED) */
 
 /* archive global flags flags */
 
 #define ZIP_AFL_TORRENT		1 /* torrent zipped */
+#define ZIP_AFL_RDONLY		2 /* read only -- cannot be cleared */
 
 /* libzip error codes */
 
@@ -100,6 +103,8 @@
 #define ZIP_ER_REMOVE        22  /* S Can't remove file */
 #define ZIP_ER_DELETED       23  /* N Entry has been deleted */
 #define ZIP_ER_ENCRNOTSUPP   24  /* N Encryption method not supported */
+#define ZIP_ER_RDONLY        25  /* N Read-only archive */ 
+#define ZIP_ER_NOPASSWD      26  /* N No password provided */
 
 
 /* type of system error value */
@@ -189,6 +194,7 @@
 ZIP_EXTERN int zip_error_get_sys_type(int);
 ZIP_EXTERN int zip_error_to_str(char *, size_t, int, int);
 ZIP_EXTERN int zip_fclose(struct zip_file *);
+ZIP_EXTERN struct zip *zip_fdopen(int, int, int *);
 ZIP_EXTERN void zip_file_error_clear(struct zip_file *);
 ZIP_EXTERN void zip_file_error_get(struct zip_file *, int *, int *);
 ZIP_EXTERN const char *zip_file_strerror(struct zip_file *);
diff --git a/lib/zip_add_dir.c b/lib/zip_add_dir.c
index 493e28d..39c13ee 100644
--- a/lib/zip_add_dir.c
+++ b/lib/zip_add_dir.c
@@ -1,6 +1,6 @@
 /*
   zip_add_dir.c -- add directory
-  Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
   The authors can be contacted at <libzip@nih.at>
@@ -47,6 +47,11 @@
     char *s;
     struct zip_source *source;
 
+    if (ZIP_IS_RDONLY(za)) {
+	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
+	return -1;
+    }
+
     if (name == NULL) {
 	_zip_error_set(&za->error, ZIP_ER_INVAL, 0);
 	return -1;
diff --git a/lib/zip_close.c b/lib/zip_close.c
index f5dc3a6..baac888 100644
--- a/lib/zip_close.c
+++ b/lib/zip_close.c
@@ -1,6 +1,6 @@
 /*
   zip_close.c -- close zip archive and update changes
-  Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
   The authors can be contacted at <libzip@nih.at>
@@ -52,7 +52,6 @@
 static int copy_data(FILE *, off_t, FILE *, struct zip_error *);
 static int write_cdir(struct zip *, struct zip_cdir *, FILE *);
 static int _zip_cdir_set_comment(struct zip_cdir *, struct zip *);
-static int _zip_changed(struct zip *, int *);
 static char *_zip_create_temp_output(struct zip *, FILE **);
 static int _zip_torrentzip_cmp(const void *, const void *);
 
@@ -604,7 +603,7 @@
 
 
 
-static int
+int
 _zip_changed(struct zip *za, int *survivorsp)
 {
     int changed, i, survivors;
@@ -623,7 +622,8 @@
 	    survivors++;
     }
 
-    *survivorsp = survivors;
+    if (survivorsp)
+	*survivorsp = survivors;
 
     return changed;
 }
diff --git a/lib/zip_delete.c b/lib/zip_delete.c
index a0545e4..382b3fd 100644
--- a/lib/zip_delete.c
+++ b/lib/zip_delete.c
@@ -1,6 +1,6 @@
 /*
   zip_delete.c -- delete file from zip archive
-  Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
   The authors can be contacted at <libzip@nih.at>
@@ -45,6 +45,11 @@
 	return -1;
     }
 
+    if (ZIP_IS_RDONLY(za)) {
+	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
+	return -1;
+    }
+
     /* allow duplicate file names, because the file will
      * be removed directly afterwards */
     if (_zip_unchange(za, idx, 1) != 0)
diff --git a/lib/zip_fdopen.c b/lib/zip_fdopen.c
new file mode 100644
index 0000000..09cdafd
--- /dev/null
+++ b/lib/zip_fdopen.c
@@ -0,0 +1,61 @@
+/*
+  zip_fdopen.c -- open read-only archive from file descriptor
+  Copyright (C) 2009 Dieter Baron and Thomas Klausner
+
+  This file is part of libzip, a library to manipulate ZIP archives.
+  The authors can be contacted at <libzip@nih.at>
+
+  Redistribution and use in source and binary forms, with or without
+  modification, are permitted provided that the following conditions
+  are met:
+  1. Redistributions of source code must retain the above copyright
+     notice, this list of conditions and the following disclaimer.
+  2. 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.
+  3. The names of the authors may not be used to endorse or promote
+     products derived from this software without specific prior
+     written permission.
+ 
+  THIS SOFTWARE IS PROVIDED BY THE AUTHORS ``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 AUTHORS 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.
+*/
+
+
+
+#include "zipint.h"
+
+
+
+ZIP_EXTERN struct zip *
+zip_fdopen(int fd_orig, int flags, int *zep)
+{
+    int fd;
+    FILE *fp;
+
+    /* XXX: we dup here to avoid messing with the passed in fd.
+       We could not restore it to the original state in case of error. */
+
+    if ((fd=dup(fd_orig)) < 0) {
+	*zep = ZIP_ER_OPEN;
+	return NULL;
+    }
+
+    if ((fp=fdopen(fd, "rb")) == NULL) {
+	close(fd);
+	*zep = ZIP_ER_OPEN;
+	return NULL;
+    }
+
+    return _zip_open(NULL, fp, flags, ZIP_AFL_RDONLY, zep);
+}
diff --git a/lib/zip_open.c b/lib/zip_open.c
index c4199d3..f49aeba 100644
--- a/lib/zip_open.c
+++ b/lib/zip_open.c
@@ -1,6 +1,6 @@
 /*
-  zip_open.c -- open zip archive
-  Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner
+  zip_open.c -- open zip archive by name
+  Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
   The authors can be contacted at <libzip@nih.at>
@@ -61,10 +61,6 @@
 zip_open(const char *fn, int flags, int *zep)
 {
     FILE *fp;
-    struct zip *za;
-    struct zip_cdir *cdir;
-    int i;
-    off_t len;
     
     switch (_zip_file_exists(fn, flags, zep)) {
     case -1:
@@ -80,7 +76,23 @@
 	return NULL;
     }
 
-    fseeko(fp, 0, SEEK_END);
+    return _zip_open(fn, fp, flags, 0, zep);
+}
+
+
+
+struct zip *
+_zip_open(const char *fn, FILE *fp, int flags, int aflags, int *zep)
+{
+    struct zip *za;
+    struct zip_cdir *cdir;
+    int i;
+    off_t len;
+
+    if (fseeko(fp, 0, SEEK_END) < 0) {
+	*zep = ZIP_ER_SEEK;
+	return NULL;
+    }
     len = ftello(fp);
 
     /* treat empty files as empty archives */
@@ -417,12 +429,16 @@
 	set_error(zep, &error, 0);
 	return NULL;
     }
-	
-    za->zn = strdup(fn);
-    if (!za->zn) {
-	_zip_free(za);
-	set_error(zep, NULL, ZIP_ER_MEMORY);
-	return NULL;
+
+    if (fn == NULL)
+	za->zn = NULL;
+    else {
+	za->zn = strdup(fn);
+	if (!za->zn) {
+	    _zip_free(za);
+	    set_error(zep, NULL, ZIP_ER_MEMORY);
+	    return NULL;
+	}
     }
     return za;
 }
diff --git a/lib/zip_rename.c b/lib/zip_rename.c
index 1d056bb..1609b5c 100644
--- a/lib/zip_rename.c
+++ b/lib/zip_rename.c
@@ -1,6 +1,6 @@
 /*
   zip_rename.c -- rename file in zip archive
-  Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
   The authors can be contacted at <libzip@nih.at>
@@ -50,6 +50,11 @@
 	return -1;
     }
 
+    if (ZIP_IS_RDONLY(za)) {
+	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
+	return -1;
+    }
+
     if ((old_name=zip_get_name(za, idx, 0)) == NULL)
 	return -1;
 								    
diff --git a/lib/zip_replace.c b/lib/zip_replace.c
index 6cdb80c..baa0b8a 100644
--- a/lib/zip_replace.c
+++ b/lib/zip_replace.c
@@ -1,6 +1,6 @@
 /*
   zip_replace.c -- replace file via callback function
-  Copyright (C) 1999-2007 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
   The authors can be contacted at <libzip@nih.at>
@@ -58,6 +58,11 @@
 _zip_replace(struct zip *za, int idx, const char *name,
 	     struct zip_source *source)
 {
+    if (ZIP_IS_RDONLY(za)) {
+	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
+	return -1;
+    }
+
     if (idx == -1) {
 	if (_zip_entry_new(za) == NULL)
 	    return -1;
diff --git a/lib/zip_set_archive_comment.c b/lib/zip_set_archive_comment.c
index 343fa54..3bb85d1 100644
--- a/lib/zip_set_archive_comment.c
+++ b/lib/zip_set_archive_comment.c
@@ -1,6 +1,6 @@
 /*
   zip_set_archive_comment.c -- set archive comment
-  Copyright (C) 2006-2007 Dieter Baron and Thomas Klausner
+  Copyright (C) 2006-2009 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
   The authors can be contacted at <libzip@nih.at>
@@ -50,6 +50,11 @@
 	return -1;
     }
 
+    if (ZIP_IS_RDONLY(za)) {
+	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
+	return -1;
+    }
+
     if (len > 0) {
 	if ((tmpcom=(char *)_zip_memdup(comment, len, &za->error)) == NULL)
 	    return -1;
diff --git a/lib/zip_set_archive_flag.c b/lib/zip_set_archive_flag.c
index 720e1f3..dc3ef94 100644
--- a/lib/zip_set_archive_flag.c
+++ b/lib/zip_set_archive_flag.c
@@ -1,6 +1,6 @@
 /*
   zip_get_archive_flag.c -- set archive global flag
-  Copyright (C) 2008 Dieter Baron and Thomas Klausner
+  Copyright (C) 2008-2009 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
   The authors can be contacted at <libzip@nih.at>
@@ -40,10 +40,30 @@
 ZIP_EXTERN int
 zip_set_archive_flag(struct zip *za, int flag, int value)
 {
+    int new_flags;
+    
     if (value)
-	za->ch_flags |= flag;
+	new_flags = za->ch_flags | flag;
     else
-	za->ch_flags &= ~flag;
+	new_flags = za->ch_flags & ~flag;
+
+    if (new_flags == za->ch_flags)
+	return 0;
+
+    if (ZIP_IS_RDONLY(za)) {
+	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
+	return -1;
+    }
+
+    if ((flag & ZIP_AFL_RDONLY) && value
+	&& (za->ch_flags & ZIP_AFL_RDONLY) == 0) {
+	if (_zip_changed(za, NULL)) {
+	    _zip_error_set(&za->error, ZIP_ER_CHANGED, 0);
+	    return -1;
+	}
+    }
+
+    za->ch_flags = new_flags;
 
     return 0;
 }
diff --git a/lib/zip_set_file_comment.c b/lib/zip_set_file_comment.c
index bc1938e..571e86c 100644
--- a/lib/zip_set_file_comment.c
+++ b/lib/zip_set_file_comment.c
@@ -1,6 +1,6 @@
 /*
   zip_set_file_comment.c -- set comment for file in archive
-  Copyright (C) 2006-2007 Dieter Baron and Thomas Klausner
+  Copyright (C) 2006-2009 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
   The authors can be contacted at <libzip@nih.at>
@@ -51,6 +51,11 @@
 	return -1;
     }
 
+    if (ZIP_IS_RDONLY(za)) {
+	_zip_error_set(&za->error, ZIP_ER_RDONLY, 0);
+	return -1;
+    }
+
     if (len > 0) {
 	if ((tmpcom=(char *)_zip_memdup(comment, len, &za->error)) == NULL)
 	    return -1;
diff --git a/lib/zipint.h b/lib/zipint.h
index da08ed7..9f17dff 100644
--- a/lib/zipint.h
+++ b/lib/zipint.h
@@ -3,7 +3,7 @@
 
 /*
   zipint.h -- internal declarations.
-  Copyright (C) 1999-2008 Dieter Baron and Thomas Klausner
+  Copyright (C) 1999-2009 Dieter Baron and Thomas Klausner
 
   This file is part of libzip, a library to manipulate ZIP archives.
   The authors can be contacted at <libzip@nih.at>
@@ -213,6 +213,8 @@
 			((x)->state == ZIP_ST_REPLACED  \
 			 || (x)->state == ZIP_ST_ADDED)
 
+#define ZIP_IS_RDONLY(za)	((za)->ch_flags & ZIP_AFL_RDONLY)
+
 
 
 int _zip_cdir_compute_crc(struct zip *, uLong *);
@@ -244,9 +246,12 @@
 
 int _zip_filerange_crc(FILE *, off_t, off_t, uLong *, struct zip_error *);
 
+struct zip *_zip_open(const char *, FILE *, int, int, int *);
+
 struct zip_source *_zip_source_file_or_p(struct zip *, const char *, FILE *,
 					 zip_uint64_t, zip_int64_t);
 
+int _zip_changed(struct zip *, int *);
 void _zip_free(struct zip *);
 const char *_zip_get_name(struct zip *, int, int, struct zip_error *);
 int _zip_local_header_read(struct zip *, int);