nm-vpn-plugin-info.c 39 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365
  1. /* -*- Mode: C; tab-width: 4; indent-tabs-mode: t; c-basic-offset: 4 -*- */
  2. /*
  3. * This library is free software; you can redistribute it and/or
  4. * modify it under the terms of the GNU Lesser General Public
  5. * License as published by the Free Software Foundation; either
  6. * version 2 of the License, or (at your option) any later version.
  7. *
  8. * This library is distributed in the hope that it will be useful,
  9. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  11. * Lesser General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU Lesser General Public
  14. * License along with this library; if not, write to the
  15. * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
  16. * Boston, MA 02110-1301 USA.
  17. *
  18. * Copyright 2015 Red Hat, Inc.
  19. */
  20. #include "nm-default.h"
  21. #include "nm-vpn-plugin-info.h"
  22. #include <string.h>
  23. #include <errno.h>
  24. #include <sys/stat.h>
  25. #include "nm-errors.h"
  26. #include "nm-core-internal.h"
  27. #define DEFAULT_DIR_ETC NMCONFDIR"/VPN"
  28. #define DEFAULT_DIR_LIB NMLIBDIR"/VPN"
  29. enum {
  30. PROP_0,
  31. PROP_NAME,
  32. PROP_FILENAME,
  33. PROP_KEYFILE,
  34. LAST_PROP,
  35. };
  36. typedef struct {
  37. char *filename;
  38. char *name;
  39. char *service;
  40. char *auth_dialog;
  41. char **aliases;
  42. GKeyFile *keyfile;
  43. /* It is convenient for nm_vpn_plugin_info_lookup_property() to return a const char *,
  44. * contrary to what g_key_file_get_string() does. Hence we must cache the returned
  45. * value somewhere... let's put it in an internal hash table.
  46. * This contains a clone of all the strings in keyfile. */
  47. GHashTable *keys;
  48. gboolean editor_plugin_loaded;
  49. NMVpnEditorPlugin *editor_plugin;
  50. } NMVpnPluginInfoPrivate;
  51. static void nm_vpn_plugin_info_initable_iface_init (GInitableIface *iface);
  52. G_DEFINE_TYPE_WITH_CODE (NMVpnPluginInfo, nm_vpn_plugin_info, G_TYPE_OBJECT,
  53. G_IMPLEMENT_INTERFACE (G_TYPE_INITABLE, nm_vpn_plugin_info_initable_iface_init);
  54. )
  55. #define NM_VPN_PLUGIN_INFO_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), NM_TYPE_VPN_PLUGIN_INFO, NMVpnPluginInfoPrivate))
  56. /*****************************************************************************/
  57. /**
  58. * nm_vpn_plugin_info_validate_filename:
  59. * @filename: the filename to check
  60. *
  61. * Regular name files have a certain pattern. That basically means
  62. * they have the file extension "name". Check if @filename
  63. * is valid according to that pattern.
  64. *
  65. * Since: 1.2
  66. */
  67. gboolean
  68. nm_vpn_plugin_info_validate_filename (const char *filename)
  69. {
  70. if (!filename || !g_str_has_suffix (filename, ".name"))
  71. return FALSE;
  72. /* originally, we didn't do further checks... but here we go. */
  73. if (filename[0] == '.') {
  74. /* this also rejects name ".name" alone. */
  75. return FALSE;
  76. }
  77. return TRUE;
  78. }
  79. static gboolean
  80. nm_vpn_plugin_info_check_file_full (const char *filename,
  81. gboolean check_absolute,
  82. gboolean do_validate_filename,
  83. gint64 check_owner,
  84. NMUtilsCheckFilePredicate check_file,
  85. gpointer user_data,
  86. struct stat *out_st,
  87. GError **error)
  88. {
  89. if (!filename || !*filename) {
  90. g_set_error (error,
  91. NM_VPN_PLUGIN_ERROR,
  92. NM_VPN_PLUGIN_ERROR_FAILED,
  93. _("missing filename"));
  94. return FALSE;
  95. }
  96. if (check_absolute && !g_path_is_absolute (filename)) {
  97. g_set_error (error,
  98. NM_VPN_PLUGIN_ERROR,
  99. NM_VPN_PLUGIN_ERROR_FAILED,
  100. _("filename must be an absolute path (%s)"), filename);
  101. return FALSE;
  102. }
  103. if ( do_validate_filename
  104. && !nm_vpn_plugin_info_validate_filename (filename)) {
  105. g_set_error (error,
  106. NM_VPN_PLUGIN_ERROR,
  107. NM_VPN_PLUGIN_ERROR_FAILED,
  108. _("filename has invalid format (%s)"), filename);
  109. return FALSE;
  110. }
  111. return _nm_utils_check_file (filename,
  112. check_owner,
  113. check_file,
  114. user_data,
  115. out_st,
  116. error);
  117. }
  118. /**
  119. * _nm_vpn_plugin_info_check_file:
  120. * @filename: the file to check
  121. * @check_absolute: if %TRUE, only allow absolute path names.
  122. * @do_validate_filename: if %TRUE, only accept the filename if
  123. * nm_vpn_plugin_info_validate_filename() succeeds.
  124. * @check_owner: if non-negative, only accept the file if the
  125. * owner UID is equal to @check_owner or if the owner is 0.
  126. * In this case, also check that the file is not writable by
  127. * other users.
  128. * @check_file: pass a callback to do your own validation.
  129. * @user_data: user data for @check_file.
  130. * @error: (allow-none): (out): the error reason if the check fails.
  131. *
  132. * Check whether the file exists and is a valid name file (in keyfile format).
  133. * Additionally, also check for file permissions.
  134. *
  135. * Returns: %TRUE if a file @filename exists and has valid permissions.
  136. *
  137. * Since: 1.2
  138. */
  139. gboolean
  140. _nm_vpn_plugin_info_check_file (const char *filename,
  141. gboolean check_absolute,
  142. gboolean do_validate_filename,
  143. gint64 check_owner,
  144. NMUtilsCheckFilePredicate check_file,
  145. gpointer user_data,
  146. GError **error)
  147. {
  148. return nm_vpn_plugin_info_check_file_full (filename, check_absolute, do_validate_filename, check_owner, check_file, user_data, NULL, error);
  149. }
  150. typedef struct {
  151. NMVpnPluginInfo *plugin_info;
  152. struct stat stat;
  153. } LoadDirInfo;
  154. static int
  155. _sort_files (LoadDirInfo *a, LoadDirInfo *b)
  156. {
  157. time_t ta, tb;
  158. ta = MAX (a->stat.st_mtime, a->stat.st_ctime);
  159. tb = MAX (b->stat.st_mtime, b->stat.st_ctime);
  160. if (ta < tb)
  161. return 1;
  162. if (ta > tb)
  163. return -1;
  164. return g_strcmp0 (nm_vpn_plugin_info_get_filename (a->plugin_info),
  165. nm_vpn_plugin_info_get_filename (b->plugin_info));
  166. }
  167. #define DEFINE_DEFAULT_DIR_LIST(dir) \
  168. const char *dir[] = { \
  169. /* We load plugins from NM_VPN_PLUGIN_DIR *and* DEFAULT_DIR*, with
  170. * preference to the former.
  171. *
  172. * load user directory with highest priority. */ \
  173. _nm_vpn_plugin_info_get_default_dir_user (), \
  174. \
  175. /* lib directory has higher priority then etc. The reason is that
  176. * etc is deprecated and used by old plugins. We expect newer plugins
  177. * to install their file in lib, where they have higher priority.
  178. *
  179. * Optimally, there are no duplicates anyway, so it doesn't really matter. */ \
  180. _nm_vpn_plugin_info_get_default_dir_lib (), \
  181. _nm_vpn_plugin_info_get_default_dir_etc (), \
  182. }
  183. /**
  184. * _nm_vpn_plugin_info_get_default_dir_etc:
  185. *
  186. * Returns: (transfer none): compile time constant of the default
  187. * VPN plugin directory.
  188. */
  189. const char *
  190. _nm_vpn_plugin_info_get_default_dir_etc ()
  191. {
  192. return DEFAULT_DIR_ETC;
  193. }
  194. /**
  195. * _nm_vpn_plugin_info_get_default_dir_lib:
  196. *
  197. * Returns: (transfer none): compile time constant of the default
  198. * VPN plugin directory.
  199. */
  200. const char *
  201. _nm_vpn_plugin_info_get_default_dir_lib ()
  202. {
  203. return DEFAULT_DIR_LIB;
  204. }
  205. /**
  206. * _nm_vpn_plugin_info_get_default_dir_user:
  207. *
  208. * Returns: The user can specify a different directory for VPN plugins
  209. * by setting NM_VPN_PLUGIN_DIR environment variable. Return
  210. * that directory.
  211. */
  212. const char *
  213. _nm_vpn_plugin_info_get_default_dir_user ()
  214. {
  215. return g_getenv ("NM_VPN_PLUGIN_DIR");
  216. }
  217. /**
  218. * _nm_vpn_plugin_info_list_load_dir:
  219. * @dirname: the name of the directory to load.
  220. * @do_validate_filename: only consider filenames that have a certain
  221. * pattern (i.e. end with ".name").
  222. * @check_owner: if set to a non-negative number, check that the file
  223. * owner is either the same uid or 0. In that case, also check
  224. * that the file is not writable by group or other.
  225. * @check_file: (allow-none): callback to check whether the file is valid.
  226. * @user_data: data for @check_file
  227. *
  228. * Iterate over the content of @dirname and load name files.
  229. *
  230. * Returns: (transfer full) (element-type NMVpnPluginInfo): list of loaded plugin infos.
  231. */
  232. GSList *
  233. _nm_vpn_plugin_info_list_load_dir (const char *dirname,
  234. gboolean do_validate_filename,
  235. gint64 check_owner,
  236. NMUtilsCheckFilePredicate check_file,
  237. gpointer user_data)
  238. {
  239. GDir *dir;
  240. const char *fn;
  241. GArray *array;
  242. GSList *res = NULL;
  243. guint i;
  244. g_return_val_if_fail (dirname, NULL);
  245. if (!dirname[0])
  246. return NULL;
  247. dir = g_dir_open (dirname, 0, NULL);
  248. if (!dir)
  249. return NULL;
  250. array = g_array_new (FALSE, FALSE, sizeof (LoadDirInfo));
  251. while ((fn = g_dir_read_name (dir))) {
  252. gs_free char *filename = NULL;
  253. LoadDirInfo info = { 0 };
  254. filename = g_build_filename (dirname, fn, NULL);
  255. if (nm_vpn_plugin_info_check_file_full (filename,
  256. FALSE,
  257. do_validate_filename,
  258. check_owner,
  259. check_file,
  260. user_data,
  261. &info.stat,
  262. NULL)) {
  263. info.plugin_info = nm_vpn_plugin_info_new_from_file (filename, NULL);
  264. if (info.plugin_info) {
  265. g_array_append_val (array, info);
  266. continue;
  267. }
  268. }
  269. }
  270. g_dir_close (dir);
  271. /* sort the files so that we have a stable behavior. The directory might contain
  272. * duplicate VPNs, so while nm_vpn_plugin_info_list_load() would load them all, the
  273. * caller probably wants to reject duplicates. Having a stable order means we always
  274. * reject the same files in face of duplicates. */
  275. g_array_sort (array, (GCompareFunc) _sort_files);
  276. for (i = 0; i < array->len; i++)
  277. res = g_slist_prepend (res, g_array_index (array, LoadDirInfo, i).plugin_info);
  278. g_array_unref (array);
  279. return g_slist_reverse (res);
  280. }
  281. /**
  282. * nm_vpn_plugin_info_list_load:
  283. *
  284. * Returns: (element-type NMVpnPluginInfo) (transfer full): list of plugins
  285. * loaded from the default directories rejecting duplicates.
  286. *
  287. * Since: 1.2
  288. */
  289. GSList *
  290. nm_vpn_plugin_info_list_load ()
  291. {
  292. int i;
  293. gint64 uid;
  294. GSList *list = NULL;
  295. GSList *infos, *info;
  296. DEFINE_DEFAULT_DIR_LIST (dir);
  297. uid = getuid ();
  298. for (i = 0; i < G_N_ELEMENTS (dir); i++) {
  299. if ( !dir[i]
  300. || nm_utils_strv_find_first ((char **) dir, i, dir[i]) >= 0)
  301. continue;
  302. infos = _nm_vpn_plugin_info_list_load_dir (dir[i], TRUE, uid, NULL, NULL);
  303. for (info = infos; info; info = info->next)
  304. nm_vpn_plugin_info_list_add (&list, info->data, NULL);
  305. g_slist_free_full (infos, g_object_unref);
  306. }
  307. return list;
  308. }
  309. /**
  310. * nm_vpn_plugin_info_new_search_file:
  311. * @name: (allow-none): the name to search for. Either @name or @service
  312. * must be present.
  313. * @service: (allow-none): the service to search for. Either @name or
  314. * @service must be present.
  315. *
  316. * This has the same effect as doing a full nm_vpn_plugin_info_list_load()
  317. * followed by a search for the first matching VPN plugin info that has the
  318. * given @name and/or @service.
  319. *
  320. * Returns: (transfer full): a newly created instance of plugin info
  321. * or %NULL if no matching value was found.
  322. *
  323. * Since: 1.4
  324. */
  325. NMVpnPluginInfo *
  326. nm_vpn_plugin_info_new_search_file (const char *name, const char *service)
  327. {
  328. int i;
  329. gint64 uid;
  330. NMVpnPluginInfo *plugin_info = NULL;
  331. GSList *infos, *info;
  332. DEFINE_DEFAULT_DIR_LIST (dir);
  333. if (!name && !service)
  334. g_return_val_if_reached (NULL);
  335. uid = getuid ();
  336. for (i = 0; !plugin_info && i < G_N_ELEMENTS (dir); i++) {
  337. if ( !dir[i]
  338. || nm_utils_strv_find_first ((char **) dir, i, dir[i]) >= 0)
  339. continue;
  340. /* We still must load the entire directory while searching for the matching
  341. * plugin-info. The reason is that reading the directory has no stable
  342. * order and we can only sort them after reading the entire directory --
  343. * which _nm_vpn_plugin_info_list_load_dir() does. */
  344. infos = _nm_vpn_plugin_info_list_load_dir (dir[i], TRUE, uid, NULL, NULL);
  345. for (info = infos; info; info = info->next) {
  346. NMVpnPluginInfo *p = info->data;
  347. if (name && !nm_streq (nm_vpn_plugin_info_get_name (p), name))
  348. continue;
  349. if ( service
  350. && !nm_streq (nm_vpn_plugin_info_get_service (p), service)
  351. && (nm_utils_strv_find_first (NM_VPN_PLUGIN_INFO_GET_PRIVATE (p)->aliases,
  352. -1, service) < 0))
  353. continue;
  354. plugin_info = g_object_ref (p);
  355. break;
  356. }
  357. g_slist_free_full (infos, g_object_unref);
  358. }
  359. return plugin_info;
  360. }
  361. /*****************************************************************************/
  362. static gboolean
  363. _check_no_conflict (NMVpnPluginInfo *i1, NMVpnPluginInfo *i2, GError **error)
  364. {
  365. NMVpnPluginInfoPrivate *priv1, *priv2;
  366. uint i;
  367. struct {
  368. const char *group;
  369. const char *key;
  370. } check_list[] = {
  371. { NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION, "service" },
  372. { NM_VPN_PLUGIN_INFO_KF_GROUP_LIBNM, "plugin" },
  373. { NM_VPN_PLUGIN_INFO_KF_GROUP_GNOME, "properties" },
  374. };
  375. priv1 = NM_VPN_PLUGIN_INFO_GET_PRIVATE (i1);
  376. priv2 = NM_VPN_PLUGIN_INFO_GET_PRIVATE (i2);
  377. for (i = 0; i < G_N_ELEMENTS (check_list); i++) {
  378. gs_free NMUtilsStrStrDictKey *k = NULL;
  379. const char *s1, *s2;
  380. k = _nm_utils_strstrdictkey_create (check_list[i].group, check_list[i].key);
  381. s1 = g_hash_table_lookup (priv1->keys, k);
  382. if (!s1)
  383. continue;
  384. s2 = g_hash_table_lookup (priv2->keys, k);
  385. if (!s2)
  386. continue;
  387. if (strcmp (s1, s2) == 0) {
  388. g_set_error (error,
  389. NM_VPN_PLUGIN_ERROR,
  390. NM_VPN_PLUGIN_ERROR_FAILED,
  391. _("there exists a conflicting plugin (%s) that has the same %s.%s value"),
  392. priv2->name,
  393. check_list[i].group, check_list[i].key);
  394. return FALSE;
  395. }
  396. }
  397. return TRUE;
  398. }
  399. /**
  400. * nm_vpn_plugin_info_list_add:
  401. * @list: (element-type NMVpnPluginInfo): list of plugins
  402. * @plugin_info: instance to add
  403. * @error: failure reason
  404. *
  405. * Returns: %TRUE if the plugin was added to @list. This will fail
  406. * to add duplicate plugins.
  407. *
  408. * Since: 1.2
  409. */
  410. gboolean
  411. nm_vpn_plugin_info_list_add (GSList **list, NMVpnPluginInfo *plugin_info, GError **error)
  412. {
  413. GSList *iter;
  414. const char *name;
  415. g_return_val_if_fail (list, FALSE);
  416. g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (plugin_info), FALSE);
  417. name = nm_vpn_plugin_info_get_name (plugin_info);
  418. for (iter = *list; iter; iter = iter->next) {
  419. if (iter->data == plugin_info)
  420. return TRUE;
  421. if (strcmp (nm_vpn_plugin_info_get_name (iter->data), name) == 0) {
  422. g_set_error (error,
  423. NM_VPN_PLUGIN_ERROR,
  424. NM_VPN_PLUGIN_ERROR_FAILED,
  425. _("there exists a conflicting plugin with the same name (%s)"),
  426. name);
  427. return FALSE;
  428. }
  429. /* the plugin must have unique values for certain properties. E.g. two different
  430. * plugins cannot share the same service type. */
  431. if (!_check_no_conflict (plugin_info, iter->data, error))
  432. return FALSE;
  433. }
  434. *list = g_slist_append (*list, g_object_ref (plugin_info));
  435. return TRUE;
  436. }
  437. /**
  438. * nm_vpn_plugin_info_list_remove:
  439. * @list: (element-type NMVpnPluginInfo): list of plugins
  440. * @plugin_info: instance
  441. *
  442. * Remove @plugin_info from @list.
  443. *
  444. * Returns: %TRUE if @plugin_info was in @list and successfully removed.
  445. *
  446. * Since: 1.2
  447. */
  448. gboolean
  449. nm_vpn_plugin_info_list_remove (GSList **list, NMVpnPluginInfo *plugin_info)
  450. {
  451. g_return_val_if_fail (list, FALSE);
  452. g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (plugin_info), FALSE);
  453. if (!g_slist_find (*list, plugin_info))
  454. return FALSE;
  455. *list = g_slist_remove (*list, plugin_info);
  456. g_object_unref (plugin_info);
  457. return TRUE;
  458. }
  459. /**
  460. * nm_vpn_plugin_info_list_find_by_name:
  461. * @list: (element-type NMVpnPluginInfo): list of plugins
  462. * @name: name to search
  463. *
  464. * Returns: (transfer none): the first plugin with a matching @name (or %NULL).
  465. *
  466. * Since: 1.2
  467. */
  468. NMVpnPluginInfo *
  469. nm_vpn_plugin_info_list_find_by_name (GSList *list, const char *name)
  470. {
  471. GSList *iter;
  472. if (!name)
  473. g_return_val_if_reached (NULL);
  474. for (iter = list; iter; iter = iter->next) {
  475. if (strcmp (nm_vpn_plugin_info_get_name (iter->data), name) == 0)
  476. return iter->data;
  477. }
  478. return NULL;
  479. }
  480. /**
  481. * nm_vpn_plugin_info_list_find_by_filename:
  482. * @list: (element-type NMVpnPluginInfo): list of plugins
  483. * @filename: filename to search
  484. *
  485. * Returns: (transfer none): the first plugin with a matching @filename (or %NULL).
  486. *
  487. * Since: 1.2
  488. */
  489. NMVpnPluginInfo *
  490. nm_vpn_plugin_info_list_find_by_filename (GSList *list, const char *filename)
  491. {
  492. GSList *iter;
  493. if (!filename)
  494. g_return_val_if_reached (NULL);
  495. for (iter = list; iter; iter = iter->next) {
  496. if (g_strcmp0 (nm_vpn_plugin_info_get_filename (iter->data), filename) == 0)
  497. return iter->data;
  498. }
  499. return NULL;
  500. }
  501. static NMVpnPluginInfo *
  502. _list_find_by_service (GSList *list, const char *service)
  503. {
  504. for (; list; list = list->next) {
  505. NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (list->data);
  506. if ( nm_streq (priv->service, service)
  507. || nm_utils_strv_find_first (priv->aliases, -1, service) >= 0)
  508. return list->data;
  509. }
  510. return NULL;
  511. }
  512. /**
  513. * nm_vpn_plugin_info_list_find_by_service:
  514. * @list: (element-type NMVpnPluginInfo): list of plugins
  515. * @service: service to search. This can be the main service-type
  516. * or one of the provided aliases.
  517. *
  518. * Returns: (transfer none): the first plugin with a matching @service (or %NULL).
  519. *
  520. * Since: 1.2
  521. */
  522. NMVpnPluginInfo *
  523. nm_vpn_plugin_info_list_find_by_service (GSList *list, const char *service)
  524. {
  525. if (!service)
  526. g_return_val_if_reached (NULL);
  527. return _list_find_by_service (list, service);
  528. }
  529. /* known_names are well known short names for the service-type. They all implicitly
  530. * have a prefix "org.freedesktop.NetworkManager." + known_name. */
  531. static const char *known_names[] = {
  532. "openvpn",
  533. "vpnc",
  534. "pptp",
  535. "openconnect",
  536. "openswan",
  537. "libreswan",
  538. "strongswan",
  539. "ssh",
  540. "l2tp",
  541. "iodine",
  542. "fortisslvpn",
  543. };
  544. /**
  545. * nm_vpn_plugin_info_list_find_service_type:
  546. * @list: (element-type NMVpnPluginInfo): a possibly empty #GSList of #NMVpnPluginInfo instances
  547. * @name: a name to lookup the service-type.
  548. *
  549. * A VPN plugin provides one or several service-types, like org.freedesktop.NetworkManager.libreswan
  550. * Certain plugins provide more then one service type, via aliases (org.freedesktop.NetworkManager.openswan).
  551. * This function looks up a service-type (or an alias) based on a name.
  552. *
  553. * Preferably, the name can be a full service-type/alias of an installed
  554. * plugin. Otherwise, it can be the name of a VPN plugin (in which case, the
  555. * primary, non-aliased service-type is returned). Otherwise, it can be
  556. * one of several well known short-names (which is a hard-coded list of
  557. * types in libnm). On success, this returns a full qualified service-type
  558. * (or an alias). It doesn't say, that such an plugin is actually available,
  559. * but it could be retrieved via nm_vpn_plugin_info_list_find_by_service().
  560. *
  561. * Returns: (transfer full): the resolved service-type or %NULL on failure.
  562. *
  563. * Since: 1.4
  564. */
  565. char *
  566. nm_vpn_plugin_info_list_find_service_type (GSList *list, const char *name)
  567. {
  568. GSList *iter;
  569. char *n;
  570. if (!name)
  571. g_return_val_if_reached (NULL);
  572. if (!*name)
  573. return NULL;
  574. /* First, try to interpret @name as a full service-type (or alias). */
  575. if (_list_find_by_service (list, name))
  576. return g_strdup (name);
  577. /* try to interpret @name as plugin name, in which case we return
  578. * the main service-type (not an alias). */
  579. for (iter = list; iter; iter = iter->next) {
  580. NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (iter->data);
  581. if (nm_streq (priv->name, name))
  582. return g_strdup (priv->service);
  583. }
  584. /* check the hard-coded list of short-names. They all have have the same
  585. * well-known prefix org.freedesktop.NetworkManager and the name. */
  586. if (nm_utils_strv_find_first ((char **) known_names, G_N_ELEMENTS (known_names), name) >= 0)
  587. return g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, name);
  588. /* try, if there exists a plugin with @name under org.freedesktop.NetworkManager.
  589. * Allow this to be a valid abbreviation. */
  590. n = g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, name);
  591. if (_list_find_by_service (list, n))
  592. return n;
  593. g_free (n);
  594. /* currently, VPN plugins have no way to define a short-name for their
  595. * alias name, unless the alias name is prefixed by org.freedesktop.NetworkManager. */
  596. return NULL;
  597. }
  598. static const char *
  599. _service_type_get_default_abbreviation (const char *service_type)
  600. {
  601. if (!g_str_has_prefix (service_type, NM_DBUS_INTERFACE))
  602. return NULL;
  603. service_type += NM_STRLEN (NM_DBUS_INTERFACE);
  604. if (service_type[0] != '.')
  605. return NULL;
  606. service_type++;
  607. if (!service_type[0])
  608. return NULL;
  609. return service_type;
  610. }
  611. /**
  612. * nm_vpn_plugin_info_list_get_service_types:
  613. * @list: (element-type NMVpnPluginInfo): a possibly empty #GSList of #NMVpnPluginInfo
  614. * @only_existing: only include results that are actually in @list.
  615. * Otherwise, the result is extended with a hard-code list or
  616. * well-known plugins
  617. * @with_abbreviations: if %FALSE, only full service types are returned.
  618. * Otherwise, this also includes abbreviated names that can be used
  619. * with nm_vpn_plugin_info_list_find_service_type().
  620. *
  621. * Returns: (transfer full): a %NULL terminated strv list of strings.
  622. * The list itself and the values must be freed with g_strfreev().
  623. *
  624. * Since: 1.4
  625. */
  626. char **
  627. nm_vpn_plugin_info_list_get_service_types (GSList *list,
  628. gboolean only_existing,
  629. gboolean with_abbreviations)
  630. {
  631. GSList *iter;
  632. GPtrArray *l;
  633. guint i, j;
  634. const char *n;
  635. l = g_ptr_array_sized_new (20);
  636. for (iter = list; iter; iter = iter->next) {
  637. NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (iter->data);
  638. g_ptr_array_add (l, g_strdup (priv->service));
  639. if (priv->aliases) {
  640. for (i = 0; priv->aliases[i]; i++)
  641. g_ptr_array_add (l, g_strdup (priv->aliases[i]));
  642. }
  643. if (with_abbreviations) {
  644. g_ptr_array_add (l, g_strdup (priv->name));
  645. n = _service_type_get_default_abbreviation (priv->service);
  646. if (n)
  647. g_ptr_array_add (l, g_strdup (n));
  648. for (i = 0; priv->aliases && priv->aliases[i]; i++) {
  649. n = _service_type_get_default_abbreviation (priv->aliases[i]);
  650. if (n)
  651. g_ptr_array_add (l, g_strdup (n));
  652. }
  653. }
  654. }
  655. if (!only_existing) {
  656. for (i = 0; i < G_N_ELEMENTS (known_names); i++) {
  657. g_ptr_array_add (l, g_strdup_printf ("%s.%s", NM_DBUS_INTERFACE, known_names[i]));
  658. if (with_abbreviations)
  659. g_ptr_array_add (l, g_strdup (known_names[i]));
  660. }
  661. }
  662. if (l->len <= 0) {
  663. g_ptr_array_free (l, TRUE);
  664. return g_new0 (char *, 1);
  665. }
  666. /* sort the result and remove duplicates. */
  667. g_ptr_array_sort (l, nm_strcmp_p);
  668. for (i = 1, j = 1; i < l->len; i++) {
  669. if (nm_streq (l->pdata[j-1], l->pdata[i]))
  670. g_free (l->pdata[i]);
  671. else
  672. l->pdata[j++] = l->pdata[i];
  673. }
  674. if (j == l->len)
  675. g_ptr_array_add (l, NULL);
  676. else
  677. l->pdata[j] = NULL;
  678. return (char **) g_ptr_array_free (l, FALSE);
  679. }
  680. /*****************************************************************************/
  681. /**
  682. * nm_vpn_plugin_info_get_filename:
  683. * @self: plugin info instance
  684. *
  685. * Returns: (transfer none): the filename. Can be %NULL.
  686. *
  687. * Since: 1.2
  688. */
  689. const char *
  690. nm_vpn_plugin_info_get_filename (NMVpnPluginInfo *self)
  691. {
  692. g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
  693. return NM_VPN_PLUGIN_INFO_GET_PRIVATE (self)->filename;
  694. }
  695. /**
  696. * nm_vpn_plugin_info_get_name:
  697. * @self: plugin info instance
  698. *
  699. * Returns: (transfer none): the name. Cannot be %NULL.
  700. *
  701. * Since: 1.2
  702. */
  703. const char *
  704. nm_vpn_plugin_info_get_name (NMVpnPluginInfo *self)
  705. {
  706. g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
  707. return NM_VPN_PLUGIN_INFO_GET_PRIVATE (self)->name;
  708. }
  709. /**
  710. * nm_vpn_plugin_info_get_service:
  711. * @self: plugin info instance
  712. *
  713. * Returns: (transfer none): the service. Cannot be %NULL.
  714. *
  715. * Since: 1.4
  716. */
  717. const char *
  718. nm_vpn_plugin_info_get_service (NMVpnPluginInfo *self)
  719. {
  720. g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
  721. return NM_VPN_PLUGIN_INFO_GET_PRIVATE (self)->service;
  722. }
  723. /**
  724. * nm_vpn_plugin_info_get_auth_dialog:
  725. * @self: plugin info instance
  726. *
  727. * Returns: the absolute path to the auth-dialog helper or %NULL.
  728. *
  729. * Since: 1.4
  730. **/
  731. const char *
  732. nm_vpn_plugin_info_get_auth_dialog (NMVpnPluginInfo *self)
  733. {
  734. NMVpnPluginInfoPrivate *priv;
  735. g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
  736. priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (self);
  737. if (G_UNLIKELY (priv->auth_dialog == NULL)) {
  738. const char *s;
  739. s = g_hash_table_lookup (priv->keys, _nm_utils_strstrdictkey_static (NM_VPN_PLUGIN_INFO_KF_GROUP_GNOME, "auth-dialog"));
  740. if (!s || !s[0])
  741. priv->auth_dialog = g_strdup ("");
  742. else if (g_path_is_absolute (s))
  743. priv->auth_dialog = g_strdup (s);
  744. else {
  745. /* for relative paths, we take the basename and assume it's in LIBEXECDIR. */
  746. gs_free char *prog_basename = g_path_get_basename (s);
  747. priv->auth_dialog = g_build_filename (LIBEXECDIR, prog_basename, NULL);
  748. }
  749. }
  750. return priv->auth_dialog[0] ? priv->auth_dialog : NULL;
  751. }
  752. /**
  753. * nm_vpn_plugin_info_supports_hints:
  754. * @self: plugin info instance
  755. *
  756. * Returns: %TRUE if the supports hints for secret requests, otherwise %FALSE
  757. *
  758. * Since: 1.4
  759. */
  760. gboolean
  761. nm_vpn_plugin_info_supports_hints (NMVpnPluginInfo *self)
  762. {
  763. const char *s;
  764. g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), FALSE);
  765. s = nm_vpn_plugin_info_lookup_property (self, NM_VPN_PLUGIN_INFO_KF_GROUP_GNOME, "supports-hints");
  766. return _nm_utils_ascii_str_to_bool (s, FALSE);
  767. }
  768. /**
  769. * nm_vpn_plugin_info_get_plugin:
  770. * @self: plugin info instance
  771. *
  772. * Returns: (transfer none): the plugin. Can be %NULL.
  773. *
  774. * Since: 1.2
  775. */
  776. const char *
  777. nm_vpn_plugin_info_get_plugin (NMVpnPluginInfo *self)
  778. {
  779. g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
  780. return g_hash_table_lookup (NM_VPN_PLUGIN_INFO_GET_PRIVATE (self)->keys,
  781. _nm_utils_strstrdictkey_static (NM_VPN_PLUGIN_INFO_KF_GROUP_LIBNM, "plugin"));
  782. }
  783. /**
  784. * nm_vpn_plugin_info_get_program:
  785. * @self: plugin info instance
  786. *
  787. * Returns: (transfer none): the program. Can be %NULL.
  788. *
  789. * Since: 1.2
  790. */
  791. const char *
  792. nm_vpn_plugin_info_get_program (NMVpnPluginInfo *self)
  793. {
  794. g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
  795. return g_hash_table_lookup (NM_VPN_PLUGIN_INFO_GET_PRIVATE (self)->keys,
  796. _nm_utils_strstrdictkey_static (NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION, "program"));
  797. }
  798. /**
  799. * nm_vpn_plugin_info_supports_multiple:
  800. * @self: plugin info instance
  801. *
  802. * Returns: %TRUE if the service supports multiple instances with different bus names, otherwise %FALSE
  803. *
  804. * Since: 1.2
  805. */
  806. gboolean
  807. nm_vpn_plugin_info_supports_multiple (NMVpnPluginInfo *self)
  808. {
  809. const char *s;
  810. g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), FALSE);
  811. s = nm_vpn_plugin_info_lookup_property (self, NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION, "supports-multiple-connections");
  812. return _nm_utils_ascii_str_to_bool (s, FALSE);
  813. }
  814. /**
  815. * nm_vpn_plugin_info_get_aliases:
  816. * @self: plugin info instance
  817. *
  818. * Returns: (array zero-terminated=1) (element-type utf8) (transfer none):
  819. * the aliases from the name-file.
  820. *
  821. * Since: 1.4
  822. */
  823. const char *const*
  824. nm_vpn_plugin_info_get_aliases (NMVpnPluginInfo *self)
  825. {
  826. NMVpnPluginInfoPrivate *priv;
  827. g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
  828. priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (self);
  829. if (priv->aliases)
  830. return (const char *const*) priv->aliases;
  831. /* For convenience, we always want to return non-NULL, even for empty
  832. * aliases. Hack around that, by making a NULL terminated array using
  833. * the NULL of priv->aliases. */
  834. return (const char *const*) &priv->aliases;
  835. }
  836. /**
  837. * nm_vpn_plugin_info_lookup_property:
  838. * @self: plugin info instance
  839. * @group: group name
  840. * @key: name of the property
  841. *
  842. * Returns: (transfer none): #NMVpnPluginInfo is internally a #GKeyFile. Returns the matching
  843. * property.
  844. *
  845. * Since: 1.2
  846. */
  847. const char *
  848. nm_vpn_plugin_info_lookup_property (NMVpnPluginInfo *self, const char *group, const char *key)
  849. {
  850. NMVpnPluginInfoPrivate *priv;
  851. gs_free NMUtilsStrStrDictKey *k = NULL;
  852. g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
  853. g_return_val_if_fail (group, NULL);
  854. g_return_val_if_fail (key, NULL);
  855. priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (self);
  856. k = _nm_utils_strstrdictkey_create (group, key);
  857. return g_hash_table_lookup (priv->keys, k);
  858. }
  859. /*****************************************************************************/
  860. /**
  861. * nm_vpn_plugin_info_get_editor_plugin:
  862. * @self: plugin info instance
  863. *
  864. * Returns: (transfer none): the cached #NMVpnEditorPlugin instance.
  865. *
  866. * Since: 1.2
  867. */
  868. NMVpnEditorPlugin *
  869. nm_vpn_plugin_info_get_editor_plugin (NMVpnPluginInfo *self)
  870. {
  871. g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
  872. return NM_VPN_PLUGIN_INFO_GET_PRIVATE (self)->editor_plugin;
  873. }
  874. /**
  875. * nm_vpn_plugin_info_set_editor_plugin:
  876. * @self: plugin info instance
  877. * @plugin: (allow-none): plugin instance
  878. *
  879. * Set the internal plugin instance. If %NULL, only clear the previous instance.
  880. *
  881. * Since: 1.2
  882. */
  883. void
  884. nm_vpn_plugin_info_set_editor_plugin (NMVpnPluginInfo *self, NMVpnEditorPlugin *plugin)
  885. {
  886. NMVpnPluginInfoPrivate *priv;
  887. NMVpnEditorPlugin *old;
  888. g_return_if_fail (NM_IS_VPN_PLUGIN_INFO (self));
  889. g_return_if_fail (!plugin || G_IS_OBJECT (plugin));
  890. priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (self);
  891. if (!plugin) {
  892. priv->editor_plugin_loaded = FALSE;
  893. g_clear_object (&priv->editor_plugin);
  894. } else {
  895. old = priv->editor_plugin;
  896. priv->editor_plugin = g_object_ref (plugin);
  897. priv->editor_plugin_loaded = TRUE;
  898. if (old)
  899. g_object_unref (old);
  900. }
  901. }
  902. /**
  903. * nm_vpn_plugin_info_load_editor_plugin:
  904. * @self: plugin info instance
  905. * @error: error reason on failure
  906. *
  907. * Returns: (transfer none): loads the plugin and returns the newly created
  908. * instance. The plugin is owned by @self and can be later retrieved again
  909. * via nm_vpn_plugin_info_get_editor_plugin(). You can load the
  910. * plugin only once, unless you reset the state via
  911. * nm_vpn_plugin_info_set_editor_plugin().
  912. *
  913. * Since: 1.2
  914. */
  915. NMVpnEditorPlugin *
  916. nm_vpn_plugin_info_load_editor_plugin (NMVpnPluginInfo *self, GError **error)
  917. {
  918. NMVpnPluginInfoPrivate *priv;
  919. const char *plugin_filename;
  920. g_return_val_if_fail (NM_IS_VPN_PLUGIN_INFO (self), NULL);
  921. priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (self);
  922. if (priv->editor_plugin)
  923. return priv->editor_plugin;
  924. plugin_filename = nm_vpn_plugin_info_get_plugin (self);
  925. if (!plugin_filename || !*plugin_filename) {
  926. g_set_error (error,
  927. NM_VPN_PLUGIN_ERROR,
  928. NM_VPN_PLUGIN_ERROR_FAILED,
  929. _("missing \"plugin\" setting"));
  930. return NULL;
  931. }
  932. /* We only try once to load the plugin. If we previously tried and it was
  933. * unsuccessful, error out immediately. */
  934. if (priv->editor_plugin_loaded) {
  935. g_set_error (error,
  936. NM_VPN_PLUGIN_ERROR,
  937. NM_VPN_PLUGIN_ERROR_FAILED,
  938. _("%s: don't retry loading plugin which already failed previously"), priv->name);
  939. return NULL;
  940. }
  941. priv->editor_plugin_loaded = TRUE;
  942. priv->editor_plugin = nm_vpn_editor_plugin_load_from_file (plugin_filename,
  943. nm_vpn_plugin_info_get_service (self),
  944. getuid (),
  945. NULL,
  946. NULL,
  947. error);
  948. if (priv->editor_plugin)
  949. nm_vpn_editor_plugin_set_plugin_info (priv->editor_plugin, self);
  950. return priv->editor_plugin;
  951. }
  952. /*****************************************************************************/
  953. /**
  954. * nm_vpn_plugin_info_new_from_file:
  955. * @filename: filename to read.
  956. * @error: on failure, the error reason.
  957. *
  958. * Read the plugin info from file @filename. Does not do
  959. * any further verification on the file. You might want to check
  960. * file permissions and ownership of the file.
  961. *
  962. * Returns: %NULL if there is any error or a newly created
  963. * #NMVpnPluginInfo instance.
  964. *
  965. * Since: 1.2
  966. */
  967. NMVpnPluginInfo *
  968. nm_vpn_plugin_info_new_from_file (const char *filename,
  969. GError **error)
  970. {
  971. g_return_val_if_fail (filename, NULL);
  972. return NM_VPN_PLUGIN_INFO (g_initable_new (NM_TYPE_VPN_PLUGIN_INFO,
  973. NULL,
  974. error,
  975. NM_VPN_PLUGIN_INFO_FILENAME, filename,
  976. NULL));
  977. }
  978. /**
  979. * nm_vpn_plugin_info_new_with_data:
  980. * @filename: optional filename.
  981. * @keyfile: inject data for the plugin info instance.
  982. * @error: construction may fail if the keyfile lacks mandatory fields.
  983. * In this case, return the error reason.
  984. *
  985. * This constructor does not read any data from file but
  986. * takes instead a @keyfile argument.
  987. *
  988. * Returns: new plugin info instance.
  989. *
  990. * Since: 1.2
  991. */
  992. NMVpnPluginInfo *
  993. nm_vpn_plugin_info_new_with_data (const char *filename,
  994. GKeyFile *keyfile,
  995. GError **error)
  996. {
  997. g_return_val_if_fail (keyfile, NULL);
  998. return NM_VPN_PLUGIN_INFO (g_initable_new (NM_TYPE_VPN_PLUGIN_INFO,
  999. NULL,
  1000. error,
  1001. NM_VPN_PLUGIN_INFO_FILENAME, filename,
  1002. NM_VPN_PLUGIN_INFO_KEYFILE, keyfile,
  1003. NULL));
  1004. }
  1005. /*****************************************************************************/
  1006. static void
  1007. nm_vpn_plugin_info_init (NMVpnPluginInfo *plugin)
  1008. {
  1009. }
  1010. static gboolean
  1011. init_sync (GInitable *initable, GCancellable *cancellable, GError **error)
  1012. {
  1013. NMVpnPluginInfo *self = NM_VPN_PLUGIN_INFO (initable);
  1014. NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (self);
  1015. gs_strfreev char **groups = NULL;
  1016. guint i, j;
  1017. if (!priv->keyfile) {
  1018. if (!priv->filename) {
  1019. g_set_error_literal (error,
  1020. NM_VPN_PLUGIN_ERROR,
  1021. NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  1022. _("missing filename to load VPN plugin info"));
  1023. return FALSE;
  1024. }
  1025. priv->keyfile = g_key_file_new ();
  1026. if (!g_key_file_load_from_file (priv->keyfile, priv->filename, G_KEY_FILE_NONE, error))
  1027. return FALSE;
  1028. }
  1029. /* we reqire at least a "name" */
  1030. priv->name = g_key_file_get_string (priv->keyfile, NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION, "name", NULL);
  1031. if (!priv->name || !priv->name[0]) {
  1032. g_set_error_literal (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  1033. _("missing name for VPN plugin info"));
  1034. return FALSE;
  1035. }
  1036. /* we also require "service", because that how we associate NMSettingVpn:service-type with the
  1037. * NMVpnPluginInfo. */
  1038. priv->service = g_key_file_get_string (priv->keyfile, NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION, "service", NULL);
  1039. if (!priv->service || !*priv->service) {
  1040. g_set_error_literal (error, NM_VPN_PLUGIN_ERROR, NM_VPN_PLUGIN_ERROR_BAD_ARGUMENTS,
  1041. _("missing service for VPN plugin info"));
  1042. return FALSE;
  1043. }
  1044. priv->aliases = g_key_file_get_string_list (priv->keyfile, NM_VPN_PLUGIN_INFO_KF_GROUP_CONNECTION, "aliases", NULL, NULL);
  1045. if (priv->aliases && !priv->aliases[0])
  1046. g_clear_pointer (&priv->aliases, g_free);
  1047. priv->keys = g_hash_table_new_full (_nm_utils_strstrdictkey_hash,
  1048. _nm_utils_strstrdictkey_equal,
  1049. g_free, g_free);
  1050. groups = g_key_file_get_groups (priv->keyfile, NULL);
  1051. for (i = 0; groups && groups[i]; i++) {
  1052. gs_strfreev char **keys = NULL;
  1053. keys = g_key_file_get_keys (priv->keyfile, groups[i], NULL, NULL);
  1054. for (j = 0; keys && keys[j]; j++) {
  1055. char *s;
  1056. /* Lookup the value via get_string(). We want that behavior for all our
  1057. * values. */
  1058. s = g_key_file_get_string (priv->keyfile, groups[i], keys[j], NULL);
  1059. if (s)
  1060. g_hash_table_insert (priv->keys, _nm_utils_strstrdictkey_create (groups[i], keys[j]), s);
  1061. }
  1062. }
  1063. g_clear_pointer (&priv->keyfile, g_key_file_unref);
  1064. return TRUE;
  1065. }
  1066. static void
  1067. set_property (GObject *object, guint prop_id,
  1068. const GValue *value, GParamSpec *pspec)
  1069. {
  1070. NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (object);
  1071. switch (prop_id) {
  1072. case PROP_FILENAME:
  1073. priv->filename = g_value_dup_string (value);
  1074. break;
  1075. case PROP_KEYFILE:
  1076. priv->keyfile = g_value_dup_boxed (value);
  1077. break;
  1078. default:
  1079. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  1080. break;
  1081. }
  1082. }
  1083. static void
  1084. get_property (GObject *object, guint prop_id,
  1085. GValue *value, GParamSpec *pspec)
  1086. {
  1087. NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (object);
  1088. switch (prop_id) {
  1089. case PROP_NAME:
  1090. g_value_set_string (value, priv->name);
  1091. break;
  1092. case PROP_FILENAME:
  1093. g_value_set_string (value, priv->filename);
  1094. break;
  1095. default:
  1096. G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
  1097. break;
  1098. }
  1099. }
  1100. static void
  1101. dispose (GObject *object)
  1102. {
  1103. NMVpnPluginInfo *self = NM_VPN_PLUGIN_INFO (object);
  1104. NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (self);
  1105. g_clear_object (&priv->editor_plugin);
  1106. G_OBJECT_CLASS (nm_vpn_plugin_info_parent_class)->dispose (object);
  1107. }
  1108. static void
  1109. finalize (GObject *object)
  1110. {
  1111. NMVpnPluginInfo *self = NM_VPN_PLUGIN_INFO (object);
  1112. NMVpnPluginInfoPrivate *priv = NM_VPN_PLUGIN_INFO_GET_PRIVATE (self);
  1113. g_free (priv->name);
  1114. g_free (priv->service);
  1115. g_free (priv->auth_dialog);
  1116. g_strfreev (priv->aliases);
  1117. g_free (priv->filename);
  1118. g_hash_table_unref (priv->keys);
  1119. g_clear_pointer (&priv->keyfile, g_key_file_unref);
  1120. G_OBJECT_CLASS (nm_vpn_plugin_info_parent_class)->finalize (object);
  1121. }
  1122. static void
  1123. nm_vpn_plugin_info_class_init (NMVpnPluginInfoClass *plugin_class)
  1124. {
  1125. GObjectClass *object_class = G_OBJECT_CLASS (plugin_class);
  1126. g_type_class_add_private (object_class, sizeof (NMVpnPluginInfoPrivate));
  1127. /* virtual methods */
  1128. object_class->set_property = set_property;
  1129. object_class->get_property = get_property;
  1130. object_class->dispose = dispose;
  1131. object_class->finalize = finalize;
  1132. /* properties */
  1133. /**
  1134. * NMVpnPluginInfo:name:
  1135. *
  1136. * The name of the VPN plugin.
  1137. *
  1138. * Since: 1.2
  1139. */
  1140. g_object_class_install_property
  1141. (object_class, PROP_NAME,
  1142. g_param_spec_string (NM_VPN_PLUGIN_INFO_NAME, "", "",
  1143. NULL,
  1144. G_PARAM_READABLE |
  1145. G_PARAM_STATIC_STRINGS));
  1146. /**
  1147. * NMVpnPluginInfo:filename:
  1148. *
  1149. * The filename from which the info was loaded.
  1150. * Can be %NULL if the instance was not loaded from
  1151. * a file (i.e. the keyfile instance was passed to the
  1152. * constructor).
  1153. *
  1154. * Since: 1.2
  1155. */
  1156. g_object_class_install_property
  1157. (object_class, PROP_FILENAME,
  1158. g_param_spec_string (NM_VPN_PLUGIN_INFO_FILENAME, "", "",
  1159. NULL,
  1160. G_PARAM_READWRITE |
  1161. G_PARAM_CONSTRUCT_ONLY |
  1162. G_PARAM_STATIC_STRINGS));
  1163. /**
  1164. * NMVpnPluginInfo:keyfile:
  1165. *
  1166. * Initialize the instance with a different keyfile instance.
  1167. * When passing a keyfile instance, the constructor will not
  1168. * try to read from filename.
  1169. *
  1170. * Since: 1.2
  1171. */
  1172. g_object_class_install_property
  1173. (object_class, PROP_KEYFILE,
  1174. g_param_spec_boxed (NM_VPN_PLUGIN_INFO_KEYFILE, "", "",
  1175. G_TYPE_KEY_FILE,
  1176. G_PARAM_WRITABLE |
  1177. G_PARAM_CONSTRUCT_ONLY |
  1178. G_PARAM_STATIC_STRINGS));
  1179. }
  1180. static void
  1181. nm_vpn_plugin_info_initable_iface_init (GInitableIface *iface)
  1182. {
  1183. iface->init = init_sync;
  1184. }