1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
2 
3 /*
4  *  Engrampa
5  *
6  *  Copyright (C) 2006 The Free Software Foundation, Inc.
7  *
8  *  This program is free software; you can redistribute it and/or modify
9  *  it under the terms of the GNU General Public License as published by
10  *  the Free Software Foundation; either version 2 of the License, or
11  *  (at your option) any later version.
12  *
13  *  This program is distributed in the hope that it will be useful,
14  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
15  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  *  GNU General Public License for more details.
17  *
18  *  You should have received a copy of the GNU General Public License
19  *  along with this program; if not, write to the Free Software
20  *  Foundation, Inc., 59 Temple Street #330, Boston, MA 02110-1301, USA.
21  */
22 
23 #include <fcntl.h>
24 #include <unistd.h>
25 #include <errno.h>
26 #include <string.h>
27 #include <glib.h>
28 #include "java-utils.h"
29 
30 
31 /*
32  * The following code conforms to the JVM specification.(Java 2 Platform)
33  * For further changes to the classfile structure, please update the
34  * following macros.
35  */
36 
37 
38 /* Tags that identify structures */
39 
40 #define CONST_CLASS				 7
41 #define CONST_FIELDREF				 9
42 #define CONST_METHODREF				10
43 #define CONST_INTERFACEMETHODREF		11
44 #define CONST_STRING				 8
45 #define CONST_INTEGER				 3
46 #define CONST_FLOAT				 4
47 #define CONST_LONG				 5
48 #define CONST_DOUBLE				 6
49 #define CONST_NAMEANDTYPE			12
50 #define CONST_UTF8				 1
51 
52 /* Sizes of structures */
53 
54 #define CONST_CLASS_INFO			 2
55 #define CONST_FIELDREF_INFO			 4
56 #define CONST_METHODREF_INFO	 		 4
57 #define CONST_INTERFACEMETHODREF_INFO		 4
58 #define CONST_STRING_INFO			 2
59 #define CONST_INTEGER_INFO			 4
60 #define CONST_FLOAT_INFO			 4
61 #define CONST_LONG_INFO				 8
62 #define CONST_DOUBLE_INFO			 8
63 #define CONST_NAMEANDTYPE_INFO			 4
64 
65 
66 /* represents the utf8 strings in class file */
67 struct utf_string
68 {
69 	guint16  index;
70 	guint16  length;
71 	char    *str;
72 };
73 
74 /* structure that holds class information in a class file */
75 struct class_info
76 {
77 	guint16 index;
78 	guint16 name_index; /* index into the utf_strings */
79 };
80 
81 typedef struct {
82 	int     fd;
83 
84 	guint32 magic_no;		/* 0xCAFEBABE (JVM Specification) :) */
85 
86 	guint16 major;			/* versions */
87 	guint16 minor;
88 
89 	guint16 const_pool_count;
90 	GSList *const_pool_class;	/* (const_pool_count - 1) elements of tye 'CONST_class_info' */
91 	GSList *const_pool_utf;		/* (const_pool_count - 1) elements of type 'utf_strings' */
92 
93 	guint16 access_flags;
94 	guint16 this_class;		/* the index of the class the file is named after. */
95 
96 #if 0 /* not needed */
97 	guint16         super_class;
98 	guint16         interfaces_count;
99 	guint16        *interfaces;
100 	guint16         fields_count;
101 	field_info     *fields;
102 	guint16         methods_count;
103 	method_info    *methods;
104 	guint16         attributes_count;
105 	attribute_info *attributes;
106 #endif
107 } JavaClassFile;
108 
109 
110 static JavaClassFile*
java_class_file_new(void)111 java_class_file_new (void)
112 {
113 	JavaClassFile *cfile;
114 
115 	cfile = g_new0 (JavaClassFile, 1);
116 	cfile->fd = -1;
117 
118 	return cfile;
119 }
120 
121 
122 static void
java_class_file_free(JavaClassFile * cfile)123 java_class_file_free (JavaClassFile *cfile)
124 {
125 	GSList *scan;
126 
127 	if (cfile->const_pool_class != NULL) {
128 		g_slist_foreach (cfile->const_pool_class, (GFunc)g_free, NULL);
129 		g_slist_free (cfile->const_pool_class);
130 	}
131 
132 	for (scan = cfile->const_pool_utf; scan ; scan = scan->next) {
133 		struct utf_string *string = scan->data;
134 		g_free (string->str);
135 	}
136 
137 	if (cfile->const_pool_utf != NULL) {
138 		g_slist_foreach (cfile->const_pool_utf, (GFunc)g_free, NULL);
139 		g_slist_free (cfile->const_pool_utf);
140 	}
141 
142 	if (cfile->fd != -1)
143 		close (cfile->fd);
144 
145 	g_free (cfile);
146 }
147 
148 
149 /* The following function loads the utf8 strings and class structures from the
150  * class file. */
151 static void
load_constant_pool_utfs(JavaClassFile * cfile)152 load_constant_pool_utfs (JavaClassFile *cfile)
153 {
154 	guint8  tag;
155 	guint16 i = 0;		/* should be comparable with const_pool_count */
156 
157 	while ((i < cfile->const_pool_count - 1) && (read (cfile->fd, &tag, 1) != -1)) {
158 		struct utf_string *txt = NULL;
159 		struct class_info *class = NULL;
160 
161 		switch (tag) {
162 		case CONST_CLASS:
163 			class = g_new0 (struct class_info, 1);
164 			class->index = i + 1;
165 			if (read (cfile->fd, &class->name_index, 2) != 2) {
166 				g_free (class);
167 				return;	/* error reading */
168 			}
169 			class->name_index = GUINT16_FROM_BE (class->name_index);
170 			cfile->const_pool_class = g_slist_append (cfile->const_pool_class, class);
171 			break;
172 
173 		case CONST_FIELDREF:
174 			lseek (cfile->fd, CONST_FIELDREF_INFO, SEEK_CUR);
175 			break;
176 
177 		case CONST_METHODREF:
178 			lseek (cfile->fd, CONST_METHODREF_INFO, SEEK_CUR);
179 			break;
180 
181 		case CONST_INTERFACEMETHODREF:
182 			lseek (cfile->fd, CONST_INTERFACEMETHODREF_INFO, SEEK_CUR);
183 			break;
184 
185 		case CONST_STRING:
186 			lseek (cfile->fd, CONST_STRING_INFO, SEEK_CUR);
187 			break;
188 
189 		case CONST_INTEGER:
190 			lseek (cfile->fd, CONST_INTEGER_INFO, SEEK_CUR);
191 			break;
192 
193 		case CONST_FLOAT:
194 			lseek (cfile->fd, CONST_FLOAT_INFO, SEEK_CUR);
195 			break;
196 
197 		case CONST_LONG:
198 			lseek (cfile->fd, CONST_LONG_INFO, SEEK_CUR);
199 			break;
200 
201 		case CONST_DOUBLE:
202 			lseek (cfile->fd, CONST_DOUBLE_INFO, SEEK_CUR);
203 			break;
204 
205 		case CONST_NAMEANDTYPE:
206 			lseek (cfile->fd, CONST_NAMEANDTYPE_INFO, SEEK_CUR);
207 			break;
208 
209 		case CONST_UTF8:
210 			txt = g_new0 (struct utf_string, 1);
211 			txt->index = i + 1;
212 			if (read (cfile->fd, &(txt->length), 2) == -1) {
213 				g_free (txt);
214 				return;	/* error while reading */
215 			}
216 			txt->length = GUINT16_FROM_BE (txt->length);
217 			txt->str = g_new0 (char, txt->length);
218 			if (read (cfile->fd, txt->str, txt->length) == -1) {
219 				g_free (txt);
220 				return;	/* error while reading */
221 			}
222 			cfile->const_pool_utf = g_slist_append (cfile->const_pool_utf, txt);
223 			break;
224 
225 		default:
226 			return;	/* error - unknown tag in class file */
227 			break;
228 		}
229 		i++;
230 	}
231 
232 #ifdef DEBUG
233 	g_print( "Number of Entries: %d\n", i );
234 #endif
235 }
236 
237 
238 static char*
close_and_exit(JavaClassFile * cfile)239 close_and_exit (JavaClassFile *cfile)
240 {
241 	java_class_file_free (cfile);
242 	return NULL;
243 }
244 
245 
246 /* This function extracts the package name from a class file */
247 char*
get_package_name_from_class_file(char * fname)248 get_package_name_from_class_file (char *fname)
249 {
250 	char          *package = NULL;
251 	JavaClassFile *cfile;
252 	guint16        length = 0, end = 0, utf_index = 0;
253 	guint32        magic;
254 	guint16        major, minor, count;
255 	int            i = 0;
256 
257 	if (! g_file_test (fname, G_FILE_TEST_EXISTS))
258 		return NULL;
259 
260 	cfile = java_class_file_new ();
261 	cfile->fd = open (fname, O_RDONLY);
262 	if (cfile->fd == -1)
263 		return close_and_exit (cfile);
264 
265 	if ((i = read (cfile->fd, &magic, 4)) != 4)
266 		return close_and_exit (cfile);
267 	cfile->magic_no = GUINT32_FROM_BE (magic);
268 
269 	if (read (cfile->fd, &major, 2 ) != 2)
270 		return close_and_exit (cfile);
271 	cfile->major = GUINT16_FROM_BE (major);
272 
273 	if (read (cfile->fd, &minor, 2) != 2)
274 		return close_and_exit (cfile);
275 	cfile->minor = GUINT16_FROM_BE (minor);
276 
277 	if (read (cfile->fd, &count, 2) != 2)
278 		return close_and_exit (cfile);
279 	cfile->const_pool_count = GUINT16_FROM_BE(count);
280 	load_constant_pool_utfs (cfile);
281 
282 	if (read (cfile->fd, &cfile->access_flags, 2) != 2)
283 		return close_and_exit (cfile);
284 	cfile->access_flags = GUINT16_FROM_BE (cfile->access_flags);
285 
286 	if (read (cfile->fd, &cfile->this_class, 2) != 2)
287 		return close_and_exit (cfile);
288 	cfile->this_class = GUINT16_FROM_BE(cfile->this_class);
289 
290 	/* now search for the class structure with index = cfile->this_class */
291 
292 	for (i = 0; (i < g_slist_length (cfile->const_pool_class)) && (utf_index == 0); i++ ) {
293 		struct class_info *class = g_slist_nth_data (cfile->const_pool_class, i);
294 		if (class->index == cfile->this_class)
295 			utf_index = class->name_index; /* terminates loop */
296 	}
297 
298 	/* now search for the utf8 string with index = utf_index */
299 
300 	for (i = 0; i < g_slist_length (cfile->const_pool_utf); i++) {
301 		struct utf_string *data = g_slist_nth_data (cfile->const_pool_utf, i);
302 		if (data->index == utf_index) {
303 			package = g_strndup (data->str, data->length);
304 			length = data->length;
305 			break;
306 		}
307 	}
308 
309 	if (package != NULL) {
310 		for (i = length; (i >= 0) && (end == 0); i-- )
311 			if (package[i] == '/')
312 				end = i;
313                 char *package_padded = g_strndup (package, end);
314                 g_free(package);
315                 package = package_padded;
316 	}
317 
318 	java_class_file_free (cfile);
319 
320 	return package;
321 }
322 
323 
324 /* This function consumes a comment from the java file
325  * multiline = TRUE implies that comment is multiline */
326 static void
consume_comment(int fdesc,gboolean multiline)327 consume_comment (int      fdesc,
328 		 gboolean multiline)
329 {
330 	gboolean escaped = FALSE;
331 	gboolean star = FALSE;
332 	char     ch;
333 
334 	while (read (fdesc, &ch, 1) == 1) {
335 		switch (ch) {
336 		case '/':
337 			if (escaped)
338 				break;
339 			else if (star)
340 				return;
341 			break;
342 
343 		case '\n':
344 			if (! multiline)
345 				return;
346 			break;
347 
348 		case '*':
349 			escaped = FALSE;
350 			star = TRUE;
351 			break;
352 
353 		case '\\':
354 			escaped = ! escaped;
355 			break;
356 
357 		default:
358 			escaped = FALSE;
359 			star = FALSE;
360 			break;
361 		}
362 	}
363 }
364 
365 
366 /* This function extracts package name from a java file */
367 char*
get_package_name_from_java_file(char * fname)368 get_package_name_from_java_file (char *fname)
369 {
370 	char          *package = NULL;
371 	JavaClassFile *cfile;
372 	gboolean       prev_char_is_bslash = FALSE;
373 	gboolean       valid_char_found = FALSE;
374 	char           ch;
375 
376 	if (! g_file_test (fname, G_FILE_TEST_EXISTS))
377 		return NULL;
378 
379 	cfile = java_class_file_new ();
380 	cfile->fd = open (fname, O_RDONLY);
381 	if (cfile->fd == -1)
382 		return close_and_exit (cfile);
383 
384 	while (! valid_char_found && (read (cfile->fd, &ch, 1) == 1)) {
385 		switch (ch) {
386 		case '/':
387 			if (prev_char_is_bslash == TRUE) {
388 				consume_comment (cfile->fd, FALSE);
389 				prev_char_is_bslash = FALSE;
390 			}
391 			else
392 				prev_char_is_bslash = TRUE;
393 			break;
394 
395 		case '*':
396 			if (prev_char_is_bslash == TRUE)
397 				consume_comment (cfile->fd, TRUE);
398 			prev_char_is_bslash = FALSE;
399 			break;
400 
401 		case ' ':
402 		case '\t':
403 		case '\r':
404 		case '\n':
405 			prev_char_is_bslash = FALSE;
406 			break;
407 
408 		default:
409 			prev_char_is_bslash = FALSE;
410 			valid_char_found = TRUE;
411 			break;
412 		}
413 	}
414 
415 	if (ch == 'p')	{
416 		char first_valid_word[8] = "";
417 
418 		first_valid_word[0] = 'p';
419 		if (read (cfile->fd, &first_valid_word[1], 6) != 6)
420 			return close_and_exit (cfile);
421 
422 		first_valid_word[7] = 0;
423 		if (g_ascii_strcasecmp (first_valid_word, "package") == 0) {
424 			char buffer[500];
425 			int  index = 0;
426 
427 			while (read (cfile->fd, &ch, 1) == 1) {
428 				if (ch == ';')
429 					break;
430 				if (ch == '.')
431 					buffer[index++] = '/';
432 				else
433 					buffer[index++] = ch;
434 			}
435 			buffer[index] = 0;
436 			package = g_strdup (buffer);
437 		}
438 	}
439 
440 	java_class_file_free (cfile);
441 
442 	return package;
443 }
444