1 /*
2  * Copyright 2014-2016, Björn Ståhl
3  * License: 3-Clause BSD, see COPYING file in arcan source repository.
4  * Reference: http://arcan-fe.com
5  */
6 
7 #include <stdlib.h>
8 #include <stdint.h>
9 #include <stdbool.h>
10 #include <stdio.h>
11 #include <libgen.h>
12 #include <string.h>
13 #include <math.h>
14 #include <assert.h>
15 #include <ctype.h>
16 
17 #include <sys/stat.h>
18 #include <sys/types.h>
19 #include <unistd.h>
20 
21 #include <arcan_math.h>
22 #include <arcan_general.h>
23 
24 static size_t appl_len = 0;
25 static char* g_appl_id = "#appl not initialized";
26 static char* appl_script = NULL;
27 
28 /*
29  * This is also planned as an entry point for implementing the
30  * APF mapping, possibly with the chainloader setting envvars
31  * for how the namespace is made, and if not (.apf extension in appl_id)
32  * setup the context and functions required to do data lookups.
33  */
arcan_verifyload_appl(const char * appl_id,const char ** errc)34 bool arcan_verifyload_appl(const char* appl_id, const char** errc)
35 {
36 	size_t app_len;
37 
38 	if (!appl_id || (app_len = strlen(appl_id)) == 0){
39 		*errc = "no application specified";
40 		return false;
41 	}
42 
43 /*
44  * NOTE: here is the place to check for .fap extension,
45  * and if found, take a separate route for setting up.
46  */
47 	char* base = strdup(appl_id);
48 	bool expand = true;
49 
50 /*
51  * absolute/relative path? don't define namespaces using SYS_*
52  * will maintain STATE namespace set by paths. Absolute paths
53  * should only be presented from a privileged state (command-line
54  * or in-engine, not Lua->engine).
55  */
56 	if (appl_id[0] == '/' || appl_id[0] == '\\' || appl_id[0] == '.'){
57 		char* work = strdup(base);
58 		char* dir = strdup( dirname( work ) );
59 		free(work);
60 
61 		work = strdup(base);
62 		free(base);
63 		base = strdup( basename(work) );
64 		free(work);
65 
66 		arcan_override_namespace(appl_id, RESOURCE_APPL);
67 
68 		arcan_softoverride_namespace(appl_id, RESOURCE_APPL_TEMP);
69 		arcan_softoverride_namespace(dir, RESOURCE_SYS_APPLBASE);
70 		arcan_softoverride_namespace(dir, RESOURCE_SYS_APPLSTORE);
71 
72 		free(dir);
73 		expand = false;
74 	}
75 
76 /*
77  * with path stripped, we can make sure to see that we have a proper name
78  */
79 	app_len = strlen(base);
80 	for (size_t i = 0; i < app_len; i++){
81 		if (!isalnum(base[i]) && base[i] != '_'){
82 			*errc = "invalid character in appl_id (only a..Z _ 0..9 allowed)\n";
83 			return false;
84 		}
85 	}
86 
87 	if (expand){
88 		char* dir = arcan_expand_resource(base, RESOURCE_SYS_APPLBASE);
89 		if (!dir){
90 			*errc = "missing application base\n";
91 			free(base);
92 			return false;
93 		}
94 		arcan_override_namespace(dir, RESOURCE_APPL);
95 		arcan_mem_free(dir);
96 
97 		dir = arcan_expand_resource(base, RESOURCE_SYS_APPLSTORE);
98 		if (!dir){
99 			*errc = "missing application temporary store\n";
100 			free(base);
101 			return false;
102 		}
103 		arcan_override_namespace(dir, RESOURCE_APPL_TEMP);
104 		arcan_mem_free(dir);
105 	}
106 
107 /* state- storage has specific rules so it is always checked for expansion,
108  * if STATE is a subpath of shared, we have global application state (i.e.
109  * multiple scripts can use the same state storage for virtual machines),
110  * otherwise we expand */
111 	char* p_a = arcan_expand_resource("", RESOURCE_APPL_SHARED);
112 	char* p_b = arcan_expand_resource("", RESOURCE_SYS_APPLSTATE);
113 	if (!p_b)
114 		arcan_override_namespace(p_a, RESOURCE_APPL_STATE);
115 	else if (strncmp(p_a, p_b, strlen(p_a)) == 0){
116 		arcan_override_namespace(p_b, RESOURCE_APPL_STATE);
117 	}
118 	else {
119 		arcan_mem_free(p_b);
120 		p_b = arcan_expand_resource(base, RESOURCE_SYS_APPLSTATE);
121 		arcan_override_namespace(p_b, RESOURCE_APPL_STATE);
122 	}
123 	arcan_mem_free(p_a);
124 	arcan_mem_free(p_b);
125 
126 	char wbuf[ app_len + sizeof(".lua")];
127 	snprintf(wbuf, sizeof(wbuf), "%s.lua", base);
128 
129 	char* script_path = arcan_expand_resource(wbuf, RESOURCE_APPL);
130 	if (!script_path || !arcan_isfile(script_path)){
131 		*errc = "missing script matching appl_id (see appname/appname.lua)";
132 		arcan_mem_free(script_path);
133 		return false;
134 	}
135 
136 /*
137  * Switch to appl- suppled fonts if a folder exists to avoid relying on res
138  * namespace pollution. Specific settings can still pin the namespace to
139  * prevent this action.
140  */
141 	char* font_path = arcan_expand_resource("/fonts", RESOURCE_APPL);
142 	if (font_path){
143 		if (arcan_isdir(font_path))
144 			arcan_override_namespace(font_path, RESOURCE_SYS_FONT);
145 		free(font_path);
146 	}
147 
148 	if (strcmp(g_appl_id, "#appl not initialized") != 0)
149 		arcan_mem_free(g_appl_id);
150 
151 	g_appl_id = base;
152 	appl_len = app_len;
153 	appl_script = script_path;
154 
155 	return true;
156 }
157 
arcan_appl_basesource(bool * file)158 const char* arcan_appl_basesource(bool* file)
159 {
160 	*file = true;
161 	return appl_script;
162 }
163 
arcan_appl_id()164 const char* arcan_appl_id()
165 {
166 	return g_appl_id;
167 }
168 
arcan_appl_id_len()169 size_t arcan_appl_id_len()
170 {
171 	return appl_len;
172 }
173 
174