Index: backend/comics/comics-document.h
===================================================================
--- backend/comics/comics-document.h	(revisión: 3331)
+++ backend/comics/comics-document.h	(copia de trabajo)
@@ -23,6 +23,8 @@
 
 G_BEGIN_DECLS
 
+#define OFFSET_7z 53
+
 #define COMICS_TYPE_DOCUMENT    (comics_document_get_type ())
 #define COMICS_DOCUMENT(obj)    (G_TYPE_CHECK_INSTANCE_CAST ((obj), COMICS_TYPE_DOCUMENT, ComicsDocument))
 #define COMICS_IS_DOCUMENT(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), COMICS_TYPE_DOCUMENT))
Index: backend/comics/comics-document.c
===================================================================
--- backend/comics/comics-document.c	(revisión: 3331)
+++ backend/comics/comics-document.c	(copia de trabajo)
@@ -24,7 +24,13 @@
 #include <string.h>
 #include <glib/gi18n.h>
 #include <gio/gio.h>
+#include <sys/wait.h>
+#include <errno.h>
+#include <glib/gprintf.h>
+#include <glib/gstdio.h>
 
+
+
 #include "comics-document.h"
 #include "ev-document-misc.h"
 #include "ev-document-thumbnails.h"
@@ -58,10 +64,33 @@
 						  gint width,
 						  gint height,
 						  gpointer data);
-static char**     extract_argv                   (EvDocument *document,
+static char**     extract_argv  (EvDocument *document,
 						  gint page);
+static gboolean   comics_check_cat_command (
+						  gchar 	**cat_command,
+						  GError 	**error);
 
+static gboolean   comics_get_decompress_command (
+						  const gchar 	*comic_format, 
+ 						  gchar 	**comic_decompress_command, 
+						  gchar 	**comic_selected_command_use ,
+						  GError	**error);
 
+static gboolean   comic_decompress_temp_dir (
+						  const gchar	*command_decompress_tmp, 
+						  GError 		**error);
+		
+static gboolean   comics_generate_command_lines (
+						  ComicsDocument 	**comics_document, 
+						  const gchar 	*comic_decompress_command, 
+						  const gchar 	*comic_selected_command_use ,
+						  gchar 	**list_files_command,
+						  glong 	**list_files_offset,
+						  GError    **error);
+
+static int 		  comics_remove_dir (
+						  gchar *path_name);
+
 EV_BACKEND_REGISTER_WITH_CODE (ComicsDocument, comics_document,
 	{
 		EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS,
@@ -102,6 +131,300 @@
     return ret;
 }
 
+static gboolean comics_check_cat_command (
+		gchar **cat_command,
+		GError 		**error)
+{
+	*cat_command = g_find_program_in_path ("cat");
+	if (*cat_command != NULL) 
+	   	return TRUE;
+	else {
+		g_set_error (error,
+		EV_DOCUMENT_ERROR,
+		EV_DOCUMENT_ERROR_INVALID,
+		"I can't find the 'cat' command.");
+		return FALSE;
+	}    
+}
+
+static gboolean comic_decompress_temp_dir (
+		const gchar	*command_decompress_tmp, 
+		GError 		**error)
+{
+	
+	gboolean success = FALSE;
+	gchar *std_out, *std_err, *message = NULL;
+	GError 		*err = NULL;
+	gint retval = -1;
+
+	success = g_spawn_command_line_sync (command_decompress_tmp, 
+		&std_out, &std_err, &retval, &err);
+				
+	/* 7z * does not return error and it dumps errors to the standard 
+	 * output when decompressing to a directory. Examples of meesages:
+	 * Corrupted File: Error: Can not open file as archive
+	 * rar file without rar support: Sub items Errors:
+
+	 */ 
+	if (success && WIFEXITED (retval) && !g_strrstr (std_out,"Error:")
+		&& !g_strrstr (std_out,"Errors:")) {
+		return TRUE;
+	}
+	else {
+		if (!success ) {
+			g_set_error (error,
+				EV_DOCUMENT_ERROR,
+				EV_DOCUMENT_ERROR_INVALID,
+				_("G_SPAWN_ERROR at decompressing in the temporary \
+				   directory: %s."), 					
+				err->message);
+			g_error_free (err);						
+		} else {
+			/* additional checking for error messages in the std output  
+			 * because the misbehaviour of 7z* explained a few lines 
+			 * above. */
+			if (g_strrstr (std_out,"Error:") || 
+				g_strrstr (std_out,"Errors:"))
+				message = g_strdup (std_out);
+			else
+				message = g_strdup (std_err);
+			g_set_error (error,
+				EV_DOCUMENT_ERROR,
+				EV_DOCUMENT_ERROR_INVALID,
+				_("Can't decompress in the temporary directory. %s."), 					
+				message);
+			g_free (message);
+			g_free (std_out);
+			g_free (std_err);
+		}
+		return FALSE;
+	}
+}
+
+static gboolean comics_generate_command_lines (
+		ComicsDocument 	**comics_document, 
+		const gchar 	*comic_decompress_command, 
+		const gchar 	*comic_selected_command_use ,
+		gchar 			**list_files_command,
+		glong 			**list_files_offset,
+		GError    		**error)
+{
+	gchar *quoted_file, *dir = NULL;
+	gchar *command_decompress_tmp, *cat_command = NULL;
+	
+	*list_files_offset=0;	
+	quoted_file = g_shell_quote ((*comics_document)->archive);
+	(*comics_document)->regex_arg = FALSE;
+	
+	/* RARLabs software	(commercial and freeware) */
+	if (g_strrstr (comic_decompress_command,"/rar") != NULL ||
+		(g_strrstr (comic_decompress_command,"unrar") != NULL &&
+		 g_strrstr (comic_selected_command_use,"freeware") != NULL)) {
+		
+		(*comics_document)->extract_command =
+			g_strdup_printf ("%s p -c- -ierr", comic_decompress_command);
+		*list_files_command =
+			g_strdup_printf ("%s vb -c- -- %s", 
+			comic_decompress_command,quoted_file);
+		g_free (quoted_file); 	
+		return TRUE;	 	
+	}
+	
+	/* Gna! unrar */
+	if (g_strrstr (comic_decompress_command,"unrar-free") != NULL ||
+		(g_strrstr (comic_decompress_command,"unrar") != NULL &&
+		 g_strrstr (comic_selected_command_use,"free_software") != NULL)) {
+		
+		dir = g_strdup_printf ("%s/%s", g_get_tmp_dir(),
+			g_compute_checksum_for_string (G_CHECKSUM_MD5, 
+			(*comics_document)->archive, -1));		
+
+		/* unrar-free can't create directories so we do it on its behalf */
+
+		if (g_mkdir_with_parents (dir, 0777) == -1) {
+			g_set_error (error,
+				EV_DOCUMENT_ERROR,
+				EV_DOCUMENT_ERROR_INVALID,
+				_("Can't create a directory on the %s directory, \
+				errno: %s"),  g_get_tmp_dir(),  errno);
+			g_free (dir);
+			g_free (quoted_file);
+			return FALSE;
+		}
+
+		command_decompress_tmp = g_strdup_printf ( "%s -xf %s %s", 
+			comic_decompress_command, quoted_file, dir);
+		
+		if (!comic_decompress_temp_dir (command_decompress_tmp, error)){
+			g_free (dir);
+			g_free (command_decompress_tmp);
+			g_free (quoted_file);
+			return FALSE;	
+		}	
+		else {
+			if (!comics_check_cat_command (&cat_command, error)){
+				g_free (dir);
+				g_free (command_decompress_tmp);
+				g_free (quoted_file);
+				return FALSE;
+			}
+			else {
+				(*comics_document)->extract_command =
+					g_strdup_printf ("%s %s/", cat_command, dir);
+				*list_files_command = 
+					g_strdup_printf ("%s t %s", comic_decompress_command, 
+					quoted_file);
+				(*comics_document)->regex_arg = TRUE;	
+				g_free (cat_command);	
+				g_free (dir);
+				g_free (command_decompress_tmp);
+				g_free (quoted_file);
+				return TRUE;
+			}	
+		}	 	
+	}
+	
+	/* InfoZIP's unzip */
+	if (g_strrstr (comic_decompress_command,"unzip") != NULL) {
+		
+		(*comics_document)->extract_command =
+			g_strdup_printf ("%s -p -C", comic_decompress_command);
+		*list_files_command = 
+			g_strdup_printf ("%s -Z -1 -- %s", comic_decompress_command
+			, quoted_file);
+		(*comics_document)->regex_arg = TRUE;
+		
+		g_free (quoted_file);
+		return TRUE;	 	
+	}
+	
+	/* p7zip family */
+	if (g_strrstr (comic_decompress_command,"7zr") != NULL ||
+		g_strrstr (comic_decompress_command,"7za") != NULL ||
+		g_strrstr (comic_decompress_command,"7z") != NULL) {
+		
+	/* For perfomance reasons of 7z* we choose to decompress on the 
+	 * temporary directory instead of decompressing on the stdout
+	 * */ 	
+		dir = g_strdup_printf ("%s/%s", g_get_tmp_dir(),
+			g_compute_checksum_for_string (G_CHECKSUM_MD5, 
+			(*comics_document)->archive, -1));	
+		command_decompress_tmp = g_strdup_printf (
+			"%s x -y -o%s %s", comic_decompress_command, dir,
+			 quoted_file);
+		if (!comic_decompress_temp_dir (command_decompress_tmp, error)){
+			g_free (dir);
+			g_free (command_decompress_tmp);
+			g_free (quoted_file);
+			return FALSE;	
+		}	
+		else {
+			if (!comics_check_cat_command (&cat_command, error)){
+				g_free (dir);
+				g_free (command_decompress_tmp);
+				g_free (quoted_file);
+				return FALSE;
+			}
+			else {
+				(*comics_document)->extract_command =
+					g_strdup_printf ("%s %s/", cat_command, dir);
+				*list_files_command = 
+					g_strdup_printf ("%s l -- %s", comic_decompress_command, 
+					quoted_file);
+				*list_files_offset= OFFSET_7z;
+				g_free (cat_command);	
+				g_free (dir);
+				g_free (command_decompress_tmp);
+				g_free (quoted_file);
+				return TRUE;
+			}	
+		}	 	
+	}
+	
+	g_free (quoted_file);
+	return FALSE;
+}
+
+static gboolean comics_get_decompress_command (
+		const gchar *comic_format, 
+ 		gchar 		**comic_decompress_command, 
+		gchar 		**comic_decompress_command_use ,
+		GError		**error)
+{
+	gchar *std_out = NULL;
+
+	if (g_strrstr (comic_format,"cbr") != NULL) {
+		
+	/* The RARLAB sells rar, a proprietary program for compressing 
+	 * and decompressing rar files.
+	 * */
+		*comic_decompress_command = g_find_program_in_path ("rar");
+		if (*comic_decompress_command != NULL) 
+	    	return TRUE;
+	    	    	
+	/* The RARLAB provides a no-charge proprietary (freeware) 
+	 * decompress-only client for Linux called unrar. Another 
+	 * option is a GPLv2-licensed command-line tool developed by 
+	 * the Gna! project. Confusingly enough, the open source RAR 
+	 * decoder is also named unrar. However, some distributions, 
+	 * like Debian, rename this free option as unrar-free.
+	 * */    	
+		*comic_decompress_command = g_find_program_in_path ("unrar");
+		if (*comic_decompress_command != NULL) {	
+			g_spawn_command_line_sync (*comic_decompress_command, 
+				&std_out, NULL, NULL, NULL);
+			if (g_strrstr (std_out,"freeware") != NULL)
+	/* The RARLAB freeware client */
+				*comic_decompress_command_use = g_strdup ("freeware");
+			else
+ 	/* The Gna! free software client */
+			 	*comic_decompress_command_use = g_strdup ("free_software");
+			 		 	
+			g_free(std_out);
+			return TRUE;
+        }
+ 	/* The Gna! free software client with Debian naming convention */
+ 		*comic_decompress_command = g_find_program_in_path ("unrar-free");
+		if (*comic_decompress_command != NULL)   	
+	    	return TRUE;       
+	}
+	if (g_strrstr (comic_format,"cbz") != NULL) {
+
+	/* InfoZIP's unzip program */
+ 		*comic_decompress_command = g_find_program_in_path ("unzip");
+		if (*comic_decompress_command != NULL)   	
+	    	return TRUE; 
+	}	
+	if (g_strrstr (comic_format,"cb7") != NULL) {
+	/* 7zr is a light stand-alone executable that supports only 
+	 * 7z/LZMA/BCJ */
+	    *comic_decompress_command = g_find_program_in_path ("7zr");
+	    if (*comic_decompress_command != NULL)   	
+	    	return TRUE;
+	}	
+	if ( g_strrstr (comic_format,"cbz") != NULL || 
+	 	 g_strrstr (comic_format,"cb7") != NULL) {
+	/* 7za is a stand-alone executable.7za handles less 7z and zip */
+		*comic_decompress_command = g_find_program_in_path ("7za");
+	    if (*comic_decompress_command != NULL)    	
+	    	return TRUE;	
+	}
+	/* 7z uses plugins (7z.so and Rar29.so) to handle 7z, zip and rar */
+	*comic_decompress_command = g_find_program_in_path ("7z");
+   	if (*comic_decompress_command != NULL)
+   		return TRUE;
+   	else {
+   		g_set_error (error,
+			EV_DOCUMENT_ERROR,
+			EV_DOCUMENT_ERROR_INVALID,
+			_("Can't find a program for decompressing %s files."), 
+			comic_format);
+		return FALSE;
+	} 
+}
+
+
+	
 static gboolean
 comics_document_load (EvDocument *document,
 		      const char *uri,
@@ -109,52 +432,64 @@
 {
 	ComicsDocument *comics_document = COMICS_DOCUMENT (document);
 	GSList *supported_extensions;
-	gchar *list_files_command = NULL, *std_out, *quoted_file;
+	gchar *list_files_command, *std_out = NULL;
 	gchar *mime_type;
 	gchar **cbr_files;
-	gboolean success;
-	int i, retval;
+	gboolean success = FALSE;
+	gint i, retval = -1;
 
+	gchar *selected_command, *selected_command_use = NULL;
+	glong list_files_offset = 0;
+ 	gchar *cb_file =NULL;
+
+	
 	comics_document->archive = g_filename_from_uri (uri, NULL, error);
-	g_return_val_if_fail (comics_document->archive != NULL, FALSE);
-
-	quoted_file = g_shell_quote (comics_document->archive);
+	g_return_val_if_fail (comics_document->archive != NULL, FALSE);	
 	mime_type = ev_file_get_mime_type (uri, FALSE, NULL);
 
-	/* FIXME, use proper cbr/cbz mime types once they're
-	 * included in shared-mime-info */
 	if (!strcmp (mime_type, "application/x-cbr") ||
-	    !strcmp (mime_type, "application/x-rar")) {
-		comics_document->extract_command =
-			g_strdup ("unrar p -c- -ierr");
-		list_files_command =
-			g_strdup_printf ("unrar vb -c- -- %s", quoted_file);
-		comics_document->regex_arg = FALSE;
+	    !strcmp (mime_type, "application/x-rar")) {    	
+		if (!comics_get_decompress_command ("cbr", &selected_command,
+			&selected_command_use, error))
+			return FALSE;
+		else if (!comics_generate_command_lines (&comics_document,
+				selected_command, selected_command_use, 
+				&list_files_command, &list_files_offset, error))
+			return FALSE;
 	} else if (!strcmp (mime_type, "application/x-cbz") ||
-		   !strcmp (mime_type, "application/zip")) {
-		comics_document->extract_command =
-			g_strdup ("unzip -p -C");
-		list_files_command = 
-			g_strdup_printf ("zipinfo -1 -- %s", quoted_file);
-		comics_document->regex_arg = TRUE;
-	} else if (!strcmp (mime_type, "application/x-cb7")) {
-		comics_document->extract_command =
-			g_strdup ("7zr x -so");
-		list_files_command = 
-			g_strdup_printf ("7zr l -- %s", quoted_file);
-		comics_document->regex_arg = TRUE;
+		!strcmp (mime_type, "application/zip")) {
+
+		if (!comics_get_decompress_command ("cbz", &selected_command,
+			&selected_command_use, error))
+			return FALSE;
+		else if (!comics_generate_command_lines (&comics_document,
+				selected_command, selected_command_use, 
+				&list_files_command, &list_files_offset, error))
+			return FALSE;
+	} else if (!strcmp (mime_type, "application/x-cb7") ||
+		!strcmp (mime_type, "application/x-7z-compressed")) {	   	
+		
+		if (!comics_get_decompress_command ("cb7", &selected_command,
+			&selected_command_use, error))
+			return FALSE;
+		else if (!comics_generate_command_lines (&comics_document,
+				selected_command, selected_command_use, 
+				&list_files_command, &list_files_offset, error))
+			return FALSE;
 	}
-
 	g_free (mime_type);
-	g_free (quoted_file);
-
+	g_free (selected_command);
+	g_free (selected_command_use);
+	
 	/* Get list of files in archive */
+	retval = -1;
 	success = g_spawn_command_line_sync (list_files_command,
 					     &std_out, NULL, &retval, error);
 	g_free (list_files_command);
-
+	
 	if (!success) {
 		return FALSE;
+		
 	} else if (retval != 0) {
 		g_set_error (error,
 			     EV_DOCUMENT_ERROR,
@@ -162,25 +497,30 @@
 			     _("File corrupted."));
 		return FALSE;
 	}
-
 	cbr_files = g_strsplit (std_out, "\n", 0);
 	supported_extensions = get_supported_image_extensions ();
 	for (i = 0; cbr_files[i] != NULL; i++) {
-		gchar *suffix = g_strrstr (cbr_files[i], ".");
+		if (list_files_offset != 0) 
+			if (g_utf8_strlen (cbr_files[i],-1) > list_files_offset)
+				cb_file = g_utf8_offset_to_pointer (cbr_files[i], 
+							list_files_offset);
+			else 
+				continue;
+		else cb_file = cbr_files[i];	
+		gchar *suffix = g_strrstr (cb_file, ".");
 		if (!suffix)
 			continue;
+			
 		suffix = g_ascii_strdown (suffix + 1, -1);
-
 		if (g_slist_find_custom (supported_extensions, suffix,
 					 (GCompareFunc) strcmp) != NULL) {
 			comics_document->page_names =
 				g_slist_insert_sorted (
 					comics_document->page_names,
-					g_strdup (g_strstrip (cbr_files[i])),
+					g_strdup (g_strstrip (cb_file)),
 					(GCompareFunc) strcmp);
 			comics_document->n_pages++;
 		}
-
 		g_free (suffix);
 	}
 
@@ -209,6 +549,8 @@
 {
 	ComicsDocument *comics_document = COMICS_DOCUMENT (document);
 
+
+
 	return ev_xfer_uri_simple (comics_document->archive, uri, error);
 }
 
@@ -350,11 +692,51 @@
 	gdk_pixbuf_loader_set_size (loader, w, h);
 }
 
+/* Removes a directory recursively 
+ * Returns  0 if it was successfully deleted,
+ *         -1 if an error occurred 				*/
+static int comics_remove_dir (gchar *path_name) {
+	GDir  *content_dir;
+	const gchar *filename;
+	int   out;
+	
+	if (g_file_test (path_name, G_FILE_TEST_IS_DIR)) {		
+		content_dir = g_dir_open  (path_name, 0, NULL);
+		filename  = g_dir_read_name (content_dir);
+		out = 0;
+		while (filename != NULL) {			
+			if (comics_remove_dir (g_strdup_printf("%s/%s", path_name, 
+				filename)) < out)
+					out = -1;
+			filename = g_dir_read_name (content_dir);
+		}
+		g_dir_close (content_dir);
+		if (g_remove (path_name) < out)
+			return -1;
+		else
+			return 0;
+	}	
+	else 							/* G_FILE_TEST_IS_REGULAR */
+		return (g_remove (path_name));
+}
+
 static void
 comics_document_finalize (GObject *object)
-{
+{	
 	ComicsDocument *comics_document = COMICS_DOCUMENT (object);
+	gchar *dir;
+	
+	dir = g_strdup_printf ("%s/%s", g_get_tmp_dir(),
+		g_compute_checksum_for_string (G_CHECKSUM_MD5, 
+		comics_document->archive, -1));	
 
+	if (g_file_test (dir, G_FILE_TEST_EXISTS)) {
+		if (comics_remove_dir (dir) == -1)
+			g_warning ("There was a error deleting %s", dir);
+	}
+	g_free (dir);
+
+
 	if (comics_document->archive)
 		g_free (comics_document->archive);
 
@@ -492,13 +874,18 @@
 			g_slist_nth_data (comics_document->page_names, page));
 	}
 
-	command_line = g_strdup_printf ("%s -- %s %s",
+	if (g_strrstr (comics_document->extract_command, g_strdup_printf (
+	"%s %s/",g_find_program_in_path ("cat"), g_get_tmp_dir ())) != NULL)
+		command_line = g_strdup_printf ("%s%s",
 					comics_document->extract_command,
+					quoted_filename);
+	else
+		command_line = g_strdup_printf ("%s -- %s %s",
+					comics_document->extract_command,
 					quoted_archive,
 					quoted_filename);
+	g_shell_parse_argv (command_line, NULL, &argv, &err);
 
-	g_shell_parse_argv (command_line, NULL, &argv, &err);
-	
 	if (err) {
 		g_warning ("Error %s", err->message);
 		return NULL;
