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