1 /* this file is part of atril, a mate document viewer
2 *
3 * Copyright (C) 2014 Avishkar Gupta
4 *
5 * Author:
6 * Avishkar Gupta <avishkar.gupta.delhi@gmail.com>
7 *
8 * Atril is free software; you can redistribute it and/or modify it
9 * under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * Atril is distributed in the hope that it will be useful, but
14 * WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * 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 Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22
23 #include "epub-document.h"
24 #include "ev-file-helpers.h"
25 #include "unzip.h"
26 #include "ev-document-thumbnails.h"
27 #include "ev-document-find.h"
28 #include "ev-backends-manager.h"
29 #include "ev-document-links.h"
30 #include "ev-document-misc.h"
31 #include <libxml/parser.h>
32 #include <libxml/xmlmemory.h>
33 #include <libxml/HTMLparser.h>
34 #include <config.h>
35
36 #include <glib/gi18n.h>
37 #include <glib/gstdio.h>
38
39 #include <gtk/gtk.h>
40
41 /*For strcasestr(),strstr()*/
42 #include <string.h>
43
44 typedef enum _xmlParseReturnType
45 {
46 XML_ATTRIBUTE,
47 XML_KEYWORD
48 }xmlParseReturnType;
49
50 typedef struct _contentListNode {
51 gchar* key ;
52 gchar* value ;
53 gint index ;
54 }contentListNode;
55
56 typedef struct _linknode {
57 gchar *pagelink;
58 GList *children;
59 gchar *linktext;
60 guint page;
61 }linknode;
62
63 typedef struct _EpubDocumentClass EpubDocumentClass;
64
65 struct _EpubDocumentClass
66 {
67 EvDocumentClass parent_class;
68 };
69
70 struct _EpubDocument
71 {
72 EvDocument parent_instance;
73 /*Stores the path to the source archive*/
74 gchar* archivename ;
75 /*Stores the path of the directory where we unzipped the epub*/
76 gchar* tmp_archive_dir ;
77 /*Stores the contentlist in a sorted manner*/
78 GList* contentList ;
79 /* A variable to hold our epubDocument for unzipping*/
80 unzFile epubDocument ;
81 /*The (sub)directory that actually houses the document*/
82 gchar* documentdir;
83 /*Stores the table of contents*/
84 GList *index;
85 /*Document title, for the sidebar links*/
86 gchar *docTitle;
87 };
88
89 static void epub_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface);
90 static void epub_document_document_find_iface_init (EvDocumentFindInterface *iface);
91 static void epub_document_document_links_iface_init (EvDocumentLinksInterface *iface);
92
93 EV_BACKEND_REGISTER_WITH_CODE (EpubDocument, epub_document,
94 {
95 EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_THUMBNAILS,
96 epub_document_document_thumbnails_iface_init);
97 EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_FIND,
98 epub_document_document_find_iface_init);
99 EV_BACKEND_IMPLEMENT_INTERFACE (EV_TYPE_DOCUMENT_LINKS,
100 epub_document_document_links_iface_init);
101
102 } );
103
104 static void
epub_document_thumbnails_get_dimensions(EvDocumentThumbnails * document,EvRenderContext * rc,gint * width,gint * height)105 epub_document_thumbnails_get_dimensions (EvDocumentThumbnails *document,
106 EvRenderContext *rc,
107 gint *width,
108 gint *height)
109 {
110 gdouble page_width, page_height;
111
112 page_width = 800;
113 page_height = 1080;
114
115 *width = MAX ((gint)(page_width * rc->scale + 0.5), 1);
116 *height = MAX ((gint)(page_height * rc->scale + 0.5), 1);
117 }
118
119 static GdkPixbuf *
epub_document_thumbnails_get_thumbnail(EvDocumentThumbnails * document,EvRenderContext * rc,gboolean border)120 epub_document_thumbnails_get_thumbnail (EvDocumentThumbnails *document,
121 EvRenderContext *rc,
122 gboolean border)
123 {
124 cairo_surface_t *webpage;
125 GdkPixbuf *thumbnailpix = NULL ;
126 gint width,height;
127 epub_document_thumbnails_get_dimensions (document, rc, &width, &height);
128 webpage = ev_document_misc_surface_rotate_and_scale (rc->page->backend_page,
129 width, height, 0);
130 thumbnailpix = ev_document_misc_pixbuf_from_surface (webpage);
131 return thumbnailpix;
132 }
133
134 static gboolean
in_tag(const char * found)135 in_tag(const char* found)
136 {
137 const char* bracket = found ;
138
139 /* Since the dump started with the body tag, the '<' will be the first
140 * character in the haystack.
141 */
142 while (*bracket != '<') {
143 bracket--;
144 if (*bracket == '>') {
145 /*We encounted a close brace before an open*/
146 return FALSE ;
147 }
148 }
149
150 return TRUE;
151 }
152
153 static int
get_substr_count(const char * haystack,const char * needle,gboolean case_sensitive)154 get_substr_count(const char * haystack,const char *needle,gboolean case_sensitive)
155 {
156 const char* tmp = haystack ;
157 char* (*string_compare_function)(const char*,const char*);
158 int count=0;
159 if (case_sensitive) {
160 string_compare_function = strstr ;
161 }
162 else {
163 string_compare_function = strcasestr;
164 }
165
166 while ((tmp=string_compare_function(tmp,needle))) {
167 if (!in_tag(tmp)) {
168 count++;
169 }
170 tmp = tmp + strlen(needle);
171 }
172
173 return count;
174 }
175
176 static guint
epub_document_check_hits(EvDocumentFind * document_find,EvPage * page,const gchar * text,gboolean case_sensitive)177 epub_document_check_hits(EvDocumentFind *document_find,
178 EvPage *page,
179 const gchar *text,
180 gboolean case_sensitive)
181 {
182 gchar *filepath = g_filename_from_uri((gchar*)page->backend_page,NULL,NULL);
183 htmlDocPtr htmldoc = xmlParseFile(filepath);
184 if (htmldoc == NULL) {
185 g_free(filepath);
186 return 0;
187 }
188 htmlNodePtr htmltag = xmlDocGetRootElement(htmldoc);
189 if (htmltag == NULL) {
190 g_free(filepath);
191 xmlFreeDoc (htmldoc);
192 return 0;
193 }
194
195 int count=0;
196 htmlNodePtr bodytag = htmltag->xmlChildrenNode;
197
198 while ( xmlStrcmp(bodytag->name,(xmlChar*)"body") ) {
199 bodytag = bodytag->next;
200 }
201
202 xmlBufferPtr bodybuffer = xmlBufferCreate();
203 xmlNodeDump(bodybuffer,htmldoc,bodytag,0,1);
204
205 count = get_substr_count((char*)bodybuffer->content,text,case_sensitive);
206
207 xmlBufferFree(bodybuffer);
208 xmlFreeDoc(htmldoc);
209 g_free (filepath);
210
211 return count;
212 }
213
214 static gboolean
epub_document_links_has_document_links(EvDocumentLinks * document_links)215 epub_document_links_has_document_links(EvDocumentLinks *document_links)
216 {
217 EpubDocument *epub_document = EPUB_DOCUMENT(document_links);
218
219 g_return_val_if_fail(EPUB_IS_DOCUMENT(epub_document), FALSE);
220
221 if (!epub_document->index)
222 return FALSE;
223
224 return TRUE;
225 }
226
227
228 typedef struct _LinksCBStruct {
229 GtkTreeModel *model;
230 GtkTreeIter *parent;
231 }LinksCBStruct;
232
233 static void
epub_document_make_tree_entry(linknode * ListData,LinksCBStruct * UserData)234 epub_document_make_tree_entry(linknode* ListData,LinksCBStruct* UserData)
235 {
236 GtkTreeIter tree_iter;
237 EvLink *link = NULL;
238 gboolean expand;
239 char *title_markup;
240
241 if (ListData->children) {
242 expand=TRUE;
243 }
244 else {
245 expand=FALSE;
246 }
247
248 EvLinkDest *ev_dest = NULL;
249 EvLinkAction *ev_action;
250
251 /* We shall use a EV_LINK_DEST_TYPE_PAGE for page links,
252 * and a EV_LINK_DEST_TYPE_HLINK(custom) for refs on a page of type url#label
253 * because we need both dest and page label for this.
254 */
255
256 if (g_strrstr(ListData->pagelink,"#") == NULL) {
257 ev_dest = ev_link_dest_new_page(ListData->page);
258 }
259 else {
260 ev_dest = ev_link_dest_new_hlink((gchar*)ListData->pagelink,ListData->page);
261 }
262
263 ev_action = ev_link_action_new_dest (ev_dest);
264
265 link = ev_link_new((gchar*)ListData->linktext,ev_action);
266
267 gtk_tree_store_append (GTK_TREE_STORE (UserData->model), &tree_iter,(UserData->parent));
268 title_markup = g_markup_escape_text ((gchar*)ListData->linktext, -1);
269
270 gtk_tree_store_set (GTK_TREE_STORE (UserData->model), &tree_iter,
271 EV_DOCUMENT_LINKS_COLUMN_MARKUP, title_markup,
272 EV_DOCUMENT_LINKS_COLUMN_LINK, link,
273 EV_DOCUMENT_LINKS_COLUMN_EXPAND, expand,
274 -1);
275
276 if (ListData->children) {
277 LinksCBStruct cbstruct;
278 cbstruct.parent = &tree_iter;
279 cbstruct.model = UserData->model;
280 g_list_foreach (ListData->children,(GFunc)epub_document_make_tree_entry,&cbstruct);
281 }
282
283 g_free (title_markup);
284 g_object_unref (link);
285 }
286
287 static GtkTreeModel *
epub_document_links_get_links_model(EvDocumentLinks * document_links)288 epub_document_links_get_links_model(EvDocumentLinks *document_links)
289 {
290 GtkTreeModel *model = NULL;
291
292 g_return_val_if_fail (EPUB_IS_DOCUMENT (document_links), NULL);
293
294 EpubDocument *epub_document = EPUB_DOCUMENT(document_links);
295
296 model = (GtkTreeModel*) gtk_tree_store_new (EV_DOCUMENT_LINKS_COLUMN_NUM_COLUMNS,
297 G_TYPE_STRING,
298 G_TYPE_OBJECT,
299 G_TYPE_BOOLEAN,
300 G_TYPE_STRING);
301
302 LinksCBStruct linkStruct;
303 linkStruct.model = model;
304 EvLink *link = ev_link_new(epub_document->docTitle,
305 ev_link_action_new_dest(ev_link_dest_new_page(0)));
306 GtkTreeIter parent;
307
308 linkStruct.parent = &parent;
309
310 gtk_tree_store_append (GTK_TREE_STORE (model), &parent,NULL);
311
312 gtk_tree_store_set (GTK_TREE_STORE (model), &parent,
313 EV_DOCUMENT_LINKS_COLUMN_MARKUP, epub_document->docTitle,
314 EV_DOCUMENT_LINKS_COLUMN_LINK, link,
315 EV_DOCUMENT_LINKS_COLUMN_EXPAND, TRUE,
316 -1);
317
318 g_object_unref(link);
319
320 if (epub_document->index) {
321 g_list_foreach (epub_document->index,(GFunc)epub_document_make_tree_entry,&linkStruct);
322 }
323
324 return model;
325 }
326
327 static EvMappingList *
epub_document_links_get_links(EvDocumentLinks * document_links,EvPage * page)328 epub_document_links_get_links (EvDocumentLinks *document_links,
329 EvPage *page)
330 {
331 /* TODO
332 * ev_mapping_list_new()
333 */
334 return NULL;
335 }
336
337 static void
epub_document_document_thumbnails_iface_init(EvDocumentThumbnailsInterface * iface)338 epub_document_document_thumbnails_iface_init (EvDocumentThumbnailsInterface *iface)
339 {
340 iface->get_thumbnail = epub_document_thumbnails_get_thumbnail;
341 iface->get_dimensions = epub_document_thumbnails_get_dimensions;
342 }
343
344 static void
epub_document_document_find_iface_init(EvDocumentFindInterface * iface)345 epub_document_document_find_iface_init (EvDocumentFindInterface *iface)
346 {
347 iface->check_for_hits = epub_document_check_hits;
348 }
349
350 static void
epub_document_document_links_iface_init(EvDocumentLinksInterface * iface)351 epub_document_document_links_iface_init(EvDocumentLinksInterface *iface)
352 {
353 iface->has_document_links = epub_document_links_has_document_links;
354 iface->get_links_model = epub_document_links_get_links_model;
355 iface->get_links = epub_document_links_get_links;
356 }
357
358 static gboolean
epub_document_save(EvDocument * document,const char * uri,GError ** error)359 epub_document_save (EvDocument *document,
360 const char *uri,
361 GError **error)
362 {
363 EpubDocument *epub_document = EPUB_DOCUMENT (document);
364
365 gchar *source_uri = g_filename_to_uri (epub_document->archivename, NULL, error);
366 if (source_uri == NULL)
367 return FALSE;
368
369 return ev_xfer_uri_simple (source_uri, uri, error);
370 }
371
372 static int
epub_document_get_n_pages(EvDocument * document)373 epub_document_get_n_pages (EvDocument *document)
374 {
375 EpubDocument *epub_document = EPUB_DOCUMENT (document);
376
377 if (epub_document-> contentList == NULL)
378 return 0;
379
380 return g_list_length(epub_document->contentList);
381 }
382
383 /**
384 * epub_remove_temporary_dir : Removes a directory recursively.
385 * This function is same as comics_remove_temporary_dir
386 * Returns:
387 * 0 if it was successfully deleted,
388 * -1 if an error occurred
389 */
390 static int
epub_remove_temporary_dir(gchar * path_name)391 epub_remove_temporary_dir (gchar *path_name)
392 {
393 GDir *content_dir;
394 const gchar *filename;
395 gchar *filename_with_path;
396
397 if (g_file_test (path_name, G_FILE_TEST_IS_DIR)) {
398 content_dir = g_dir_open (path_name, 0, NULL);
399 filename = g_dir_read_name (content_dir);
400 while (filename) {
401 filename_with_path =
402 g_build_filename (path_name,
403 filename, NULL);
404 epub_remove_temporary_dir (filename_with_path);
405 g_free (filename_with_path);
406 filename = g_dir_read_name (content_dir);
407 }
408 g_dir_close (content_dir);
409 }
410 /* Note from g_remove() documentation: on Windows, it is in general not
411 * possible to remove a file that is open to some process, or mapped
412 * into memory.*/
413 return (g_remove (path_name));
414 }
415
416
417 static gboolean
418 check_mime_type (const gchar* uri,
419 GError** error);
420
421 static gboolean
422 open_xml_document (const gchar* filename);
423
424 static gboolean
425 set_xml_root_node (xmlChar* rootname);
426
427 static xmlNodePtr
428 xml_get_pointer_to_node (xmlChar* parserfor,
429 xmlChar* attributename,
430 xmlChar* attributevalue);
431 static void
432 xml_parse_children_of_node (xmlNodePtr parent,
433 xmlChar* parserfor,
434 xmlChar* attributename,
435 xmlChar* attributevalue);
436
437 static gboolean
438 xml_check_attribute_value (xmlNode* node,
439 xmlChar * attributename,
440 xmlChar* attributevalue);
441
442 static xmlChar*
443 xml_get_data_from_node (xmlNodePtr node,
444 xmlParseReturnType rettype,
445 xmlChar* attributename);
446
447 static void
448 xml_free_doc (void);
449
450 static void
451 free_tree_nodes (gpointer data);
452
453 /*Global variables for XML parsing*/
454 static xmlDocPtr xmldocument ;
455 static xmlNodePtr xmlroot ;
456 static xmlNodePtr xmlretval ;
457
458 /*
459 **Functions to parse the xml files.
460 **Open a XML document for reading
461 */
462 static gboolean
open_xml_document(const gchar * filename)463 open_xml_document ( const gchar* filename )
464 {
465 xmldocument = xmlParseFile(filename);
466
467 if ( xmldocument == NULL )
468 {
469 return FALSE ;
470 }
471 else
472 {
473 return TRUE ;
474 }
475 }
476
477 /**
478 *Check if the root value is same as rootname .
479 *if supplied rootvalue = NULL ,just set root to rootnode .
480 **/
481 static gboolean
set_xml_root_node(xmlChar * rootname)482 set_xml_root_node(xmlChar* rootname)
483 {
484 xmlroot = xmlDocGetRootElement(xmldocument);
485
486 if (xmlroot == NULL) {
487
488 xmlFreeDoc(xmldocument);
489 return FALSE;
490 }
491
492 if ( rootname == NULL )
493 {
494 return TRUE ;
495 }
496
497 if ( !xmlStrcmp(xmlroot->name,rootname))
498 {
499 return TRUE ;
500 }
501 else
502 {
503 return FALSE;
504 }
505 }
506
507 static xmlNodePtr
xml_get_pointer_to_node(xmlChar * parserfor,xmlChar * attributename,xmlChar * attributevalue)508 xml_get_pointer_to_node(xmlChar* parserfor,
509 xmlChar* attributename,
510 xmlChar* attributevalue )
511 {
512 xmlNodePtr topchild;
513
514 xmlretval = NULL ;
515
516 if ( !xmlStrcmp( xmlroot->name, parserfor) )
517 {
518 return xmlroot ;
519 }
520
521 topchild = xmlroot->xmlChildrenNode ;
522
523 while ( topchild != NULL )
524 {
525 if ( !xmlStrcmp(topchild->name,parserfor) )
526 {
527 if ( xml_check_attribute_value(topchild,attributename,attributevalue) == TRUE )
528 {
529 xmlretval = topchild;
530 return xmlretval;
531 }
532 else
533 {
534 /*No need to parse children node*/
535 topchild = topchild->next ;
536 continue ;
537 }
538 }
539
540 xml_parse_children_of_node(topchild , parserfor, attributename, attributevalue) ;
541
542 topchild = topchild->next ;
543 }
544
545 return xmlretval ;
546 }
547
548 static void
xml_parse_children_of_node(xmlNodePtr parent,xmlChar * parserfor,xmlChar * attributename,xmlChar * attributevalue)549 xml_parse_children_of_node(xmlNodePtr parent,
550 xmlChar* parserfor,
551 xmlChar* attributename,
552 xmlChar* attributevalue )
553 {
554 xmlNodePtr child = parent->xmlChildrenNode ;
555
556 while ( child != NULL )
557 {
558 if ( !xmlStrcmp(child->name,parserfor))
559 {
560 if ( xml_check_attribute_value(child,attributename,attributevalue) == TRUE )
561 {
562 xmlretval = child;
563 return ;
564 }
565 else
566 {
567 /*No need to parse children node*/
568 child = child->next ;
569 continue ;
570 }
571 }
572
573 /*return already if we have xmlretval set*/
574 if ( xmlretval != NULL )
575 {
576 return ;
577 }
578
579 xml_parse_children_of_node(child,parserfor,attributename,attributevalue) ;
580 child = child->next ;
581 }
582 }
583
584 static void
xml_free_doc()585 xml_free_doc()
586 {
587 xmlFreeDoc(xmldocument);
588 xmldocument = NULL;
589 }
590
591 static gboolean
xml_check_attribute_value(xmlNode * node,xmlChar * attributename,xmlChar * attributevalue)592 xml_check_attribute_value(xmlNode* node,
593 xmlChar * attributename,
594 xmlChar* attributevalue)
595 {
596 xmlChar* attributefromfile ;
597 if ( attributename == NULL || attributevalue == NULL )
598 {
599 return TRUE ;
600 }
601 else if ( !xmlStrcmp(( attributefromfile = xmlGetProp(node,attributename)),
602 attributevalue) )
603 {
604 xmlFree(attributefromfile);
605 return TRUE ;
606 }
607 xmlFree(attributefromfile);
608 return FALSE ;
609 }
610
611 static xmlChar*
xml_get_data_from_node(xmlNodePtr node,xmlParseReturnType rettype,xmlChar * attributename)612 xml_get_data_from_node(xmlNodePtr node,
613 xmlParseReturnType rettype,
614 xmlChar* attributename)
615 {
616 xmlChar* datastring ;
617 if ( rettype == XML_ATTRIBUTE )
618 datastring= xmlGetProp(node,attributename);
619 else
620 datastring= xmlNodeListGetString(xmldocument,node->xmlChildrenNode, 1);
621
622 return datastring;
623 }
624
625 static gboolean
check_mime_type(const gchar * uri,GError ** error)626 check_mime_type(const gchar* uri,GError** error)
627 {
628 GError * err = NULL ;
629 const gchar* mimeFromFile = ev_file_get_mime_type(uri,FALSE,&err);
630
631 gchar* mimetypes[] = {"application/epub+zip","application/x-booki+zip"};
632 int typecount = 2;
633 if ( !mimeFromFile )
634 {
635 if (err) {
636 g_propagate_error (error, err);
637 }
638 else {
639 g_set_error_literal (error,
640 EV_DOCUMENT_ERROR,
641 EV_DOCUMENT_ERROR_INVALID,
642 _("Unknown MIME Type"));
643 }
644 return FALSE;
645 }
646 else
647 {
648 int i=0;
649 for (i=0; i < typecount ;i++) {
650 if ( g_strcmp0(mimeFromFile, mimetypes[i]) == 0 ) {
651 return TRUE;
652 }
653 }
654
655 /*We didn't find a match*/
656 g_set_error_literal (error,
657 EV_DOCUMENT_ERROR,
658 EV_DOCUMENT_ERROR_INVALID,
659 _("Not an ePub document"));
660
661 return FALSE;
662 }
663 }
664
665 static gboolean
extract_one_file(EpubDocument * epub_document,GError ** error)666 extract_one_file(EpubDocument* epub_document,GError ** error)
667 {
668 GFile * outfile ;
669 gsize writesize = 0;
670 GString * gfilepath ;
671 unz_file_info64 info ;
672 gchar* directory;
673 GString* dir_create;
674 GFileOutputStream * outstream ;
675
676 if ( unzOpenCurrentFile(epub_document->epubDocument) != UNZ_OK )
677 {
678 return FALSE ;
679 }
680
681 gboolean result = TRUE;
682
683 gpointer currentfilename = g_malloc0(512);
684 unzGetCurrentFileInfo64(epub_document->epubDocument,&info,currentfilename,512,NULL,0,NULL,0) ;
685 directory = g_strrstr(currentfilename,"/") ;
686
687 if ( directory != NULL )
688 directory++;
689
690 gfilepath = g_string_new(epub_document->tmp_archive_dir) ;
691 g_string_append_printf(gfilepath,"/%s",(gchar*)currentfilename);
692
693 /*if we encounter a directory, make a directory inside our temporary folder.*/
694 if (directory != NULL && *directory == '\0')
695 {
696 g_mkdir(gfilepath->str,0777);
697 goto out;
698 }
699 else if (directory != NULL && *directory != '\0' ) {
700 gchar* createdir = currentfilename;
701 /*Since a substring can't be longer than the parent string, allocating space equal to the parent's size should suffice*/
702 gchar *createdirname = g_malloc0(strlen(currentfilename));
703 /* Add the name of the directory and subdirectories,if any to a buffer and then create it */
704 gchar *createdirnametemp = createdirname;
705 while ( createdir != directory ) {
706 (*createdirnametemp) = (*createdir);
707 createdirnametemp++;
708 createdir++;
709 }
710 (*createdirnametemp) = '\0';
711
712 dir_create = g_string_new(epub_document->tmp_archive_dir);
713 g_string_append_printf(dir_create,"/%s",createdirname);
714 g_free(createdirname);
715
716 g_mkdir_with_parents(dir_create->str,0777);
717 g_string_free(dir_create,TRUE);
718 }
719
720 outfile = g_file_new_for_path(gfilepath->str);
721 outstream = g_file_create(outfile,G_FILE_CREATE_PRIVATE,NULL,error);
722 gpointer buffer = g_malloc0(512);
723 while ( (writesize = unzReadCurrentFile(epub_document->epubDocument,buffer,512) ) != 0 )
724 {
725 if ( g_output_stream_write((GOutputStream*)outstream,buffer,writesize,NULL,error) == -1 )
726 {
727 result = FALSE;
728 break;
729 }
730 }
731 g_free(buffer);
732 g_output_stream_close((GOutputStream*)outstream,NULL,error);
733 g_object_unref(outfile) ;
734 g_object_unref(outstream) ;
735
736 out:
737 unzCloseCurrentFile (epub_document->epubDocument) ;
738 g_string_free(gfilepath,TRUE);
739 g_free(currentfilename);
740 return result;
741 }
742
743 static gboolean
extract_epub_from_container(const gchar * uri,EpubDocument * epub_document,GError ** error)744 extract_epub_from_container (const gchar* uri,
745 EpubDocument *epub_document,
746 GError ** error)
747 {
748 GError *err = NULL;
749 epub_document->archivename = g_filename_from_uri(uri,NULL,error);
750
751 if ( !epub_document->archivename )
752 {
753 if (err) {
754 g_propagate_error (error, err);
755 }
756 else {
757 g_set_error_literal (error,
758 EV_DOCUMENT_ERROR,
759 EV_DOCUMENT_ERROR_INVALID,
760 _("could not retrieve filename"));
761 }
762 return FALSE;
763 }
764
765 gchar *epubfilename = g_strrstr(epub_document->archivename,"/");
766 if ( *epubfilename == '/' )
767 epubfilename++ ;
768
769 GString *temporary_sub_directory = g_string_new(epubfilename);
770 g_string_append(temporary_sub_directory,"XXXXXX") ;
771 epub_document->tmp_archive_dir = ev_mkdtemp(temporary_sub_directory->str, error);
772 g_string_free(temporary_sub_directory, TRUE);
773
774 if (!epub_document->tmp_archive_dir) {
775 return FALSE;
776 }
777
778 epub_document->epubDocument = unzOpen64(epub_document->archivename);
779 if ( epub_document->epubDocument == NULL )
780 {
781 if (err) {
782 g_propagate_error (error, err);
783 }
784 else {
785 g_set_error_literal (error,
786 EV_DOCUMENT_ERROR,
787 EV_DOCUMENT_ERROR_INVALID,
788 _("could not open archive"));
789 }
790 return FALSE;
791 }
792
793 gboolean result = FALSE;
794
795 if ( unzGoToFirstFile(epub_document->epubDocument) != UNZ_OK )
796 {
797 if (err) {
798 g_propagate_error (error, err);
799 }
800 else {
801 g_set_error_literal (error,
802 EV_DOCUMENT_ERROR,
803 EV_DOCUMENT_ERROR_INVALID,
804 _("could not extract archive"));
805 }
806 goto out;
807 }
808
809 while ( TRUE )
810 {
811 if ( extract_one_file(epub_document,&err) == FALSE )
812 {
813 if (err) {
814 g_propagate_error (error, err);
815 }
816 else {
817 g_set_error_literal (error,
818 EV_DOCUMENT_ERROR,
819 EV_DOCUMENT_ERROR_INVALID,
820 _("could not extract archive"));
821 }
822 goto out;
823 }
824
825 if ( unzGoToNextFile(epub_document->epubDocument) == UNZ_END_OF_LIST_OF_FILE ) {
826 result = TRUE;
827 break;
828 }
829 }
830
831 out:
832 unzClose(epub_document->epubDocument);
833 return result;
834 }
835
836 static gchar*
get_uri_to_content(const gchar * uri,GError ** error,EpubDocument * epub_document)837 get_uri_to_content(const gchar* uri,GError ** error,EpubDocument *epub_document)
838 {
839 gchar* tmp_archive_dir = epub_document->tmp_archive_dir;
840 GError *err = NULL ;
841
842 gchar *containerpath = g_filename_from_uri(uri,NULL,&err);
843 if ( !containerpath )
844 {
845 if (err) {
846 g_propagate_error (error,err);
847 }
848 else {
849 g_set_error_literal (error,
850 EV_DOCUMENT_ERROR,
851 EV_DOCUMENT_ERROR_INVALID,
852 _("could not retrieve container file"));
853 }
854 return NULL ;
855 }
856
857 gboolean result = open_xml_document(containerpath);
858 g_free (containerpath);
859 if ( result == FALSE )
860 {
861 g_set_error_literal(error,
862 EV_DOCUMENT_ERROR,
863 EV_DOCUMENT_ERROR_INVALID,
864 _("could not open container file"));
865
866 return NULL ;
867 }
868
869 if ( set_xml_root_node((xmlChar*)"container") == FALSE) {
870
871 g_set_error_literal(error,
872 EV_DOCUMENT_ERROR,
873 EV_DOCUMENT_ERROR_INVALID,
874 _("container file is corrupt"));
875 return NULL ;
876 }
877
878 xmlNodePtr rootfileNode = xml_get_pointer_to_node((xmlChar*)"rootfile",(xmlChar*)"media-type",(xmlChar*)"application/oebps-package+xml");
879 if ( rootfileNode == NULL)
880 {
881 g_set_error_literal(error,
882 EV_DOCUMENT_ERROR,
883 EV_DOCUMENT_ERROR_INVALID,
884 _("epub file is invalid or corrupt"));
885 return NULL ;
886 }
887
888 xmlChar *relativepath = xml_get_data_from_node(rootfileNode,XML_ATTRIBUTE,(xmlChar*)"full-path") ;
889 if ( relativepath == NULL )
890 {
891 g_set_error_literal(error,
892 EV_DOCUMENT_ERROR,
893 EV_DOCUMENT_ERROR_INVALID,
894 _("epub file is corrupt, no container"));
895 return NULL ;
896 }
897
898 gchar* documentfolder = g_strrstr((gchar*)relativepath,"/");
899 if (documentfolder != NULL) {
900 gchar* copybuffer = (gchar*)relativepath ;
901 gchar* directorybuffer = g_malloc0(sizeof(gchar*)*100);
902 gchar* writer = directorybuffer;
903
904 while(copybuffer != documentfolder) {
905 (*writer) = (*copybuffer);
906 writer++;copybuffer++;
907 }
908 *writer = '\0';
909
910 GString *documentdir = g_string_new(tmp_archive_dir);
911 g_string_append_printf(documentdir,"/%s",directorybuffer);
912 g_free(directorybuffer);
913 epub_document->documentdir = g_string_free(documentdir,FALSE);
914 }
915 else
916 {
917 epub_document->documentdir = g_strdup(tmp_archive_dir);
918 }
919
920 GString *absolutepath = g_string_new(tmp_archive_dir);
921 g_string_append_printf(absolutepath,"/%s",relativepath);
922 g_free (relativepath);
923
924 gchar *content_uri = g_filename_to_uri(absolutepath->str,NULL,&err);
925 g_string_free(absolutepath,TRUE);
926 if ( !content_uri ) {
927 if (err) {
928 g_propagate_error (error,err);
929 }
930 else
931 {
932 g_set_error_literal (error,
933 EV_DOCUMENT_ERROR,
934 EV_DOCUMENT_ERROR_INVALID,
935 _("could not retrieve container file"));
936 }
937 return NULL ;
938 }
939 xml_free_doc();
940 return content_uri ;
941 }
942
943 static gboolean
link_present_on_page(const gchar * link,const gchar * page_uri)944 link_present_on_page(const gchar* link,const gchar *page_uri)
945 {
946 gchar *res;
947 if ((res=g_strrstr(link, page_uri)) != NULL) {
948 return TRUE;
949 }
950 else {
951 return FALSE;
952 }
953 }
954
955 static GList*
setup_document_content_list(const gchar * content_uri,GError ** error,gchar * documentdir)956 setup_document_content_list(const gchar* content_uri, GError** error,gchar *documentdir)
957 {
958 GError *err = NULL;
959 gint indexcounter = 1;
960 xmlNodePtr manifest,spine,itemrefptr,itemptr;
961 gboolean errorflag = FALSE;
962
963 if ( open_xml_document(content_uri) == FALSE )
964 {
965 g_set_error_literal(error,
966 EV_DOCUMENT_ERROR,
967 EV_DOCUMENT_ERROR_INVALID,
968 _("could not parse content manifest"));
969
970 return FALSE ;
971 }
972 if ( set_xml_root_node((xmlChar*)"package") == FALSE) {
973
974 g_set_error_literal(error,
975 EV_DOCUMENT_ERROR,
976 EV_DOCUMENT_ERROR_INVALID,
977 _("content file is invalid"));
978 return FALSE ;
979 }
980
981 if ( ( spine = xml_get_pointer_to_node((xmlChar*)"spine",NULL,NULL) )== NULL )
982 {
983 g_set_error_literal(error,
984 EV_DOCUMENT_ERROR,
985 EV_DOCUMENT_ERROR_INVALID,
986 _("epub file has no spine"));
987 return FALSE ;
988 }
989
990 if ( ( manifest = xml_get_pointer_to_node((xmlChar*)"manifest",NULL,NULL) )== NULL )
991 {
992 g_set_error_literal(error,
993 EV_DOCUMENT_ERROR,
994 EV_DOCUMENT_ERROR_INVALID,
995 _("epub file has no manifest"));
996 return FALSE ;
997 }
998
999 xmlretval = NULL ;
1000
1001 /*Get first instance of itemref from the spine*/
1002 xml_parse_children_of_node(spine,(xmlChar*)"itemref",NULL,NULL);
1003
1004 if ( xmlretval != NULL )
1005 itemrefptr = xmlretval ;
1006 else
1007 {
1008 errorflag=TRUE;
1009 }
1010
1011 GList *newlist = NULL;
1012
1013 /*Parse the spine for remaining itemrefs*/
1014 do
1015 {
1016 /*for the first time that we enter the loop, if errorflag is set we break*/
1017 if ( errorflag )
1018 {
1019 break;
1020 }
1021 if ( xmlStrcmp(itemrefptr->name,(xmlChar*)"itemref") == 0)
1022 {
1023 contentListNode *newnode = g_malloc0(sizeof(newnode));
1024 newnode->key = (gchar*)xml_get_data_from_node(itemrefptr,XML_ATTRIBUTE,(xmlChar*)"idref");
1025 if ( newnode->key == NULL )
1026 {
1027 g_free (newnode);
1028 errorflag = TRUE;
1029 break;
1030 }
1031 xmlretval=NULL ;
1032 xml_parse_children_of_node(manifest,(xmlChar*)"item",(xmlChar*)"id",(xmlChar*)newnode->key);
1033
1034 if ( xmlretval != NULL )
1035 {
1036 itemptr = xmlretval ;
1037 }
1038 else
1039 {
1040 g_free (newnode->key);
1041 g_free (newnode);
1042 errorflag = TRUE;
1043 break;
1044 }
1045
1046 GString* absolutepath = g_string_new(documentdir);
1047 gchar *relativepath = (gchar*)xml_get_data_from_node(itemptr,XML_ATTRIBUTE,(xmlChar*)"href");
1048 g_string_append_printf(absolutepath,"/%s",relativepath);
1049 g_free (relativepath);
1050
1051 newnode->value = g_filename_to_uri(absolutepath->str,NULL,&err);
1052 g_string_free(absolutepath,TRUE);
1053
1054 if ( newnode->value == NULL )
1055 {
1056 g_free (newnode->key);
1057 g_free (newnode);
1058 errorflag = TRUE;
1059 break;
1060 }
1061
1062 newnode->index = indexcounter++ ;
1063
1064 newlist = g_list_prepend(newlist, newnode);
1065 }
1066 itemrefptr = itemrefptr->next ;
1067 }
1068 while ( itemrefptr != NULL );
1069
1070 if ( errorflag )
1071 {
1072 if ( err )
1073 {
1074 g_propagate_error(error,err);
1075 }
1076 else
1077 {
1078 g_set_error_literal(error,
1079 EV_DOCUMENT_ERROR,
1080 EV_DOCUMENT_ERROR_INVALID,
1081 _("Could not set up document tree for loading, some files missing"));
1082 }
1083 /*free any nodes that were set up and return empty*/
1084 g_list_free_full(newlist, (GDestroyNotify)free_tree_nodes);
1085 return NULL;
1086 }
1087
1088 newlist = g_list_reverse(newlist);
1089 xml_free_doc();
1090 return newlist;
1091
1092 }
1093
1094 /* Callback function to free the contentlist.*/
1095 static void
free_tree_nodes(gpointer data)1096 free_tree_nodes(gpointer data)
1097 {
1098 contentListNode* dataptr = data ;
1099 g_free(dataptr->value);
1100 g_free(dataptr->key);
1101 g_free(dataptr);
1102 }
1103
1104 static void
free_link_nodes(gpointer data)1105 free_link_nodes(gpointer data)
1106 {
1107 linknode* dataptr = data ;
1108 g_free(dataptr->pagelink);
1109 g_free(dataptr->linktext);
1110
1111 if (dataptr->children) {
1112 g_list_free_full(dataptr->children,(GDestroyNotify)free_link_nodes);
1113 }
1114 g_free(dataptr);
1115 }
1116
1117 static gchar*
get_toc_file_name(gchar * containeruri)1118 get_toc_file_name(gchar *containeruri)
1119 {
1120 gchar *containerfilename = g_filename_from_uri(containeruri,NULL,NULL);
1121 open_xml_document(containerfilename);
1122 g_free (containerfilename);
1123
1124 set_xml_root_node(NULL);
1125
1126 xmlNodePtr manifest = xml_get_pointer_to_node((xmlChar*)"manifest",NULL,NULL);
1127 xmlNodePtr spine = xml_get_pointer_to_node((xmlChar*)"spine",NULL,NULL);
1128
1129 xmlChar *ncx = xml_get_data_from_node(spine,XML_ATTRIBUTE,(xmlChar*)"toc");
1130
1131 /*In an epub3, there is sometimes no toc, and we need to then use the nav file for this.*/
1132 if (ncx == NULL) {
1133 return NULL;
1134 }
1135
1136 xmlretval = NULL;
1137 xml_parse_children_of_node(manifest,(xmlChar*)"item",(xmlChar*)"id",ncx);
1138
1139 gchar* tocfilename = (gchar*)xml_get_data_from_node(xmlretval,XML_ATTRIBUTE,(xmlChar*)"href");
1140 xml_free_doc();
1141
1142 return tocfilename;
1143 }
1144
1145 static gchar*
epub_document_get_nav_file(gchar * containeruri)1146 epub_document_get_nav_file(gchar* containeruri)
1147 {
1148 open_xml_document(containeruri);
1149 set_xml_root_node(NULL);
1150 xmlNodePtr manifest = xml_get_pointer_to_node((xmlChar*)"manifest",NULL,NULL);
1151 xmlretval = NULL;
1152 xml_parse_children_of_node(manifest,(xmlChar*)"item",(xmlChar*)"properties",(xmlChar*)"nav");
1153
1154 gchar *uri = (gchar*)xml_get_data_from_node(xmlretval,XML_ATTRIBUTE, (xmlChar*)"href");
1155
1156 xml_free_doc();
1157 return uri;
1158 }
1159
1160 static GList*
get_child_list(xmlNodePtr ol,gchar * documentdir)1161 get_child_list(xmlNodePtr ol,gchar* documentdir)
1162 {
1163 GList *childlist = NULL;
1164 xmlNodePtr li = ol->xmlChildrenNode;
1165
1166 while (li != NULL) {
1167 if (xmlStrcmp(li->name,(xmlChar*)"li")) {
1168 li = li->next;
1169 continue;
1170 }
1171 xmlNodePtr children = li->xmlChildrenNode;
1172 linknode *newlinknode = g_new0(linknode, 1);
1173 while (children != NULL) {
1174 if ( !xmlStrcmp(children->name,(xmlChar*)"a")) {
1175 newlinknode->linktext = (gchar*)xml_get_data_from_node(children,XML_KEYWORD,NULL);
1176 gchar* filename = (gchar*)xml_get_data_from_node(children,XML_ATTRIBUTE,(xmlChar*)"href");
1177 gchar *filepath = g_strdup_printf("%s/%s",documentdir,filename);
1178 newlinknode->pagelink = g_filename_to_uri(filepath,NULL,NULL);
1179 g_free(filename);
1180 g_free(filepath);
1181 newlinknode->children = NULL;
1182 childlist = g_list_prepend(childlist,newlinknode);
1183 }
1184 else if ( !xmlStrcmp(children->name,(xmlChar*)"ol")){
1185 newlinknode->children = get_child_list(children,documentdir);
1186 }
1187
1188 children = children->next;
1189 }
1190
1191 li = li->next;
1192 }
1193
1194 return g_list_reverse(childlist);
1195 }
1196
1197 /* For an epub3 style navfile */
1198 static GList*
setup_index_from_navfile(gchar * tocpath)1199 setup_index_from_navfile(gchar *tocpath)
1200 {
1201 GList *index = NULL;
1202 open_xml_document(tocpath);
1203 set_xml_root_node(NULL);
1204 xmlNodePtr nav = xml_get_pointer_to_node((xmlChar*)"nav",(xmlChar*)"id",(xmlChar*)"toc");
1205 xmlretval=NULL;
1206 xml_parse_children_of_node(nav,(xmlChar*)"ol", NULL,NULL);
1207 gchar *navdirend = g_strrstr(tocpath,"/");
1208 gchar *navdir = g_malloc0(strlen(tocpath));
1209 gchar *reader = tocpath;
1210 gchar *writer = navdir;
1211
1212 while (reader != navdirend) {
1213 (*writer) = (*reader) ;
1214 writer++;reader++;
1215 }
1216 index = get_child_list(xmlretval,navdir);
1217 g_free(navdir);
1218 xml_free_doc();
1219 return index;
1220 }
1221
1222 static GList*
setup_document_index(EpubDocument * epub_document,gchar * containeruri)1223 setup_document_index(EpubDocument *epub_document,gchar *containeruri)
1224 {
1225 GString *tocpath = g_string_new(epub_document->documentdir);
1226 gchar *tocfilename = get_toc_file_name(containeruri);
1227 GList *index = NULL;
1228
1229 if (tocfilename == NULL) {
1230 tocfilename = epub_document_get_nav_file(containeruri);
1231
1232 //Apparently, sometimes authors don't even care to add a TOC!! Guess standards are just guidelines.
1233
1234 if (tocfilename == NULL) {
1235 //We didn't even find a nav file.The document has no TOC.
1236 g_string_free(tocpath,TRUE);
1237 return NULL;
1238 }
1239
1240 g_string_append_printf (tocpath,"/%s",tocfilename);
1241 index = setup_index_from_navfile(tocpath->str);
1242 g_string_free(tocpath,TRUE);
1243 g_free (tocfilename);
1244 return index;
1245 }
1246
1247 g_string_append_printf (tocpath,"/%s",tocfilename);
1248 g_free (tocfilename);
1249
1250 GString *pagelink;
1251 open_xml_document(tocpath->str);
1252 g_string_free(tocpath,TRUE);
1253 set_xml_root_node((xmlChar*)"ncx");
1254
1255 xmlNodePtr docTitle = xml_get_pointer_to_node((xmlChar*)"docTitle",NULL,NULL);
1256 xmlretval = NULL;
1257 xml_parse_children_of_node(docTitle,(xmlChar*)"text",NULL,NULL);
1258
1259 while (epub_document->docTitle == NULL && xmlretval != NULL) {
1260 epub_document->docTitle = (gchar*)xml_get_data_from_node(xmlretval,XML_KEYWORD,NULL);
1261 xmlretval = xmlretval->next;
1262 }
1263 xmlNodePtr navMap = xml_get_pointer_to_node((xmlChar*)"navMap",NULL,NULL);
1264 xmlretval = NULL;
1265 xml_parse_children_of_node(navMap,(xmlChar*)"navPoint",NULL,NULL);
1266
1267 xmlNodePtr navPoint = xmlretval;
1268
1269 while(navPoint != NULL) {
1270
1271 if ( !xmlStrcmp(navPoint->name,(xmlChar*)"navPoint")) {
1272 xmlretval = NULL;
1273 xml_parse_children_of_node(navPoint,(xmlChar*)"navLabel",NULL,NULL);
1274 xmlNodePtr navLabel = xmlretval;
1275 xmlretval = NULL;
1276 gchar *fragment=NULL,*end=NULL;
1277 GString *uri = NULL;
1278
1279 xml_parse_children_of_node(navLabel,(xmlChar*)"text",NULL,NULL);
1280 linknode *newnode = g_new0(linknode,1);
1281 newnode->linktext = NULL;
1282 while (newnode->linktext == NULL) {
1283 newnode->linktext = (gchar*)xml_get_data_from_node(xmlretval,XML_KEYWORD,NULL);
1284 xmlretval = xmlretval->next;
1285 }
1286 xmlretval = NULL;
1287 xml_parse_children_of_node(navPoint,(xmlChar*)"content",NULL,NULL);
1288 pagelink = g_string_new(epub_document->documentdir);
1289 newnode->pagelink = (gchar*)xml_get_data_from_node(xmlretval,XML_ATTRIBUTE,(xmlChar*)"src");
1290 g_string_append_printf(pagelink,"/%s",newnode->pagelink);
1291 xmlFree(newnode->pagelink);
1292
1293 gchar *escaped = g_strdup(pagelink->str);
1294
1295 //unescaping any special characters
1296 pagelink->str = g_uri_unescape_string (escaped,NULL);
1297 g_free(escaped);
1298
1299 if ((end = g_strrstr(pagelink->str,"#")) != NULL) {
1300 fragment = g_strdup(g_strrstr(pagelink->str,"#"));
1301 *end = '\0';
1302 }
1303 uri = g_string_new(g_filename_to_uri(pagelink->str,NULL,NULL));
1304 g_string_free(pagelink,TRUE);
1305
1306 if (fragment) {
1307 g_string_append(uri,fragment);
1308 }
1309
1310 newnode->pagelink = g_strdup(uri->str);
1311 g_string_free(uri,TRUE);
1312 index = g_list_prepend(index,newnode);
1313 }
1314
1315 navPoint = navPoint->next;
1316
1317 }
1318
1319 xml_free_doc();
1320
1321 return g_list_reverse(index);
1322 }
1323
1324 static EvDocumentInfo*
epub_document_get_info(EvDocument * document)1325 epub_document_get_info(EvDocument *document)
1326 {
1327 EpubDocument *epub_document = EPUB_DOCUMENT(document);
1328 GError *error = NULL ;
1329 gchar* infofile ;
1330 xmlNodePtr metanode ;
1331 GString* buffer ;
1332
1333 GString* containerpath = g_string_new(epub_document->tmp_archive_dir);
1334 g_string_append_printf(containerpath,"/META-INF/container.xml");
1335 gchar* containeruri = g_filename_to_uri(containerpath->str,NULL,&error);
1336 g_string_free (containerpath, TRUE);
1337 if ( error )
1338 {
1339 return NULL ;
1340 }
1341
1342 gchar* uri = get_uri_to_content (containeruri,&error,epub_document);
1343 g_free (containeruri);
1344 if ( error )
1345 {
1346 return NULL ;
1347 }
1348
1349 EvDocumentInfo* epubinfo = g_new0 (EvDocumentInfo, 1);
1350
1351 epubinfo->fields_mask = EV_DOCUMENT_INFO_TITLE |
1352 EV_DOCUMENT_INFO_FORMAT |
1353 EV_DOCUMENT_INFO_AUTHOR |
1354 EV_DOCUMENT_INFO_SUBJECT |
1355 EV_DOCUMENT_INFO_KEYWORDS |
1356 EV_DOCUMENT_INFO_LAYOUT |
1357 EV_DOCUMENT_INFO_CREATOR |
1358 EV_DOCUMENT_INFO_LINEARIZED |
1359 EV_DOCUMENT_INFO_PERMISSIONS |
1360 EV_DOCUMENT_INFO_N_PAGES ;
1361
1362 infofile = g_filename_from_uri(uri,NULL,&error);
1363 g_free (uri);
1364 if ( error )
1365 {
1366 return epubinfo;
1367 }
1368
1369 open_xml_document(infofile);
1370 g_free (infofile);
1371
1372 set_xml_root_node((xmlChar*)"package");
1373
1374 metanode = xml_get_pointer_to_node((xmlChar*)"title",NULL,NULL);
1375 if ( metanode == NULL )
1376 epubinfo->title = NULL ;
1377 else
1378 epubinfo->title = (char*)xml_get_data_from_node(metanode,XML_KEYWORD,NULL);
1379
1380 metanode = xml_get_pointer_to_node((xmlChar*)"creator",NULL,NULL);
1381 if ( metanode == NULL )
1382 epubinfo->author = g_strdup("unknown");
1383 else
1384 epubinfo->author = (char*)xml_get_data_from_node(metanode,XML_KEYWORD,NULL);
1385
1386 metanode = xml_get_pointer_to_node((xmlChar*)"subject",NULL,NULL);
1387 if ( metanode == NULL )
1388 epubinfo->subject = g_strdup("unknown");
1389 else
1390 epubinfo->subject = (char*)xml_get_data_from_node(metanode,XML_KEYWORD,NULL);
1391
1392 buffer = g_string_new((gchar*)xml_get_data_from_node (xmlroot,XML_ATTRIBUTE,(xmlChar*)"version"));
1393 g_string_prepend(buffer,"epub ");
1394 epubinfo->format = g_string_free(buffer,FALSE);
1395
1396 /*FIXME: Add more of these as you write the corresponding modules*/
1397
1398 epubinfo->layout = EV_DOCUMENT_LAYOUT_SINGLE_PAGE;
1399
1400 metanode = xml_get_pointer_to_node((xmlChar*)"publisher",NULL,NULL);
1401 if ( metanode == NULL )
1402 epubinfo->creator = g_strdup("unknown");
1403 else
1404 epubinfo->creator = (char*)xml_get_data_from_node(metanode,XML_KEYWORD,NULL);
1405
1406 /* number of pages */
1407 epubinfo->n_pages = epub_document_get_n_pages(document);
1408
1409 /*Copying*/
1410 epubinfo->permissions = EV_DOCUMENT_PERMISSIONS_OK_TO_COPY;
1411 /*TODO : Add a function to get date*/
1412
1413 if (xmldocument)
1414 xml_free_doc();
1415 return epubinfo ;
1416 }
1417
1418 static EvPage*
epub_document_get_page(EvDocument * document,gint index)1419 epub_document_get_page(EvDocument *document,
1420 gint index)
1421 {
1422 EpubDocument *epub_document = EPUB_DOCUMENT(document);
1423 EvPage* page = ev_page_new(index);
1424 contentListNode *listptr = g_list_nth_data (epub_document->contentList,index);
1425 page->backend_page = g_strdup(listptr->value);
1426 return page ;
1427 }
1428
1429 static void
change_to_night_sheet(contentListNode * nodedata,gpointer user_data)1430 change_to_night_sheet(contentListNode *nodedata,gpointer user_data)
1431 {
1432 gchar *filename = g_filename_from_uri(nodedata->value,NULL,NULL);
1433 open_xml_document(filename);
1434 set_xml_root_node(NULL);
1435 xmlNodePtr head =xml_get_pointer_to_node((xmlChar*)"head",NULL,NULL);
1436 gchar *class = NULL;
1437 xmlretval = NULL;
1438 xml_parse_children_of_node(head,(xmlChar*)"link",(xmlChar*)"rel",(xmlChar*)"stylesheet");
1439
1440 xmlNodePtr day = xmlretval;
1441 if ( (class = (gchar*)xml_get_data_from_node(day,XML_ATTRIBUTE,(xmlChar*)"class")) == NULL) {
1442 xmlSetProp(day,(xmlChar*)"class",(xmlChar*)"day");
1443 }
1444 g_free(class);
1445 xmlSetProp(day,(xmlChar*)"rel",(xmlChar*)"alternate stylesheet");
1446 xmlretval = NULL;
1447 xml_parse_children_of_node(head,(xmlChar*)"link",(xmlChar*)"class",(xmlChar*)"night");
1448 xmlSetProp(xmlretval,(xmlChar*)"rel",(xmlChar*)"stylesheet");
1449 xmlSaveFormatFile (filename, xmldocument, 0);
1450 xml_free_doc();
1451 g_free(filename);
1452 }
1453
1454 static void
change_to_day_sheet(contentListNode * nodedata,gpointer user_data)1455 change_to_day_sheet(contentListNode *nodedata,gpointer user_data)
1456 {
1457 gchar *filename = g_filename_from_uri(nodedata->value,NULL,NULL);
1458 open_xml_document(filename);
1459 set_xml_root_node(NULL);
1460 xmlNodePtr head =xml_get_pointer_to_node((xmlChar*)"head",NULL,NULL);
1461
1462 xmlretval = NULL;
1463 xml_parse_children_of_node(head,(xmlChar*)"link",(xmlChar*)"rel",(xmlChar*)"stylesheet");
1464
1465 xmlNodePtr day = xmlretval;
1466 xmlSetProp(day,(xmlChar*)"rel",(xmlChar*)"alternate stylesheet");
1467
1468 xmlretval = NULL;
1469 xml_parse_children_of_node(head,(xmlChar*)"link",(xmlChar*)"class",(xmlChar*)"day");
1470 xmlSetProp(xmlretval,(xmlChar*)"rel",(xmlChar*)"stylesheet");
1471 xmlSaveFormatFile (filename, xmldocument, 0);
1472 xml_free_doc();
1473 g_free(filename);
1474 }
1475
1476 static gchar*
epub_document_get_alternate_stylesheet(gchar * docuri)1477 epub_document_get_alternate_stylesheet(gchar *docuri)
1478 {
1479 gchar *filename = g_filename_from_uri(docuri,NULL,NULL);
1480 open_xml_document(filename);
1481 g_free(filename);
1482
1483 set_xml_root_node(NULL);
1484
1485 xmlNodePtr head= xml_get_pointer_to_node((xmlChar*)"head",NULL,NULL);
1486
1487 xmlretval = NULL;
1488
1489 xml_parse_children_of_node(head,(xmlChar*)"link",(xmlChar*)"class",(xmlChar*)"night");
1490
1491 if (xmlretval != NULL) {
1492 return (gchar*)xml_get_data_from_node(xmlretval,XML_ATTRIBUTE,(xmlChar*)"href");
1493 }
1494 xml_free_doc();
1495 return NULL;
1496 }
1497
1498 static void
add_night_sheet(contentListNode * listdata,gchar * sheet)1499 add_night_sheet(contentListNode *listdata,gchar *sheet)
1500 {
1501 gchar *sheeturi = g_filename_to_uri(sheet,NULL,NULL);
1502 open_xml_document(listdata->value);
1503
1504 set_xml_root_node(NULL);
1505 xmlNodePtr head = xml_get_pointer_to_node((xmlChar*)"head",NULL,NULL);
1506
1507 xmlNodePtr link = xmlNewTextChild (head, NULL, (xmlChar*) "link", NULL);
1508 xmlNewProp (link, (xmlChar*) "href", (xmlChar*) sheeturi);
1509 xmlNewProp (link, (xmlChar*) "rel", (xmlChar*) "alternate stylesheet");
1510 xmlNewProp (link, (xmlChar*) "class", (xmlChar*) "night");
1511
1512 xmlSaveFormatFile (listdata->value, xmldocument, 0);
1513 xml_free_doc();
1514 g_free(sheeturi);
1515 }
1516
1517 static void
epub_document_check_add_night_sheet(EvDocument * document)1518 epub_document_check_add_night_sheet(EvDocument *document)
1519 {
1520 EpubDocument *epub_document = EPUB_DOCUMENT(document);
1521
1522 g_return_if_fail(EPUB_IS_DOCUMENT(epub_document));
1523
1524 /*
1525 * We'll only check the first page for a supplied night mode stylesheet.
1526 * Odds are, if this one has it, all others have it too.
1527 */
1528 contentListNode *node = epub_document->contentList->data;
1529 gchar* stylesheetfilename = epub_document_get_alternate_stylesheet((gchar*)node->value) ;
1530
1531 if (stylesheetfilename == NULL) {
1532 gchar *style = "body {color:rgb(255,255,255);\
1533 background-color:rgb(0,0,0);\
1534 text-align:justify;\
1535 line-spacing:1.8;\
1536 margin-top:0px;\
1537 margin-bottom:4px;\
1538 margin-right:50px;\
1539 margin-left:50px;\
1540 text-indent:3em;}\
1541 h1, h2, h3, h4, h5, h6\
1542 {color:white;\
1543 text-align:center;\
1544 font-style:italic;\
1545 font-weight:bold;}";
1546
1547 gchar *csspath = g_strdup_printf("%s/atrilnightstyle.css",epub_document->documentdir);
1548
1549
1550 GFile *styles = g_file_new_for_path (csspath);
1551 GOutputStream *outstream = (GOutputStream*)g_file_create(styles,G_FILE_CREATE_PRIVATE,NULL,NULL);
1552 if ( g_output_stream_write((GOutputStream*)outstream,style,strlen(style),NULL,NULL) == -1 )
1553 {
1554 return ;
1555 }
1556 g_output_stream_close((GOutputStream*)outstream,NULL,NULL);
1557 g_object_unref(styles) ;
1558 g_object_unref(outstream) ;
1559 //add this stylesheet to each document, for later.
1560 g_list_foreach(epub_document->contentList,(GFunc)add_night_sheet,csspath);
1561 g_free(csspath);
1562 }
1563 g_free(stylesheetfilename);
1564 }
1565
1566 static void
epub_document_toggle_night_mode(EvDocument * document,gboolean night)1567 epub_document_toggle_night_mode(EvDocument *document,gboolean night)
1568 {
1569 EpubDocument *epub_document = EPUB_DOCUMENT(document);
1570
1571 g_return_if_fail(EPUB_IS_DOCUMENT(epub_document));
1572 if (night)
1573 g_list_foreach(epub_document->contentList,(GFunc)change_to_night_sheet,NULL);
1574 else
1575 g_list_foreach(epub_document->contentList,(GFunc)change_to_day_sheet,NULL);
1576 }
1577
1578 static gchar*
epub_document_set_document_title(gchar * containeruri)1579 epub_document_set_document_title(gchar *containeruri)
1580 {
1581 open_xml_document(containeruri);
1582 gchar *doctitle;
1583 set_xml_root_node(NULL);
1584
1585 xmlNodePtr title = xml_get_pointer_to_node((xmlChar*)"title",NULL,NULL);
1586
1587 doctitle = (gchar*)xml_get_data_from_node(title, XML_KEYWORD, NULL);
1588 xml_free_doc();
1589
1590 return doctitle;
1591 }
1592
1593 static void
page_set_function(linknode * Link,GList * contentList)1594 page_set_function(linknode *Link, GList *contentList)
1595 {
1596 GList *listiter = contentList;
1597 contentListNode *pagedata;
1598
1599 guint flag=0;
1600 while (!flag) {
1601 pagedata = listiter->data;
1602 if (link_present_on_page(Link->pagelink, pagedata->value)) {
1603 flag=1;
1604 Link->page = pagedata->index - 1;
1605 }
1606 listiter = listiter->next;
1607 }
1608
1609 if (Link->children) {
1610 g_list_foreach(Link->children,(GFunc)page_set_function,contentList);
1611 }
1612 }
1613
1614 static void
epub_document_set_index_pages(GList * index,GList * contentList)1615 epub_document_set_index_pages(GList *index,GList *contentList)
1616 {
1617 g_return_if_fail (index != NULL);
1618 g_return_if_fail (contentList != NULL);
1619
1620 g_list_foreach(index,(GFunc)page_set_function,contentList);
1621 }
1622
1623
1624 static void
add_mathjax_script_node_to_file(gchar * filename,gchar * data)1625 add_mathjax_script_node_to_file(gchar *filename, gchar *data)
1626 {
1627 xmlDocPtr mathdocument = xmlParseFile (filename);
1628 xmlNodePtr mathroot = xmlDocGetRootElement(mathdocument);
1629
1630 if (mathroot == NULL)
1631 return;
1632
1633 xmlNodePtr head = mathroot->children;
1634
1635 while(head != NULL) {
1636 if (!xmlStrcmp(head->name,(xmlChar*)"head")) {
1637 break;
1638 }
1639 head = head->next;
1640 }
1641
1642 if (xmlStrcmp(head->name,(xmlChar*)"head")) {
1643 return ;
1644 }
1645
1646 xmlNodePtr script = xmlNewTextChild (head,NULL,(xmlChar*)"script",(xmlChar*)"");
1647 xmlNewProp(script,(xmlChar*)"type",(xmlChar*)"text/javascript");
1648 xmlNewProp(script,(xmlChar*)"src",(xmlChar*)data);
1649
1650 xmlSaveFormatFile(filename, mathdocument, 0);
1651 xmlFreeDoc (mathdocument);
1652 }
1653
1654 static void
epub_document_add_mathJax(gchar * containeruri,gchar * documentdir)1655 epub_document_add_mathJax(gchar* containeruri,gchar* documentdir)
1656 {
1657 gchar *containerfilename= g_filename_from_uri(containeruri,NULL,NULL);
1658 GString *mathjaxdir = g_string_new("/usr/share/javascript/mathjax");
1659
1660 gchar *mathjaxref = g_filename_to_uri(mathjaxdir->str,NULL,NULL);
1661 gchar *nodedata = g_strdup_printf("%s/MathJax.js?config=TeX-AMS-MML_SVG",mathjaxref);
1662
1663 open_xml_document(containerfilename);
1664 set_xml_root_node(NULL);
1665 xmlNodePtr manifest = xml_get_pointer_to_node((xmlChar*)"manifest",NULL,NULL);
1666
1667 xmlNodePtr item = manifest->xmlChildrenNode;
1668
1669 while (item != NULL) {
1670 if (xmlStrcmp(item->name,(xmlChar*)"item")) {
1671 item = item->next;
1672 continue;
1673 }
1674
1675 xmlChar *mathml = xml_get_data_from_node(item,XML_ATTRIBUTE, (xmlChar*)"properties");
1676
1677 if (mathml != NULL &&
1678 !xmlStrcmp(mathml, (xmlChar*)"mathml") ) {
1679 gchar *href = (gchar*)xml_get_data_from_node(item, XML_ATTRIBUTE, (xmlChar*)"href");
1680 gchar *filename = g_strdup_printf("%s/%s",documentdir,href);
1681
1682 add_mathjax_script_node_to_file(filename,nodedata);
1683 g_free(href);
1684 g_free(filename);
1685 }
1686 g_free(mathml);
1687 item = item->next;
1688 }
1689 xml_free_doc();
1690 g_free(mathjaxref);
1691 g_free(containerfilename);
1692 g_free(nodedata);
1693 g_string_free(mathjaxdir,TRUE);
1694 }
1695
1696 static gboolean
epub_document_load(EvDocument * document,const char * uri,GError ** error)1697 epub_document_load (EvDocument* document,
1698 const char* uri,
1699 GError** error)
1700 {
1701 EpubDocument *epub_document = EPUB_DOCUMENT(document);
1702 GError *err = NULL;
1703
1704 if ( check_mime_type (uri, &err) == FALSE )
1705 {
1706 /*Error would've been set by the function*/
1707 g_propagate_error(error,err);
1708 return FALSE;
1709 }
1710
1711 extract_epub_from_container (uri,epub_document,&err);
1712
1713 if ( err )
1714 {
1715 g_propagate_error( error,err );
1716 return FALSE;
1717 }
1718
1719 /*FIXME : can this be different, ever?*/
1720 GString *containerpath = g_string_new(epub_document->tmp_archive_dir);
1721 g_string_append_printf(containerpath,"/META-INF/container.xml");
1722 gchar *containeruri = g_filename_to_uri(containerpath->str,NULL,&err);
1723 g_string_free (containerpath, TRUE);
1724
1725 if ( err )
1726 {
1727 g_propagate_error(error,err);
1728 return FALSE;
1729 }
1730
1731 gchar *contentOpfUri = get_uri_to_content (containeruri,&err,epub_document);
1732 g_free (containeruri);
1733
1734 if ( contentOpfUri == NULL )
1735 {
1736 g_propagate_error(error,err);
1737 return FALSE;
1738 }
1739
1740 epub_document->docTitle = epub_document_set_document_title(contentOpfUri);
1741 epub_document->index = setup_document_index(epub_document,contentOpfUri);
1742
1743 epub_document->contentList = setup_document_content_list (contentOpfUri,&err,epub_document->documentdir);
1744
1745 if (epub_document->index != NULL && epub_document->contentList != NULL)
1746 epub_document_set_index_pages(epub_document->index, epub_document->contentList);
1747
1748 epub_document_add_mathJax(contentOpfUri,epub_document->documentdir);
1749 g_free (contentOpfUri);
1750
1751 if ( epub_document->contentList == NULL )
1752 {
1753 g_propagate_error(error,err);
1754 return FALSE;
1755 }
1756
1757 return TRUE;
1758 }
1759
1760 static void
epub_document_init(EpubDocument * epub_document)1761 epub_document_init (EpubDocument *epub_document)
1762 {
1763 epub_document->archivename = NULL ;
1764 epub_document->tmp_archive_dir = NULL ;
1765 epub_document->contentList = NULL ;
1766 epub_document->documentdir = NULL;
1767 epub_document->index = NULL;
1768 epub_document->docTitle = NULL;
1769 }
1770
1771
1772 static void
epub_document_finalize(GObject * object)1773 epub_document_finalize (GObject *object)
1774 {
1775 EpubDocument *epub_document = EPUB_DOCUMENT (object);
1776
1777 if (epub_document->epubDocument != NULL) {
1778 if (epub_remove_temporary_dir (epub_document->tmp_archive_dir) == -1)
1779 g_warning (_("There was an error deleting “%s”."),
1780 epub_document->tmp_archive_dir);
1781 }
1782
1783 if ( epub_document->contentList ) {
1784 g_list_free_full(epub_document->contentList,(GDestroyNotify)free_tree_nodes);
1785 epub_document->contentList = NULL;
1786 }
1787
1788 if (epub_document->index) {
1789 g_list_free_full(epub_document->index,(GDestroyNotify)free_link_nodes);
1790 epub_document->index = NULL;
1791 }
1792
1793 if ( epub_document->tmp_archive_dir) {
1794 g_free (epub_document->tmp_archive_dir);
1795 epub_document->tmp_archive_dir = NULL;
1796 }
1797
1798 if (epub_document->docTitle) {
1799 g_free(epub_document->docTitle);
1800 epub_document->docTitle = NULL;
1801 }
1802 if ( epub_document->archivename) {
1803 g_free (epub_document->archivename);
1804 epub_document->archivename = NULL;
1805 }
1806 if ( epub_document->documentdir) {
1807 g_free (epub_document->documentdir);
1808 epub_document->documentdir = NULL;
1809 }
1810 G_OBJECT_CLASS (epub_document_parent_class)->finalize (object);
1811 }
1812
1813
1814 static void
epub_document_class_init(EpubDocumentClass * klass)1815 epub_document_class_init (EpubDocumentClass *klass)
1816 {
1817 GObjectClass *gobject_class = G_OBJECT_CLASS (klass);
1818 EvDocumentClass *ev_document_class = EV_DOCUMENT_CLASS (klass);
1819
1820 gobject_class->finalize = epub_document_finalize;
1821 ev_document_class->load = epub_document_load;
1822 ev_document_class->save = epub_document_save;
1823 ev_document_class->get_n_pages = epub_document_get_n_pages;
1824 ev_document_class->get_info = epub_document_get_info;
1825 ev_document_class->get_page = epub_document_get_page;
1826 ev_document_class->toggle_night_mode = epub_document_toggle_night_mode;
1827 ev_document_class->check_add_night_sheet = epub_document_check_add_night_sheet;
1828 }
1829