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