Now can protect sheets with a password. So everyone may cease whining about it
already :-) git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@552425 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
parent
313191f5aa
commit
b8933243ec
|
@ -93,6 +93,9 @@ public class Sheet implements Model
|
||||||
protected ProtectRecord protect = null;
|
protected ProtectRecord protect = null;
|
||||||
protected PageBreakRecord rowBreaks = null;
|
protected PageBreakRecord rowBreaks = null;
|
||||||
protected PageBreakRecord colBreaks = null;
|
protected PageBreakRecord colBreaks = null;
|
||||||
|
protected ObjectProtectRecord objprotect = null;
|
||||||
|
protected ScenarioProtectRecord scenprotect = null;
|
||||||
|
protected PasswordRecord password = null;
|
||||||
|
|
||||||
|
|
||||||
public static final byte PANE_LOWER_RIGHT = (byte)0;
|
public static final byte PANE_LOWER_RIGHT = (byte)0;
|
||||||
|
@ -285,6 +288,18 @@ public class Sheet implements Model
|
||||||
{
|
{
|
||||||
retval.protect = (ProtectRecord) rec;
|
retval.protect = (ProtectRecord) rec;
|
||||||
}
|
}
|
||||||
|
else if ( rec.getSid() == ObjectProtectRecord.sid )
|
||||||
|
{
|
||||||
|
retval.objprotect = (ObjectProtectRecord) rec;
|
||||||
|
}
|
||||||
|
else if ( rec.getSid() == ScenarioProtectRecord.sid )
|
||||||
|
{
|
||||||
|
retval.scenprotect = (ScenarioProtectRecord) rec;
|
||||||
|
}
|
||||||
|
else if ( rec.getSid() == PasswordRecord.sid )
|
||||||
|
{
|
||||||
|
retval.password = (PasswordRecord) rec;
|
||||||
|
}
|
||||||
else if (rec.getSid() == PageBreakRecord.HORIZONTAL_SID)
|
else if (rec.getSid() == PageBreakRecord.HORIZONTAL_SID)
|
||||||
{
|
{
|
||||||
retval.rowBreaks = (PageBreakRecord)rec;
|
retval.rowBreaks = (PageBreakRecord)rec;
|
||||||
|
@ -2500,7 +2515,38 @@ public class Sheet implements Model
|
||||||
ProtectRecord retval = new ProtectRecord();
|
ProtectRecord retval = new ProtectRecord();
|
||||||
|
|
||||||
retval.setProtect(false);
|
retval.setProtect(false);
|
||||||
// by default even when we support encryption we won't
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creates an ObjectProtect record with protect set to false.
|
||||||
|
* @see org.apache.poi.hssf.record.ObjectProtectRecord
|
||||||
|
* @see org.apache.poi.hssf.record.Record
|
||||||
|
* @return an ObjectProtectRecord
|
||||||
|
*/
|
||||||
|
protected ObjectProtectRecord createObjectProtect()
|
||||||
|
{
|
||||||
|
if (log.check( POILogger.DEBUG ))
|
||||||
|
log.log(POILogger.DEBUG, "create protect record with protection disabled");
|
||||||
|
ObjectProtectRecord retval = new ObjectProtectRecord();
|
||||||
|
|
||||||
|
retval.setProtect(false);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creates a ScenarioProtect record with protect set to false.
|
||||||
|
* @see org.apache.poi.hssf.record.ScenarioProtectRecord
|
||||||
|
* @see org.apache.poi.hssf.record.Record
|
||||||
|
* @return a ScenarioProtectRecord
|
||||||
|
*/
|
||||||
|
protected ScenarioProtectRecord createScenarioProtect()
|
||||||
|
{
|
||||||
|
if (log.check( POILogger.DEBUG ))
|
||||||
|
log.log(POILogger.DEBUG, "create protect record with protection disabled");
|
||||||
|
ScenarioProtectRecord retval = new ScenarioProtectRecord();
|
||||||
|
|
||||||
|
retval.setProtect(false);
|
||||||
return retval;
|
return retval;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2518,6 +2564,38 @@ public class Sheet implements Model
|
||||||
return protect;
|
return protect;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Returns the PasswordRecord.
|
||||||
|
* If one is not contained in the sheet, then one is created.
|
||||||
|
*/
|
||||||
|
public PasswordRecord getPassword()
|
||||||
|
{
|
||||||
|
if (password == null) {
|
||||||
|
password = createPassword();
|
||||||
|
//Insert the newly created password record at the end of the record (just before the EOF)
|
||||||
|
int loc = findFirstRecordLocBySid(EOFRecord.sid);
|
||||||
|
records.add(loc, password);
|
||||||
|
}
|
||||||
|
return password;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* creates a Password record with password set to 00.
|
||||||
|
* @see org.apache.poi.hssf.record.PasswordRecord
|
||||||
|
* @see org.apache.poi.hssf.record.Record
|
||||||
|
* @return a PasswordRecord
|
||||||
|
*/
|
||||||
|
protected PasswordRecord createPassword()
|
||||||
|
{
|
||||||
|
if (log.check( POILogger.DEBUG ))
|
||||||
|
log.log(POILogger.DEBUG, "create password record with 00 password");
|
||||||
|
PasswordRecord retval = new PasswordRecord();
|
||||||
|
|
||||||
|
retval.setPassword((short)00);
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets whether the gridlines are shown in a viewer.
|
* Sets whether the gridlines are shown in a viewer.
|
||||||
* @param show whether to show gridlines or not
|
* @param show whether to show gridlines or not
|
||||||
|
@ -2791,6 +2869,68 @@ public class Sheet implements Model
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* protect a spreadsheet with a password (not encypted, just sets protect
|
||||||
|
* flags and the password.
|
||||||
|
* @param password to set
|
||||||
|
* @param objects are protected
|
||||||
|
* @param scenarios are protected
|
||||||
|
*/
|
||||||
|
public void protectSheet( String password, boolean objects, boolean scenarios ) {
|
||||||
|
int protIdx = -1;
|
||||||
|
ProtectRecord prec = getProtect();
|
||||||
|
PasswordRecord pass = getPassword();
|
||||||
|
prec.setProtect(true);
|
||||||
|
pass.setPassword(PasswordRecord.hashPassword(password));
|
||||||
|
if((objprotect == null && objects) || (scenprotect != null && scenarios)) {
|
||||||
|
protIdx = records.indexOf( protect );
|
||||||
|
}
|
||||||
|
if(objprotect == null && objects) {
|
||||||
|
ObjectProtectRecord rec = createObjectProtect();
|
||||||
|
rec.setProtect(true);
|
||||||
|
records.add(protIdx+1,rec);
|
||||||
|
objprotect = rec;
|
||||||
|
}
|
||||||
|
if(scenprotect == null && scenarios) {
|
||||||
|
ScenarioProtectRecord srec = createScenarioProtect();
|
||||||
|
srec.setProtect(true);
|
||||||
|
records.add(protIdx+2,srec);
|
||||||
|
scenprotect = srec;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* unprotect objects in the sheet (will not protect them, but any set to false are
|
||||||
|
* unprotected.
|
||||||
|
* @param sheet is unprotected (false = unprotect)
|
||||||
|
* @param objects are unprotected (false = unprotect)
|
||||||
|
* @param scenarios are unprotected (false = unprotect)
|
||||||
|
*/
|
||||||
|
public void unprotectSheet( boolean sheet, boolean objects, boolean scenarios ) {
|
||||||
|
int protIdx = -1;
|
||||||
|
if (!sheet) {
|
||||||
|
ProtectRecord prec = getProtect();
|
||||||
|
prec.setProtect(sheet);
|
||||||
|
PasswordRecord pass = getPassword();
|
||||||
|
pass.setPassword((short)00);
|
||||||
|
}
|
||||||
|
if(objprotect != null && !objects) {
|
||||||
|
objprotect.setProtect(false);
|
||||||
|
}
|
||||||
|
if(scenprotect != null && !scenarios) {
|
||||||
|
scenprotect.setProtect(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return {sheet is protected, objects are proteced, scenarios are protected}
|
||||||
|
*/
|
||||||
|
public boolean[] isProtected() {
|
||||||
|
return new boolean[] { (protect != null && protect.getProtect()),
|
||||||
|
(objprotect != null && objprotect.getProtect()),
|
||||||
|
(scenprotect != null && scenprotect.getProtect())};
|
||||||
|
}
|
||||||
|
|
||||||
// private void collapseColumn( short columnNumber )
|
// private void collapseColumn( short columnNumber )
|
||||||
// {
|
// {
|
||||||
// int idx = findColumnIdx( columnNumber, 0 );
|
// int idx = findColumnIdx( columnNumber, 0 );
|
||||||
|
|
|
@ -29,14 +29,11 @@ import org.apache.poi.util.LittleEndian;
|
||||||
* @version 2.0-pre
|
* @version 2.0-pre
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class PasswordRecord
|
public class PasswordRecord extends Record {
|
||||||
extends Record
|
|
||||||
{
|
|
||||||
public final static short sid = 0x13;
|
public final static short sid = 0x13;
|
||||||
private short field_1_password; // not sure why this is only 2 bytes, but it is... go figure
|
private short field_1_password; // not sure why this is only 2 bytes, but it is... go figure
|
||||||
|
|
||||||
public PasswordRecord()
|
public PasswordRecord() {
|
||||||
{
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -44,32 +41,46 @@ public class PasswordRecord
|
||||||
* @param in the RecordInputstream to read the record from
|
* @param in the RecordInputstream to read the record from
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public PasswordRecord(RecordInputStream in)
|
public PasswordRecord(RecordInputStream in) {
|
||||||
{
|
|
||||||
super(in);
|
super(in);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void validateSid(short id)
|
protected void validateSid(short id) {
|
||||||
{
|
if (id != sid) {
|
||||||
if (id != sid)
|
|
||||||
{
|
|
||||||
throw new RecordFormatException("NOT A PASSWORD RECORD");
|
throw new RecordFormatException("NOT A PASSWORD RECORD");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
protected void fillFields(RecordInputStream in)
|
protected void fillFields(RecordInputStream in) {
|
||||||
{
|
|
||||||
field_1_password = in.readShort();
|
field_1_password = in.readShort();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//this is the world's lamest "security". thanks to Wouter van Vugt for making me
|
||||||
|
//not have to try real hard. -ACO
|
||||||
|
public static short hashPassword(String password) {
|
||||||
|
byte[] passwordCharacters = password.getBytes();
|
||||||
|
int hash = 0;
|
||||||
|
if (passwordCharacters.length > 0) {
|
||||||
|
int charIndex = passwordCharacters.length;
|
||||||
|
while (charIndex-- > 0) {
|
||||||
|
hash = ((hash >> 14) & 0x01) | ((hash << 1) & 0x7fff);
|
||||||
|
hash ^= passwordCharacters[charIndex];
|
||||||
|
}
|
||||||
|
// also hash with charcount
|
||||||
|
hash = ((hash >> 14) & 0x01) | ((hash << 1) & 0x7fff);
|
||||||
|
hash ^= passwordCharacters.length;
|
||||||
|
hash ^= (0x8000 | ('N' << 8) | 'K');
|
||||||
|
}
|
||||||
|
return (short)hash;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* set the password
|
* set the password
|
||||||
*
|
*
|
||||||
* @param password representing the password
|
* @param password representing the password
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public void setPassword(short password)
|
public void setPassword(short password) {
|
||||||
{
|
|
||||||
field_1_password = password;
|
field_1_password = password;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,14 +89,11 @@ public class PasswordRecord
|
||||||
*
|
*
|
||||||
* @return short representing the password
|
* @return short representing the password
|
||||||
*/
|
*/
|
||||||
|
public short getPassword() {
|
||||||
public short getPassword()
|
|
||||||
{
|
|
||||||
return field_1_password;
|
return field_1_password;
|
||||||
}
|
}
|
||||||
|
|
||||||
public String toString()
|
public String toString() {
|
||||||
{
|
|
||||||
StringBuffer buffer = new StringBuffer();
|
StringBuffer buffer = new StringBuffer();
|
||||||
|
|
||||||
buffer.append("[PASSWORD]\n");
|
buffer.append("[PASSWORD]\n");
|
||||||
|
@ -95,8 +103,7 @@ public class PasswordRecord
|
||||||
return buffer.toString();
|
return buffer.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int serialize(int offset, byte [] data)
|
public int serialize(int offset, byte [] data) {
|
||||||
{
|
|
||||||
LittleEndian.putShort(data, 0 + offset, sid);
|
LittleEndian.putShort(data, 0 + offset, sid);
|
||||||
LittleEndian.putShort(data, 2 + offset,
|
LittleEndian.putShort(data, 2 + offset,
|
||||||
(( short ) 0x02)); // 2 bytes (6 total)
|
(( short ) 0x02)); // 2 bytes (6 total)
|
||||||
|
@ -104,13 +111,11 @@ public class PasswordRecord
|
||||||
return getRecordSize();
|
return getRecordSize();
|
||||||
}
|
}
|
||||||
|
|
||||||
public int getRecordSize()
|
public int getRecordSize() {
|
||||||
{
|
|
||||||
return 6;
|
return 6;
|
||||||
}
|
}
|
||||||
|
|
||||||
public short getSid()
|
public short getSid() {
|
||||||
{
|
|
||||||
return sid;
|
return sid;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -26,9 +26,10 @@ import org.apache.poi.util.LittleEndian;
|
||||||
* Description: defines whether a sheet or workbook is protected (HSSF DOES NOT SUPPORT ENCRYPTION)<P>
|
* Description: defines whether a sheet or workbook is protected (HSSF DOES NOT SUPPORT ENCRYPTION)<P>
|
||||||
* (kindly ask the US government to stop having arcane stupid encryption laws and we'll support it) <P>
|
* (kindly ask the US government to stop having arcane stupid encryption laws and we'll support it) <P>
|
||||||
* (after all terrorists will all use US-legal encrypton right??)<P>
|
* (after all terrorists will all use US-legal encrypton right??)<P>
|
||||||
|
* HSSF now supports the simple "protected" sheets (where they are not encrypted and open office et al
|
||||||
|
* ignore the password record entirely).
|
||||||
* REFERENCE: PG 373 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)<P>
|
* REFERENCE: PG 373 Microsoft Excel 97 Developer's Kit (ISBN: 1-57231-498-2)<P>
|
||||||
* @author Andrew C. Oliver (acoliver at apache dot org)
|
* @author Andrew C. Oliver (acoliver at apache dot org)
|
||||||
* @version 2.0-pre
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class ProtectRecord
|
public class ProtectRecord
|
||||||
|
|
|
@ -74,7 +74,7 @@ public class RecordFactory
|
||||||
PaletteRecord.class, StringRecord.class, RecalcIdRecord.class, SharedFormulaRecord.class,
|
PaletteRecord.class, StringRecord.class, RecalcIdRecord.class, SharedFormulaRecord.class,
|
||||||
HorizontalPageBreakRecord.class, VerticalPageBreakRecord.class,
|
HorizontalPageBreakRecord.class, VerticalPageBreakRecord.class,
|
||||||
WriteProtectRecord.class, FilePassRecord.class, PaneRecord.class,
|
WriteProtectRecord.class, FilePassRecord.class, PaneRecord.class,
|
||||||
NoteRecord.class
|
NoteRecord.class, ObjectProtectRecord.class, ScenarioProtectRecord.class
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
private static Map recordsMap = recordsToMap(records);
|
private static Map recordsMap = recordsToMap(records);
|
||||||
|
|
|
@ -872,17 +872,49 @@ public class HSSFSheet
|
||||||
* @return true => protection enabled; false => protection disabled
|
* @return true => protection enabled; false => protection disabled
|
||||||
*/
|
*/
|
||||||
public boolean getProtect() {
|
public boolean getProtect() {
|
||||||
return getSheet().getProtect().getProtect();
|
return getSheet().isProtected()[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return hashed password
|
||||||
|
*/
|
||||||
|
public short getPassword() {
|
||||||
|
return getSheet().getPassword().getPassword();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Answer whether object protection is enabled or disabled
|
||||||
|
* @return true => protection enabled; false => protection disabled
|
||||||
|
*/
|
||||||
|
public boolean getObjectProtect() {
|
||||||
|
return getSheet().isProtected()[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Answer whether scenario protection is enabled or disabled
|
||||||
|
* @return true => protection enabled; false => protection disabled
|
||||||
|
*/
|
||||||
|
public boolean getScenarioProtect() {
|
||||||
|
return getSheet().isProtected()[2];
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the protection on enabled or disabled
|
* Sets the protection on enabled or disabled
|
||||||
* @param protect true => protection enabled; false => protection disabled
|
* @param protect true => protection enabled; false => protection disabled
|
||||||
|
* @deprecated use protectSheet(String, boolean, boolean)
|
||||||
*/
|
*/
|
||||||
public void setProtect(boolean protect) {
|
public void setProtect(boolean protect) {
|
||||||
getSheet().getProtect().setProtect(protect);
|
getSheet().getProtect().setProtect(protect);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the protection enabled as well as the password
|
||||||
|
* @param password to set for protection
|
||||||
|
*/
|
||||||
|
public void protectSheet(String password) {
|
||||||
|
getSheet().protectSheet(password, true, true); //protect objs&scenarios(normal)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets the zoom magnication for the sheet. The zoom is expressed as a
|
* Sets the zoom magnication for the sheet. The zoom is expressed as a
|
||||||
* fraction. For example to express a zoom of 75% use 3 for the numerator
|
* fraction. For example to express a zoom of 75% use 3 for the numerator
|
||||||
|
|
|
@ -26,6 +26,7 @@ import junit.framework.TestCase;
|
||||||
import org.apache.poi.hssf.model.Sheet;
|
import org.apache.poi.hssf.model.Sheet;
|
||||||
import org.apache.poi.hssf.record.HCenterRecord;
|
import org.apache.poi.hssf.record.HCenterRecord;
|
||||||
import org.apache.poi.hssf.record.ProtectRecord;
|
import org.apache.poi.hssf.record.ProtectRecord;
|
||||||
|
import org.apache.poi.hssf.record.PasswordRecord;
|
||||||
import org.apache.poi.hssf.record.SCLRecord;
|
import org.apache.poi.hssf.record.SCLRecord;
|
||||||
import org.apache.poi.hssf.record.VCenterRecord;
|
import org.apache.poi.hssf.record.VCenterRecord;
|
||||||
import org.apache.poi.hssf.record.WSBoolRecord;
|
import org.apache.poi.hssf.record.WSBoolRecord;
|
||||||
|
@ -38,6 +39,7 @@ import org.apache.poi.util.TempFile;
|
||||||
*
|
*
|
||||||
*
|
*
|
||||||
* @author Glen Stampoultzis (glens at apache.org)
|
* @author Glen Stampoultzis (glens at apache.org)
|
||||||
|
* @author Andrew C. Oliver (acoliver apache org)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
public class TestHSSFSheet
|
public class TestHSSFSheet
|
||||||
|
@ -242,6 +244,20 @@ public class TestHSSFSheet
|
||||||
assertTrue(hssfSheet.getProtect());
|
assertTrue(hssfSheet.getProtect());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void testProtectSheet() {
|
||||||
|
short expected = (short)0xfef1;
|
||||||
|
HSSFWorkbook wb = new HSSFWorkbook();
|
||||||
|
HSSFSheet s = wb.createSheet();
|
||||||
|
s.protectSheet("abcdefghij");
|
||||||
|
Sheet sheet = s.getSheet();
|
||||||
|
ProtectRecord protect = sheet.getProtect();
|
||||||
|
PasswordRecord pass = sheet.getPassword();
|
||||||
|
assertTrue("protection should be on",protect.getProtect());
|
||||||
|
assertTrue("object protection should be on",sheet.isProtected()[1]);
|
||||||
|
assertTrue("scenario protection should be on",sheet.isProtected()[2]);
|
||||||
|
assertEquals("well known value for top secret hash should be "+Integer.toHexString(expected).substring(4),expected,pass.getPassword());
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public void testZoom()
|
public void testZoom()
|
||||||
throws Exception
|
throws Exception
|
||||||
|
|
Loading…
Reference in New Issue