]> source.dussan.org Git - poi.git/commitdiff
Now can protect sheets with a password. So everyone may cease whining about it
authorAndrew C. Oliver <acoliver@apache.org>
Mon, 2 Jul 2007 08:10:28 +0000 (08:10 +0000)
committerAndrew C. Oliver <acoliver@apache.org>
Mon, 2 Jul 2007 08:10:28 +0000 (08:10 +0000)
already :-)

git-svn-id: https://svn.apache.org/repos/asf/poi/trunk@552425 13f79535-47bb-0310-9956-ffa450edef68

src/java/org/apache/poi/hssf/model/Sheet.java
src/java/org/apache/poi/hssf/record/PasswordRecord.java
src/java/org/apache/poi/hssf/record/ProtectRecord.java
src/java/org/apache/poi/hssf/record/RecordFactory.java
src/java/org/apache/poi/hssf/usermodel/HSSFSheet.java
src/testcases/org/apache/poi/hssf/usermodel/TestHSSFSheet.java

index 2899005ecb1cd28d8e8da1c4f26a659ef8b5e921..1f8deec96727c18b6bf7486cf55e6d9869c15cba 100644 (file)
@@ -93,6 +93,9 @@ public class Sheet implements Model
     protected ProtectRecord              protect           =     null;
     protected PageBreakRecord            rowBreaks         =     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;
@@ -285,6 +288,18 @@ public class Sheet implements Model
                        {
                                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) 
                        {       
                                retval.rowBreaks = (PageBreakRecord)rec;                                
@@ -2500,7 +2515,38 @@ public class Sheet implements Model
         ProtectRecord retval = new ProtectRecord();
 
         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;
     }
 
@@ -2518,6 +2564,38 @@ public class Sheet implements Model
         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.
      * @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 )
 //    {
 //        int idx = findColumnIdx( columnNumber, 0 );
index 13e8a438936fcc00296c9657be4d395170a73100..67f210da5b14209b5d0b51ced15ea76dd6dcd49d 100644 (file)
@@ -29,14 +29,11 @@ import org.apache.poi.util.LittleEndian;
  * @version 2.0-pre
  */
 
-public class PasswordRecord
-    extends Record
-{
+public class PasswordRecord extends Record {
     public final static short sid = 0x13;
     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
      */
 
-    public PasswordRecord(RecordInputStream in)
-    {
+    public PasswordRecord(RecordInputStream in) {
         super(in);
     }
 
-    protected void validateSid(short id)
-    {
-        if (id != sid)
-        {
+    protected void validateSid(short id) {
+        if (id != sid) {
             throw new RecordFormatException("NOT A PASSWORD RECORD");
         }
     }
 
-    protected void fillFields(RecordInputStream in)
-    {
+    protected void fillFields(RecordInputStream in) {
         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
      *
      * @param password  representing the password
      */
 
-    public void setPassword(short password)
-    {
+    public void setPassword(short password) {
         field_1_password = password;
     }
 
@@ -78,14 +89,11 @@ public class PasswordRecord
      *
      * @return short  representing the password
      */
-
-    public short getPassword()
-    {
+    public short getPassword() {
         return field_1_password;
     }
 
-    public String toString()
-    {
+    public String toString() {
         StringBuffer buffer = new StringBuffer();
 
         buffer.append("[PASSWORD]\n");
@@ -95,8 +103,7 @@ public class PasswordRecord
         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, 2 + offset,
                               (( short ) 0x02));   // 2 bytes (6 total)
@@ -104,13 +111,11 @@ public class PasswordRecord
         return getRecordSize();
     }
 
-    public int getRecordSize()
-    {
+    public int getRecordSize() {
         return 6;
     }
 
-    public short getSid()
-    {
+    public short getSid() {
         return sid;
     }
 
index 6d3ea06e6910bfa97c90654f03f0778c8e2dafac..94868e0a6055916e21fc0b9afaff5d0caaa830bb 100644 (file)
@@ -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>
  * (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>
+ * 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>
  * @author Andrew C. Oliver (acoliver at apache dot org)
- * @version 2.0-pre
  */
 
 public class ProtectRecord
index 0ddfb6040887a9a2b3d42bf9e0a866683a2efe69..48b0d3fd8ae55da5f1c084f4e020875f3b53edc8 100644 (file)
@@ -74,7 +74,7 @@ public class RecordFactory
                 PaletteRecord.class, StringRecord.class, RecalcIdRecord.class, SharedFormulaRecord.class,
                 HorizontalPageBreakRecord.class, VerticalPageBreakRecord.class, 
                 WriteProtectRecord.class, FilePassRecord.class, PaneRecord.class,
-                NoteRecord.class
+                NoteRecord.class, ObjectProtectRecord.class, ScenarioProtectRecord.class
             };
     }
     private static Map           recordsMap  = recordsToMap(records);
index d5b7737e32433a005ab40a7398993a4c64623246..74bda60fbc45fbc45cbf4cb6a163444ab501f000 100644 (file)
@@ -872,17 +872,49 @@ public class HSSFSheet
         * @return true => protection enabled; false => protection disabled
         */
        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
         * @param protect true => protection enabled; false => protection disabled
+         * @deprecated use protectSheet(String, boolean, boolean)
         */
        public void setProtect(boolean 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
      * fraction.  For example to express a zoom of 75% use 3 for the numerator
index 2a8561ebd68ce5bb83c092c8a730c6cdb93fcc00..6d7feef3b36fadc682c8bdeae2aa2031fcb23de0 100644 (file)
@@ -26,6 +26,7 @@ import junit.framework.TestCase;
 import org.apache.poi.hssf.model.Sheet;
 import org.apache.poi.hssf.record.HCenterRecord;
 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.VCenterRecord;
 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 Andrew C. Oliver (acoliver apache org)
  */
 
 public class TestHSSFSheet
@@ -242,6 +244,20 @@ public class TestHSSFSheet
                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()
             throws Exception