naev 0.12.5
plugin.c
Go to the documentation of this file.
1/*
2 * See Licensing and Copyright notice in naev.h
3 */
10#include "physfs.h"
11
12#include "naev.h"
14
15#include "plugin.h"
16
17#define PCRE2_CODE_UNIT_WIDTH 8
18#include <pcre2.h>
19
20#include "array.h"
21#include "log.h"
22#include "nfile.h"
23#include "nxml.h"
24#include "physfs_archiver_blacklist.h"
25
27
28/*
29 * Prototypes.
30 */
31static int plugin_parse( plugin_t *plg, const char *file, const char *path,
32 int apply );
33
37plugin_t *plugin_test( const char *filename )
38{
39 int ret;
40 PHYSFS_Stat stat;
41 const char *realdir;
42
43 /* File must exist. */
44 if ( !nfile_fileExists( filename ) )
45 return NULL;
46
47 /* Try to mount. */
48 if ( PHYSFS_mount( filename, NULL, 0 ) == 0 ) {
49 WARN( _( "Failed to mount plugin '%s': %s" ), filename,
50 _( PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
51 return NULL;
52 }
53
54 /* Check to see if plugin file exists. */
55 PHYSFS_stat( "plugin.xml", &stat );
56 realdir = PHYSFS_getRealDir( "plugin.xml" );
57 if ( ( stat.filetype != PHYSFS_FILETYPE_REGULAR ) || ( realdir == NULL ) ||
58 strcmp( realdir, filename ) != 0 )
59 return NULL;
60
61 /* Load data and send over. */
62 plugin_t *plg = calloc( 1, sizeof( plugin_t ) );
63 ret = plugin_parse( plg, "plugin.xml", filename, 0 );
64
65 /* Set some defaults. */
66 if ( plg->author == NULL )
67 plg->author = strdup( _( "Unknown" ) );
68 if ( plg->version == NULL )
69 plg->version = strdup( _( "Unknown" ) );
70 if ( plg->description == NULL )
71 plg->description = strdup( _( "Unknown" ) );
72
73 /* Clean up. */
74 PHYSFS_unmount( filename );
75
76 if ( ret )
77 return NULL;
78 return plg;
79}
80
84static int plugin_parse( plugin_t *plg, const char *file, const char *path,
85 int apply )
86{
87 xmlNodePtr node, parent;
88 xmlDocPtr doc = xml_parsePhysFS( file );
89 if ( doc == NULL )
90 return -1;
91
92 parent = doc->xmlChildrenNode; /* first faction node */
93 if ( parent == NULL ) {
94 char buf[PATH_MAX];
95 nfile_concatPaths( buf, sizeof( buf ), plg->mountpoint, file );
96 WARN( _( "Malformed '%s' file: does not contain elements" ), buf );
97 return -1;
98 }
99
100 xmlr_attr_strd( parent, "name", plg->name );
101 if ( plg->name == NULL )
102 WARN( _( "Plugin '%s' has no name!" ), path );
103
104 node = parent->xmlChildrenNode;
105 do {
106 xml_onlyNodes( node );
107
108 xmlr_strd( node, "author", plg->author );
109 xmlr_strd( node, "version", plg->version );
110 xmlr_strd( node, "description", plg->description );
111 xmlr_strd( node, "compatibility", plg->compatibility );
112 xmlr_int( node, "priority", plg->priority );
113 xmlr_strd( node, "source", plg->source );
114 if ( xml_isNode( node, "blacklist" ) ) {
115 if ( apply )
116 blacklist_append( xml_get( node ) );
117 continue;
118 }
119 if ( xml_isNode( node, "whitelist" ) ) {
120 if ( apply )
121 whitelist_append( xml_get( node ) );
122 continue;
123 }
124 if ( xml_isNode( node, "total_conversion" ) ) {
125 if ( apply ) {
126 const char *blk[] = {
127 "^ssys/.*\\.xml",
128 "^spob/.*\\.xml",
129 "^spob_virtual/.*\\.xml",
130 "^factions/.*\\.xml",
131 "^commodities/.*\\.xml",
132 "^ships/.*\\.xml",
133 "^outfits/.*\\.xml",
134 "^missions/.*\\.lua",
135 "^events/.*\\.lua",
136 "^tech/.*\\.xml",
137 "^asteroids/.*\\.xml",
138 "^unidiff/.*\\.xml",
139 "^map_decorator/.*\\.xml",
140 "^naevpedia/.*\\.xml",
141 "^intro",
142 NULL,
143 };
144 const char *wht[] = {
145 "^events/settings.lua",
146 NULL,
147 };
148 for ( int i = 0; blk[i] != NULL; i++ )
149 blacklist_append( blk[i] );
150 for ( int i = 0; wht[i] != NULL; i++ )
151 whitelist_append( wht[i] );
152 }
153 plg->total_conversion = 1;
154 continue;
155 }
156
157 WARN( _( "Plugin '%s' has unknown metadata node '%s'!" ),
158 plugin_name( plg ), xml_get( node ) );
159 } while ( xml_nextNode( node ) );
160
161 xmlFreeDoc( doc );
162
163#define MELEMENT( o, s ) \
164 if ( o ) \
165 WARN( _( "Plugin '%s' missing/invalid '%s' element" ), plg->name, \
166 s )
167 MELEMENT( plg->author == NULL, "author" );
168 MELEMENT( plg->version == NULL, "version" );
169 MELEMENT( plg->description == NULL, "description" );
170 MELEMENT( plg->compatibility == NULL, "compatibility" );
171 MELEMENT( plg->source == NULL, "source" );
172#undef MELEMENT
173
174 return 0;
175}
176
180static int plugin_cmp( const void *a, const void *b )
181{
182 const plugin_t *pa = (const plugin_t *)a;
183 const plugin_t *pb = (const plugin_t *)b;
184
185 if ( pa->priority < pb->priority )
186 return -1;
187 else if ( pa->priority > pb->priority )
188 return 1;
189
190 return strcmp( pa->mountpoint, pb->mountpoint );
191}
192
196const char *plugin_dir( void )
197{
198 static char dir[PATH_MAX];
199 nfile_concatPaths( dir, sizeof( dir ), PHYSFS_getWriteDir(), "plugins" );
200 return dir;
201}
202
208int plugin_init( void )
209{
210 char buf[PATH_MAX];
211 PHYSFS_Stat stat;
212
214
215 PHYSFS_stat( "plugins", &stat );
216 if ( stat.filetype == PHYSFS_FILETYPE_DIRECTORY ) {
217 int n;
218 char **files = PHYSFS_enumerateFiles( "plugins" );
219 for ( char **f = files; *f != NULL; f++ ) {
220 const char *realdir;
221 plugin_t *plg;
222 nfile_concatPaths( buf, PATH_MAX, PHYSFS_getWriteDir(), "plugins",
223 *f );
224 if ( !nfile_fileExists( buf ) && !nfile_dirExists( buf ) )
225 continue;
226
227 if ( PHYSFS_mount( buf, NULL, 0 ) == 0 ) {
228 WARN( _( "Failed to mount plugin '%s': %s" ), buf,
229 _( PHYSFS_getErrorByCode( PHYSFS_getLastErrorCode() ) ) );
230 continue;
231 }
232
233 /* Initialize new plugin. */
234 plg = &array_grow( &plugins );
235 memset( plg, 0, sizeof( plugin_t ) );
236 plg->mountpoint = strdup( buf );
237 plg->priority = 5;
238
239 PHYSFS_stat( "plugin.xml", &stat );
240 realdir = PHYSFS_getRealDir( "plugin.xml" );
241 if ( ( stat.filetype == PHYSFS_FILETYPE_REGULAR ) && realdir &&
242 strcmp( realdir, buf ) == 0 )
243 plugin_parse( plg, "plugin.xml", *f, 1 );
244 else
245 WARN( _( "Plugin '%s' does not have a valid '%s'!" ), buf,
246 "plugin.xml" );
247
248 /* Set some defaults. */
249 if ( plg->author == NULL )
250 plg->author = strdup( _( "Unknown" ) );
251 if ( plg->version == NULL )
252 plg->version = strdup( _( "Unknown" ) );
253 if ( plg->description == NULL )
254 plg->description = strdup( _( "Unknown" ) );
255 }
256 PHYSFS_freeList( files );
257 n = array_size( plugins );
258
259 /* Unmount all. */
260 for ( int i = 0; i < n; i++ )
261 PHYSFS_unmount( plugins[i].mountpoint );
262
263 /* Sort by priority. */
264 qsort( plugins, n, sizeof( plugin_t ), plugin_cmp );
265
266 /* Initialize blacklist. */
268
269 /* Remount. */
270 for ( int i = n - 1; i >= 0; i-- ) /* Reverse order as we prepend. */
271 PHYSFS_mount( plugins[i].mountpoint, NULL, 0 );
272
273 if ( n > 0 ) {
274 DEBUG( "Loaded plugins:" );
275 for ( int i = 0; i < n; i++ ) { /* Reverse order. */
276 DEBUG( " %s", plugin_name( &plugins[i] ) );
277 }
278 }
279 } else {
280 nfile_concatPaths( buf, PATH_MAX, PHYSFS_getWriteDir(), "plugins" );
281 nfile_dirMakeExist( buf );
282 }
283
284 return 0;
285}
286
292{
293 array_push_back( &plugins, *plg );
294}
295
299void plugin_exit( void )
300{
301 for ( int i = 0; i < array_size( plugins ); i++ )
302 plugin_free( &plugins[i] );
304 plugins = NULL;
305
307}
308
315const char *plugin_name( const plugin_t *plg )
316{
317 if ( plg->name != NULL )
318 return plg->name;
319 return plg->mountpoint;
320}
321
326{
327 free( plg->name );
328 free( plg->author );
329 free( plg->version );
330 free( plg->description );
331 free( plg->compatibility );
332 free( plg->source );
333 free( plg->mountpoint );
334}
335
341int plugin_check( void )
342{
343 int failed = 0;
344 const char *version = naev_version( 0 );
345 for ( int i = 0; i < array_size( plugins ); i++ ) {
346 int errornumber;
347 PCRE2_SIZE erroroffset;
348 pcre2_code *re;
349 pcre2_match_data *match_data;
350
351 if ( plugins[i].compatibility == NULL )
352 continue;
353
354 re = pcre2_compile( (PCRE2_SPTR)plugins[i].compatibility,
355 PCRE2_ZERO_TERMINATED, 0, &errornumber, &erroroffset,
356 NULL );
357 if ( re == NULL ) {
358 PCRE2_UCHAR buffer[256];
359 pcre2_get_error_message( errornumber, buffer, sizeof( buffer ) );
360 WARN( _( "Plugin '%s' PCRE2 compilation failed at offset %d: %s" ),
361 plugin_name( &plugins[i] ), (int)erroroffset, buffer );
362 failed++;
363 continue;
364 }
365
366 match_data = pcre2_match_data_create_from_pattern( re, NULL );
367 int rc = pcre2_match( re, (PCRE2_SPTR)version, strlen( version ), 0, 0,
368 match_data, NULL );
369 pcre2_match_data_free( match_data );
370 if ( rc < 0 ) {
371 switch ( rc ) {
372 case PCRE2_ERROR_NOMATCH:
373 WARN( "Plugin '%s' does not support Naev version '%s'.",
374 plugin_name( &plugins[i] ), version );
375 failed++;
376 break;
377 default:
378 WARN( _( "Matching error %d" ), rc );
379 failed++;
380 break;
381 }
382 } else
383 plugins[i].compatible = 1;
384
385 pcre2_code_free( re );
386 }
387
388 return failed;
389}
390
396const plugin_t *plugin_list( void )
397{
398 return plugins;
399}
Provides macros to work with dynamic arrays.
#define array_free(ptr_array)
Frees memory allocated and sets array to NULL.
Definition array.h:170
static ALWAYS_INLINE int array_size(const void *array)
Returns number of elements in the array.
Definition array.h:179
#define array_grow(ptr_array)
Increases the number of elements by one and returns the last element.
Definition array.h:122
#define array_push_back(ptr_array, element)
Adds a new element at the end of the array.
Definition array.h:134
#define array_create(basic_type)
Creates a new dynamic array of ‘basic_type’.
Definition array.h:93
Header file with generic functions and naev-specifics.
const char * naev_version(int long_version)
Returns the version in a human readable string.
#define PATH_MAX
Definition naev.h:57
int nfile_dirMakeExist(const char *path)
Creates a directory if it doesn't exist.
Definition nfile.c:277
int nfile_fileExists(const char *path)
Checks to see if a file exists.
Definition nfile.c:329
int nfile_dirExists(const char *path)
Checks to see if a directory exists.
Definition nfile.c:309
xmlDocPtr xml_parsePhysFS(const char *filename)
Analogous to xmlParseMemory/xmlParseFile.
Definition nxml.c:70
int blacklist_init(void)
Initializes the blacklist system if necessary. If no plugin is blacklisting, it will not do anything.
int blacklist_append(const char *path)
Appends a regex string to be blacklisted.
int whitelist_append(const char *path)
Appends a regex string to be whitelisted.
void blacklist_exit(void)
Exits the blacklist system and cleans up as necessary.
plugin_t * plugin_test(const char *filename)
Tests to see if a file is a plugin and loads information.
Definition plugin.c:37
void plugin_insert(plugin_t *plg)
Inserts a plugin to the list, but does not properly enable it (requires restart).
Definition plugin.c:291
int plugin_init(void)
Initialize and loads all the available plugins.
Definition plugin.c:208
int plugin_check(void)
Checks to see if the plugins are self-declared compatible with Naev.
Definition plugin.c:341
const char * plugin_name(const plugin_t *plg)
Tries to tget the name of a plugin.
Definition plugin.c:315
static plugin_t * plugins
Definition plugin.c:26
void plugin_exit(void)
Exits the plugin stuff.
Definition plugin.c:299
static int plugin_parse(plugin_t *plg, const char *file, const char *path, int apply)
Parses a plugin description file.
Definition plugin.c:84
static int plugin_cmp(const void *a, const void *b)
For qsort on plugins.
Definition plugin.c:180
const char * plugin_dir(void)
Gets the plugin directory.
Definition plugin.c:196
void plugin_free(plugin_t *plg)
Frees a previously allocated plugin.
Definition plugin.c:325
const plugin_t * plugin_list(void)
Returns the list of all the plugins.
Definition plugin.c:396
int priority
Definition plugin.h:14
char * mountpoint
Definition plugin.h:13
char * compatibility
Definition plugin.h:11
char * author
Definition plugin.h:8
char * version
Definition plugin.h:9
int total_conversion
Definition plugin.h:18
char * name
Definition plugin.h:7
char * description
Definition plugin.h:10
char * source
Definition plugin.h:12