]> www.fi.muni.cz Git - evince.git/blob - pdf/xpdf/XRef.cc
Fix for a number of integer overflow bugs discovered by Chris Evans.
[evince.git] / pdf / xpdf / XRef.cc
1 //========================================================================
2 //
3 // XRef.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 <stdlib.h>
16 #include <stddef.h>
17 #include <string.h>
18 #include <ctype.h>
19 #include "gmem.h"
20 #include "Object.h"
21 #include "Stream.h"
22 #include "Lexer.h"
23 #include "Parser.h"
24 #include "Dict.h"
25 #ifndef NO_DECRYPTION
26 #include "Decrypt.h"
27 #endif
28 #include "Error.h"
29 #include "ErrorCodes.h"
30 #include "XRef.h"
31
32 //------------------------------------------------------------------------
33
34 #define xrefSearchSize 1024     // read this many bytes at end of file
35                                 //   to look for 'startxref'
36
37 #ifndef NO_DECRYPTION
38 //------------------------------------------------------------------------
39 // Permission bits
40 //------------------------------------------------------------------------
41
42 #define permPrint    (1<<2)
43 #define permChange   (1<<3)
44 #define permCopy     (1<<4)
45 #define permNotes    (1<<5)
46 #define defPermFlags 0xfffc
47 #endif
48
49 //------------------------------------------------------------------------
50 // ObjectStream
51 //------------------------------------------------------------------------
52
53 class ObjectStream {
54 public:
55
56   // Create an object stream, using object number <objStrNum>,
57   // generation 0.
58   ObjectStream(XRef *xref, int objStrNumA);
59
60   ~ObjectStream();
61
62   // Return the object number of this object stream.
63   int getObjStrNum() { return objStrNum; }
64
65   // Get the <objIdx>th object from this stream, which should be
66   // object number <objNum>, generation 0.
67   Object *getObject(int objIdx, int objNum, Object *obj);
68
69 private:
70
71   int objStrNum;                // object number of the object stream
72   int nObjects;                 // number of objects in the stream
73   Object *objs;                 // the objects (length = nObjects)
74   int *objNums;                 // the object numbers (length = nObjects)
75 };
76
77 ObjectStream::ObjectStream(XRef *xref, int objStrNumA) {
78   Stream *str;
79   Parser *parser;
80   int *offsets;
81   Object objStr, obj1, obj2;
82   int first, i;
83
84   objStrNum = objStrNumA;
85   nObjects = 0;
86   objs = NULL;
87   objNums = NULL;
88
89   if (!xref->fetch(objStrNum, 0, &objStr)->isStream()) {
90     goto err1;
91   }
92
93   if (!objStr.streamGetDict()->lookup("N", &obj1)->isInt()) {
94     obj1.free();
95     goto err1;
96   }
97   nObjects = obj1.getInt();
98   obj1.free();
99   if (nObjects <= 0) {
100     goto err1;
101   }
102
103   if (!objStr.streamGetDict()->lookup("First", &obj1)->isInt()) {
104     obj1.free();
105     goto err1;
106   }
107   first = obj1.getInt();
108   obj1.free();
109   if (first < 0) {
110     goto err1;
111   }
112
113   if (nObjects*sizeof(int)/sizeof(int) != nObjects) {
114     error(-1, "Invalid 'nObjects'");
115     goto err1;
116   }
117  
118   objs = new Object[nObjects];
119   objNums = (int *)gmalloc(nObjects * sizeof(int));
120   offsets = (int *)gmalloc(nObjects * sizeof(int));
121
122   // parse the header: object numbers and offsets
123   objStr.streamReset();
124   obj1.initNull();
125   str = new EmbedStream(objStr.getStream(), &obj1, gTrue, first);
126   parser = new Parser(xref, new Lexer(xref, str));
127   for (i = 0; i < nObjects; ++i) {
128     parser->getObj(&obj1);
129     parser->getObj(&obj2);
130     if (!obj1.isInt() || !obj2.isInt()) {
131       obj1.free();
132       obj2.free();
133       delete parser;
134       gfree(offsets);
135       goto err1;
136     }
137     objNums[i] = obj1.getInt();
138     offsets[i] = obj2.getInt();
139     obj1.free();
140     obj2.free();
141     if (objNums[i] < 0 || offsets[i] < 0 ||
142         (i > 0 && offsets[i] < offsets[i-1])) {
143       delete parser;
144       gfree(offsets);
145       goto err1;
146     }
147   }
148   while (str->getChar() != EOF) ;
149   delete parser;
150
151   // skip to the first object - this shouldn't be necessary because
152   // the First key is supposed to be equal to offsets[0], but just in
153   // case...
154   for (i = first; i < offsets[0]; ++i) {
155     objStr.getStream()->getChar();
156   }
157
158   // parse the objects
159   for (i = 0; i < nObjects; ++i) {
160     obj1.initNull();
161     if (i == nObjects - 1) {
162       str = new EmbedStream(objStr.getStream(), &obj1, gFalse, 0);
163     } else {
164       str = new EmbedStream(objStr.getStream(), &obj1, gTrue,
165                             offsets[i+1] - offsets[i]);
166     }
167     parser = new Parser(xref, new Lexer(xref, str));
168     parser->getObj(&objs[i]);
169     while (str->getChar() != EOF) ;
170     delete parser;
171   }
172
173   gfree(offsets);
174
175  err1:
176   objStr.free();
177   return;
178 }
179
180 ObjectStream::~ObjectStream() {
181   int i;
182
183   if (objs) {
184     for (i = 0; i < nObjects; ++i) {
185       objs[i].free();
186     }
187     delete[] objs;
188   }
189   gfree(objNums);
190 }
191
192 Object *ObjectStream::getObject(int objIdx, int objNum, Object *obj) {
193   if (objIdx < 0 || objIdx >= nObjects || objNum != objNums[objIdx]) {
194     return obj->initNull();
195   }
196   return objs[objIdx].copy(obj);
197 }
198
199 //------------------------------------------------------------------------
200 // XRef
201 //------------------------------------------------------------------------
202
203 XRef::XRef(BaseStream *strA, GString *ownerPassword, GString *userPassword) {
204   Guint pos;
205   Object obj;
206
207   ok = gTrue;
208   errCode = errNone;
209   size = 0;
210   entries = NULL;
211   streamEnds = NULL;
212   streamEndsLen = 0;
213   objStr = NULL;
214
215   // read the trailer
216   str = strA;
217   start = str->getStart();
218   pos = getStartXref();
219
220   // if there was a problem with the 'startxref' position, try to
221   // reconstruct the xref table
222   if (pos == 0) {
223     if (!(ok = constructXRef())) {
224       errCode = errDamaged;
225       return;
226     }
227
228   // read the xref table
229   } else {
230     while (readXRef(&pos)) ;
231
232     // if there was a problem with the xref table,
233     // try to reconstruct it
234     if (!ok) {
235       if (!(ok = constructXRef())) {
236         errCode = errDamaged;
237         return;
238       }
239     }
240   }
241
242   // get the root dictionary (catalog) object
243   trailerDict.dictLookupNF("Root", &obj);
244   if (obj.isRef()) {
245     rootNum = obj.getRefNum();
246     rootGen = obj.getRefGen();
247     obj.free();
248   } else {
249     obj.free();
250     if (!(ok = constructXRef())) {
251       errCode = errDamaged;
252       return;
253     }
254   }
255
256   // now set the trailer dictionary's xref pointer so we can fetch
257   // indirect objects from it
258   trailerDict.getDict()->setXRef(this);
259
260   // check for encryption
261 #ifndef NO_DECRYPTION
262   encrypted = gFalse;
263 #endif
264   if (checkEncrypted(ownerPassword, userPassword)) {
265     ok = gFalse;
266     errCode = errEncrypted;
267     return;
268   }
269 }
270
271 XRef::~XRef() {
272   gfree(entries);
273   trailerDict.free();
274   if (streamEnds) {
275     gfree(streamEnds);
276   }
277   if (objStr) {
278     delete objStr;
279   }
280 }
281
282 // Read the 'startxref' position.
283 Guint XRef::getStartXref() {
284   char buf[xrefSearchSize+1];
285   char *p;
286   int c, n, i;
287
288   // read last xrefSearchSize bytes
289   str->setPos(xrefSearchSize, -1);
290   for (n = 0; n < xrefSearchSize; ++n) {
291     if ((c = str->getChar()) == EOF) {
292       break;
293     }
294     buf[n] = c;
295   }
296   buf[n] = '\0';
297
298   // find startxref
299   for (i = n - 9; i >= 0; --i) {
300     if (!strncmp(&buf[i], "startxref", 9)) {
301       break;
302     }
303   }
304   if (i < 0) {
305     return 0;
306   }
307   for (p = &buf[i+9]; isspace(*p); ++p) ;
308   lastXRefPos = strToUnsigned(p);
309
310   return lastXRefPos;
311 }
312
313 // Read one xref table section.  Also reads the associated trailer
314 // dictionary, and returns the prev pointer (if any).
315 GBool XRef::readXRef(Guint *pos) {
316   Parser *parser;
317   Object obj;
318   GBool more;
319
320   // start up a parser, parse one token
321   obj.initNull();
322   parser = new Parser(NULL,
323              new Lexer(NULL,
324                str->makeSubStream(start + *pos, gFalse, 0, &obj)));
325   parser->getObj(&obj);
326
327   // parse an old-style xref table
328   if (obj.isCmd("xref")) {
329     obj.free();
330     more = readXRefTable(parser, pos);
331
332   // parse an xref stream
333   } else if (obj.isInt()) {
334     obj.free();
335     if (!parser->getObj(&obj)->isInt()) {
336       goto err1;
337     }
338     obj.free();
339     if (!parser->getObj(&obj)->isCmd("obj")) {
340       goto err1;
341     }
342     obj.free();
343     if (!parser->getObj(&obj)->isStream()) {
344       goto err1;
345     }
346     more = readXRefStream(obj.getStream(), pos);
347     obj.free();
348
349   } else {
350     goto err1;
351   }
352
353   delete parser;
354   return more;
355
356  err1:
357   obj.free();
358   delete parser;
359   ok = gFalse;
360   return gFalse;
361 }
362
363 GBool XRef::readXRefTable(Parser *parser, Guint *pos) {
364   XRefEntry entry;
365   GBool more;
366   Object obj, obj2;
367   Guint pos2;
368   int first, n, newSize, i;
369
370   while (1) {
371     parser->getObj(&obj);
372     if (obj.isCmd("trailer")) {
373       obj.free();
374       break;
375     }
376     if (!obj.isInt()) {
377       goto err1;
378     }
379     first = obj.getInt();
380     obj.free();
381     if (!parser->getObj(&obj)->isInt()) {
382       goto err1;
383     }
384     n = obj.getInt();
385     obj.free();
386     if (first < 0 || n < 0 || first + n < 0) {
387       goto err1;
388     }
389     if (first + n > size) {
390       for (newSize = size ? 2 * size : 1024;
391            first + n > newSize && newSize > 0;
392            newSize <<= 1) ;
393       if (newSize < 0) {
394         goto err1;
395       }
396       if (newSize*sizeof(XRefEntry)/sizeof(XRefEntry) != newSize) {
397         error(-1, "Invalid 'obj' parameters'");
398         goto err1;
399       }
400  
401       entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
402       for (i = size; i < newSize; ++i) {
403         entries[i].offset = 0xffffffff;
404         entries[i].type = xrefEntryFree;
405       }
406       size = newSize;
407     }
408     for (i = first; i < first + n; ++i) {
409       if (!parser->getObj(&obj)->isInt()) {
410         goto err1;
411       }
412       entry.offset = (Guint)obj.getInt();
413       obj.free();
414       if (!parser->getObj(&obj)->isInt()) {
415         goto err1;
416       }
417       entry.gen = obj.getInt();
418       obj.free();
419       parser->getObj(&obj);
420       if (obj.isCmd("n")) {
421         entry.type = xrefEntryUncompressed;
422       } else if (obj.isCmd("f")) {
423         entry.type = xrefEntryFree;
424       } else {
425         goto err1;
426       }
427       obj.free();
428       if (entries[i].offset == 0xffffffff) {
429         entries[i] = entry;
430         // PDF files of patents from the IBM Intellectual Property
431         // Network have a bug: the xref table claims to start at 1
432         // instead of 0.
433         if (i == 1 && first == 1 &&
434             entries[1].offset == 0 && entries[1].gen == 65535 &&
435             entries[1].type == xrefEntryFree) {
436           i = first = 0;
437           entries[0] = entries[1];
438           entries[1].offset = 0xffffffff;
439         }
440       }
441     }
442   }
443
444   // read the trailer dictionary
445   if (!parser->getObj(&obj)->isDict()) {
446     goto err1;
447   }
448
449   // get the 'Prev' pointer
450   obj.getDict()->lookupNF("Prev", &obj2);
451   if (obj2.isInt()) {
452     *pos = (Guint)obj2.getInt();
453     more = gTrue;
454   } else if (obj2.isRef()) {
455     // certain buggy PDF generators generate "/Prev NNN 0 R" instead
456     // of "/Prev NNN"
457     *pos = (Guint)obj2.getRefNum();
458     more = gTrue;
459   } else {
460     more = gFalse;
461   }
462   obj2.free();
463
464   // save the first trailer dictionary
465   if (trailerDict.isNone()) {
466     obj.copy(&trailerDict);
467   }
468
469   // check for an 'XRefStm' key
470   if (obj.getDict()->lookup("XRefStm", &obj2)->isInt()) {
471     pos2 = (Guint)obj2.getInt();
472     readXRef(&pos2);
473     if (!ok) {
474       goto err1;
475     }
476   }
477   obj2.free();
478
479   obj.free();
480   return more;
481
482  err1:
483   obj.free();
484   ok = gFalse;
485   return gFalse;
486 }
487
488 GBool XRef::readXRefStream(Stream *xrefStr, Guint *pos) {
489   Dict *dict;
490   int w[3];
491   GBool more;
492   Object obj, obj2, idx;
493   int newSize, first, n, i;
494
495   dict = xrefStr->getDict();
496
497   if (!dict->lookupNF("Size", &obj)->isInt()) {
498     goto err1;
499   }
500   newSize = obj.getInt();
501   obj.free();
502   if (newSize < 0) {
503     goto err1;
504   }
505   if (newSize > size) {
506     if (newSize * sizeof(XRefEntry)/sizeof(XRefEntry) != newSize) {
507       error(-1, "Invalid 'size' parameter.");
508       return gFalse;
509     }
510     entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
511     for (i = size; i < newSize; ++i) {
512       entries[i].offset = 0xffffffff;
513       entries[i].type = xrefEntryFree;
514     }
515     size = newSize;
516   }
517
518   if (!dict->lookupNF("W", &obj)->isArray() ||
519       obj.arrayGetLength() < 3) {
520     goto err1;
521   }
522   for (i = 0; i < 3; ++i) {
523     if (!obj.arrayGet(i, &obj2)->isInt()) {
524       obj2.free();
525       goto err1;
526     }
527     w[i] = obj2.getInt();
528     obj2.free();
529     if (w[i] < 0 || w[i] > 4) {
530       goto err1;
531     }
532   }
533   obj.free();
534
535   xrefStr->reset();
536   dict->lookupNF("Index", &idx);
537   if (idx.isArray()) {
538     for (i = 0; i+1 < idx.arrayGetLength(); i += 2) {
539       if (!idx.arrayGet(i, &obj)->isInt()) {
540         idx.free();
541         goto err1;
542       }
543       first = obj.getInt();
544       obj.free();
545       if (!idx.arrayGet(i+1, &obj)->isInt()) {
546         idx.free();
547         goto err1;
548       }
549       n = obj.getInt();
550       obj.free();
551       if (first < 0 || n < 0 ||
552           !readXRefStreamSection(xrefStr, w, first, n)) {
553         idx.free();
554         goto err0;
555       }
556     }
557   } else {
558     if (!readXRefStreamSection(xrefStr, w, 0, newSize)) {
559       idx.free();
560       goto err0;
561     }
562   }
563   idx.free();
564
565   dict->lookupNF("Prev", &obj);
566   if (obj.isInt()) {
567     *pos = (Guint)obj.getInt();
568     more = gTrue;
569   } else {
570     more = gFalse;
571   }
572   obj.free();
573   if (trailerDict.isNone()) {
574     trailerDict.initDict(dict);
575   }
576
577   return more;
578
579  err1:
580   obj.free();
581  err0:
582   ok = gFalse;
583   return gFalse;
584 }
585
586 GBool XRef::readXRefStreamSection(Stream *xrefStr, int *w, int first, int n) {
587   Guint offset;
588   int type, gen, c, newSize, i, j;
589
590   if (first + n < 0) {
591     return gFalse;
592   }
593   if (first + n > size) {
594     for (newSize = size ? 2 * size : 1024;
595          first + n > newSize && newSize > 0;
596          newSize <<= 1) ;
597     if (newSize < 0) {
598       return gFalse;
599     }
600     if (newSize*sizeof(XRefEntry)/sizeof(XRefEntry) != newSize) {
601       error(-1, "Invalid 'size' inside xref table.");
602       return gFalse;
603     }
604     entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
605     for (i = size; i < newSize; ++i) {
606       entries[i].offset = 0xffffffff;
607       entries[i].type = xrefEntryFree;
608     }
609     size = newSize;
610   }
611   for (i = first; i < first + n; ++i) {
612     if (w[0] == 0) {
613       type = 1;
614     } else {
615       for (type = 0, j = 0; j < w[0]; ++j) {
616         if ((c = xrefStr->getChar()) == EOF) {
617           return gFalse;
618         }
619         type = (type << 8) + c;
620       }
621     }
622     for (offset = 0, j = 0; j < w[1]; ++j) {
623       if ((c = xrefStr->getChar()) == EOF) {
624         return gFalse;
625       }
626       offset = (offset << 8) + c;
627     }
628     for (gen = 0, j = 0; j < w[2]; ++j) {
629       if ((c = xrefStr->getChar()) == EOF) {
630         return gFalse;
631       }
632       gen = (gen << 8) + c;
633     }
634     if (entries[i].offset == 0xffffffff) {
635       switch (type) {
636       case 0:
637         entries[i].offset = offset;
638         entries[i].gen = gen;
639         entries[i].type = xrefEntryFree;
640         break;
641       case 1:
642         entries[i].offset = offset;
643         entries[i].gen = gen;
644         entries[i].type = xrefEntryUncompressed;
645         break;
646       case 2:
647         entries[i].offset = offset;
648         entries[i].gen = gen;
649         entries[i].type = xrefEntryCompressed;
650         break;
651       default:
652         return gFalse;
653       }
654     }
655   }
656
657   return gTrue;
658 }
659
660 // Attempt to construct an xref table for a damaged file.
661 GBool XRef::constructXRef() {
662   Parser *parser;
663   Object newTrailerDict, obj;
664   char buf[256];
665   Guint pos;
666   int num, gen;
667   int newSize;
668   int streamEndsSize;
669   char *p;
670   int i;
671   GBool gotRoot;
672
673   gfree(entries);
674   size = 0;
675   entries = NULL;
676
677   error(0, "PDF file is damaged - attempting to reconstruct xref table...");
678   gotRoot = gFalse;
679   streamEndsLen = streamEndsSize = 0;
680
681   str->reset();
682   while (1) {
683     pos = str->getPos();
684     if (!str->getLine(buf, 256)) {
685       break;
686     }
687     p = buf;
688
689     // got trailer dictionary
690     if (!strncmp(p, "trailer", 7)) {
691       obj.initNull();
692       parser = new Parser(NULL,
693                  new Lexer(NULL,
694                    str->makeSubStream(start + pos + 7, gFalse, 0, &obj)));
695       parser->getObj(&newTrailerDict);
696       if (newTrailerDict.isDict()) {
697         newTrailerDict.dictLookupNF("Root", &obj);
698         if (obj.isRef()) {
699           rootNum = obj.getRefNum();
700           rootGen = obj.getRefGen();
701           if (!trailerDict.isNone()) {
702             trailerDict.free();
703           }
704           newTrailerDict.copy(&trailerDict);
705           gotRoot = gTrue;
706         }
707         obj.free();
708       }
709       newTrailerDict.free();
710       delete parser;
711
712     // look for object
713     } else if (isdigit(*p)) {
714       num = atoi(p);
715       if (num > 0) {
716         do {
717           ++p;
718         } while (*p && isdigit(*p));
719         if (isspace(*p)) {
720           do {
721             ++p;
722           } while (*p && isspace(*p));
723           if (isdigit(*p)) {
724             gen = atoi(p);
725             do {
726               ++p;
727             } while (*p && isdigit(*p));
728             if (isspace(*p)) {
729               do {
730                 ++p;
731               } while (*p && isspace(*p));
732               if (!strncmp(p, "obj", 3)) {
733                 if (num >= size) {
734                   newSize = (num + 1 + 255) & ~255;
735                   if (newSize < 0) {
736                     error(-1, "Bad object number");
737                     return gFalse;
738                   }
739                   if (newSize*sizeof(XRefEntry)/sizeof(XRefEntry) != newSize) {
740                     error(-1, "Invalid 'obj' parameters.");
741                     return gFalse;
742                   }
743                   entries = (XRefEntry *)
744                       grealloc(entries, newSize * sizeof(XRefEntry));
745                   for (i = size; i < newSize; ++i) {
746                     entries[i].offset = 0xffffffff;
747                     entries[i].type = xrefEntryFree;
748                   }
749                   size = newSize;
750                 }
751                 if (entries[num].type == xrefEntryFree ||
752                     gen >= entries[num].gen) {
753                   entries[num].offset = pos - start;
754                   entries[num].gen = gen;
755                   entries[num].type = xrefEntryUncompressed;
756                 }
757               }
758             }
759           }
760         }
761       }
762
763     } else if (!strncmp(p, "endstream", 9)) {
764       if (streamEndsLen == streamEndsSize) {
765         streamEndsSize += 64;
766         if (streamEndsSize*sizeof(int)/sizeof(int) != streamEndsSize) {
767           error(-1, "Invalid 'endstream' parameter.");
768           return gFalse;
769         }
770         streamEnds = (Guint *)grealloc(streamEnds,
771                                        streamEndsSize * sizeof(int));
772       }
773       streamEnds[streamEndsLen++] = pos;
774     }
775   }
776
777   if (gotRoot)
778     return gTrue;
779
780   error(-1, "Couldn't find trailer dictionary");
781   return gFalse;
782 }
783
784 #ifndef NO_DECRYPTION
785 GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
786   Object encrypt, filterObj, versionObj, revisionObj, lengthObj;
787   Object ownerKey, userKey, permissions, fileID, fileID1;
788   GBool encrypted1;
789   GBool ret;
790
791   keyLength = 0;
792   encVersion = encRevision = 0;
793   ret = gFalse;
794
795   permFlags = defPermFlags;
796   ownerPasswordOk = gFalse;
797   trailerDict.dictLookup("Encrypt", &encrypt);
798   if ((encrypted1 = encrypt.isDict())) {
799     ret = gTrue;
800     encrypt.dictLookup("Filter", &filterObj);
801     if (filterObj.isName("Standard")) {
802       encrypt.dictLookup("V", &versionObj);
803       encrypt.dictLookup("R", &revisionObj);
804       encrypt.dictLookup("Length", &lengthObj);
805       encrypt.dictLookup("O", &ownerKey);
806       encrypt.dictLookup("U", &userKey);
807       encrypt.dictLookup("P", &permissions);
808       trailerDict.dictLookup("ID", &fileID);
809       if (versionObj.isInt() &&
810           revisionObj.isInt() &&
811           ownerKey.isString() && ownerKey.getString()->getLength() == 32 &&
812           userKey.isString() && userKey.getString()->getLength() == 32 &&
813           permissions.isInt() &&
814           fileID.isArray()) {
815         encVersion = versionObj.getInt();
816         encRevision = revisionObj.getInt();
817         if (lengthObj.isInt()) {
818           keyLength = lengthObj.getInt() / 8;
819         } else {
820           keyLength = 5;
821         }
822         permFlags = permissions.getInt();
823         if (encVersion >= 1 && encVersion <= 2 &&
824             encRevision >= 2 && encRevision <= 3) {
825           fileID.arrayGet(0, &fileID1);
826           if (fileID1.isString()) {
827             if (Decrypt::makeFileKey(encVersion, encRevision, keyLength,
828                                      ownerKey.getString(), userKey.getString(),
829                                      permFlags, fileID1.getString(),
830                                      ownerPassword, userPassword, fileKey,
831                                      &ownerPasswordOk)) {
832               if (ownerPassword && !ownerPasswordOk) {
833                 error(-1, "Incorrect owner password");
834               }
835               ret = gFalse;
836             } else {
837               error(-1, "Incorrect password");
838             }
839           } else {
840             error(-1, "Weird encryption info");
841           }
842           fileID1.free();
843         } else {
844           error(-1, "Unsupported version/revision (%d/%d) of Standard security handler",
845                 encVersion, encRevision);
846         }
847       } else {
848         error(-1, "Weird encryption info");
849       }
850       fileID.free();
851       permissions.free();
852       userKey.free();
853       ownerKey.free();
854       lengthObj.free();
855       revisionObj.free();
856       versionObj.free();
857     } else {
858       error(-1, "Unknown security handler '%s'",
859             filterObj.isName() ? filterObj.getName() : "???");
860     }
861     filterObj.free();
862   }
863   encrypt.free();
864
865   // this flag has to be set *after* we read the O/U/P strings
866   encrypted = encrypted1;
867
868   return ret;
869 }
870 #else
871 GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
872   Object obj;
873   GBool encrypted;
874
875   trailerDict.dictLookup("Encrypt", &obj);
876   if ((encrypted = !obj.isNull())) {
877     error(-1, "PDF file is encrypted and this version of the Xpdf tools");
878     error(-1, "was built without decryption support.");
879   }
880   obj.free();
881   return encrypted;
882 }
883 #endif
884
885 GBool XRef::okToPrint(GBool ignoreOwnerPW) {
886 #ifndef NO_DECRYPTION
887   return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permPrint);
888 #else
889   return gTrue;
890 #endif
891 }
892
893 GBool XRef::okToChange(GBool ignoreOwnerPW) {
894 #ifndef NO_DECRYPTION
895   return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permChange);
896 #else
897   return gTrue;
898 #endif
899 }
900
901 GBool XRef::okToCopy(GBool ignoreOwnerPW) {
902 #ifndef NO_DECRYPTION
903   return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permCopy);
904 #else
905   return gTrue;
906 #endif
907 }
908
909 GBool XRef::okToAddNotes(GBool ignoreOwnerPW) {
910 #ifndef NO_DECRYPTION
911   return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permNotes);
912 #else
913   return gTrue;
914 #endif
915 }
916
917 Object *XRef::fetch(int num, int gen, Object *obj) {
918   XRefEntry *e;
919   Parser *parser;
920   Object obj1, obj2, obj3;
921
922   // check for bogus ref - this can happen in corrupted PDF files
923   if (num < 0 || num >= size) {
924     goto err;
925   }
926
927   e = &entries[num];
928   switch (e->type) {
929
930   case xrefEntryUncompressed:
931     if (e->gen != gen) {
932       goto err;
933     }
934     obj1.initNull();
935     parser = new Parser(this,
936                new Lexer(this,
937                  str->makeSubStream(start + e->offset, gFalse, 0, &obj1)));
938     parser->getObj(&obj1);
939     parser->getObj(&obj2);
940     parser->getObj(&obj3);
941     if (!obj1.isInt() || obj1.getInt() != num ||
942         !obj2.isInt() || obj2.getInt() != gen ||
943         !obj3.isCmd("obj")) {
944       goto err;
945     }
946 #ifndef NO_DECRYPTION
947     parser->getObj(obj, encrypted ? fileKey : (Guchar *)NULL, keyLength,
948                    num, gen);
949 #else
950     parser->getObj(obj);
951 #endif
952     obj1.free();
953     obj2.free();
954     obj3.free();
955     delete parser;
956     break;
957
958   case xrefEntryCompressed:
959     if (gen != 0) {
960       goto err;
961     }
962     if (!objStr || objStr->getObjStrNum() != (int)e->offset) {
963       if (objStr) {
964         delete objStr;
965       }
966       objStr = new ObjectStream(this, e->offset);
967     }
968     objStr->getObject(e->gen, num, obj);
969     break;
970
971   default:
972     goto err;
973   }
974
975   return obj;
976
977  err:
978   return obj->initNull();
979 }
980
981 Object *XRef::getDocInfo(Object *obj) {
982   return trailerDict.dictLookup("Info", obj);
983 }
984
985 // Added for the pdftex project.
986 Object *XRef::getDocInfoNF(Object *obj) {
987   return trailerDict.dictLookupNF("Info", obj);
988 }
989
990 GBool XRef::getStreamEnd(Guint streamStart, Guint *streamEnd) {
991   int a, b, m;
992
993   if (streamEndsLen == 0 ||
994       streamStart > streamEnds[streamEndsLen - 1]) {
995     return gFalse;
996   }
997
998   a = -1;
999   b = streamEndsLen - 1;
1000   // invariant: streamEnds[a] < streamStart <= streamEnds[b]
1001   while (b - a > 1) {
1002     m = (a + b) / 2;
1003     if (streamStart <= streamEnds[m]) {
1004       b = m;
1005     } else {
1006       a = m;
1007     }
1008   }
1009   *streamEnd = streamEnds[b];
1010   return gTrue;
1011 }
1012
1013 Guint XRef::strToUnsigned(char *s) {
1014   Guint x;
1015   char *p;
1016   int i;
1017
1018   x = 0;
1019   for (p = s, i = 0; *p && isdigit(*p) && i < 10; ++p, ++i) {
1020     x = 10 * x + (*p - '0');
1021   }
1022   return x;
1023 }