]> www.fi.muni.cz Git - evince.git/blob - pdf/xpdf/Catalog.cc
Fix for a number of integer overflow bugs discovered by Chris Evans.
[evince.git] / pdf / xpdf / Catalog.cc
1 //========================================================================
2 //
3 // Catalog.cc
4 //
5 // Copyright 1996-2003 Glyph & Cog, LLC
6 //
7 //========================================================================
8
9 #include <aconf.h>
10
11 #ifdef USE_GCC_PRAGMAS
12 #pragma implementation
13 #endif
14
15 #include <stddef.h>
16 #include "gmem.h"
17 #include "Object.h"
18 #include "XRef.h"
19 #include "Array.h"
20 #include "Dict.h"
21 #include "Page.h"
22 #include "Error.h"
23 #include "Link.h"
24 #include "Catalog.h"
25
26 //------------------------------------------------------------------------
27 // Catalog
28 //------------------------------------------------------------------------
29
30 Catalog::Catalog(XRef *xrefA) {
31   Object catDict, pagesDict;
32   Object obj, obj2;
33   int numPages0;
34   int i;
35
36   ok = gTrue;
37   xref = xrefA;
38   pages = NULL;
39   pageRefs = NULL;
40   numPages = pagesSize = 0;
41   baseURI = NULL;
42
43   xref->getCatalog(&catDict);
44   if (!catDict.isDict()) {
45     error(-1, "Catalog object is wrong type (%s)", catDict.getTypeName());
46     goto err1;
47   }
48
49   // read page tree
50   catDict.dictLookup("Pages", &pagesDict);
51   // This should really be isDict("Pages"), but I've seen at least one
52   // PDF file where the /Type entry is missing.
53   if (!pagesDict.isDict()) {
54     error(-1, "Top-level pages object is wrong type (%s)",
55           pagesDict.getTypeName());
56     goto err2;
57   }
58   pagesDict.dictLookup("Count", &obj);
59   // some PDF files actually use real numbers here ("/Count 9.0")
60   if (!obj.isNum()) {
61     error(-1, "Page count in top-level pages object is wrong type (%s)",
62           obj.getTypeName());
63     goto err3;
64   }
65   pagesSize = numPages0 = (int)obj.getNum();
66   obj.free();
67   // The gcc doesnt optimize this away, so this check is ok,
68   // even if it looks like a pagesSize != pagesSize check
69   if (pagesSize*sizeof(Page *)/sizeof(Page *) != pagesSize ||
70       pagesSize*sizeof(Ref)/sizeof(Ref) != pagesSize) {
71     error(-1, "Invalid 'pagesSize'");
72     ok = gFalse;
73     return;
74   }
75
76   pages = (Page **)gmalloc(pagesSize * sizeof(Page *));
77   pageRefs = (Ref *)gmalloc(pagesSize * sizeof(Ref));
78   for (i = 0; i < pagesSize; ++i) {
79     pages[i] = NULL;
80     pageRefs[i].num = -1;
81     pageRefs[i].gen = -1;
82   }
83   numPages = readPageTree(pagesDict.getDict(), NULL, 0);
84   if (numPages != numPages0) {
85     error(-1, "Page count in top-level pages object is incorrect");
86   }
87   pagesDict.free();
88
89   // read named destination dictionary
90   catDict.dictLookup("Dests", &dests);
91
92   // read root of named destination tree
93   if (catDict.dictLookup("Names", &obj)->isDict())
94     obj.dictLookup("Dests", &nameTree);
95   else
96     nameTree.initNull();
97   obj.free();
98
99   // read base URI
100   if (catDict.dictLookup("URI", &obj)->isDict()) {
101     if (obj.dictLookup("Base", &obj2)->isString()) {
102       baseURI = obj2.getString()->copy();
103     }
104     obj2.free();
105   }
106   obj.free();
107
108   // get the metadata stream
109   catDict.dictLookup("Metadata", &metadata);
110
111   // get the structure tree root
112   catDict.dictLookup("StructTreeRoot", &structTreeRoot);
113
114   // get the outline dictionary
115   catDict.dictLookup("Outlines", &outline);
116
117   catDict.free();
118   return;
119
120  err3:
121   obj.free();
122  err2:
123   pagesDict.free();
124  err1:
125   catDict.free();
126   dests.initNull();
127   nameTree.initNull();
128   ok = gFalse;
129 }
130
131 Catalog::~Catalog() {
132   int i;
133
134   if (pages) {
135     for (i = 0; i < pagesSize; ++i) {
136       if (pages[i]) {
137         delete pages[i];
138       }
139     }
140     gfree(pages);
141     gfree(pageRefs);
142   }
143   dests.free();
144   nameTree.free();
145   if (baseURI) {
146     delete baseURI;
147   }
148   metadata.free();
149   structTreeRoot.free();
150   outline.free();
151 }
152
153 GString *Catalog::readMetadata() {
154   GString *s;
155   Dict *dict;
156   Object obj;
157   int c;
158
159   if (!metadata.isStream()) {
160     return NULL;
161   }
162   dict = metadata.streamGetDict();
163   if (!dict->lookup("Subtype", &obj)->isName("XML")) {
164     error(-1, "Unknown Metadata type: '%s'",
165           obj.isName() ? obj.getName() : "???");
166   }
167   obj.free();
168   s = new GString();
169   metadata.streamReset();
170   while ((c = metadata.streamGetChar()) != EOF) {
171     s->append(c);
172   }
173   metadata.streamClose();
174   return s;
175 }
176
177 int Catalog::readPageTree(Dict *pagesDict, PageAttrs *attrs, int start) {
178   Object kids;
179   Object kid;
180   Object kidRef;
181   PageAttrs *attrs1, *attrs2;
182   Page *page;
183   int i, j;
184
185   attrs1 = new PageAttrs(attrs, pagesDict);
186   pagesDict->lookup("Kids", &kids);
187   if (!kids.isArray()) {
188     error(-1, "Kids object (page %d) is wrong type (%s)",
189           start+1, kids.getTypeName());
190     goto err1;
191   }
192   for (i = 0; i < kids.arrayGetLength(); ++i) {
193     kids.arrayGet(i, &kid);
194     if (kid.isDict("Page")) {
195       attrs2 = new PageAttrs(attrs1, kid.getDict());
196       page = new Page(xref, start+1, kid.getDict(), attrs2);
197       if (!page->isOk()) {
198         ++start;
199         goto err3;
200       }
201       if (start >= pagesSize) {
202         pagesSize += 32;
203         if (pagesSize*sizeof(Page *)/sizeof(Page *) != pagesSize ||
204             pagesSize*sizeof(Ref)/sizeof(Ref) != pagesSize) {
205           error(-1, "Invalid 'pagesSize' parameter.");
206           goto err3;
207         }
208         pages = (Page **)grealloc(pages, pagesSize * sizeof(Page *));
209         pageRefs = (Ref *)grealloc(pageRefs, pagesSize * sizeof(Ref));
210         for (j = pagesSize - 32; j < pagesSize; ++j) {
211           pages[j] = NULL;
212           pageRefs[j].num = -1;
213           pageRefs[j].gen = -1;
214         }
215       }
216       pages[start] = page;
217       kids.arrayGetNF(i, &kidRef);
218       if (kidRef.isRef()) {
219         pageRefs[start].num = kidRef.getRefNum();
220         pageRefs[start].gen = kidRef.getRefGen();
221       }
222       kidRef.free();
223       ++start;
224     // This should really be isDict("Pages"), but I've seen at least one
225     // PDF file where the /Type entry is missing.
226     } else if (kid.isDict()) {
227       if ((start = readPageTree(kid.getDict(), attrs1, start))
228           < 0)
229         goto err2;
230     } else {
231       error(-1, "Kid object (page %d) is wrong type (%s)",
232             start+1, kid.getTypeName());
233       goto err2;
234     }
235     kid.free();
236   }
237   delete attrs1;
238   kids.free();
239   return start;
240
241  err3:
242   delete page;
243  err2:
244   kid.free();
245  err1:
246   kids.free();
247   delete attrs1;
248   ok = gFalse;
249   return -1;
250 }
251
252 int Catalog::findPage(int num, int gen) {
253   int i;
254
255   for (i = 0; i < numPages; ++i) {
256     if (pageRefs[i].num == num && pageRefs[i].gen == gen)
257       return i + 1;
258   }
259   return 0;
260 }
261
262 LinkDest *Catalog::findDest(GString *name) {
263   LinkDest *dest;
264   Object obj1, obj2;
265   GBool found;
266
267   // try named destination dictionary then name tree
268   found = gFalse;
269   if (dests.isDict()) {
270     if (!dests.dictLookup(name->getCString(), &obj1)->isNull())
271       found = gTrue;
272     else
273       obj1.free();
274   }
275   if (!found && nameTree.isDict()) {
276     if (!findDestInTree(&nameTree, name, &obj1)->isNull())
277       found = gTrue;
278     else
279       obj1.free();
280   }
281   if (!found)
282     return NULL;
283
284   // construct LinkDest
285   dest = NULL;
286   if (obj1.isArray()) {
287     dest = new LinkDest(obj1.getArray());
288   } else if (obj1.isDict()) {
289     if (obj1.dictLookup("D", &obj2)->isArray())
290       dest = new LinkDest(obj2.getArray());
291     else
292       error(-1, "Bad named destination value");
293     obj2.free();
294   } else {
295     error(-1, "Bad named destination value");
296   }
297   obj1.free();
298   if (dest && !dest->isOk()) {
299     delete dest;
300     dest = NULL;
301   }
302
303   return dest;
304 }
305
306 Object *Catalog::findDestInTree(Object *tree, GString *name, Object *obj) {
307   Object names, name1;
308   Object kids, kid, limits, low, high;
309   GBool done, found;
310   int cmp, i;
311
312   // leaf node
313   if (tree->dictLookup("Names", &names)->isArray()) {
314     done = found = gFalse;
315     for (i = 0; !done && i < names.arrayGetLength(); i += 2) {
316       if (names.arrayGet(i, &name1)->isString()) {
317         cmp = name->cmp(name1.getString());
318         if (cmp == 0) {
319           names.arrayGet(i+1, obj);
320           found = gTrue;
321           done = gTrue;
322         } else if (cmp < 0) {
323           done = gTrue;
324         }
325       }
326       name1.free();
327     }
328     names.free();
329     if (!found)
330       obj->initNull();
331     return obj;
332   }
333   names.free();
334
335   // root or intermediate node
336   done = gFalse;
337   if (tree->dictLookup("Kids", &kids)->isArray()) {
338     for (i = 0; !done && i < kids.arrayGetLength(); ++i) {
339       if (kids.arrayGet(i, &kid)->isDict()) {
340         if (kid.dictLookup("Limits", &limits)->isArray()) {
341           if (limits.arrayGet(0, &low)->isString() &&
342               name->cmp(low.getString()) >= 0) {
343             if (limits.arrayGet(1, &high)->isString() &&
344                 name->cmp(high.getString()) <= 0) {
345               findDestInTree(&kid, name, obj);
346               done = gTrue;
347             }
348             high.free();
349           }
350           low.free();
351         }
352         limits.free();
353       }
354       kid.free();
355     }
356   }
357   kids.free();
358
359   // name was outside of ranges of all kids
360   if (!done)
361     obj->initNull();
362
363   return obj;
364 }