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