components/visual-panels/core/src/cmd/rad/mod/panels/mod_panels.c
changeset 827 0944d8c0158b
child 901 19b502ccabc8
equal deleted inserted replaced
826:c6aad84d2493 827:0944d8c0158b
       
     1 /*
       
     2  * CDDL HEADER START
       
     3  *
       
     4  * The contents of this file are subject to the terms of the
       
     5  * Common Development and Distribution License (the "License").
       
     6  * You may not use this file except in compliance with the License.
       
     7  *
       
     8  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
       
     9  * or http://www.opensolaris.org/os/licensing.
       
    10  * See the License for the specific language governing permissions
       
    11  * and limitations under the License.
       
    12  *
       
    13  * When distributing Covered Code, include this CDDL HEADER in each
       
    14  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
       
    15  * If applicable, add the following below this CDDL HEADER, with the
       
    16  * fields enclosed by brackets "[]" replaced with your own identifying
       
    17  * information: Portions Copyright [yyyy] [name of copyright owner]
       
    18  *
       
    19  * CDDL HEADER END
       
    20  */
       
    21 
       
    22 /*
       
    23  * Copyright (c) 2009, 2012, Oracle and/or its affiliates. All rights reserved.
       
    24  */
       
    25 
       
    26 #include <assert.h>
       
    27 #include <dirent.h>
       
    28 #include <errno.h>
       
    29 #include <fcntl.h>
       
    30 #include <libxml/tree.h>
       
    31 #include <limits.h>
       
    32 #include <openssl/evp.h>
       
    33 #include <openssl/md5.h>
       
    34 #include <string.h>
       
    35 #include <sys/stat.h>
       
    36 #include <unistd.h>
       
    37 #include "api_panels.h"
       
    38 
       
    39 #define	PANELDESCDIR	"/usr/share/vpanels/conf"
       
    40 
       
    41 #define	HASH_BSIZE	65536
       
    42 
       
    43 /*
       
    44  * Function prototypes
       
    45  */
       
    46 
       
    47 static int md5_file(const char *fname, unsigned char *hash);
       
    48 static data_t *get_text(xmlDoc *doc, xmlNode *node);
       
    49 static char *get_text_str(xmlDoc *doc, xmlNode *node);
       
    50 static data_t *get_prop(xmlNode *node, const char *name);
       
    51 static data_t *file_to_token(rad_locale_t *rlocale, const char *pname,
       
    52     char *file);
       
    53 static conerr_t token_to_file(data_t *t, char **f);
       
    54 static data_t *create_resource(rad_locale_t *rlocale, const char *pname,
       
    55     char *file);
       
    56 static void add_localized(rad_locale_t *rlocale, const char *pname,
       
    57     data_t *farray, char *file);
       
    58 static data_t *read_panel_path(rad_locale_t *rlocale, const char *path);
       
    59 static data_t *read_panel(const char *locale, const char *pname);
       
    60 
       
    61 /*
       
    62  * Static data
       
    63  */
       
    64 
       
    65 static xmlDtd *dtd;
       
    66 static rad_modinfo_t modinfo = { "panels", "Visual Panels module" };
       
    67 
       
    68 /*
       
    69  * Static functions
       
    70  */
       
    71 
       
    72 static int
       
    73 md5_file(const char *fname, unsigned char *hash)
       
    74 {
       
    75 	int fd, len = -1;
       
    76 	MD5_CTX ctx;
       
    77 
       
    78 	char *buffer = malloc(HASH_BSIZE);
       
    79 	if (buffer == NULL)
       
    80 		return (-1);
       
    81 
       
    82 	if ((fd = open(fname, O_RDONLY)) == -1) {
       
    83 		free(buffer);
       
    84 		return (-1);
       
    85 	}
       
    86 
       
    87 	if (MD5_Init(&ctx) == 0)
       
    88 		goto out;
       
    89 
       
    90 	while ((len = read(fd, buffer, HASH_BSIZE)) > 0)
       
    91 		if (MD5_Update(&ctx, buffer, len) == 0) {
       
    92 			len = -1;
       
    93 			break;
       
    94 		}
       
    95 	if (MD5_Final(hash, &ctx) == 0)
       
    96 		len = -1;
       
    97 
       
    98 out:
       
    99 	(void) close(fd);
       
   100 	free(buffer);
       
   101 	return (len);	/* Should be 0 or -1 */
       
   102 }
       
   103 
       
   104 /*
       
   105  * Seems like overkill, but it's better than mixing xml-allocated
       
   106  * and other strings.
       
   107  */
       
   108 static data_t *
       
   109 get_text(xmlDoc *doc, xmlNode *node)
       
   110 {
       
   111 	data_t *result;
       
   112 	xmlChar *d = xmlNodeListGetString(doc, node, 1);
       
   113 	if (d == NULL)
       
   114 		return (NULL);
       
   115 
       
   116 	result = data_new_string((char *)d, lt_copy);
       
   117 	xmlFree(d);
       
   118 	return (result);
       
   119 }
       
   120 
       
   121 static char *
       
   122 get_text_str(xmlDoc *doc, xmlNode *node)
       
   123 {
       
   124 	char *result;
       
   125 	xmlChar *d = xmlNodeListGetString(doc, node, 1);
       
   126 	if (d == NULL)
       
   127 		return (NULL);
       
   128 
       
   129 	result = strdup((char *)d);
       
   130 	xmlFree(d);
       
   131 	return (result);
       
   132 }
       
   133 
       
   134 static data_t *
       
   135 get_prop(xmlNode *node, const char *name)
       
   136 {
       
   137 	data_t *result;
       
   138 	xmlChar *d = xmlGetProp(node, (xmlChar *)name);
       
   139 	if (d == NULL)
       
   140 		return (NULL);
       
   141 
       
   142 	result = data_new_string((char *)d, lt_copy);
       
   143 	xmlFree(d);
       
   144 	return (result);
       
   145 }
       
   146 
       
   147 static data_t *
       
   148 file_to_token(rad_locale_t *rlocale, const char *pname, char *file)
       
   149 {
       
   150 	int llen = strlen(rlocale->locale);
       
   151 	int plen = strlen(pname);
       
   152 	int flen = strlen(file);
       
   153 	int tokenlen = llen + 1 + plen + 1 + flen + 1;
       
   154 
       
   155 	char *token = rad_zalloc(tokenlen);
       
   156 	if (token == NULL) {
       
   157 		return (NULL);
       
   158 	}
       
   159 
       
   160 	char *p = token;
       
   161 	(void) strcpy(p, rlocale->locale);
       
   162 	p += llen + 1;
       
   163 	(void) strcpy(p, pname);
       
   164 	p += plen + 1;
       
   165 	(void) strcpy(p, file);
       
   166 
       
   167 	return (data_new_opaque(token, tokenlen, lt_free));
       
   168 }
       
   169 
       
   170 static conerr_t
       
   171 token_to_file(data_t *t, char **f)
       
   172 {
       
   173 	char *token = data_to_opaque(t);
       
   174 	char tokenlen = opaque_size(t);
       
   175 
       
   176 	/* Cursory validation */
       
   177 	int nullcnt = 0;
       
   178 	for (int i = 0; i < tokenlen; i++) {
       
   179 		if (token[i] == '\0') {
       
   180 			nullcnt++;
       
   181 		}
       
   182 	}
       
   183 	if (nullcnt != 3 || token[tokenlen - 1] != '\0') {
       
   184 		/* Bad token */
       
   185 		return (ce_object);
       
   186 	}
       
   187 
       
   188 	char *locale = token;
       
   189 	char *pname = locale + strlen(locale) + 1;
       
   190 	char *file = pname + strlen(pname) + 1;
       
   191 
       
   192 	data_t *panel = read_panel(locale, pname);
       
   193 	if (panel == NULL) {
       
   194 		/* Bad panel */
       
   195 		return (ce_object);
       
   196 	}
       
   197 
       
   198 	data_t *resources = struct_get(panel, "resourceDescriptors");
       
   199 	static const char * const path[] = { "file", NULL };
       
   200 	int index = array_search(resources, file, path);
       
   201 	data_free(panel);
       
   202 	if (index == -1) {
       
   203 		/* Bad file */
       
   204 		return (ce_object);
       
   205 	}
       
   206 
       
   207 	*f = strdup(file);
       
   208 	if (*f == NULL) {
       
   209 		return (ce_nomem);
       
   210 	}
       
   211 
       
   212 	return (ce_ok);
       
   213 }
       
   214 
       
   215 static data_t *
       
   216 create_resource(rad_locale_t *rlocale, const char *pname, char *file)
       
   217 {
       
   218 	unsigned char hbuf[MD5_DIGEST_LENGTH];
       
   219 	if (md5_file(file, hbuf) != 0) {
       
   220 		return (NULL);
       
   221 	}
       
   222 
       
   223 	data_t *result = data_new_struct(&t__ResourceDescriptor);
       
   224 	struct_set(result, "token", file_to_token(rlocale, pname, file));
       
   225 	struct_set(result, "file", data_new_string(file, lt_copy));
       
   226 	struct_set(result, "hashAlgorithm", data_new_string("MD5", lt_const));
       
   227 	struct_set(result, "hash", data_new_opaque(hbuf, MD5_DIGEST_LENGTH,
       
   228 	    lt_copy));
       
   229 
       
   230 	return (result);
       
   231 }
       
   232 
       
   233 static void
       
   234 add_localized(rad_locale_t *rlocale, const char *pname, data_t *farray,
       
   235     char *file)
       
   236 {
       
   237 	if (rlocale != NULL && rlocale->language != NULL &&
       
   238 	    strlen(rlocale->language)) {
       
   239 		char path[PATH_MAX + 1];
       
   240 		(void) strlcpy(path, file, PATH_MAX);
       
   241 
       
   242 		char *ext = strrchr(path, '.');
       
   243 		if (ext != NULL && strcmp(ext, ".jar") == 0) {
       
   244 			*ext = '\0';
       
   245 			char *base = strrchr(path, '/');
       
   246 			if (base == NULL) {
       
   247 				return;
       
   248 			}
       
   249 			*base++ = '\0';
       
   250 
       
   251 			char *fmt[] = {NULL, NULL, NULL};
       
   252 
       
   253 			/*
       
   254 			 * Use a ResourceBundle.getBundle-like algorithm -
       
   255 			 * <language>[_<territory>[@<modifier>]] - and order
       
   256 			 * from most- to least-specific.
       
   257 			 */
       
   258 			fmt[2] = "%s/locale/%s/%5$s_l10n.jar";
       
   259 			if (rlocale->territory != NULL) {
       
   260 				fmt[1] = "%s/locale/%s_%s/%5$s_l10n.jar";
       
   261 				if (rlocale->modifier != NULL) {
       
   262 					fmt[0] = "%s/locale/%s_%s@%s/"
       
   263 					    "%5$s_l10n.jar";
       
   264 				}
       
   265 			}
       
   266 
       
   267 			char l10njar[PATH_MAX];
       
   268 			for (int i = 0; i < RAD_COUNT(fmt); i++) {
       
   269 				if (fmt[i] == NULL) {
       
   270 					continue;
       
   271 				}
       
   272 				/* LINTED: E_SEC_PRINTF_VAR_FMT */
       
   273 				(void) snprintf(l10njar, RAD_COUNT(l10njar),
       
   274 				    fmt[i], path, rlocale->language,
       
   275 				    rlocale->territory, rlocale->modifier,
       
   276 				    base);
       
   277 				if (access(l10njar, F_OK) == 0) {
       
   278 					(void) array_add(farray,
       
   279 					    create_resource(rlocale, pname,
       
   280 					    l10njar));
       
   281 				}
       
   282 			}
       
   283 		}
       
   284 	}
       
   285 	(void) array_add(farray, create_resource(rlocale, pname, file));
       
   286 }
       
   287 
       
   288 static data_t *
       
   289 read_panel_path(rad_locale_t *rlocale, const char *path)
       
   290 {
       
   291 	xmlParserCtxt *ctx;
       
   292 	xmlValidCtxt *vctx;
       
   293 	xmlDoc *doc;
       
   294 	data_t *panel = NULL;
       
   295 
       
   296 	ctx = xmlNewParserCtxt();
       
   297 	vctx = xmlNewValidCtxt();
       
   298 	if (vctx == NULL || ctx == NULL)
       
   299 		return (NULL);
       
   300 
       
   301 	doc = xmlCtxtReadFile(ctx, path, NULL, 0);
       
   302 	if (doc == NULL) {
       
   303 		xmlFreeValidCtxt(vctx);
       
   304 		xmlFreeParserCtxt(ctx);
       
   305 		rad_log(RL_WARN, "Empty/no such document: %s\n", path);
       
   306 		return (NULL);
       
   307 	}
       
   308 
       
   309 	/*
       
   310 	 * Validate against *our* DTD.
       
   311 	 */
       
   312 	if (xmlValidateDtd(vctx, doc, dtd) == 0) {
       
   313 		rad_log(RL_WARN, "Invalid document: %s\n", path);
       
   314 		goto out;
       
   315 	}
       
   316 
       
   317 	xmlNodePtr root = xmlDocGetRootElement(doc);
       
   318 	if (root == NULL || strcmp((const char *)root->name, "panel") != 0) {
       
   319 		rad_log(RL_WARN, "Not a panel definition: %s\n", path);
       
   320 		goto out;
       
   321 	}
       
   322 
       
   323 	panel = data_new_struct(&t__CustomPanel);
       
   324 	struct_set(panel, "locale", data_new_string(rlocale->locale, lt_copy));
       
   325 
       
   326 	data_t *pname = get_prop(root, "name");
       
   327 	struct_set(panel, "name", pname);
       
   328 
       
   329 	data_t *farray = data_new_array(&t_array__ResourceDescriptor, 1);
       
   330 	struct_set(panel, "resourceDescriptors", farray);
       
   331 
       
   332 	char *aroot = NULL;
       
   333 	for (xmlNode *np = root->children; np != NULL; np = np->next) {
       
   334 		if (np->type != XML_ELEMENT_NODE)
       
   335 			continue;
       
   336 		if (strcmp((const char *)np->name, "mainclass") == 0) {
       
   337 			data_t *mc = get_text(doc, np->children);
       
   338 			struct_set(panel, "panelDescriptorClassName", mc);
       
   339 		} else if (strcmp((const char *)np->name, "approot") == 0) {
       
   340 			if (aroot != NULL)
       
   341 				continue;	/* schema violation */
       
   342 			aroot = get_text_str(doc, np->children);
       
   343 		} else if (strcmp((const char *)np->name, "file") == 0) {
       
   344 			char *file = get_text_str(doc, np->children);
       
   345 			if (file == NULL) {
       
   346 				rad_log(RL_WARN,
       
   347 				    "Empty <file> declaration within %s\n",
       
   348 				    path);
       
   349 				continue;
       
   350 			}
       
   351 
       
   352 			if (aroot == NULL) {
       
   353 				rad_log(RL_WARN, "App root not specified\n");
       
   354 				continue;
       
   355 			}
       
   356 
       
   357 			char full[PATH_MAX];
       
   358 			(void) snprintf(full, RAD_COUNT(full), "%s/%s", aroot,
       
   359 			    file);
       
   360 			free(file);
       
   361 
       
   362 			add_localized(rlocale, data_to_string(pname), farray,
       
   363 			    full);
       
   364 		}
       
   365 	}
       
   366 	if (aroot != NULL)
       
   367 		free(aroot);
       
   368 out:
       
   369 	xmlFreeValidCtxt(vctx);
       
   370 	xmlFreeDoc(doc);
       
   371 	xmlFreeParserCtxt(ctx);
       
   372 
       
   373 	return (data_purify_deep(panel));
       
   374 }
       
   375 
       
   376 static data_t *
       
   377 read_panel(const char *locale, const char *pname)
       
   378 {
       
   379 	rad_locale_t *rlocale;
       
   380 	if (rad_locale_parse(locale, &rlocale) != 0) {
       
   381 		return (NULL);
       
   382 	}
       
   383 
       
   384 	char path[PATH_MAX];
       
   385 	(void) snprintf(path, RAD_COUNT(path), "%s/%s.xml", PANELDESCDIR,
       
   386 	    pname);
       
   387 	data_t *panel = read_panel_path(rlocale, path);
       
   388 
       
   389 	if (panel != NULL) {
       
   390 		/* Sanity check - ensure panel @name matches file name */
       
   391 		data_t *nameattr = struct_get(panel, "name");
       
   392 		if (strcmp(data_to_string(nameattr), pname) != 0) {
       
   393 			data_free(panel);
       
   394 			panel = NULL;
       
   395 		}
       
   396 	}
       
   397 
       
   398 	rad_locale_free(rlocale);
       
   399 
       
   400 	return (panel);
       
   401 }
       
   402 
       
   403 /*
       
   404  * Extern functions
       
   405  */
       
   406 
       
   407 int
       
   408 _rad_init(void *handle)
       
   409 {
       
   410 	if (rad_module_register(handle, RAD_MODVERSION, &modinfo) == -1)
       
   411 		return (-1);
       
   412 
       
   413 	dtd = xmlParseDTD(NULL,
       
   414 	    (xmlChar *)"/usr/share/lib/xml/dtd/vpanel.dtd.1");
       
   415 
       
   416 	adr_name_t *name = adr_name_fromstr(
       
   417 	    "com.oracle.solaris.vp.panel.common.api.panel:type=Panel");
       
   418 	(void) cont_insert_singleton(rad_container, name, &interface_Panel_svr);
       
   419 
       
   420 	return (0);
       
   421 }
       
   422 
       
   423 /* ARGSUSED */
       
   424 conerr_t
       
   425 interface_Panel_invoke_getPanel(rad_instance_t *inst, adr_method_t *meth,
       
   426     data_t **ret, data_t **args, int count, data_t **error)
       
   427 {
       
   428 	const char *pname = data_to_string(args[0]);
       
   429 	const char *locale = args[1] == NULL ? NULL : data_to_string(args[1]);
       
   430 
       
   431 	data_t *panel = read_panel(locale, pname);
       
   432 	if (panel == NULL) {
       
   433 		/*
       
   434 		 * Could be a memory or system error, but more likely an invalid
       
   435 		 * name was specified.
       
   436 		 */
       
   437 		return (ce_object);
       
   438 	}
       
   439 	*ret = panel;
       
   440 
       
   441 	return (ce_ok);
       
   442 }
       
   443 
       
   444 /* ARGSUSED */
       
   445 conerr_t
       
   446 interface_Panel_read_panelNames(rad_instance_t *inst, adr_attribute_t *attr,
       
   447     data_t **data, data_t **error)
       
   448 {
       
   449 	data_t *array = data_new_array(&t_array_string, 0);
       
   450 	if (array == NULL) {
       
   451 		return (ce_nomem);
       
   452 	}
       
   453 
       
   454 	DIR *d;
       
   455 	if ((d = opendir(PANELDESCDIR)) == NULL) {
       
   456 		if (errno == ENOENT) {
       
   457 			return (ce_ok);
       
   458 		}
       
   459 		return (ce_system);
       
   460 	}
       
   461 
       
   462 	struct dirent *ent;
       
   463 	while ((ent = readdir(d)) != NULL) {
       
   464 		char *ext = ".xml";
       
   465 		size_t len = strlen(ent->d_name) - strlen(ext);
       
   466 		if (len < 1 || strcmp(ent->d_name + len, ext) != 0) {
       
   467 			continue;
       
   468 		}
       
   469 		(void) array_add(array, data_new_nstring(ent->d_name, len));
       
   470 	}
       
   471 
       
   472 	(void) closedir(d);
       
   473 	*data = data_purify(array);
       
   474 
       
   475 	return (*data == NULL ? ce_nomem : ce_ok);
       
   476 }
       
   477 
       
   478 /* ARGSUSED */
       
   479 conerr_t
       
   480 interface_Panel_invoke_getResource(rad_instance_t *inst, adr_method_t *meth,
       
   481     data_t **ret, data_t **args, int count, data_t **error)
       
   482 {
       
   483 	char *file;
       
   484 	conerr_t result = token_to_file(args[0], &file);
       
   485 	if (result != ce_ok) {
       
   486 		return (result);
       
   487 	}
       
   488 
       
   489 	struct stat st;
       
   490 	if (stat(file, &st) != 0) {
       
   491 		free(file);
       
   492 		return (ce_object);
       
   493 	}
       
   494 
       
   495 	char *buffer = malloc(st.st_size);
       
   496 	if (buffer == NULL) {
       
   497 		free(file);
       
   498 		return (ce_nomem);
       
   499 	}
       
   500 
       
   501 	int fd = open(file, O_RDONLY);
       
   502 	free(file);
       
   503 	if (fd == -1) {
       
   504 		free(buffer);
       
   505 		return (ce_priv);
       
   506 	}
       
   507 
       
   508 	if (read(fd, buffer, st.st_size) != st.st_size) {
       
   509 		(void) close(fd);
       
   510 		free(buffer);
       
   511 		return (ce_system);
       
   512 	}
       
   513 
       
   514 	(void) close(fd);
       
   515 
       
   516 	*ret = data_new_opaque(buffer, st.st_size, lt_free);
       
   517 	return (*ret == NULL ? ce_nomem : ce_ok);
       
   518 }