]> www.fi.muni.cz Git - evince.git/blob - pdf/xpdf/XRef.cc
Imported Xpdf 2.03 and fixed build.
[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 // XRef
51 //------------------------------------------------------------------------
52
53 XRef::XRef(BaseStream *strA, GString *ownerPassword, GString *userPassword) {
54   Guint pos;
55   int i;
56
57   ok = gTrue;
58   errCode = errNone;
59   size = 0;
60   entries = NULL;
61   streamEnds = NULL;
62   streamEndsLen = 0;
63
64   // read the trailer
65   str = strA;
66   start = str->getStart();
67   pos = readTrailer();
68
69   // if there was a problem with the trailer,
70   // try to reconstruct the xref table
71   if (pos == 0) {
72     if (!(ok = constructXRef())) {
73       errCode = errDamaged;
74       return;
75     }
76
77   // trailer is ok - read the xref table
78   } else {
79     entries = (XRefEntry *)gmalloc(size * sizeof(XRefEntry));
80     for (i = 0; i < size; ++i) {
81       entries[i].offset = 0xffffffff;
82       entries[i].used = gFalse;
83     }
84     while (readXRef(&pos)) ;
85
86     // if there was a problem with the xref table,
87     // try to reconstruct it
88     if (!ok) {
89       gfree(entries);
90       size = 0;
91       entries = NULL;
92       if (!(ok = constructXRef())) {
93         errCode = errDamaged;
94         return;
95       }
96     }
97   }
98
99   // now set the trailer dictionary's xref pointer so we can fetch
100   // indirect objects from it
101   trailerDict.getDict()->setXRef(this);
102
103   // check for encryption
104 #ifndef NO_DECRYPTION
105   encrypted = gFalse;
106 #endif
107   if (checkEncrypted(ownerPassword, userPassword)) {
108     ok = gFalse;
109     errCode = errEncrypted;
110     return;
111   }
112 }
113
114 XRef::~XRef() {
115   gfree(entries);
116   trailerDict.free();
117   if (streamEnds) {
118     gfree(streamEnds);
119   }
120 }
121
122 // Read startxref position, xref table size, and root.  Returns
123 // first xref position.
124 Guint XRef::readTrailer() {
125   Parser *parser;
126   Object obj;
127   char buf[xrefSearchSize+1];
128   int n;
129   Guint pos, pos1;
130   char *p;
131   int c;
132   int i;
133
134   // read last xrefSearchSize bytes
135   str->setPos(xrefSearchSize, -1);
136   for (n = 0; n < xrefSearchSize; ++n) {
137     if ((c = str->getChar()) == EOF) {
138       break;
139     }
140     buf[n] = c;
141   }
142   buf[n] = '\0';
143
144   // find startxref
145   for (i = n - 9; i >= 0; --i) {
146     if (!strncmp(&buf[i], "startxref", 9)) {
147       break;
148     }
149   }
150   if (i < 0) {
151     goto err1;
152   }
153   for (p = &buf[i+9]; isspace(*p); ++p) ;
154   pos = lastXRefPos = strToUnsigned(p);
155
156   // find trailer dict by looking after first xref table
157   // (NB: we can't just use the trailer dict at the end of the file --
158   // this won't work for linearized files.)
159   str->setPos(start + pos);
160   for (i = 0; i < 4; ++i) {
161     buf[i] = str->getChar();
162   }
163   if (strncmp(buf, "xref", 4)) {
164     goto err1;
165   }
166   pos1 = pos + 4;
167   while (1) {
168     str->setPos(start + pos1);
169     for (i = 0; i < 35; ++i) {
170       if ((c = str->getChar()) == EOF) {
171         goto err1;
172       }
173       buf[i] = c;
174     }
175     if (!strncmp(buf, "trailer", 7)) {
176       break;
177     }
178     p = buf;
179     while (isspace(*p)) ++p;
180     while ('0' <= *p && *p <= '9') ++p;
181     while (isspace(*p)) ++p;
182     n = atoi(p);
183     while ('0' <= *p && *p <= '9') ++p;
184     while (isspace(*p)) ++p;
185     if (p == buf) {
186       goto err1;
187     }
188     pos1 += (p - buf) + n * 20;
189   }
190   pos1 += 7;
191
192   // read trailer dict
193   obj.initNull();
194   parser = new Parser(NULL,
195              new Lexer(NULL,
196                str->makeSubStream(start + pos1, gFalse, 0, &obj)));
197   parser->getObj(&trailerDict);
198   if (trailerDict.isDict()) {
199     trailerDict.dictLookupNF("Size", &obj);
200     if (obj.isInt()) {
201       size = obj.getInt();
202     } else {
203       goto err3;
204     }
205     obj.free();
206     trailerDict.dictLookupNF("Root", &obj);
207     if (obj.isRef()) {
208       rootNum = obj.getRefNum();
209       rootGen = obj.getRefGen();
210     } else {
211       goto err3;
212     }
213     obj.free();
214   } else {
215     goto err2;
216   }
217   delete parser;
218
219   // return first xref position
220   return pos;
221
222  err3:
223   obj.free();
224  err2:
225   trailerDict.free();
226   delete parser;
227  err1:
228   size = 0;
229   return 0;
230 }
231
232 // Read an xref table and the prev pointer from the trailer.
233 GBool XRef::readXRef(Guint *pos) {
234   Parser *parser;
235   Object obj, obj2;
236   char s[20];
237   GBool more;
238   int first, newSize, n, i, j;
239   int c;
240
241   // seek to xref in stream
242   str->setPos(start + *pos);
243
244   // make sure it's an xref table
245   while ((c = str->getChar()) != EOF && isspace(c)) ;
246   s[0] = (char)c;
247   s[1] = (char)str->getChar();
248   s[2] = (char)str->getChar();
249   s[3] = (char)str->getChar();
250   if (!(s[0] == 'x' && s[1] == 'r' && s[2] == 'e' && s[3] == 'f')) {
251     goto err2;
252   }
253
254   // read xref
255   while (1) {
256     while ((c = str->lookChar()) != EOF && isspace(c)) {
257       str->getChar();
258     }
259     if (c == 't') {
260       break;
261     }
262     for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i) {
263       s[i] = (char)c;
264     }
265     if (i == 0) {
266       goto err2;
267     }
268     s[i] = '\0';
269     first = atoi(s);
270     while ((c = str->lookChar()) != EOF && isspace(c)) {
271       str->getChar();
272     }
273     for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i) {
274       s[i] = (char)c;
275     }
276     if (i == 0) {
277       goto err2;
278     }
279     s[i] = '\0';
280     n = atoi(s);
281     while ((c = str->lookChar()) != EOF && isspace(c)) {
282       str->getChar();
283     }
284     // check for buggy PDF files with an incorrect (too small) xref
285     // table size
286     if (first + n > size) {
287       newSize = first + n;
288       entries = (XRefEntry *)grealloc(entries, newSize * sizeof(XRefEntry));
289       for (i = size; i < newSize; ++i) {
290         entries[i].offset = 0xffffffff;
291         entries[i].used = gFalse;
292       }
293       size = newSize;
294     }
295     for (i = first; i < first + n; ++i) {
296       for (j = 0; j < 20; ++j) {
297         if ((c = str->getChar()) == EOF) {
298           goto err2;
299         }
300         s[j] = (char)c;
301       }
302       if (entries[i].offset == 0xffffffff) {
303         s[10] = '\0';
304         entries[i].offset = strToUnsigned(s);
305         s[16] = '\0';
306         entries[i].gen = atoi(&s[11]);
307         if (s[17] == 'n') {
308           entries[i].used = gTrue;
309         } else if (s[17] == 'f') {
310           entries[i].used = gFalse;
311         } else {
312           goto err2;
313         }
314         // PDF files of patents from the IBM Intellectual Property
315         // Network have a bug: the xref table claims to start at 1
316         // instead of 0.
317         if (i == 1 && first == 1 &&
318             entries[1].offset == 0 && entries[1].gen == 65535 &&
319             !entries[1].used) {
320           i = first = 0;
321           entries[0] = entries[1];
322           entries[1].offset = 0xffffffff;
323         }
324       }
325     }
326   }
327
328   // read prev pointer from trailer dictionary
329   obj.initNull();
330   parser = new Parser(NULL,
331              new Lexer(NULL,
332                str->makeSubStream(str->getPos(), gFalse, 0, &obj)));
333   parser->getObj(&obj);
334   if (!obj.isCmd("trailer")) {
335     goto err1;
336   }
337   obj.free();
338   parser->getObj(&obj);
339   if (!obj.isDict()) {
340     goto err1;
341   }
342   obj.getDict()->lookupNF("Prev", &obj2);
343   if (obj2.isInt()) {
344     *pos = (Guint)obj2.getInt();
345     more = gTrue;
346   } else if (obj2.isRef()) {
347     // certain buggy PDF generators generate "/Prev NNN 0 R" instead
348     // of "/Prev NNN"
349     *pos = (Guint)obj2.getRefNum();
350     more = gTrue;
351   } else {
352     more = gFalse;
353   }
354   obj.free();
355   obj2.free();
356
357   delete parser;
358   return more;
359
360  err1:
361   obj.free();
362  err2:
363   ok = gFalse;
364   return gFalse;
365 }
366
367 // Attempt to construct an xref table for a damaged file.
368 GBool XRef::constructXRef() {
369   Parser *parser;
370   Object obj;
371   char buf[256];
372   Guint pos;
373   int num, gen;
374   int newSize;
375   int streamEndsSize;
376   char *p;
377   int i;
378   GBool gotRoot;
379
380   error(0, "PDF file is damaged - attempting to reconstruct xref table...");
381   gotRoot = gFalse;
382   streamEndsLen = streamEndsSize = 0;
383
384   str->reset();
385   while (1) {
386     pos = str->getPos();
387     if (!str->getLine(buf, 256)) {
388       break;
389     }
390     p = buf;
391
392     // got trailer dictionary
393     if (!strncmp(p, "trailer", 7)) {
394       gotRoot = gFalse;
395       obj.initNull();
396       parser = new Parser(NULL,
397                  new Lexer(NULL,
398                    str->makeSubStream(start + pos + 7, gFalse, 0, &obj)));
399       if (!trailerDict.isNone()) {
400         trailerDict.free();
401       }
402       parser->getObj(&trailerDict);
403       if (trailerDict.isDict()) {
404         trailerDict.dictLookupNF("Root", &obj);
405         if (obj.isRef()) {
406           rootNum = obj.getRefNum();
407           rootGen = obj.getRefGen();
408           gotRoot = gTrue;
409         }
410         obj.free();
411       } else {
412         trailerDict.free();
413       }
414       delete parser;
415
416     // look for object
417     } else if (isdigit(*p)) {
418       num = atoi(p);
419       do {
420         ++p;
421       } while (*p && isdigit(*p));
422       if (isspace(*p)) {
423         do {
424           ++p;
425         } while (*p && isspace(*p));
426         if (isdigit(*p)) {
427           gen = atoi(p);
428           do {
429             ++p;
430           } while (*p && isdigit(*p));
431           if (isspace(*p)) {
432             do {
433               ++p;
434             } while (*p && isspace(*p));
435             if (!strncmp(p, "obj", 3)) {
436               if (num >= size) {
437                 newSize = (num + 1 + 255) & ~255;
438                 entries = (XRefEntry *)
439                             grealloc(entries, newSize * sizeof(XRefEntry));
440                 for (i = size; i < newSize; ++i) {
441                   entries[i].offset = 0xffffffff;
442                   entries[i].used = gFalse;
443                 }
444                 size = newSize;
445               }
446               if (!entries[num].used || gen >= entries[num].gen) {
447                 entries[num].offset = pos - start;
448                 entries[num].gen = gen;
449                 entries[num].used = gTrue;
450               }
451             }
452           }
453         }
454       }
455
456     } else if (!strncmp(p, "endstream", 9)) {
457       if (streamEndsLen == streamEndsSize) {
458         streamEndsSize += 64;
459         streamEnds = (Guint *)grealloc(streamEnds,
460                                        streamEndsSize * sizeof(int));
461       }
462       streamEnds[streamEndsLen++] = pos;
463     }
464   }
465
466   if (gotRoot)
467     return gTrue;
468
469   error(-1, "Couldn't find trailer dictionary");
470   return gFalse;
471 }
472
473 #ifndef NO_DECRYPTION
474 GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
475   Object encrypt, filterObj, versionObj, revisionObj, lengthObj;
476   Object ownerKey, userKey, permissions, fileID, fileID1;
477   GBool encrypted1;
478   GBool ret;
479
480   keyLength = 0;
481   encVersion = encRevision = 0;
482   ret = gFalse;
483
484   permFlags = defPermFlags;
485   ownerPasswordOk = gFalse;
486   trailerDict.dictLookup("Encrypt", &encrypt);
487   if ((encrypted1 = encrypt.isDict())) {
488     ret = gTrue;
489     encrypt.dictLookup("Filter", &filterObj);
490     if (filterObj.isName("Standard")) {
491       encrypt.dictLookup("V", &versionObj);
492       encrypt.dictLookup("R", &revisionObj);
493       encrypt.dictLookup("Length", &lengthObj);
494       encrypt.dictLookup("O", &ownerKey);
495       encrypt.dictLookup("U", &userKey);
496       encrypt.dictLookup("P", &permissions);
497       trailerDict.dictLookup("ID", &fileID);
498       if (versionObj.isInt() &&
499           revisionObj.isInt() &&
500           ownerKey.isString() && ownerKey.getString()->getLength() == 32 &&
501           userKey.isString() && userKey.getString()->getLength() == 32 &&
502           permissions.isInt() &&
503           fileID.isArray()) {
504         encVersion = versionObj.getInt();
505         encRevision = revisionObj.getInt();
506         if (lengthObj.isInt()) {
507           keyLength = lengthObj.getInt() / 8;
508         } else {
509           keyLength = 5;
510         }
511         permFlags = permissions.getInt();
512         if (encVersion >= 1 && encVersion <= 2 &&
513             encRevision >= 2 && encRevision <= 3) {
514           fileID.arrayGet(0, &fileID1);
515           if (fileID1.isString()) {
516             if (Decrypt::makeFileKey(encVersion, encRevision, keyLength,
517                                      ownerKey.getString(), userKey.getString(),
518                                      permFlags, fileID1.getString(),
519                                      ownerPassword, userPassword, fileKey,
520                                      &ownerPasswordOk)) {
521               if (ownerPassword && !ownerPasswordOk) {
522                 error(-1, "Incorrect owner password");
523               }
524               ret = gFalse;
525             } else {
526               error(-1, "Incorrect password");
527             }
528           } else {
529             error(-1, "Weird encryption info");
530           }
531           fileID1.free();
532         } else {
533           error(-1, "Unsupported version/revision (%d/%d) of Standard security handler",
534                 encVersion, encRevision);
535         }
536       } else {
537         error(-1, "Weird encryption info");
538       }
539       fileID.free();
540       permissions.free();
541       userKey.free();
542       ownerKey.free();
543       lengthObj.free();
544       revisionObj.free();
545       versionObj.free();
546     } else {
547       error(-1, "Unknown security handler '%s'",
548             filterObj.isName() ? filterObj.getName() : "???");
549     }
550     filterObj.free();
551   }
552   encrypt.free();
553
554   // this flag has to be set *after* we read the O/U/P strings
555   encrypted = encrypted1;
556
557   return ret;
558 }
559 #else
560 GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
561   Object obj;
562   GBool encrypted;
563
564   trailerDict.dictLookup("Encrypt", &obj);
565   if ((encrypted = !obj.isNull())) {
566     error(-1, "PDF file is encrypted and this version of the Xpdf tools");
567     error(-1, "was built without decryption support.");
568   }
569   obj.free();
570   return encrypted;
571 }
572 #endif
573
574 GBool XRef::okToPrint(GBool ignoreOwnerPW) {
575 #ifndef NO_DECRYPTION
576   return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permPrint);
577 #else
578   return gTrue;
579 #endif
580 }
581
582 GBool XRef::okToChange(GBool ignoreOwnerPW) {
583 #ifndef NO_DECRYPTION
584   return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permChange);
585 #else
586   return gTrue;
587 #endif
588 }
589
590 GBool XRef::okToCopy(GBool ignoreOwnerPW) {
591 #ifndef NO_DECRYPTION
592   return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permCopy);
593 #else
594   return gTrue;
595 #endif
596 }
597
598 GBool XRef::okToAddNotes(GBool ignoreOwnerPW) {
599 #ifndef NO_DECRYPTION
600   return (!ignoreOwnerPW && ownerPasswordOk) || (permFlags & permNotes);
601 #else
602   return gTrue;
603 #endif
604 }
605
606 Object *XRef::fetch(int num, int gen, Object *obj) {
607   XRefEntry *e;
608   Parser *parser;
609   Object obj1, obj2, obj3;
610
611   // check for bogus ref - this can happen in corrupted PDF files
612   if (num < 0 || num >= size) {
613     obj->initNull();
614     return obj;
615   }
616
617   e = &entries[num];
618   if (e->gen == gen && e->offset != 0xffffffff) {
619     obj1.initNull();
620     parser = new Parser(this,
621                new Lexer(this,
622                  str->makeSubStream(start + e->offset, gFalse, 0, &obj1)));
623     parser->getObj(&obj1);
624     parser->getObj(&obj2);
625     parser->getObj(&obj3);
626     if (obj1.isInt() && obj1.getInt() == num &&
627         obj2.isInt() && obj2.getInt() == gen &&
628         obj3.isCmd("obj")) {
629 #ifndef NO_DECRYPTION
630       parser->getObj(obj, encrypted ? fileKey : (Guchar *)NULL, keyLength,
631                      num, gen);
632 #else
633       parser->getObj(obj);
634 #endif
635     } else {
636       obj->initNull();
637     }
638     obj1.free();
639     obj2.free();
640     obj3.free();
641     delete parser;
642   } else {
643     obj->initNull();
644   }
645   return obj;
646 }
647
648 Object *XRef::getDocInfo(Object *obj) {
649   return trailerDict.dictLookup("Info", obj);
650 }
651
652 // Added for the pdftex project.
653 Object *XRef::getDocInfoNF(Object *obj) {
654   return trailerDict.dictLookupNF("Info", obj);
655 }
656
657 GBool XRef::getStreamEnd(Guint streamStart, Guint *streamEnd) {
658   int a, b, m;
659
660   if (streamEndsLen == 0 ||
661       streamStart > streamEnds[streamEndsLen - 1]) {
662     return gFalse;
663   }
664
665   a = -1;
666   b = streamEndsLen - 1;
667   // invariant: streamEnds[a] < streamStart <= streamEnds[b]
668   while (b - a > 1) {
669     m = (a + b) / 2;
670     if (streamStart <= streamEnds[m]) {
671       b = m;
672     } else {
673       a = m;
674     }
675   }
676   *streamEnd = streamEnds[b];
677   return gTrue;
678 }
679
680 Guint XRef::strToUnsigned(char *s) {
681   Guint x;
682   char *p;
683   int i;
684
685   x = 0;
686   for (p = s, i = 0; *p && isdigit(*p) && i < 10; ++p, ++i) {
687     x = 10 * x + (*p - '0');
688   }
689   return x;
690 }