1:
37:
38:
39: package ;
40:
41: import ;
42:
43: import ;
44: import ;
45: import ;
46: import ;
47: import ;
48: import ;
49: import ;
50: import ;
51: import ;
52: import ;
53: import ;
54: import ;
55:
56:
63: public abstract class JarUtils
64: {
65: private static final Logger log = Logger.getLogger(JarUtils.class.getName());
66: public static final String META_INF = "META-INF/";
67: public static final String DSA_SUFFIX = ".DSA";
68: public static final String SF_SUFFIX = ".SF";
69: public static final String NAME = "Name";
70:
71:
74: public static final String MANIFEST_VERSION = "Manifest-Version";
75:
76:
80: public static final String SIGNATURE_VERSION = "Signature-Version";
81:
82:
83: public static final byte[] CRLF = new byte[] { 0x0D, 0x0A };
84: private static final String DEFAULT_MF_VERSION = "1.0";
85: private static final String DEFAULT_SF_VERSION = "1.0";
86: private static final Name CREATED_BY = new Name("Created-By");
87: private static final String CREATOR = SystemProperties.getProperty("java.version")
88: + " ("
89: + SystemProperties.getProperty("java.vendor")
90: + ")";
91:
92:
93:
94:
95:
96: public static void
97: readMFManifest(Attributes attr, Map entries, InputStream in)
98: throws IOException
99: {
100: BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
101: readMainSection(attr, br);
102: readIndividualSections(entries, br);
103: }
104:
105: public static void
106: readSFManifest(Attributes attr, Map entries, InputStream in)
107: throws IOException
108: {
109: BufferedReader br = new BufferedReader(new InputStreamReader(in, "UTF-8"));
110: String version_header = Name.SIGNATURE_VERSION.toString();
111: try
112: {
113: String version = expectHeader(version_header, br);
114: attr.putValue(SIGNATURE_VERSION, version);
115: if (! DEFAULT_SF_VERSION.equals(version))
116: log.warning("Unexpected version number: " + version
117: + ". Continue (but may fail later)");
118: }
119: catch (IOException ioe)
120: {
121: throw new JarException("Signature file MUST start with a "
122: + version_header + ": " + ioe.getMessage());
123: }
124: read_attributes(attr, br);
125:
126:
127: String s = br.readLine();
128: while (s != null && s.length() > 0)
129: {
130: Attributes eAttr = readSectionName(s, br, entries);
131: read_attributes(eAttr, br);
132: s = br.readLine();
133: }
134: }
135:
136: private static void readMainSection(Attributes attr, BufferedReader br)
137: throws IOException
138: {
139:
140: read_attributes(attr, br);
141:
142:
143:
144: if (attr.getValue(Name.MANIFEST_VERSION) == null)
145: attr.putValue(MANIFEST_VERSION, "0.0");
146: }
147:
148: private static void readIndividualSections(Map entries, BufferedReader br)
149: throws IOException
150: {
151: String s = br.readLine();
152: while (s != null && (! s.equals("")))
153: {
154: Attributes attr = readSectionName(s, br, entries);
155: read_attributes(attr, br);
156: s = br.readLine();
157: }
158: }
159:
160:
165: private static void readVersionInfo(Attributes attr, BufferedReader br)
166: throws IOException
167: {
168: String version_header = Name.MANIFEST_VERSION.toString();
169: try
170: {
171: String value = expectHeader(version_header, br);
172: attr.putValue(MANIFEST_VERSION, value);
173: }
174: catch (IOException ioe)
175: {
176: throw new JarException("Manifest should start with a " + version_header
177: + ": " + ioe.getMessage());
178: }
179: }
180:
181: private static String expectHeader(String header, BufferedReader br)
182: throws IOException
183: {
184: String s = br.readLine();
185: if (s == null)
186: throw new JarException("unexpected end of file");
187:
188: return expectHeader(header, br, s);
189: }
190:
191: private static void read_attributes(Attributes attr, BufferedReader br)
192: throws IOException
193: {
194: String s = br.readLine();
195: while (s != null && (! s.equals("")))
196: {
197: readAttribute(attr, s, br);
198: s = br.readLine();
199: }
200: }
201:
202: private static void
203: readAttribute(Attributes attr, String s, BufferedReader br) throws IOException
204: {
205: try
206: {
207: int colon = s.indexOf(": ");
208: String name = s.substring(0, colon);
209: String value_start = s.substring(colon + 2);
210: String value = readHeaderValue(value_start, br);
211: attr.putValue(name, value);
212: }
213: catch (IndexOutOfBoundsException iobe)
214: {
215: throw new JarException("Manifest contains a bad header: " + s);
216: }
217: }
218:
219: private static String readHeaderValue(String s, BufferedReader br)
220: throws IOException
221: {
222: boolean try_next = true;
223: while (try_next)
224: {
225:
226: br.mark(1);
227: if (br.read() == ' ')
228: s += br.readLine();
229: else
230: {
231: br.reset();
232: try_next = false;
233: }
234: }
235: return s;
236: }
237:
238: private static Attributes
239: readSectionName(String s, BufferedReader br, Map entries) throws JarException
240: {
241: try
242: {
243: String name = expectHeader(NAME, br, s);
244: Attributes attr = new Attributes();
245: entries.put(name, attr);
246: return attr;
247: }
248: catch (IOException ioe)
249: {
250: throw new JarException("Section should start with a Name header: "
251: + ioe.getMessage());
252: }
253: }
254:
255: private static String expectHeader(String header, BufferedReader br, String s)
256: throws IOException
257: {
258: try
259: {
260: String name = s.substring(0, header.length() + 1);
261: if (name.equalsIgnoreCase(header + ":"))
262: {
263: String value_start = s.substring(header.length() + 2);
264: return readHeaderValue(value_start, br);
265: }
266: }
267: catch (IndexOutOfBoundsException ignored)
268: {
269: }
270:
271: throw new JarException("unexpected '" + s + "'");
272: }
273:
274:
275:
276: public static void
277: writeMFManifest(Attributes attr, Map entries, OutputStream stream)
278: throws IOException
279: {
280: BufferedOutputStream out = stream instanceof BufferedOutputStream
281: ? (BufferedOutputStream) stream
282: : new BufferedOutputStream(stream, 4096);
283: writeVersionInfo(attr, out);
284: Iterator i;
285: Map.Entry e;
286: for (i = attr.entrySet().iterator(); i.hasNext();)
287: {
288: e = (Map.Entry) i.next();
289:
290: if (! Name.MANIFEST_VERSION.equals(e.getKey()))
291: writeAttributeEntry(e, out);
292: }
293: out.write(CRLF);
294:
295: Iterator j;
296: for (i = entries.entrySet().iterator(); i.hasNext();)
297: {
298: e = (Map.Entry) i.next();
299: writeHeader(NAME, e.getKey().toString(), out);
300: Attributes eAttr = (Attributes) e.getValue();
301: for (j = eAttr.entrySet().iterator(); j.hasNext();)
302: {
303: Map.Entry e2 = (Map.Entry) j.next();
304: writeAttributeEntry(e2, out);
305: }
306: out.write(CRLF);
307: }
308:
309: out.flush();
310: }
311:
312: public static void
313: writeSFManifest(Attributes attr, Map entries, OutputStream stream)
314: throws IOException
315: {
316: BufferedOutputStream out = stream instanceof BufferedOutputStream
317: ? (BufferedOutputStream) stream
318: : new BufferedOutputStream(stream, 4096);
319: writeHeader(Name.SIGNATURE_VERSION.toString(), DEFAULT_SF_VERSION, out);
320: writeHeader(CREATED_BY.toString(), CREATOR, out);
321: Iterator i;
322: Map.Entry e;
323: for (i = attr.entrySet().iterator(); i.hasNext();)
324: {
325: e = (Map.Entry) i.next();
326: Name name = (Name) e.getKey();
327: if (Name.SIGNATURE_VERSION.equals(name) || CREATED_BY.equals(name))
328: continue;
329:
330: writeHeader(name.toString(), (String) e.getValue(), out);
331: }
332: out.write(CRLF);
333:
334: Iterator j;
335: for (i = entries.entrySet().iterator(); i.hasNext();)
336: {
337: e = (Map.Entry) i.next();
338: writeHeader(NAME, e.getKey().toString(), out);
339: Attributes eAttr = (Attributes) e.getValue();
340: for (j = eAttr.entrySet().iterator(); j.hasNext();)
341: {
342: Map.Entry e2 = (Map.Entry) j.next();
343: writeHeader(e2.getKey().toString(), (String) e2.getValue(), out);
344: }
345: out.write(CRLF);
346: }
347:
348: out.flush();
349: }
350:
351: private static void writeVersionInfo(Attributes attr, OutputStream out)
352: throws IOException
353: {
354:
355: String version = attr.getValue(Name.MANIFEST_VERSION);
356: if (version == null)
357: version = DEFAULT_MF_VERSION;
358:
359: writeHeader(Name.MANIFEST_VERSION.toString(), version, out);
360: }
361:
362: private static void writeAttributeEntry(Map.Entry entry, OutputStream out)
363: throws IOException
364: {
365: String name = entry.getKey().toString();
366: String value = entry.getValue().toString();
367: if (name.equalsIgnoreCase(NAME))
368: throw new JarException("Attributes cannot be called 'Name'");
369:
370: if (name.startsWith("From"))
371: throw new JarException("Header cannot start with the four letters 'From'"
372: + name);
373:
374: writeHeader(name, value, out);
375: }
376:
377:
400: private static void writeHeader(String name, String value, OutputStream out)
401: throws IOException
402: {
403: String target = name + ": ";
404: byte[] b = target.getBytes("UTF-8");
405: if (b.length > 72)
406: throw new IOException("Attribute's name already longer than 70 bytes");
407:
408: if (b.length == 72)
409: {
410: out.write(b);
411: out.write(CRLF);
412: target = " " + value;
413: }
414: else
415: target = target + value;
416:
417: int n;
418: while (true)
419: {
420: b = target.getBytes("UTF-8");
421: if (b.length < 73)
422: {
423: out.write(b);
424: break;
425: }
426:
427:
428: n = 72;
429: while (true)
430: {
431: b = target.substring(0, n).getBytes("UTF-8");
432: if (b.length < 73)
433: break;
434:
435: n--;
436: if (n < 1)
437: throw new IOException("Header is unbreakable and longer than 72 bytes");
438: }
439:
440: out.write(b);
441: out.write(CRLF);
442: target = " " + target.substring(n);
443: }
444:
445: out.write(CRLF);
446: }
447: }