View Javadoc

1   /*
2    * jGuild Project: jRPM
3    * Released under the Apache License ( http://www.apache.org/LICENSE )
4    */
5   package com.jguild.jrpm.io;
6   
7   import java.io.BufferedInputStream;
8   import java.io.DataInputStream;
9   import java.io.File;
10  import java.io.FileInputStream;
11  import java.io.FilterInputStream;
12  import java.io.IOException;
13  import java.io.InputStream;
14  import java.util.ArrayList;
15  import java.util.zip.GZIPInputStream;
16  
17  import org.apache.log4j.Logger;
18  
19  import com.jguild.jrpm.io.bzip2.CBZip2InputStream;
20  import com.jguild.jrpm.io.cpio.CPIOEntry;
21  import com.jguild.jrpm.io.cpio.CPIOInputStream;
22  import com.jguild.jrpm.io.datatype.DataTypeIf;
23  import com.jguild.jrpm.io.datatype.I18NSTRING;
24  import com.jguild.jrpm.io.datatype.STRING_ARRAY;
25  import com.jguild.jrpm.io.datatype.TypeFactory;
26  
27  /***
28   * This class allows IO access to an RPM file.
29   * 
30   * @version $Id: RPMFile.java,v 1.22 2004/05/06 20:59:22 mkuss Exp $
31   */
32  public class RPMFile {
33  
34      private static final Logger logger = Logger.getLogger(RPMFile.class);
35  
36      private RPMHeader header = null;
37  
38      private RPMLead lead = null;
39  
40      private RPMSignature signature = null;
41  
42      private int localePosition;
43  
44      private File rpmFile = null;
45  
46      private boolean editingRpmFile = false;
47  
48      /***
49       * Creates a new empty RPMFile object.
50       */
51      public RPMFile() {
52      }
53  
54      /***
55       * Creates a new RPMFile object out of a file.
56       * 
57       * @param fh
58       *           The file object representing a rpm file
59       */
60      public RPMFile(File fh) {
61          rpmFile = fh;
62      }
63  
64      private void reset() {
65          header = null;
66          lead = null;
67          signature = null;
68          editingRpmFile = false;
69      }
70  
71      /***
72       * Set the file this RPMFile should represent
73       * 
74       * @param fh
75       *           The file object representing a rpm file
76       */
77      public synchronized void setFile(File fh) {
78          if (editingRpmFile)
79                  throw new IllegalStateException("RPM file is currently edited");
80          rpmFile = fh;
81          reset();
82      }
83  
84      /***
85       * Parse the RPMFile and will extract all informations. This must be called
86       * before any informations can be read from the rpm file.
87       * 
88       * @throws IOException
89       *            If an error occurs during read of the rpm file
90       */
91      public synchronized void parse() throws IOException {
92          if (rpmFile == null)
93                  throw new IllegalStateException("A file must be specified");
94  
95          if (!rpmFile.exists())
96                  throw new IllegalStateException(
97                          "The specified file does not exist");
98  
99          try {
100             readFromStream(new BufferedInputStream(new FileInputStream(rpmFile)));
101         } catch (IOException e) {
102             reset();
103             throw e;
104         }
105         editingRpmFile = true;
106     }
107 
108     /***
109      * Get the header section of this rpm file.
110      * 
111      * @return The rpm header
112      */
113     public synchronized RPMHeader getHeader() {
114         if (header == null)
115                 throw new IllegalStateException(
116                         "There are no header informations");
117 
118         return header;
119     }
120 
121     /***
122      * Get all known tags of this rpm file. This is equivalent to the
123      * --querytags option in rpm.
124      * 
125      * @return An array of all tag names
126      */
127     public static String[] getKnownTagNames() {
128         return Header.getKnownTagNames();
129     }
130 
131     /***
132      * Get the lead section of this rpm file
133      * 
134      * @return The rpm lead
135      */
136     public synchronized RPMLead getLead() {
137         if (lead == null)
138                 throw new IllegalStateException(
139                         "There are no lead informations");
140 
141         return lead;
142     }
143 
144     /***
145      * Set the locale as int for all I18N strings that are returned by
146      * getTag(). The position has to correspond with the same position in the
147      * array returned by getLocales().
148      * 
149      * @param pos
150      *           The position in the array returned by getLocales().
151      */
152     public synchronized void setLocale(int pos) {
153         localePosition = pos;
154     }
155 
156     /***
157      * Set the locale as string for all I18N strings that are returned by
158      * getTag(). The string must match with a string returned by getLocales().
159      * 
160      * @param locale
161      *           A locale matching a locale returned by getLocales()
162      * @throws IllegalArgumentException
163      *            If the locale is not defined by getLocales().
164      */
165     public synchronized void setLocale(String locale) {
166         String[] locales = ((STRING_ARRAY) getTag("HEADERI18NTABLE")).getData();
167 
168         for (int pos = 0; pos < locales.length; pos++) {
169             if (locales[pos].equals(locale)) {
170                 setLocale(pos);
171 
172                 return;
173             }
174         }
175 
176         throw new IllegalArgumentException("Unknown locale <" + locale + ">");
177     }
178 
179     /***
180      * Return all known locales that are supported by this RPM file. The array
181      * is read out of the RPM file with the tag "HEADERI18NTABLE". The RPM has
182      * one entry for all I18N strings defined by this tag.
183      * 
184      * @return A string array of all defined locales
185      */
186     public synchronized String[] getLocales() {
187         return ((STRING_ARRAY) getTag("HEADERI18NTABLE")).getData();
188     }
189 
190     /***
191      * Get the signature section of this rpm file
192      * 
193      * @return The rpm signature
194      */
195     public synchronized RPMSignature getSignature() {
196         if (signature == null)
197                 throw new IllegalStateException(
198                         "There are no signature informations");
199 
200         return signature;
201     }
202 
203     /***
204      * Get a tag by id as a Long
205      * 
206      * @param tag
207      *           A tag id as a Long
208      * @return A data struct containing the data of this tag
209      */
210     public synchronized DataTypeIf getTag(Long tag) {
211         DataTypeIf data = getHeader().getTag(tag);
212 
213         // set the locale for all I18N strings
214         if (data instanceof I18NSTRING) {
215             ((I18NSTRING) data).setLocaleIndex(localePosition);
216         }
217 
218         return data;
219     }
220 
221     /***
222      * Get a tag by id as a long
223      * 
224      * @param tag
225      *           A tag id as a long
226      * @return A data struct containing the data of this tag
227      */
228     public synchronized DataTypeIf getTag(long tag) {
229         return getTag(new Long(tag));
230     }
231 
232     /***
233      * Get a tag by name
234      * 
235      * @param tagname
236      *           A tag name
237      * @return A data struct containing the data of this tag
238      */
239     public synchronized DataTypeIf getTag(String tagname) {
240         return getTag(getTagIdForName(tagname));
241     }
242 
243     /***
244      * Read a tag with a given tag name.
245      * 
246      * @param tagname
247      *           A RPM tag name
248      * @return The id of the RPM tag
249      * @throws IllegalArgumentException
250      *            if the tag name was not found
251      * @see Header#getTagIdForName(String)
252      */
253     public synchronized long getTagIdForName(String tagname) {
254         return getHeader().getTagIdForName(tagname);
255     }
256 
257     /***
258      * Get all tag ids contained in this rpm file.
259      * 
260      * @return All tag ids contained in this rpm file.
261      */
262     public synchronized long[] getTagIds() {
263         return getHeader().getTagIds();
264     }
265 
266     /***
267      * Read a tag with a given tag id.
268      * 
269      * @param tagid
270      *           A RPM tag id
271      * @return The name of the RPM tag
272      * @throws IllegalArgumentException
273      *            if the tag id was not found
274      * @see Header#getTagNameForId(long)
275      */
276     public synchronized String getTagNameForId(long tagid) {
277         return getHeader().getTagNameForId(tagid);
278     }
279 
280     /***
281      * Get all tag names contained in this rpm file.
282      * 
283      * @return All tag names contained in this rpm file.
284      */
285     public synchronized String[] getTagNames() {
286         return getHeader().getTagNames();
287     }
288 
289     /***
290      * Read informations of a rpm file out of an input stream.
291      * 
292      * @param rpmInputStream
293      *           The input stream representing the rpm file
294      * @throws IOException
295      *            if an error occurs during read of the rpm file
296      */
297     private void readFromStream(InputStream rpmInputStream) throws IOException {
298         InputStream inputStream = new DataInputStream(rpmInputStream);
299 
300         lead = new RPMLead((DataInputStream) inputStream);
301         signature = new RPMSignature((DataInputStream) inputStream);
302 
303         if (logger.isDebugEnabled()) {
304             logger.debug("Signature Size: " + signature.getSize());
305         }
306 
307         header = new RPMHeader((DataInputStream) inputStream);
308 
309         if (logger.isDebugEnabled()) {
310             logger.debug("Header Size: " + header.getSize());
311         }
312 
313         String payloadFormat = getTag("PAYLOADFORMAT").toString();
314         String payloadCompressor = getTag("PAYLOADCOMPRESSOR").toString();
315         if (payloadFormat.equals("cpio")) {
316             if (logger.isDebugEnabled()) {
317                 logger.debug("PAYLOADCOMPRESSOR: " + payloadCompressor);
318             }
319 
320             if (payloadCompressor.equals("gzip")) {
321                 inputStream = new GZIPInputStream(rpmInputStream);
322             } else if (payloadCompressor.equals("bzip2")) {
323                 inputStream = new CBZip2InputStream(rpmInputStream);
324             } else if (payloadCompressor.equals("none")) {
325                 inputStream = rpmInputStream;
326             } else {
327                 throw new IOException("Unsupported compressor type "
328                         + payloadCompressor);
329             }
330 
331             ByteCountInputStream countInputStream = new ByteCountInputStream(
332                     inputStream);
333             CPIOInputStream cpioInputStream = new CPIOInputStream(
334                     countInputStream);
335             CPIOEntry readEntry;
336             ArrayList fileNamesList = new ArrayList();
337             String fileEntry;
338             while ((readEntry = cpioInputStream.getNextEntry()) != null) {
339                 if (logger.isDebugEnabled()) {
340                     logger.debug("Read CPIO entry: " + readEntry.getName()
341                             + " ;mode:" + readEntry.getMode());
342                 }
343                 if (readEntry.isRegularFile() || readEntry.isSymbolicLink()
344                         || readEntry.isDirectory()) {
345                     fileEntry = readEntry.getName();
346                     if (fileEntry.startsWith("./"))
347                             fileEntry = fileEntry.substring(1);
348                     fileNamesList.add(fileEntry);
349                 }
350             }
351             getHeader().setTag(
352                     "FILENAMES",
353                     TypeFactory.createSTRING_ARRAY((String[]) fileNamesList
354                             .toArray(new String[0])));
355         } else {
356             throw new IOException("Unsupported Payload type " + payloadFormat);
357         }
358         //@TODO check ARCHIVESIZE with countInputStream.getCount();
359         
360         // filling in signatures
361         // TODO: check signatures!
362         setHeaderTagFromSignature("SIGSIZE", "SIZE");
363         setHeaderTagFromSignature("SIGLEMD5_1", "LEMD5_1");
364         setHeaderTagFromSignature("SIGPGP", "PGP");
365         setHeaderTagFromSignature("SIGLEMD5_2", "LEMD5_2");
366         setHeaderTagFromSignature("SIGMD5", "MD5");
367         setHeaderTagFromSignature("SIGGPG", "GPG");
368         setHeaderTagFromSignature("SIGPGP5", "PGP5");
369         setHeaderTagFromSignature("BADSHA1_1", "BADSHA1_1");
370         setHeaderTagFromSignature("BADSHA1_2", "BADSHA1_2");
371         setHeaderTagFromSignature("DSAHEADER", "DSA");
372         setHeaderTagFromSignature("RSAHEADER", "RSA");
373         setHeaderTagFromSignature("SHA1HEADER", "SHA1");
374         setHeaderTagFromSignature("ARCHIVESIZE", "PAYLOADSIZE");
375         rpmInputStream.close();
376     }
377 
378     private void setHeaderTagFromSignature(String headerTag, String signatureTag) {
379         if (getHeader().getTag(headerTag) == null)
380                 getHeader().setTag(headerTag,
381                         getSignature().getTag(signatureTag));
382     }
383 
384     /***
385      * Release locked resources.
386      */
387     public void close() {
388         reset();
389     }
390 
391     private class ByteCountInputStream extends FilterInputStream {
392 
393         private int count = 0;
394 
395         public ByteCountInputStream(InputStream is) {
396             super(is);
397         }
398 
399         public int getCount() {
400             return count;
401         }
402 
403         public int read() throws IOException {
404             count++;
405             return in.read();
406         }
407 
408         public int read(byte b[]) throws IOException {
409             int size = read(b, 0, b.length);
410             count += size;
411             return size;
412         }
413 
414         public int read(byte b[], int off, int len) throws IOException {
415             int size = in.read(b, off, len);
416             count += size;
417             return size;
418         }
419 
420         public long skip(long n) throws IOException {
421             long size = in.skip(n);
422             count += size;
423             return size;
424         }
425     }
426 }