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