1 
2 /*
3  * boot.c: the external interface to the MS Excel import/export
4  *
5  * Copyright (C) 2000-2007 Jody Goldberg (jody@gnome.org)
6  * Copyright (C) 1998-2001 Michael Meeks (miguel@kernel.org)
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License as
10  * published by the Free Software Foundation; either version 2 of the
11  * License, or (at your option) version 3.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301
21  * USA
22  */
23 #include <gnumeric-config.h>
24 #include <gnumeric.h>
25 
26 #include <libgnumeric.h>
27 #include <command-context.h>
28 #include <workbook-view.h>
29 #include <workbook.h>
30 #include <gnm-plugin.h>
31 
32 #include "excel.h"
33 #include "ms-excel-write.h"
34 #include "boot.h"
35 #include "ms-excel-util.h"
36 #include "ms-excel-read.h"
37 
38 #include <goffice/goffice.h>
39 #include <gsf/gsf-input.h>
40 #include <gsf/gsf-infile.h>
41 #include <gsf/gsf-infile-msole.h>
42 #include <gsf/gsf-infile-msvba.h>
43 #include <gsf/gsf-msole-utils.h>
44 #include <gsf/gsf-output-stdio.h>
45 #include <gsf/gsf-outfile.h>
46 #include <gsf/gsf-outfile-msole.h>
47 #include <gsf/gsf-structured-blob.h>
48 #include <glib/gi18n-lib.h>
49 #include <string.h>
50 
51 GNM_PLUGIN_MODULE_HEADER;
52 
53 /* Used to toggle debug messages on & off */
54 /*
55  * As a convention
56  * 0 = quiet, no experimental features.
57  * 1 = enable experimental features
58  * >1 increasing levels of detail.
59  */
60 gint ms_excel_read_debug = 0;
61 gint ms_excel_pivot_debug = 0;
62 gint ms_excel_escher_debug = 0;
63 gint ms_excel_formula_debug = 0;
64 gint ms_excel_chart_debug = 0;
65 gint ms_excel_write_debug = 0;
66 gint ms_excel_object_debug = 0;
67 
68 gboolean excel_file_probe (GOFileOpener const *fo, GsfInput *input, GOFileProbeLevel pl);
69 void excel_file_open (GOFileOpener const *fo, GOIOContext *context, WorkbookView *wbv, GsfInput *input);
70 void excel_enc_file_open (GOFileOpener const *fo, char const *enc, GOIOContext *context, WorkbookView *wbv, GsfInput *input);
71 void excel_biff7_file_save (GOFileSaver const *fs, GOIOContext *context, WorkbookView const *wbv, GsfOutput *output);
72 void excel_biff8_file_save (GOFileSaver const *fs, GOIOContext *context, WorkbookView const *wbv, GsfOutput *output);
73 void excel_dsf_file_save   (GOFileSaver const *fs, GOIOContext *context, WorkbookView const *wbv, GsfOutput *output);
74 
75 static GsfInput *
find_content_stream(GsfInfile * ole,gboolean * is_97)76 find_content_stream (GsfInfile *ole, gboolean *is_97)
77 {
78 	static char const * const stream_names[] = {
79 		"Workbook",	"WORKBOOK",	"workbook",
80 		"Book",		"BOOK",		"book"
81 	};
82 	GsfInput *stream;
83 	unsigned i;
84 
85 	for (i = 0 ; i < G_N_ELEMENTS (stream_names) ; i++) {
86 		stream = gsf_infile_child_by_name (ole, stream_names[i]);
87 		if (stream != NULL) {
88 			if (is_97 != NULL)
89 				*is_97 = (i < 3);
90 			return stream;
91 		}
92 	}
93 
94 	return  NULL;
95 }
96 
97 gboolean
excel_file_probe(GOFileOpener const * fo,GsfInput * input,GOFileProbeLevel pl)98 excel_file_probe (GOFileOpener const *fo, GsfInput *input, GOFileProbeLevel pl)
99 {
100 	GsfInfile *ole;
101 	GsfInput  *stream;
102 	gboolean res = FALSE;
103 
104 	if (input == NULL)
105 		return FALSE;
106 	ole = gsf_infile_msole_new (input, NULL);
107 	if (ole == NULL) {	/* Test for non-OLE BIFF file */
108 		guint8 const *data;
109 		gsf_input_seek (input, 0, G_SEEK_SET);
110 		data = gsf_input_read (input, 2, NULL);
111 		return data && data[0] == 0x09 && (data[1] & 0xf1) == 0;
112 	}
113 
114 	stream = find_content_stream (ole, NULL);
115 	if (stream != NULL) {
116 		g_object_unref (stream);
117 		res = TRUE;
118 	}
119 	g_object_unref (ole);
120 
121 	return res;
122 }
123 
124 static void
excel_read_metadata(GsfDocMetaData * meta_data,GsfInfile * ole,char const * name,GOIOContext * context)125 excel_read_metadata (GsfDocMetaData *meta_data, GsfInfile *ole, char const *name,
126 		     GOIOContext *context)
127 {
128 	GsfInput *stream = gsf_infile_child_by_name (ole, name);
129 
130 	if (stream != NULL) {
131 		GError *err = gsf_doc_meta_data_read_from_msole (meta_data, stream);
132 		if (err != NULL) {
133 			go_io_warning (context, "%s", err->message);
134 			g_error_free (err);
135 		}
136 
137 		g_object_unref (stream);
138 	}
139 }
140 
141 #ifdef SPEW_VBA
142 static void
cb_dump_vba(char const * name,guint8 const * src_code)143 cb_dump_vba (char const *name, guint8 const *src_code)
144 {
145 	g_printerr ("<module name=\"%s\">\n<![CDATA[%s]]>\n</module>\n", name, src_code);
146 }
147 #endif
148 
149 /* Service entry point */
150 void
excel_enc_file_open(GOFileOpener const * fo,char const * enc,GOIOContext * context,WorkbookView * wbv,GsfInput * input)151 excel_enc_file_open (GOFileOpener const *fo, char const *enc, GOIOContext *context,
152 		     WorkbookView *wbv, GsfInput *input)
153 {
154 	GsfInput  *stream = NULL;
155 	GError    *err = NULL;
156 	GsfInfile *ole = gsf_infile_msole_new (input, &err);
157 	Workbook  *wb = wb_view_get_workbook (wbv);
158 	gboolean   is_double_stream_file, is_97;
159 	GsfDocMetaData *meta_data;
160 
161 	if (ole == NULL) {
162 		guint8 const *data;
163 
164 		/* Test for non-OLE BIFF file */
165 		gsf_input_seek (input, 0, G_SEEK_SET);
166 		data = gsf_input_read (input, 2, NULL);
167 		if (data && data[0] == 0x09 && (data[1] & 0xf1) == 0) {
168 			gsf_input_seek (input, -2, G_SEEK_CUR);
169 			excel_read_workbook (context, wbv, input,
170 					     &is_double_stream_file, enc);
171 			/* NOTE : we lack a saver for the early formats */
172 			g_clear_error (&err);
173 			return;
174 		}
175 
176 		/* OK, it really isn't an Excel file */
177 		g_return_if_fail (err != NULL);
178 		go_cmd_context_error_import (GO_CMD_CONTEXT (context),
179 			err->message);
180 		g_error_free (err);
181 		return;
182 	}
183 
184 	stream = find_content_stream (ole, &is_97);
185 	if (stream == NULL) {
186 		go_cmd_context_error_import (GO_CMD_CONTEXT (context),
187 			 _("No Workbook or Book streams found."));
188 		g_object_unref (ole);
189 		return;
190 	}
191 
192 	excel_read_workbook (context, wbv, stream, &is_double_stream_file, enc);
193 	g_object_unref (stream);
194 
195 	meta_data = gsf_doc_meta_data_new ();
196 	excel_read_metadata (meta_data, ole, "\05SummaryInformation", context);
197 	excel_read_metadata (meta_data, ole, "\05DocumentSummaryInformation", context);
198 	go_doc_set_meta_data (GO_DOC (wb), meta_data);
199 	g_object_unref (meta_data);
200 
201 	/* See if there are any macros to keep around */
202 	stream = gsf_infile_child_by_name (ole, "\01CompObj");
203 	if (stream != NULL) {
204 		GsfInput *macros = gsf_infile_child_by_name (ole, "_VBA_PROJECT_CUR");
205 		if (macros != NULL) {
206 			GsfInput *vba_child = gsf_infile_child_by_name (GSF_INFILE (macros), "VBA");
207 			GsfInfile *vba = vba_child
208 				? gsf_infile_msvba_new (GSF_INFILE (vba_child), NULL)
209 				: NULL;
210 			GsfStructuredBlob *blob;
211 
212 			if (NULL != vba) {
213 				GHashTable *modules =
214 					gsf_infile_msvba_steal_modules (GSF_INFILE_MSVBA (vba));
215 				if (NULL != modules) {
216 #ifdef SPEW_VBA
217 					g_hash_table_foreach (modules,
218 						(GHFunc) cb_dump_vba, NULL);
219 #endif
220 					g_object_set_data_full (G_OBJECT (wb), "VBA",
221 						modules, (GDestroyNotify) g_hash_table_destroy);
222 				}
223 				g_object_unref (vba);
224 			}
225 			if (vba_child)
226 				g_object_unref (vba_child);
227 
228 			blob = gsf_structured_blob_read (stream);
229 			if (blob)
230 				g_object_set_data_full (G_OBJECT (wb),
231 							"MS_EXCEL_COMPOBJ_STREAM",
232 							blob, g_object_unref);
233 
234 			blob = gsf_structured_blob_read (macros);
235 			if (blob)
236 				g_object_set_data_full (G_OBJECT (wb),
237 							"MS_EXCEL_MACROS",
238 							blob, g_object_unref);
239 
240 			g_object_unref (macros);
241 		}
242 		g_object_unref (stream);
243 	}
244 
245 	stream = gsf_infile_child_by_name (ole, "\01Ole");
246 	if (stream) {
247 		GsfStructuredBlob *blob = gsf_structured_blob_read (stream);
248 		if (blob)
249 			g_object_set_data_full (G_OBJECT (wb), "MS_EXCEL_OLE_STREAM",
250 						blob, g_object_unref);
251 		g_object_unref (stream);
252 	}
253 
254 	g_object_unref (ole);
255 
256 	/* simple guess of format based on stream names */
257 	if (is_double_stream_file)
258 		workbook_set_saveinfo (wb, GO_FILE_FL_AUTO,
259 			go_file_saver_for_id ("Gnumeric_Excel:excel_dsf"));
260 	else if (is_97)
261 		workbook_set_saveinfo (wb, GO_FILE_FL_AUTO,
262 			go_file_saver_for_id ("Gnumeric_Excel:excel_biff8"));
263 	else
264 		workbook_set_saveinfo (wb, GO_FILE_FL_AUTO,
265 			go_file_saver_for_id ("Gnumeric_Excel:excel_biff7"));
266 }
267 
268 void
excel_file_open(GOFileOpener const * fo,GOIOContext * context,WorkbookView * wbv,GsfInput * input)269 excel_file_open (GOFileOpener const *fo, GOIOContext *context,
270                  WorkbookView *wbv, GsfInput *input)
271 {
272 	excel_enc_file_open (fo, NULL, context, wbv, input);
273 }
274 
275 static void
excel_save(GOIOContext * context,WorkbookView const * wbv,GsfOutput * output,gboolean biff7,gboolean biff8)276 excel_save (GOIOContext *context, WorkbookView const *wbv, GsfOutput *output,
277 	    gboolean biff7, gboolean biff8)
278 {
279 	Workbook *wb;
280 	GsfOutput *content;
281 	GsfOutfile *outfile;
282 	ExcelWriteState *ewb = NULL;
283 	GsfStructuredBlob *blob;
284 	GsfDocMetaData *meta_data;
285 
286 	go_io_progress_message (context, _("Preparing to save..."));
287 	go_io_progress_range_push (context, 0.0, 0.1);
288 	ewb = excel_write_state_new (context, wbv, biff7, biff8);
289 	go_io_progress_range_pop (context);
290 	if (ewb == NULL)
291 		return;
292 
293 	wb = wb_view_get_workbook (wbv);
294 	outfile = gsf_outfile_msole_new (output);
295 	ewb->export_macros = (biff8 &&
296 		NULL != g_object_get_data (G_OBJECT (wb), "MS_EXCEL_MACROS"));
297 
298 	go_io_progress_message (context, _("Saving file..."));
299 	go_io_progress_range_push (context, 0.1, 1.0);
300 	if (biff7)
301 		excel_write_v7 (ewb, outfile);
302 	if (biff8)
303 		excel_write_v8 (ewb, outfile);
304 	excel_write_state_free (ewb);
305 	go_io_progress_range_pop (context);
306 
307 	meta_data = go_doc_get_meta_data (GO_DOC (wb));
308 	if (meta_data != NULL) {
309 		content = gsf_outfile_new_child (outfile,
310 			"\05DocumentSummaryInformation", FALSE);
311 		gsf_doc_meta_data_write_to_msole (meta_data, content, TRUE);
312 		gsf_output_close (content);
313 		g_object_unref (content);
314 
315 		content = gsf_outfile_new_child (outfile,
316 			"\05SummaryInformation", FALSE);
317 		gsf_doc_meta_data_write_to_msole (meta_data, content, FALSE);
318 		gsf_output_close (content);
319 		g_object_unref (content);
320 	}
321 
322 	/* restore the macros we loaded */
323 	blob = g_object_get_data (G_OBJECT (wb), "MS_EXCEL_COMPOBJ_STREAM");
324 	if (blob != NULL)
325 		gsf_structured_blob_write (blob, outfile);
326 
327 	blob = g_object_get_data (G_OBJECT (wb), "MS_EXCEL_OLE_STREAM");
328 	if (blob != NULL)
329 		gsf_structured_blob_write (blob, outfile);
330 
331 	blob = g_object_get_data (G_OBJECT (wb), "MS_EXCEL_MACROS");
332 	if (blob)
333 		gsf_structured_blob_write (blob, outfile);
334 
335 	gsf_output_close (GSF_OUTPUT (outfile));
336 	g_object_unref (outfile);
337 }
338 
339 void
excel_dsf_file_save(GOFileSaver const * fs,GOIOContext * context,WorkbookView const * wbv,GsfOutput * output)340 excel_dsf_file_save (GOFileSaver const *fs, GOIOContext *context,
341 		     WorkbookView const *wbv, GsfOutput *output)
342 {
343 	excel_save (context, wbv, output, TRUE, TRUE);
344 }
345 void
excel_biff8_file_save(GOFileSaver const * fs,GOIOContext * context,WorkbookView const * wbv,GsfOutput * output)346 excel_biff8_file_save (GOFileSaver const *fs, GOIOContext *context,
347 		       WorkbookView const *wbv, GsfOutput *output)
348 {
349 	excel_save (context, wbv, output, FALSE, TRUE);
350 }
351 
352 void
excel_biff7_file_save(GOFileSaver const * fs,GOIOContext * context,WorkbookView const * wbv,GsfOutput * output)353 excel_biff7_file_save (GOFileSaver const *fs, GOIOContext *context,
354 		       WorkbookView const *wbv, GsfOutput *output)
355 {
356 	excel_save (context, wbv, output, TRUE, FALSE);
357 }
358 
359 
360 #include <formula-types.h>
361 G_MODULE_EXPORT void
go_plugin_init(GOPlugin * plugin,GOCmdContext * cc)362 go_plugin_init (GOPlugin *plugin, GOCmdContext *cc)
363 {
364 	excel_read_init ();
365 
366 #if 0
367 {
368 	int i;
369 	char const *name;
370 
371 	for (i = 0 ; i < excel_func_desc_size; i++) {
372 		ExcelFuncDesc const *fd = excel_func_desc + i;
373 		name = fd->name;
374 		if (fd->flags & (XL_UNKNOWN | XL_MAGIC))
375 			continue;
376 		if (fd->flags & XL_XLM) {
377 			if (fd->flags != XL_XLM)
378 				g_printerr ("%s : flags in addition to XLM\n", name);
379 			if (fd->min_args != fd->max_args)
380 				g_printerr ("%s : min != max\n", name);
381 			continue;
382 		}
383 		if (fd->min_args < 0)
384 			g_printerr ("%s : min_args < 0\n", name);
385 		if (fd->max_args < 0)
386 			g_printerr ("%s : min_args < 0\n", name);
387 		if (fd->known_args != NULL &&
388 		    fd->num_known_args != strlen (fd->known_args))
389 			g_printerr ("%s : num_expected_args inconsistent\n", name);
390 	}
391 }
392 #endif
393 }
394 
395 /*
396  * Cleanup allocations made by this plugin.
397  * (Called right before we are unloaded.)
398  */
399 G_MODULE_EXPORT void
go_plugin_shutdown(GOPlugin * plugin,GOCmdContext * cc)400 go_plugin_shutdown (GOPlugin *plugin, GOCmdContext *cc)
401 {
402 	destroy_xl_font_widths ();
403 	excel_read_cleanup ();
404 }
405