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