30#include "ErrorCodes.h"
35#define xrefSearchSize 1024
43#define permPrint (1<<2)
44#define permChange (1<<3)
45#define permCopy (1<<4)
46#define permNotes (1<<5)
47#define defPermFlags 0xfffc
54XRef::XRef(BaseStream *strA, GString *ownerPassword, GString *userPassword) {
67 start = str->getStart();
73 if (!(ok = constructXRef())) {
80 if ((
unsigned) size >= INT_MAX /
sizeof(XRefEntry)) {
81 error(-1,
"Invalid 'size' inside xref table.");
86 entries = (XRefEntry *)gmalloc(size *
sizeof(XRefEntry));
87 for (i = 0; i < size; ++i) {
88 entries[i].offset = 0xffffffff;
89 entries[i].used = gFalse;
91 while (readXRef(&pos)) ;
99 if (!(ok = constructXRef())) {
100 errCode = errDamaged;
108 trailerDict.getDict()->setXRef(
this);
114 if (checkEncrypted(ownerPassword, userPassword)) {
116 errCode = errEncrypted;
131Guint XRef::readTrailer() {
134 char buf[xrefSearchSize+1];
142 str->setPos(xrefSearchSize, -1);
143 for (n = 0; n < xrefSearchSize; ++n) {
144 if ((c = str->getChar()) == EOF)
151 for (i = n - 9; i >= 0; --i) {
152 if (!strncmp(&buf[i],
"startxref", 9))
157 for (p = &buf[i+9]; isspace(*p); ++p) ;
158 pos = lastXRefPos = strToUnsigned(p);
163 str->setPos(start + pos);
164 for (i = 0; i < 4; ++i)
165 buf[i] = str->getChar();
166 if (strncmp(buf,
"xref", 4))
170 str->setPos(start + pos1);
171 for (i = 0; i < 35; ++i) {
172 if ((c = str->getChar()) == EOF)
176 if (!strncmp(buf,
"trailer", 7))
179 while (isspace(*p)) ++p;
180 while (
'0' <= *p && *p <=
'9') ++p;
181 while (isspace(*p)) ++p;
183 while (
'0' <= *p && *p <=
'9') ++p;
184 while (isspace(*p)) ++p;
187 pos1 += (p - buf) + n * 20;
193 parser =
new Parser(NULL,
195 str->makeSubStream(start + pos1, gFalse, 0, &obj)));
196 parser->getObj(&trailerDict);
197 if (trailerDict.isDict()) {
198 trailerDict.dictLookupNF(
"Size", &obj);
204 trailerDict.dictLookupNF(
"Root", &obj);
206 rootNum = obj.getRefNum();
207 rootGen = obj.getRefGen();
222GBool XRef::readXRef(Guint *pos) {
227 int first, newSize, n, i, j;
231 str->setPos(start + *pos);
234 while ((c = str->getChar()) != EOF && isspace(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')) {
245 while ((c = str->lookChar()) != EOF && isspace(c)) {
251 for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i) {
259 while ((c = str->lookChar()) != EOF && isspace(c)) {
262 for (i = 0; (c = str->getChar()) != EOF && isdigit(c) && i < 20; ++i) {
270 while ((c = str->lookChar()) != EOF && isspace(c)) {
275 if (first + n > size) {
276 newSize = size + 256;
277 if ((
unsigned) newSize >= INT_MAX /
sizeof(XRefEntry)) {
278 error(-1,
"Invalid 'newSize'");
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;
288 for (i = first; i < first + n; ++i) {
289 for (j = 0; j < 20; ++j) {
290 if ((c = str->getChar()) == EOF) {
295 if (entries[i].offset == 0xffffffff) {
297 entries[i].offset = strToUnsigned(s);
299 entries[i].gen = atoi(&s[11]);
301 entries[i].used = gTrue;
302 }
else if (s[17] ==
'f') {
303 entries[i].used = gFalse;
310 if (i == 1 && first == 1 &&
311 entries[1].offset == 0 && entries[1].gen == 65535 &&
314 entries[0] = entries[1];
315 entries[1].offset = 0xffffffff;
323 parser =
new Parser(NULL,
325 str->makeSubStream(str->getPos(), gFalse, 0, &obj)));
326 parser->getObj(&obj);
327 if (!obj.isCmd(
"trailer")) {
331 parser->getObj(&obj);
335 obj.getDict()->lookupNF(
"Prev", &obj2);
337 *pos = (Guint)obj2.getInt();
356GBool XRef::constructXRef() {
368 error(0,
"PDF file is damaged - attempting to reconstruct xref table...");
370 streamEndsLen = streamEndsSize = 0;
375 if (!str->getLine(buf, 256)) {
381 if (!strncmp(p,
"trailer", 7)) {
383 parser =
new Parser(NULL,
385 str->makeSubStream(start + pos + 7, gFalse, 0, &obj)));
386 if (!trailerDict.isNone())
388 parser->getObj(&trailerDict);
389 if (trailerDict.isDict()) {
390 trailerDict.dictLookupNF(
"Root", &obj);
392 rootNum = obj.getRefNum();
393 rootGen = obj.getRefGen();
403 }
else if (isdigit(*p)) {
407 }
while (*p && isdigit(*p));
411 }
while (*p && isspace(*p));
416 }
while (*p && isdigit(*p));
420 }
while (*p && isspace(*p));
421 if (!strncmp(p,
"obj", 3)) {
423 newSize = (num + 1 + 255) & ~255;
424 if ((
unsigned) newSize >= INT_MAX /
sizeof(XRefEntry)) {
425 error(-1,
"Invalid 'obj' parameters.");
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;
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;
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.");
454 streamEnds = (Guint *)grealloc(streamEnds,
455 streamEndsSize *
sizeof(
int));
457 streamEnds[streamEndsLen++] = pos;
464 error(-1,
"Couldn't find trailer dictionary");
469GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
470 Object encrypt, filterObj, versionObj, revisionObj, lengthObj;
471 Object ownerKey, userKey, permissions, fileID, fileID1;
477 permFlags = defPermFlags;
478 ownerPasswordOk = gFalse;
479 trailerDict.dictLookup(
"Encrypt", &encrypt);
480 if ((encrypted1 = encrypt.isDict())) {
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() &&
497 encVersion = versionObj.getInt();
498 encRevision = revisionObj.getInt();
499 if (lengthObj.isInt()) {
500 keyLength = lengthObj.getInt() / 8;
507 if (keyLength > 16) {
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,
520 if (ownerPassword && !ownerPasswordOk) {
521 error(-1,
"Incorrect owner password");
525 error(-1,
"Incorrect password");
528 error(-1,
"Weird encryption info");
532 error(-1,
"Unsupported version/revision (%d/%d) of Standard security handler",
533 encVersion, encRevision);
536 error(-1,
"Weird encryption info");
546 error(-1,
"Unknown security handler '%s'",
547 filterObj.isName() ? filterObj.getName() :
"???");
554 encrypted = encrypted1;
559GBool XRef::checkEncrypted(GString *ownerPassword, GString *userPassword) {
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.");
573GBool XRef::okToPrint(GBool ignoreOwnerPW) {
575 if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permPrint)) {
582GBool XRef::okToChange(GBool ignoreOwnerPW) {
584 if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permChange)) {
591GBool XRef::okToCopy(GBool ignoreOwnerPW) {
593 if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permCopy)) {
600GBool XRef::okToAddNotes(GBool ignoreOwnerPW) {
602 if ((ignoreOwnerPW || !ownerPasswordOk) && !(permFlags & permNotes)) {
609Object *XRef::fetch(
int num,
int gen, Object *obj) {
612 Object obj1, obj2, obj3;
615 if (num < 0 || num >= size) {
621 if (e->gen == gen && e->offset != 0xffffffff) {
623 parser =
new Parser(
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 &&
633 parser->getObj(obj, encrypted ? fileKey : (Guchar *)NULL, keyLength,
651Object *XRef::getDocInfo(Object *obj) {
652 return trailerDict.dictLookup(
"Info", obj);
656Object *XRef::getDocInfoNF(Object *obj) {
657 return trailerDict.dictLookupNF(
"Info", obj);
660GBool XRef::getStreamEnd(Guint streamStart, Guint *streamEnd) {
663 if (streamEndsLen == 0 ||
664 streamStart > streamEnds[streamEndsLen - 1]) {
669 b = streamEndsLen - 1;
673 if (streamStart <= streamEnds[m]) {
679 *streamEnd = streamEnds[b];
683Guint XRef::strToUnsigned(
char *s) {
689 for (p = s, i = 0; *p && isdigit(*p) && i < 10; ++p, ++i) {
690 x = 10 * x + (*p -
'0');