Graphical package manager for pacman based on pamac 5.x.x
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2087 lines
66KB

  1. /*
  2. * pamac-vala
  3. *
  4. * Copyright (C) 2014-2016 Guillaume Benoit <guillaume@manjaro.org>
  5. *
  6. * This program is free software; you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation; either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * This program is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  14. * GNU General Public License for more details.
  15. *
  16. * You should have received a get of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. // i18n
  20. const string GETTEXT_PACKAGE = "pamac";
  21. Pamac.Daemon pamac_daemon;
  22. MainLoop loop;
  23. public delegate void AlpmActionDelegate ();
  24. [Compact]
  25. public class AlpmAction {
  26. public unowned AlpmActionDelegate action_delegate;
  27. public AlpmAction (AlpmActionDelegate action_delegate) {
  28. this.action_delegate = action_delegate;
  29. }
  30. public void run () {
  31. action_delegate ();
  32. }
  33. }
  34. private int alpm_pkg_compare_name (Alpm.Package pkg_a, Alpm.Package pkg_b) {
  35. return strcmp (pkg_a.name, pkg_b.name);
  36. }
  37. namespace Pamac {
  38. [DBus (name = "org.manjaro.pamac")]
  39. public class Daemon: Object {
  40. private AlpmConfig alpm_config;
  41. private Alpm.Handle? alpm_handle;
  42. public Cond provider_cond;
  43. public Mutex provider_mutex;
  44. public int? choosen_provider;
  45. private bool force_refresh;
  46. private ThreadPool<AlpmAction> thread_pool;
  47. private Mutex databases_lock_mutex;
  48. private Json.Array aur_updates_results;
  49. private HashTable<string, Json.Array> aur_search_results;
  50. private HashTable<string, Json.Object> aur_infos;
  51. private bool intern_lock;
  52. private bool extern_lock;
  53. private GLib.File lockfile;
  54. private ErrorInfos current_error;
  55. public Timer timer;
  56. public Cancellable cancellable;
  57. public Curl.Easy curl;
  58. private GLib.List<string> aur_dep_list;
  59. public signal void emit_event (uint primary_event, uint secondary_event, string[] details);
  60. public signal void emit_providers (string depend, string[] providers);
  61. public signal void emit_progress (uint progress, string pkgname, uint percent, uint n_targets, uint current_target);
  62. public signal void emit_download (string filename, uint64 xfered, uint64 total);
  63. public signal void emit_totaldownload (uint64 total);
  64. public signal void emit_log (uint level, string msg);
  65. public signal void set_pkgreason_finished ();
  66. public signal void refresh_finished (bool success);
  67. public signal void get_updates_finished (Updates updates);
  68. public signal void trans_prepare_finished (bool success);
  69. public signal void trans_commit_finished (bool success);
  70. public signal void get_authorization_finished (bool authorized);
  71. public signal void write_pamac_config_finished (bool recurse, uint64 refresh_period, bool no_update_hide_icon,
  72. bool enable_aur, bool search_aur, bool check_aur_updates,
  73. bool no_confirm_build);
  74. public signal void write_alpm_config_finished (bool checkspace);
  75. public signal void write_mirrors_config_finished (string choosen_country, string choosen_generation_method);
  76. public signal void generate_mirrors_list_data (string line);
  77. public signal void generate_mirrors_list_finished ();
  78. public Daemon () {
  79. alpm_config = new AlpmConfig ("/etc/pacman.conf");
  80. databases_lock_mutex = Mutex ();
  81. aur_updates_results = new Json.Array ();
  82. aur_search_results = new HashTable<string, Json.Array> (str_hash, str_equal);
  83. aur_infos = new HashTable<string, Json.Object> (str_hash, str_equal);
  84. timer = new Timer ();
  85. intern_lock = false;
  86. extern_lock = false;
  87. refresh_handle ();
  88. Timeout.add (500, check_pacman_running);
  89. create_thread_pool ();
  90. cancellable = new Cancellable ();
  91. Curl.global_init (Curl.GLOBAL_SSL);
  92. }
  93. ~Daemon () {
  94. Curl.global_cleanup ();
  95. }
  96. public void set_environment_variables (HashTable<string,string> variables) {
  97. string[] keys = { "HTTP_USER_AGENT",
  98. "http_proxy",
  99. "https_proxy",
  100. "ftp_proxy",
  101. "socks_proxy",
  102. "no_proxy" };
  103. foreach (unowned string key in keys) {
  104. unowned string val;
  105. if (variables.lookup_extended (key, null, out val)) {
  106. Environment.set_variable (key, val, true);
  107. }
  108. }
  109. }
  110. public ErrorInfos get_current_error () {
  111. return current_error;
  112. }
  113. private void create_thread_pool () {
  114. // create a thread pool which will run alpm action one after one
  115. try {
  116. thread_pool = new ThreadPool<AlpmAction>.with_owned_data (
  117. // call alpm_action.run () on thread start
  118. (alpm_action) => {
  119. alpm_action.run ();
  120. },
  121. // only one thread created so alpm action will run one after one
  122. 1,
  123. // exclusive thread
  124. true
  125. );
  126. } catch (ThreadError e) {
  127. stderr.printf ("Thread Error %s\n", e.message);
  128. }
  129. }
  130. private void refresh_handle () {
  131. alpm_handle = alpm_config.get_handle ();
  132. if (alpm_handle == null) {
  133. current_error = ErrorInfos () {
  134. message = _("Failed to initialize alpm library")
  135. };
  136. trans_commit_finished (false);
  137. } else {
  138. alpm_handle.eventcb = (Alpm.EventCallBack) cb_event;
  139. alpm_handle.progresscb = (Alpm.ProgressCallBack) cb_progress;
  140. alpm_handle.questioncb = (Alpm.QuestionCallBack) cb_question;
  141. alpm_handle.fetchcb = (Alpm.FetchCallBack) cb_fetch;
  142. alpm_handle.totaldlcb = (Alpm.TotalDownloadCallBack) cb_totaldownload;
  143. alpm_handle.logcb = (Alpm.LogCallBack) cb_log;
  144. lockfile = GLib.File.new_for_path (alpm_handle.lockfile);
  145. }
  146. }
  147. private bool check_pacman_running () {
  148. if (extern_lock) {
  149. if (!lockfile.query_exists ()) {
  150. extern_lock = false;
  151. refresh_handle ();
  152. }
  153. } else {
  154. if (lockfile.query_exists ()) {
  155. if (!intern_lock) {
  156. extern_lock = true;
  157. }
  158. }
  159. }
  160. return true;
  161. }
  162. private async bool check_authorization (GLib.BusName sender) {
  163. SourceFunc callback = check_authorization.callback;
  164. bool authorized = false;
  165. try {
  166. Polkit.Authority authority = Polkit.Authority.get_sync ();
  167. Polkit.Subject subject = Polkit.SystemBusName.new (sender);
  168. authority.check_authorization.begin (
  169. subject,
  170. "org.manjaro.pamac.commit",
  171. null,
  172. Polkit.CheckAuthorizationFlags.ALLOW_USER_INTERACTION,
  173. null,
  174. (obj, res) => {
  175. try {
  176. var result = authority.check_authorization.end (res);
  177. authorized = result.get_is_authorized ();
  178. } catch (GLib.Error e) {
  179. stderr.printf ("%s\n", e.message);
  180. }
  181. Idle.add ((owned) callback);
  182. }
  183. );
  184. yield;
  185. } catch (GLib.Error e) {
  186. stderr.printf ("%s\n", e.message);
  187. }
  188. return authorized;
  189. }
  190. public void start_get_authorization (GLib.BusName sender) {
  191. check_authorization.begin (sender, (obj, res) => {
  192. bool authorized = check_authorization.end (res);
  193. get_authorization_finished (authorized);
  194. });
  195. }
  196. public void start_write_pamac_config (HashTable<string,Variant> new_pamac_conf, GLib.BusName sender) {
  197. check_authorization.begin (sender, (obj, res) => {
  198. var pamac_config = new Pamac.Config ("/etc/pamac.conf");
  199. bool authorized = check_authorization.end (res);
  200. if (authorized ) {
  201. pamac_config.write (new_pamac_conf);
  202. pamac_config.reload ();
  203. }
  204. write_pamac_config_finished (pamac_config.recurse, pamac_config.refresh_period, pamac_config.no_update_hide_icon,
  205. pamac_config.enable_aur, pamac_config.search_aur, pamac_config.check_aur_updates,
  206. pamac_config.no_confirm_build);
  207. });
  208. }
  209. public void start_write_alpm_config (HashTable<string,Variant> new_alpm_conf, GLib.BusName sender) {
  210. check_authorization.begin (sender, (obj, res) => {
  211. bool authorized = check_authorization.end (res);
  212. if (authorized ) {
  213. alpm_config.write (new_alpm_conf);
  214. alpm_config.reload ();
  215. refresh_handle ();
  216. }
  217. write_alpm_config_finished ((alpm_handle.checkspace == 1));
  218. });
  219. }
  220. private bool process_line (IOChannel channel, IOCondition condition, string stream_name) {
  221. if (condition == IOCondition.HUP) {
  222. return false;
  223. }
  224. try {
  225. string line;
  226. channel.read_line (out line, null, null);
  227. generate_mirrors_list_data (line);
  228. } catch (IOChannelError e) {
  229. stderr.printf ("%s: IOChannelError: %s\n", stream_name, e.message);
  230. return false;
  231. } catch (ConvertError e) {
  232. stderr.printf ("%s: ConvertError: %s\n", stream_name, e.message);
  233. return false;
  234. }
  235. return true;
  236. }
  237. public void start_generate_mirrors_list () {
  238. int standard_output;
  239. int standard_error;
  240. Pid child_pid;
  241. try {
  242. Process.spawn_async_with_pipes (null,
  243. {"pacman-mirrors", "-g"},
  244. null,
  245. SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD,
  246. null,
  247. out child_pid,
  248. null,
  249. out standard_output,
  250. out standard_error);
  251. // stdout
  252. IOChannel output = new IOChannel.unix_new (standard_output);
  253. output.add_watch (IOCondition.IN | IOCondition.HUP, (channel, condition) => {
  254. return process_line (channel, condition, "stdout");
  255. });
  256. // stderr
  257. IOChannel error = new IOChannel.unix_new (standard_error);
  258. error.add_watch (IOCondition.IN | IOCondition.HUP, (channel, condition) => {
  259. return process_line (channel, condition, "stderr");
  260. });
  261. ChildWatch.add (child_pid, (pid, status) => {
  262. // Triggered when the child indicated by child_pid exits
  263. Process.close_pid (pid);
  264. refresh_handle ();
  265. generate_mirrors_list_finished ();
  266. });
  267. } catch (SpawnError e) {
  268. generate_mirrors_list_finished ();
  269. stdout.printf ("SpawnError: %s\n", e.message);
  270. }
  271. }
  272. public void start_write_mirrors_config (HashTable<string,Variant> new_mirrors_conf, GLib.BusName sender) {
  273. var mirrors_config = new MirrorsConfig ("/etc/pacman-mirrors.conf");
  274. check_authorization.begin (sender, (obj, res) => {
  275. bool authorized = check_authorization.end (res);
  276. if (authorized) {
  277. mirrors_config.write (new_mirrors_conf);
  278. mirrors_config.reload ();
  279. }
  280. write_mirrors_config_finished (mirrors_config.choosen_country, mirrors_config.choosen_generation_method);
  281. });
  282. }
  283. public void start_set_pkgreason (string pkgname, uint reason, GLib.BusName sender) {
  284. check_authorization.begin (sender, (obj, res) => {
  285. bool authorized = check_authorization.end (res);
  286. if (authorized) {
  287. unowned Alpm.Package? pkg = alpm_handle.localdb.get_pkg (pkgname);
  288. if (pkg != null) {
  289. pkg.reason = (Alpm.Package.Reason) reason;
  290. refresh_handle ();
  291. }
  292. }
  293. set_pkgreason_finished ();
  294. });
  295. }
  296. private void refresh () {
  297. intern_lock = true;
  298. current_error = ErrorInfos ();
  299. int force = (force_refresh) ? 1 : 0;
  300. uint success = 0;
  301. cancellable.reset ();
  302. unowned Alpm.List<unowned Alpm.DB> syncdbs = alpm_handle.syncdbs;
  303. while (syncdbs != null) {
  304. unowned Alpm.DB db = syncdbs.data;
  305. if (cancellable.is_cancelled ()) {
  306. refresh_handle ();
  307. refresh_finished (false);
  308. intern_lock = false;
  309. return;
  310. }
  311. if (db.update (force) >= 0) {
  312. success++;
  313. }
  314. syncdbs.next ();
  315. }
  316. refresh_handle ();
  317. // We should always succeed if at least one DB was upgraded - we may possibly
  318. // fail later with unresolved deps, but that should be rare, and would be expected
  319. if (success == 0) {
  320. Alpm.Errno errno = alpm_handle.errno ();
  321. current_error.errno = (uint) errno;
  322. current_error.message = _("Failed to synchronize any databases");
  323. current_error.details = { Alpm.strerror (errno) };
  324. refresh_finished (false);
  325. } else {
  326. refresh_finished (true);
  327. }
  328. intern_lock = false;
  329. }
  330. public void start_refresh (bool force) {
  331. force_refresh = force;
  332. try {
  333. thread_pool.add (new AlpmAction (refresh));
  334. } catch (ThreadError e) {
  335. stderr.printf ("Thread Error %s\n", e.message);
  336. }
  337. }
  338. public bool get_checkspace () {
  339. return alpm_handle.checkspace == 1 ? true : false;
  340. }
  341. public string get_lockfile () {
  342. return alpm_handle.lockfile;
  343. }
  344. public string[] get_ignorepkgs () {
  345. string[] result = {};
  346. unowned Alpm.List<unowned string> ignorepkgs = alpm_handle.ignorepkgs;
  347. while (ignorepkgs != null) {
  348. unowned string ignorepkg = ignorepkgs.data;
  349. result += ignorepkg;
  350. ignorepkgs.next ();
  351. }
  352. return result;
  353. }
  354. public void add_ignorepkg (string pkgname) {
  355. alpm_handle.add_ignorepkg (pkgname);
  356. }
  357. public void remove_ignorepkg (string pkgname) {
  358. alpm_handle.remove_ignorepkg (pkgname);
  359. }
  360. public bool should_hold (string pkgname) {
  361. if (alpm_config.get_holdpkgs ().find_custom (pkgname, strcmp) != null) {
  362. return true;
  363. }
  364. return false;
  365. }
  366. public uint get_pkg_reason (string pkgname) {
  367. unowned Alpm.Package? pkg = alpm_handle.localdb.get_pkg (pkgname);
  368. if (pkg != null) {
  369. return pkg.reason;
  370. }
  371. return 0;
  372. }
  373. public uint get_pkg_origin (string pkgname) {
  374. unowned Alpm.Package? pkg = alpm_handle.localdb.get_pkg (pkgname);
  375. if (pkg != null) {
  376. return pkg.origin;
  377. } else {
  378. pkg = get_syncpkg (pkgname);
  379. if (pkg != null) {
  380. return pkg.origin;
  381. }
  382. }
  383. return 0;
  384. }
  385. private AlpmPackage initialise_pkg_struct (Alpm.Package? alpm_pkg) {
  386. if (alpm_pkg != null) {
  387. string repo_name = "";
  388. if (alpm_pkg.origin == Alpm.Package.From.LOCALDB) {
  389. unowned Alpm.Package? sync_pkg = get_syncpkg (alpm_pkg.name);
  390. if (sync_pkg != null) {
  391. repo_name = sync_pkg.db.name;
  392. }
  393. } else if (alpm_pkg.origin == Alpm.Package.From.SYNCDB) {
  394. repo_name = alpm_pkg.db.name;
  395. }
  396. return AlpmPackage () {
  397. name = alpm_pkg.name,
  398. version = alpm_pkg.version,
  399. // desc can be null
  400. desc = alpm_pkg.desc != null ? Markup.escape_text (alpm_pkg.desc) : "",
  401. repo = (owned) repo_name,
  402. size = alpm_pkg.isize,
  403. origin = (uint) alpm_pkg.origin
  404. };
  405. } else {
  406. return AlpmPackage () {
  407. name = "",
  408. version = "",
  409. desc = "",
  410. repo = ""
  411. };
  412. }
  413. }
  414. public async AlpmPackage[] get_installed_pkgs () {
  415. AlpmPackage[] pkgs = {};
  416. unowned Alpm.List<unowned Alpm.Package> pkgcache = alpm_handle.localdb.pkgcache;
  417. while (pkgcache != null) {
  418. unowned Alpm.Package alpm_pkg = pkgcache.data;
  419. pkgs += initialise_pkg_struct (alpm_pkg);
  420. pkgcache.next ();
  421. }
  422. return pkgs;
  423. }
  424. public async AlpmPackage[] get_foreign_pkgs () {
  425. AlpmPackage[] pkgs = {};
  426. unowned Alpm.List<unowned Alpm.Package> pkgcache = alpm_handle.localdb.pkgcache;
  427. while (pkgcache != null) {
  428. unowned Alpm.Package alpm_pkg = pkgcache.data;
  429. bool sync_found = false;
  430. unowned Alpm.List<unowned Alpm.DB> syncdbs = alpm_handle.syncdbs;
  431. while (syncdbs != null) {
  432. unowned Alpm.DB db = syncdbs.data;
  433. unowned Alpm.Package? sync_pkg = db.get_pkg (alpm_pkg.name);
  434. if (sync_pkg != null) {
  435. sync_found = true;
  436. break;
  437. }
  438. syncdbs.next ();
  439. }
  440. if (sync_found == false) {
  441. pkgs += initialise_pkg_struct (alpm_pkg);
  442. }
  443. pkgcache.next ();
  444. }
  445. return pkgs;
  446. }
  447. public async AlpmPackage[] get_orphans () {
  448. AlpmPackage[] pkgs = {};
  449. unowned Alpm.List<unowned Alpm.Package> pkgcache = alpm_handle.localdb.pkgcache;
  450. while (pkgcache != null) {
  451. unowned Alpm.Package alpm_pkg = pkgcache.data;
  452. if (alpm_pkg.reason == Alpm.Package.Reason.DEPEND) {
  453. Alpm.List<string> requiredby = alpm_pkg.compute_requiredby ();
  454. if (requiredby.length == 0) {
  455. Alpm.List<string> optionalfor = alpm_pkg.compute_optionalfor ();
  456. if (optionalfor.length == 0) {
  457. pkgs += initialise_pkg_struct (alpm_pkg);
  458. } else {
  459. optionalfor.free_inner (GLib.free);
  460. }
  461. } else {
  462. requiredby.free_inner (GLib.free);
  463. }
  464. }
  465. pkgcache.next ();
  466. }
  467. return pkgs;
  468. }
  469. public AlpmPackage get_installed_pkg (string pkgname) {
  470. return initialise_pkg_struct (alpm_handle.localdb.get_pkg (pkgname));
  471. }
  472. public AlpmPackage find_installed_satisfier (string depstring) {
  473. return initialise_pkg_struct (Alpm.find_satisfier (alpm_handle.localdb.pkgcache, depstring));
  474. }
  475. private unowned Alpm.Package? get_syncpkg (string name) {
  476. unowned Alpm.Package? pkg = null;
  477. unowned Alpm.List<unowned Alpm.DB> syncdbs = alpm_handle.syncdbs;
  478. while (syncdbs != null) {
  479. unowned Alpm.DB db = syncdbs.data;
  480. pkg = db.get_pkg (name);
  481. if (pkg != null) {
  482. break;
  483. }
  484. syncdbs.next ();
  485. }
  486. return pkg;
  487. }
  488. public AlpmPackage get_sync_pkg (string pkgname) {
  489. return initialise_pkg_struct (get_syncpkg (pkgname));
  490. }
  491. public AlpmPackage find_sync_satisfier (string depstring) {
  492. return initialise_pkg_struct (alpm_handle.find_dbs_satisfier (alpm_handle.syncdbs, depstring));
  493. }
  494. private Alpm.List<unowned Alpm.Package> search_all_dbs (string search_string) {
  495. Alpm.List<unowned string> needles = null;
  496. string[] splitted = search_string.split (" ");
  497. foreach (unowned string part in splitted) {
  498. needles.add (part);
  499. }
  500. Alpm.List<unowned Alpm.Package> result = alpm_handle.localdb.search (needles);
  501. Alpm.List<unowned Alpm.Package> syncpkgs = null;
  502. unowned Alpm.List<unowned Alpm.DB> syncdbs = alpm_handle.syncdbs;
  503. while (syncdbs != null) {
  504. unowned Alpm.DB db = syncdbs.data;
  505. if (syncpkgs.length == 0) {
  506. syncpkgs = db.search (needles);
  507. } else {
  508. syncpkgs.join (db.search (needles).diff (syncpkgs, (Alpm.List.CompareFunc) alpm_pkg_compare_name));
  509. }
  510. syncdbs.next ();
  511. }
  512. result.join (syncpkgs.diff (result, (Alpm.List.CompareFunc) alpm_pkg_compare_name));
  513. //result.sort ((Alpm.List.CompareFunc) alpm_pkg_compare_name);
  514. return result;
  515. }
  516. public async AlpmPackage[] search_pkgs (string search_string) {
  517. AlpmPackage[] result = {};
  518. Alpm.List<unowned Alpm.Package> alpm_pkgs = search_all_dbs (search_string);
  519. unowned Alpm.List<unowned Alpm.Package> list = alpm_pkgs;
  520. while (list != null) {
  521. unowned Alpm.Package alpm_pkg = list.data;
  522. result += initialise_pkg_struct (alpm_pkg);
  523. list.next ();
  524. }
  525. return result;
  526. }
  527. AURPackage initialise_aur_struct (Json.Object json_object) {
  528. return AURPackage () {
  529. name = json_object.get_string_member ("Name"),
  530. version = json_object.get_string_member ("Version"),
  531. // desc can be null
  532. desc = json_object.get_null_member ("Description") ? "" : Markup.escape_text (json_object.get_string_member ("Description")),
  533. popularity = json_object.get_double_member ("Popularity")
  534. };
  535. }
  536. public async AURPackage[] search_in_aur (string search_string) {
  537. if (!aur_search_results.contains (search_string)) {
  538. Json.Array pkgs = yield AUR.search (search_string.split (" "));
  539. aur_search_results.insert (search_string, pkgs);
  540. }
  541. AURPackage[] result = {};
  542. Json.Array aur_pkgs = aur_search_results.get (search_string);
  543. aur_pkgs.foreach_element ((array, index, node) => {
  544. Json.Object aur_pkg = node.get_object ();
  545. // remove results which exist in repos
  546. if (get_syncpkg (aur_pkg.get_string_member ("Name")) == null) {
  547. result += initialise_aur_struct (node.get_object ());
  548. }
  549. });
  550. return result;
  551. }
  552. public async AURPackageDetails get_aur_details (string pkgname) {
  553. string name = "";
  554. string version = "";
  555. string desc = "";
  556. double popularity = 0;
  557. string packagebase = "";
  558. string url = "";
  559. string maintainer = "";
  560. int64 firstsubmitted = 0;
  561. int64 lastmodified = 0;
  562. int64 outofdate = 0;
  563. int64 numvotes = 0;
  564. string[] licenses = {};
  565. string[] depends = {};
  566. string[] makedepends = {};
  567. string[] checkdepends = {};
  568. string[] optdepends = {};
  569. string[] provides = {};
  570. string[] replaces = {};
  571. string[] conflicts = {};
  572. var details = AURPackageDetails ();
  573. if (!aur_infos.contains (pkgname)) {
  574. Json.Array results = yield AUR.multiinfo ({pkgname});
  575. if (results.get_length () > 0) {
  576. aur_infos.insert (pkgname, results.get_object_element (0));
  577. }
  578. }
  579. unowned Json.Object? json_object = aur_infos.lookup (pkgname);
  580. if (json_object != null) {
  581. // name
  582. name = json_object.get_string_member ("Name");
  583. // version
  584. version = json_object.get_string_member ("Version");
  585. // desc can be null
  586. if (!json_object.get_null_member ("Description")) {
  587. desc = Markup.escape_text (json_object.get_string_member ("Description"));
  588. }
  589. popularity = json_object.get_double_member ("Popularity");
  590. // packagebase
  591. packagebase = json_object.get_string_member ("PackageBase");
  592. // url can be null
  593. unowned Json.Node? node = json_object.get_member ("URL");
  594. if (!node.is_null ()) {
  595. url = Markup.escape_text (node.get_string ());
  596. }
  597. // maintainer can be null
  598. node = json_object.get_member ("Maintainer");
  599. if (!node.is_null ()) {
  600. maintainer = node.get_string ();
  601. }
  602. // firstsubmitted
  603. firstsubmitted = json_object.get_int_member ("FirstSubmitted");
  604. // lastmodified
  605. lastmodified = json_object.get_int_member ("LastModified");
  606. // outofdate can be null
  607. node = json_object.get_member ("OutOfDate");
  608. if (!node.is_null ()) {
  609. outofdate = node.get_int ();
  610. }
  611. //numvotes
  612. numvotes = json_object.get_int_member ("NumVotes");
  613. // licenses
  614. node = json_object.get_member ("License");
  615. if (!node.is_null ()) {
  616. node.get_array ().foreach_element ((array, index, _node) => {
  617. licenses += _node.get_string ();
  618. });
  619. } else {
  620. licenses += _("Unknown");
  621. }
  622. // depends
  623. node = json_object.get_member ("Depends");
  624. if (node != null) {
  625. node.get_array ().foreach_element ((array, index, _node) => {
  626. depends += _node.get_string ();
  627. });
  628. }
  629. // optdepends
  630. node = json_object.get_member ("OptDepends");
  631. if (node != null) {
  632. node.get_array ().foreach_element ((array, index, _node) => {
  633. optdepends += _node.get_string ();
  634. });
  635. }
  636. // makedepends
  637. node = json_object.get_member ("MakeDepends");
  638. if (node != null) {
  639. node.get_array ().foreach_element ((array, index, _node) => {
  640. makedepends += _node.get_string ();
  641. });
  642. }
  643. // checkdepends
  644. node = json_object.get_member ("CheckDepends");
  645. if (node != null) {
  646. node.get_array ().foreach_element ((array, index, _node) => {
  647. checkdepends += _node.get_string ();
  648. });
  649. }
  650. // provides
  651. node = json_object.get_member ("Provides");
  652. if (node != null) {
  653. node.get_array ().foreach_element ((array, index, _node) => {
  654. provides += _node.get_string ();
  655. });
  656. }
  657. // replaces
  658. node = json_object.get_member ("Replaces");
  659. if (node != null) {
  660. node.get_array ().foreach_element ((array, index, _node) => {
  661. replaces += _node.get_string ();
  662. });
  663. }
  664. // conflicts
  665. node = json_object.get_member ("Conflicts");
  666. if (node != null) {
  667. node.get_array ().foreach_element ((array, index, _node) => {
  668. conflicts += _node.get_string ();
  669. });
  670. }
  671. }
  672. details.name = (owned) name;
  673. details.version = (owned) version ;
  674. details.desc = (owned) desc;
  675. details.popularity = popularity;
  676. details.packagebase = (owned) packagebase;
  677. details.url = (owned) url;
  678. details.maintainer = (owned) maintainer ;
  679. details.firstsubmitted = firstsubmitted;
  680. details.lastmodified = lastmodified;
  681. details.outofdate = outofdate;
  682. details.numvotes = numvotes;
  683. details.licenses = (owned) licenses;
  684. details.depends = (owned) depends;
  685. details.optdepends = (owned) optdepends;
  686. details.checkdepends = (owned) checkdepends;
  687. details.makedepends = (owned) makedepends;
  688. details.provides = (owned) provides;
  689. details.replaces = (owned) replaces;
  690. details.conflicts = (owned) conflicts;
  691. return details;
  692. }
  693. public async string[] get_aur_build_list (string pkgname) {
  694. string[] results = {};
  695. aur_dep_list = new GLib.List<string> ();
  696. bool success = yield set_aur_dep_list (pkgname);
  697. if (success) {
  698. foreach (unowned string name in aur_dep_list) {
  699. results += name;
  700. }
  701. }
  702. return results;
  703. }
  704. private async bool set_aur_dep_list (string pkgname) {
  705. bool success = false;
  706. Json.Array results = yield AUR.multiinfo ({pkgname});
  707. Json.Object json_object = results.get_object_element (0);
  708. if (json_object != null) {
  709. success = true;
  710. // add aur pkg to global list or move it to the end of the list;
  711. unowned GLib.List<string> element = aur_dep_list.find_custom (pkgname, strcmp);
  712. if (element == null) {
  713. aur_dep_list.append (pkgname);
  714. } else {
  715. aur_dep_list.delete_link (element);
  716. aur_dep_list.append (pkgname);
  717. }
  718. unowned Json.Node? node = json_object.get_member ("MakeDepends");
  719. if (node != null) {
  720. GLib.List<unowned Json.Node> list = node.get_array ().get_elements ();
  721. foreach (unowned Json.Node? _node in list) {
  722. unowned string depstring = _node.get_string ();
  723. if (Alpm.find_satisfier (alpm_handle.localdb.pkgcache, depstring) == null) {
  724. if (alpm_handle.find_dbs_satisfier (alpm_handle.syncdbs, depstring) == null) {
  725. success = yield set_aur_dep_list (depstring);
  726. }
  727. }
  728. if (!success) {
  729. break;
  730. }
  731. }
  732. }
  733. if (success) {
  734. node = json_object.get_member ("Depends");
  735. if (node != null) {
  736. GLib.List<unowned Json.Node> list = node.get_array ().get_elements ();
  737. foreach (unowned Json.Node? _node in list) {
  738. unowned string depstring = _node.get_string ();
  739. if (Alpm.find_satisfier (alpm_handle.localdb.pkgcache, depstring) == null) {
  740. if (alpm_handle.find_dbs_satisfier (alpm_handle.syncdbs, depstring) == null) {
  741. success = yield set_aur_dep_list (depstring);
  742. }
  743. }
  744. if (!success) {
  745. break;
  746. }
  747. }
  748. }
  749. }
  750. if (success) {
  751. node = json_object.get_member ("CheckDepends");
  752. if (node != null) {
  753. GLib.List<unowned Json.Node> list = node.get_array ().get_elements ();
  754. foreach (unowned Json.Node? _node in list) {
  755. unowned string depstring = _node.get_string ();
  756. if (Alpm.find_satisfier (alpm_handle.localdb.pkgcache, depstring) == null) {
  757. if (alpm_handle.find_dbs_satisfier (alpm_handle.syncdbs, depstring) == null) {
  758. success = yield set_aur_dep_list (depstring);
  759. }
  760. }
  761. if (!success) {
  762. break;
  763. }
  764. }
  765. }
  766. }
  767. } else {
  768. stdout.printf ("can't find %s in AUR\n", pkgname);
  769. }
  770. return success;
  771. }
  772. public string[] get_repos_names () {
  773. string[] repos_names = {};
  774. unowned Alpm.List<unowned Alpm.DB> syncdbs = alpm_handle.syncdbs;
  775. while (syncdbs != null) {
  776. unowned Alpm.DB db = syncdbs.data;
  777. repos_names += db.name;
  778. syncdbs.next ();
  779. }
  780. return repos_names;
  781. }
  782. public async AlpmPackage[] get_repo_pkgs (string repo) {
  783. AlpmPackage[] pkgs = {};
  784. unowned Alpm.List<unowned Alpm.DB> syncdbs = alpm_handle.syncdbs;
  785. while (syncdbs != null) {
  786. unowned Alpm.DB db = syncdbs.data;
  787. if (db.name == repo) {
  788. unowned Alpm.List<unowned Alpm.Package> pkgcache = db.pkgcache;
  789. while (pkgcache != null) {
  790. unowned Alpm.Package sync_pkg = pkgcache.data;
  791. unowned Alpm.Package? local_pkg = alpm_handle.localdb.get_pkg (sync_pkg.name);
  792. if (local_pkg != null) {
  793. pkgs += initialise_pkg_struct (local_pkg);
  794. } else {
  795. pkgs += initialise_pkg_struct (sync_pkg);
  796. }
  797. pkgcache.next ();
  798. }
  799. break;
  800. }
  801. syncdbs.next ();
  802. }
  803. return pkgs;
  804. }
  805. public string[] get_groups_names () {
  806. string[] groups_names = {};
  807. unowned Alpm.List<unowned Alpm.Group> groupcache = alpm_handle.localdb.groupcache;
  808. while (groupcache != null) {
  809. unowned Alpm.Group group = groupcache.data;
  810. if (!(group.name in groups_names)) {
  811. groups_names += group.name;
  812. }
  813. groupcache.next ();
  814. }
  815. unowned Alpm.List<unowned Alpm.DB> syncdbs = alpm_handle.syncdbs;
  816. while (syncdbs != null) {
  817. unowned Alpm.DB db = syncdbs.data;
  818. groupcache = db.groupcache;
  819. while (groupcache != null) {
  820. unowned Alpm.Group group = groupcache.data;
  821. if (!(group.name in groups_names)) {
  822. groups_names += group.name;
  823. }
  824. groupcache.next ();
  825. }
  826. syncdbs.next ();
  827. }
  828. return groups_names;
  829. }
  830. private Alpm.List<unowned Alpm.Package> group_pkgs (string group_name) {
  831. Alpm.List<unowned Alpm.Package> result = null;
  832. unowned Alpm.Group? grp = alpm_handle.localdb.get_group (group_name);
  833. if (grp != null) {
  834. unowned Alpm.List<unowned Alpm.Package> packages = grp.packages;
  835. while (packages != null) {
  836. unowned Alpm.Package pkg = packages.data;
  837. result.add (pkg);
  838. packages.next ();
  839. }
  840. }
  841. unowned Alpm.List<unowned Alpm.DB> syncdbs = alpm_handle.syncdbs;
  842. while (syncdbs != null) {
  843. unowned Alpm.DB db = syncdbs.data;
  844. grp = db.get_group (group_name);
  845. if (grp != null) {
  846. unowned Alpm.List<unowned Alpm.Package> packages = grp.packages;
  847. while (packages != null) {
  848. unowned Alpm.Package pkg = packages.data;
  849. if (result.find (pkg, (Alpm.List.CompareFunc) alpm_pkg_compare_name) == null) {
  850. result.add (pkg);
  851. }
  852. packages.next ();
  853. }
  854. }
  855. syncdbs.next ();
  856. }
  857. return result;
  858. }
  859. public async AlpmPackage[] get_group_pkgs (string groupname) {
  860. AlpmPackage[] pkgs = {};
  861. Alpm.List<unowned Alpm.Package> alpm_pkgs = group_pkgs (groupname);
  862. unowned Alpm.List<unowned Alpm.Package> list = alpm_pkgs;
  863. while (list != null) {
  864. unowned Alpm.Package alpm_pkg = list.data;
  865. pkgs += initialise_pkg_struct (alpm_pkg);
  866. list.next ();
  867. }
  868. return pkgs;
  869. }
  870. public string[] get_pkg_uninstalled_optdeps (string pkgname) {
  871. string[] optdeps = {};
  872. unowned Alpm.Package? alpm_pkg = alpm_handle.localdb.get_pkg (pkgname);
  873. if (alpm_pkg == null) {
  874. alpm_pkg = get_syncpkg (pkgname);
  875. }
  876. if (alpm_pkg != null) {
  877. unowned Alpm.List<unowned Alpm.Depend> optdepends = alpm_pkg.optdepends;
  878. while (optdepends != null) {
  879. unowned Alpm.Depend optdep = optdepends.data;
  880. if (Alpm.find_satisfier (alpm_handle.localdb.pkgcache, optdep.name) == null) {
  881. optdeps += optdep.compute_string ();
  882. }
  883. optdepends.next ();
  884. }
  885. }
  886. return optdeps;
  887. }
  888. public AlpmPackageDetails get_pkg_details (string pkgname) {
  889. string name = "";
  890. string version = "";
  891. string desc = "";
  892. string url = "";
  893. string repo = "";
  894. string has_signature = "";
  895. string reason = "";
  896. string packager = "";
  897. string builddate = "";
  898. string installdate = "";
  899. string[] groups = {};
  900. string[] backups = {};
  901. string[] files = {};
  902. string[] licenses = {};
  903. string[] depends = {};
  904. string[] optdepends = {};
  905. string[] requiredby = {};
  906. string[] optionalfor = {};
  907. string[] provides = {};
  908. string[] replaces = {};
  909. string[] conflicts = {};
  910. var details = AlpmPackageDetails ();
  911. unowned Alpm.Package? alpm_pkg = alpm_handle.localdb.get_pkg (pkgname);
  912. if (alpm_pkg == null) {
  913. alpm_pkg = get_syncpkg (pkgname);
  914. }
  915. if (alpm_pkg != null) {
  916. // name
  917. name = alpm_pkg.name;
  918. // version
  919. version = alpm_pkg.version;
  920. // desc can be null
  921. if (alpm_pkg.desc != null) {
  922. desc = Markup.escape_text (alpm_pkg.desc);
  923. }
  924. // url can be null
  925. if (alpm_pkg.url != null) {
  926. url = Markup.escape_text (alpm_pkg.url);
  927. }
  928. // packager can be null
  929. packager = alpm_pkg.packager ?? "";
  930. // groups
  931. unowned Alpm.List list = alpm_pkg.groups;
  932. while (list != null) {
  933. groups += ((Alpm.List<unowned string>) list).data;
  934. list.next ();
  935. }
  936. // licenses
  937. list = alpm_pkg.licenses;
  938. while (list != null) {
  939. licenses += ((Alpm.List<unowned string>) list).data;
  940. list.next ();
  941. }
  942. // build_date
  943. GLib.Time time = GLib.Time.local ((time_t) alpm_pkg.builddate);
  944. builddate = time.format ("%a %d %b %Y %X %Z");
  945. // local pkg
  946. if (alpm_pkg.origin == Alpm.Package.From.LOCALDB) {
  947. // repo
  948. unowned Alpm.Package? sync_pkg = get_syncpkg (alpm_pkg.name);
  949. if (sync_pkg != null) {
  950. repo = sync_pkg.db.name;
  951. }
  952. // reason
  953. if (alpm_pkg.reason == Alpm.Package.Reason.EXPLICIT) {
  954. reason = _("Explicitly installed");
  955. } else if (alpm_pkg.reason == Alpm.Package.Reason.DEPEND) {
  956. reason = _("Installed as a dependency for another package");
  957. } else {
  958. reason = _("Unknown");
  959. }
  960. // install_date
  961. time = GLib.Time.local ((time_t) alpm_pkg.installdate);
  962. installdate = time.format ("%a %d %b %Y %X %Z");
  963. // backups
  964. list = alpm_pkg.backups;
  965. while (list != null) {
  966. backups += "/" + ((Alpm.List<unowned Alpm.Backup>) list).data.name;
  967. list.next ();
  968. }
  969. // requiredby
  970. Alpm.List<string> pkg_requiredby = alpm_pkg.compute_requiredby ();
  971. list = pkg_requiredby;
  972. while (list != null) {
  973. requiredby += ((Alpm.List<unowned string>) list).data;
  974. list.next ();
  975. }
  976. pkg_requiredby.free_inner (GLib.free);
  977. // optionalfor
  978. Alpm.List<string> pkg_optionalfor = alpm_pkg.compute_optionalfor ();
  979. list = pkg_optionalfor;
  980. while (list != null) {
  981. optionalfor += ((Alpm.List<unowned string>) list).data;
  982. list.next ();
  983. }
  984. pkg_optionalfor.free_inner (GLib.free);
  985. // files
  986. unowned Alpm.FileList filelist = alpm_pkg.files;
  987. Alpm.File* file_ptr = filelist.files;
  988. for (size_t i = 0; i < filelist.count; i++, file_ptr++) {
  989. if (!file_ptr->name.has_suffix ("/")) {
  990. files += "/" + file_ptr->name;
  991. }
  992. }
  993. // sync pkg
  994. } else if (alpm_pkg.origin == Alpm.Package.From.SYNCDB) {
  995. // repos
  996. repo = alpm_pkg.db.name;
  997. // signature
  998. has_signature = alpm_pkg.base64_sig != null ? _("Yes") : _("No");
  999. }
  1000. // depends
  1001. list = alpm_pkg.depends;
  1002. while (list != null) {
  1003. depends += ((Alpm.List<unowned Alpm.Depend>) list).data.compute_string ();
  1004. list.next ();
  1005. }
  1006. // optdepends
  1007. list = alpm_pkg.optdepends;
  1008. while (list != null) {
  1009. optdepends += ((Alpm.List<unowned Alpm.Depend>) list).data.compute_string ();
  1010. list.next ();
  1011. }
  1012. // provides
  1013. list = alpm_pkg.provides;
  1014. while (list != null) {
  1015. provides += ((Alpm.List<unowned Alpm.Depend>) list).data.compute_string ();
  1016. list.next ();
  1017. }
  1018. // replaces
  1019. list = alpm_pkg.replaces;
  1020. while (list != null) {
  1021. replaces += ((Alpm.List<unowned Alpm.Depend>) list).data.compute_string ();
  1022. list.next ();
  1023. }
  1024. // conflicts
  1025. list = alpm_pkg.conflicts;
  1026. while (list != null) {
  1027. conflicts += ((Alpm.List<unowned Alpm.Depend>) list).data.compute_string ();
  1028. list.next ();
  1029. }
  1030. }
  1031. details.name = (owned) name;
  1032. details.version = (owned) version;
  1033. details.desc = (owned) desc;
  1034. details.repo = (owned) repo;
  1035. details.url = (owned) url;
  1036. details.packager = (owned) packager;
  1037. details.builddate = (owned) builddate;
  1038. details.installdate = (owned) installdate;
  1039. details.reason = (owned) reason;
  1040. details.has_signature = (owned) has_signature;
  1041. details.licenses = (owned) licenses;
  1042. details.depends = (owned) depends;
  1043. details.optdepends = (owned) optdepends;
  1044. details.requiredby = (owned) requiredby;
  1045. details.optionalfor = (owned) optionalfor;
  1046. details.provides = (owned) provides;
  1047. details.replaces = (owned) replaces;
  1048. details.conflicts = (owned) conflicts;
  1049. details.groups = (owned) groups;
  1050. details.backups = (owned) backups;
  1051. details.files = (owned) files;
  1052. return details;
  1053. }
  1054. public void start_get_updates (bool check_aur_updates) {
  1055. UpdateInfos[] updates_infos = {};
  1056. unowned Alpm.Package? pkg = null;
  1057. unowned Alpm.Package? candidate = null;
  1058. foreach (unowned string name in alpm_config.get_syncfirsts ()) {
  1059. pkg = Alpm.find_satisfier (alpm_handle.localdb.pkgcache, name);
  1060. if (pkg != null) {
  1061. candidate = pkg.sync_newversion (alpm_handle.syncdbs);
  1062. if (candidate != null) {
  1063. var infos = UpdateInfos () {
  1064. name = candidate.name,
  1065. old_version = pkg.version,
  1066. new_version = candidate.version,
  1067. repo = candidate.db.name,
  1068. download_size = candidate.download_size
  1069. };
  1070. updates_infos += (owned) infos;
  1071. }
  1072. }
  1073. }
  1074. if (updates_infos.length != 0) {
  1075. var updates = Updates () {
  1076. is_syncfirst = true,
  1077. repos_updates = (owned) updates_infos,
  1078. aur_updates = {}
  1079. };
  1080. get_updates_finished (updates);
  1081. return;
  1082. } else {
  1083. string[] local_pkgs = {};
  1084. unowned Alpm.List<unowned Alpm.Package> pkgcache = alpm_handle.localdb.pkgcache;
  1085. while (pkgcache != null) {
  1086. unowned Alpm.Package installed_pkg = pkgcache.data;
  1087. // check if installed_pkg is in IgnorePkg or IgnoreGroup
  1088. if (alpm_handle.should_ignore (installed_pkg) == 0) {
  1089. candidate = installed_pkg.sync_newversion (alpm_handle.syncdbs);
  1090. if (candidate != null) {
  1091. var infos = UpdateInfos () {
  1092. name = candidate.name,
  1093. old_version = installed_pkg.version,
  1094. new_version = candidate.version,
  1095. repo = candidate.db.name,
  1096. download_size = candidate.download_size
  1097. };
  1098. updates_infos += (owned) infos;
  1099. } else {
  1100. if (check_aur_updates && (aur_updates_results.get_length () == 0)) {
  1101. // check if installed_pkg is a local pkg
  1102. unowned Alpm.List<unowned Alpm.DB> syncdbs = alpm_handle.syncdbs;
  1103. while (syncdbs != null) {
  1104. unowned Alpm.DB db = syncdbs.data;
  1105. pkg = Alpm.find_satisfier (db.pkgcache, installed_pkg.name);
  1106. if (pkg != null) {
  1107. break;
  1108. }
  1109. syncdbs.next ();
  1110. }
  1111. if (pkg == null) {
  1112. local_pkgs += installed_pkg.name;
  1113. }
  1114. }
  1115. }
  1116. }
  1117. pkgcache.next ();
  1118. }
  1119. if (check_aur_updates) {
  1120. // get aur updates
  1121. if (aur_updates_results.get_length () == 0) {
  1122. AUR.multiinfo.begin (local_pkgs, (obj, res) => {
  1123. aur_updates_results = AUR.multiinfo.end (res);
  1124. var updates = Updates () {
  1125. is_syncfirst = false,
  1126. repos_updates = (owned) updates_infos,
  1127. aur_updates = get_aur_updates_infos ()
  1128. };
  1129. get_updates_finished (updates);
  1130. });
  1131. } else {
  1132. var updates = Updates () {
  1133. is_syncfirst = false,
  1134. repos_updates = (owned) updates_infos,
  1135. aur_updates = get_aur_updates_infos ()
  1136. };
  1137. get_updates_finished (updates);
  1138. }
  1139. } else {
  1140. var updates = Updates () {
  1141. is_syncfirst = false,
  1142. repos_updates = (owned) updates_infos,
  1143. aur_updates = {}
  1144. };
  1145. get_updates_finished (updates);
  1146. }
  1147. }
  1148. }
  1149. private UpdateInfos[] get_aur_updates_infos () {
  1150. UpdateInfos[] aur_updates_infos = {};
  1151. aur_updates_results.foreach_element ((array, index, node) => {
  1152. unowned Json.Object pkg_info = node.get_object ();
  1153. unowned string name = pkg_info.get_string_member ("Name");
  1154. unowned string new_version = pkg_info.get_string_member ("Version");
  1155. unowned string old_version = alpm_handle.localdb.get_pkg (name).version;
  1156. if (Alpm.pkg_vercmp (new_version, old_version) == 1) {
  1157. var infos = UpdateInfos () {
  1158. name = name,
  1159. old_version = old_version,
  1160. new_version = new_version,
  1161. repo = ""
  1162. };
  1163. aur_updates_infos += (owned) infos;
  1164. }
  1165. });
  1166. return aur_updates_infos;
  1167. }
  1168. public bool trans_init (Alpm.TransFlag transflags) {
  1169. current_error = ErrorInfos ();
  1170. cancellable.reset ();
  1171. if (alpm_handle.trans_init (transflags) == -1) {
  1172. Alpm.Errno errno = alpm_handle.errno ();
  1173. current_error.errno = (uint) errno;
  1174. current_error.message = _("Failed to init transaction");
  1175. current_error.details = { Alpm.strerror (errno) };
  1176. return false;
  1177. } else {
  1178. intern_lock = true;
  1179. }
  1180. return true;
  1181. }
  1182. public bool trans_sysupgrade (bool enable_downgrade) {
  1183. current_error = ErrorInfos ();
  1184. if (alpm_handle.trans_sysupgrade ((enable_downgrade) ? 1 : 0) == -1) {
  1185. Alpm.Errno errno = alpm_handle.errno ();
  1186. current_error.errno = (uint) errno;
  1187. current_error.message = _("Failed to prepare transaction");
  1188. current_error.details = { Alpm.strerror (errno) };
  1189. return false;
  1190. }
  1191. return true;
  1192. }
  1193. private bool trans_add_pkg_real (Alpm.Package pkg) {
  1194. current_error = ErrorInfos ();
  1195. if (alpm_handle.trans_add_pkg (pkg) == -1) {
  1196. Alpm.Errno errno = alpm_handle.errno ();
  1197. if (errno == Alpm.Errno.TRANS_DUP_TARGET || errno == Alpm.Errno.PKG_IGNORED) {
  1198. // just skip duplicate or ignored targets
  1199. return true;
  1200. } else {
  1201. current_error.errno = (uint) errno;
  1202. current_error.message = _("Failed to prepare transaction");
  1203. current_error.details = { "%s: %s".printf (pkg.name, Alpm.strerror (errno)) };
  1204. return false;
  1205. }
  1206. }
  1207. return true;
  1208. }
  1209. public bool trans_add_pkg (string pkgname) {
  1210. current_error = ErrorInfos ();
  1211. unowned Alpm.Package? pkg = get_syncpkg (pkgname);
  1212. if (pkg == null) {
  1213. current_error.message = _("Failed to prepare transaction");
  1214. current_error.details = { _("target not found: %s").printf (pkgname) };
  1215. return false;
  1216. } else {
  1217. bool success = trans_add_pkg_real (pkg);
  1218. if (success) {
  1219. if (("linux31" in pkg.name) || ("linux4" in pkg.name)) {
  1220. string[] installed_kernels = {};
  1221. string[] installed_modules = {};
  1222. unowned Alpm.List<unowned Alpm.Package> pkgcache = alpm_handle.localdb.pkgcache;
  1223. while (pkgcache != null) {
  1224. unowned Alpm.Package local_pkg = pkgcache.data;
  1225. if (("linux31" in local_pkg.name) || ("linux4" in local_pkg.name)) {
  1226. string[] local_pkg_splitted = local_pkg.name.split ("-", 2);
  1227. if ((local_pkg_splitted[0] in installed_kernels) == false) {
  1228. installed_kernels += local_pkg_splitted[0];
  1229. }
  1230. if (local_pkg_splitted.length == 2) {
  1231. if ((local_pkg_splitted[1] in installed_modules) == false) {
  1232. installed_modules += local_pkg_splitted[1];
  1233. }
  1234. }
  1235. }
  1236. pkgcache.next ();
  1237. }
  1238. string[] splitted = pkg.name.split ("-", 2);
  1239. if (splitted.length == 2) {
  1240. // we are adding a module
  1241. // add the same module for other installed kernels
  1242. foreach (unowned string installed_kernel in installed_kernels) {
  1243. string module = installed_kernel + "-" + splitted[1];
  1244. unowned Alpm.Package? module_pkg = get_syncpkg (module);
  1245. if (module_pkg != null) {
  1246. trans_add_pkg_real (module_pkg);
  1247. }
  1248. }
  1249. } else if (splitted.length == 1) {
  1250. // we are adding a kernel
  1251. // add all installed modules for other kernels
  1252. foreach (unowned string installed_module in installed_modules) {
  1253. string module = splitted[0] + "-" + installed_module;
  1254. unowned Alpm.Package? module_pkg = get_syncpkg (module);
  1255. if (module_pkg != null) {
  1256. trans_add_pkg_real (module_pkg);
  1257. }
  1258. }
  1259. }
  1260. }
  1261. }
  1262. return success;
  1263. }
  1264. }
  1265. public bool trans_load_pkg (string pkgpath) {
  1266. current_error = ErrorInfos ();
  1267. Alpm.Package* pkg;
  1268. if (alpm_handle.load_tarball (pkgpath, 1, alpm_handle.localfilesiglevel, out pkg) == -1) {
  1269. Alpm.Errno errno = alpm_handle.errno ();
  1270. current_error.errno = (uint) errno;
  1271. current_error.message = _("Failed to prepare transaction");
  1272. current_error.details = { "%s: %s".printf (pkgpath, Alpm.strerror (errno)) };
  1273. return false;
  1274. } else if (alpm_handle.trans_add_pkg (pkg) == -1) {
  1275. Alpm.Errno errno = alpm_handle.errno ();
  1276. current_error.errno = (uint) errno;
  1277. current_error.message = _("Failed to prepare transaction");
  1278. current_error.details = { "%s: %s".printf (pkg->name, Alpm.strerror (errno)) };
  1279. // free the package because it will not be used
  1280. delete pkg;
  1281. return false;
  1282. }
  1283. return true;
  1284. }
  1285. public bool trans_remove_pkg (string pkgname) {
  1286. current_error = ErrorInfos ();
  1287. unowned Alpm.Package? pkg = alpm_handle.localdb.get_pkg (pkgname);
  1288. if (pkg == null) {
  1289. current_error.message = _("Failed to prepare transaction");
  1290. current_error.details = { _("target not found: %s").printf (pkgname) };
  1291. return false;
  1292. } else if (alpm_handle.trans_remove_pkg (pkg) == -1) {
  1293. Alpm.Errno errno = alpm_handle.errno ();
  1294. current_error.errno = (uint) errno;
  1295. current_error.message = _("Failed to prepare transaction");
  1296. current_error.details = { "%s: %s".printf (pkg.name, Alpm.strerror (errno)) };
  1297. return false;
  1298. }
  1299. return true;
  1300. }
  1301. private void trans_prepare () {
  1302. current_error = ErrorInfos ();
  1303. string[] details = {};
  1304. Alpm.List err_data;
  1305. if (alpm_handle.trans_prepare (out err_data) == -1) {
  1306. Alpm.Errno errno = alpm_handle.errno ();
  1307. current_error.errno = (uint) errno;
  1308. current_error.message = _("Failed to prepare transaction");
  1309. string detail = Alpm.strerror (errno);
  1310. switch (errno) {
  1311. case Alpm.Errno.PKG_INVALID_ARCH:
  1312. detail += ":";
  1313. details += (owned) detail;
  1314. unowned Alpm.List<string*> list = err_data;
  1315. while (list != null) {
  1316. string* pkgname = list.data;
  1317. details += _("package %s does not have a valid architecture").printf (pkgname);
  1318. delete pkgname;
  1319. list.next ();
  1320. }
  1321. break;
  1322. case Alpm.Errno.UNSATISFIED_DEPS:
  1323. detail += ":";
  1324. details += (owned) detail;
  1325. unowned Alpm.List<Alpm.DepMissing*> list = err_data;
  1326. while (list != null) {
  1327. Alpm.DepMissing* miss = list.data;
  1328. details += _("%s: requires %s").printf (miss->target, miss->depend.compute_string ());
  1329. delete miss;
  1330. list.next ();
  1331. }
  1332. break;
  1333. case Alpm.Errno.CONFLICTING_DEPS:
  1334. detail += ":";
  1335. details += (owned) detail;
  1336. unowned Alpm.List<Alpm.Conflict*> list = err_data;
  1337. while (list != null) {
  1338. Alpm.Conflict* conflict = list.data;
  1339. string conflict_detail = _("%s and %s are in conflict").printf (conflict->package1, conflict->package2);
  1340. // only print reason if it contains new information
  1341. if (conflict->reason.mod != Alpm.Depend.Mode.ANY) {
  1342. conflict_detail += " (%s)".printf (conflict->reason.compute_string ());
  1343. }
  1344. details += (owned) conflict_detail;
  1345. delete conflict;
  1346. list.next ();
  1347. }
  1348. break;
  1349. default:
  1350. details += (owned) detail;
  1351. break;
  1352. }
  1353. current_error.details = (owned) details;
  1354. trans_release ();
  1355. trans_prepare_finished (false);
  1356. } else {
  1357. // Search for holdpkg in target list
  1358. bool found_locked_pkg = false;
  1359. unowned Alpm.List<unowned Alpm.Package> to_remove = alpm_handle.trans_to_remove ();
  1360. while (to_remove != null) {
  1361. unowned Alpm.Package pkg = to_remove.data;
  1362. if (alpm_config.get_holdpkgs ().find_custom (pkg.name, strcmp) != null) {
  1363. details += _("%s needs to be removed but it is a locked package").printf (pkg.name);
  1364. found_locked_pkg = true;
  1365. break;
  1366. }
  1367. to_remove.next ();
  1368. }
  1369. if (found_locked_pkg) {
  1370. current_error.message = _("Failed to prepare transaction");
  1371. current_error.details = (owned) details;
  1372. trans_release ();
  1373. trans_prepare_finished (false);
  1374. } else {
  1375. trans_prepare_finished (true);
  1376. }
  1377. }
  1378. }
  1379. public void start_trans_prepare () {
  1380. try {
  1381. thread_pool.add (new AlpmAction (trans_prepare));
  1382. } catch (ThreadError e) {
  1383. stderr.printf ("Thread Error %s\n", e.message);
  1384. }
  1385. }
  1386. public void choose_provider (int provider) {
  1387. provider_mutex.lock ();
  1388. choosen_provider = provider;
  1389. provider_cond.signal ();
  1390. provider_mutex.unlock ();
  1391. }
  1392. public TransactionSummary get_transaction_summary () {
  1393. UpdateInfos[] to_install = {};
  1394. UpdateInfos[] to_upgrade = {};
  1395. UpdateInfos[] to_downgrade = {};
  1396. UpdateInfos[] to_reinstall = {};
  1397. UpdateInfos[] to_remove = {};
  1398. unowned Alpm.List<unowned Alpm.Package> pkgs_to_add = alpm_handle.trans_to_add ();
  1399. while (pkgs_to_add != null) {
  1400. unowned Alpm.Package trans_pkg = pkgs_to_add.data;
  1401. unowned Alpm.Package? local_pkg = alpm_handle.localdb.get_pkg (trans_pkg.name);
  1402. var infos = UpdateInfos () {
  1403. name = trans_pkg.name,
  1404. old_version = local_pkg != null ? local_pkg.version : "",
  1405. new_version = trans_pkg.version,
  1406. // if pkg was load from a file, pkg.db is null
  1407. repo =trans_pkg.db != null ? trans_pkg.db.name : "",
  1408. download_size = trans_pkg.download_size
  1409. };
  1410. if (local_pkg == null) {
  1411. to_install += (owned) infos;
  1412. } else {
  1413. int cmp = Alpm.pkg_vercmp (trans_pkg.version, local_pkg.version);
  1414. if (cmp == 1) {
  1415. to_upgrade += (owned) infos;
  1416. } else if (cmp == 0) {
  1417. to_reinstall += (owned) infos;
  1418. } else {
  1419. to_downgrade += (owned) infos;
  1420. }
  1421. }
  1422. pkgs_to_add.next ();
  1423. }
  1424. unowned Alpm.List<unowned Alpm.Package> pkgs_to_remove = alpm_handle.trans_to_remove ();
  1425. while (pkgs_to_remove != null) {
  1426. unowned Alpm.Package trans_pkg = pkgs_to_remove.data;
  1427. var infos = UpdateInfos () {
  1428. name = trans_pkg.name,
  1429. old_version = trans_pkg.version,
  1430. new_version = "",
  1431. repo = trans_pkg.db.name
  1432. };
  1433. to_remove += (owned) infos;
  1434. pkgs_to_remove.next ();
  1435. }
  1436. var summary = TransactionSummary () {
  1437. to_install = (owned) to_install,
  1438. to_upgrade = (owned) to_upgrade,
  1439. to_downgrade = (owned) to_downgrade,
  1440. to_reinstall = (owned) to_reinstall,
  1441. to_remove = (owned) to_remove
  1442. };
  1443. return summary;
  1444. }
  1445. private void trans_commit () {
  1446. current_error = ErrorInfos ();
  1447. bool success = true;
  1448. Alpm.List err_data;
  1449. if (alpm_handle.trans_commit (out err_data) == -1) {
  1450. Alpm.Errno errno = alpm_handle.errno ();
  1451. current_error.errno = (uint) errno;
  1452. // cancel the download return an EXTERNAL_DOWNLOAD error
  1453. if (errno == Alpm.Errno.EXTERNAL_DOWNLOAD && cancellable.is_cancelled ()) {
  1454. trans_release ();
  1455. refresh_handle ();
  1456. trans_commit_finished (false);
  1457. return;
  1458. }
  1459. current_error.message = _("Failed to commit transaction");
  1460. string detail = Alpm.strerror (errno);
  1461. string[] details = {};
  1462. switch (errno) {
  1463. case Alpm.Errno.FILE_CONFLICTS:
  1464. detail += ":";
  1465. details += (owned) detail;
  1466. //TransFlag flags = alpm_handle.trans_get_flags ();
  1467. //if ((flags & TransFlag.FORCE) != 0) {
  1468. //details += _("unable to %s directory-file conflicts").printf ("--force");
  1469. //}
  1470. unowned Alpm.List<Alpm.FileConflict*> list = err_data;
  1471. while (list != null) {
  1472. Alpm.FileConflict* conflict = list.data;
  1473. switch (conflict->type) {
  1474. case Alpm.FileConflict.Type.TARGET:
  1475. details += _("%s exists in both %s and %s").printf (conflict->file, conflict->target, conflict->ctarget);
  1476. break;
  1477. case Alpm.FileConflict.Type.FILESYSTEM:
  1478. details += _("%s: %s already exists in filesystem").printf (conflict->target, conflict->file);
  1479. break;
  1480. }
  1481. delete conflict;
  1482. list.next ();
  1483. }
  1484. break;
  1485. case Alpm.Errno.PKG_INVALID:
  1486. case Alpm.Errno.PKG_INVALID_CHECKSUM:
  1487. case Alpm.Errno.PKG_INVALID_SIG:
  1488. case Alpm.Errno.DLT_INVALID:
  1489. detail += ":";
  1490. details += (owned) detail;
  1491. unowned Alpm.List<string*> list = err_data;
  1492. while (list != null) {
  1493. string* filename = list.data;
  1494. details += _("%s is invalid or corrupted").printf (filename);
  1495. delete filename;
  1496. list.next ();
  1497. }
  1498. break;
  1499. default:
  1500. details += (owned) detail;
  1501. break;
  1502. }
  1503. current_error.details = (owned) details;
  1504. success = false;
  1505. }
  1506. trans_release ();
  1507. refresh_handle ();
  1508. trans_commit_finished (success);
  1509. }
  1510. public void start_trans_commit (GLib.BusName sender) {
  1511. check_authorization.begin (sender, (obj, res) => {
  1512. bool authorized = check_authorization.end (res);
  1513. if (authorized) {
  1514. try {
  1515. thread_pool.add (new AlpmAction (trans_commit));
  1516. } catch (ThreadError e) {
  1517. stderr.printf ("Thread Error %s\n", e.message);
  1518. }
  1519. } else {
  1520. current_error = ErrorInfos () {
  1521. message = _("Authentication failed")
  1522. };
  1523. trans_release ();
  1524. refresh_handle ();
  1525. trans_commit_finished (false);
  1526. }
  1527. });
  1528. }
  1529. public void trans_release () {
  1530. alpm_handle.trans_release ();
  1531. intern_lock = false;
  1532. }
  1533. [DBus (no_reply = true)]
  1534. public void trans_cancel () {
  1535. if (alpm_handle.trans_interrupt () == 0) {
  1536. // a transaction is being interrupted
  1537. // it will end the normal way
  1538. return;
  1539. }
  1540. cancellable.cancel ();
  1541. }
  1542. [DBus (no_reply = true)]
  1543. public void quit () {
  1544. // to be sure to not quit with locked databases,
  1545. // the above function will wait for all task in queue
  1546. // to be processed before return;
  1547. ThreadPool.free ((owned) thread_pool, false, true);
  1548. alpm_handle.unlock ();
  1549. loop.quit ();
  1550. }
  1551. // End of Daemon Object
  1552. }
  1553. }
  1554. private void write_log_file (string event) {
  1555. var now = new DateTime.now_local ();
  1556. string log = "%s %s".printf (now.format ("[%Y-%m-%d %H:%M]"), event);
  1557. var file = GLib.File.new_for_path ("/var/log/pamac.log");
  1558. try {
  1559. // creating a DataOutputStream to the file
  1560. var dos = new DataOutputStream (file.append_to (FileCreateFlags.NONE));
  1561. // writing a short string to the stream
  1562. dos.put_string (log);
  1563. } catch (GLib.Error e) {
  1564. stderr.printf ("%s\n", e.message);
  1565. }
  1566. }
  1567. private void cb_event (Alpm.Event.Data data) {
  1568. string[] details = {};
  1569. uint secondary_type = 0;
  1570. switch (data.type) {
  1571. case Alpm.Event.Type.HOOK_START:
  1572. switch (data.hook_when) {
  1573. case Alpm.HookWhen.PRE_TRANSACTION:
  1574. secondary_type = (uint) Alpm.HookWhen.PRE_TRANSACTION;
  1575. break;
  1576. case Alpm.HookWhen.POST_TRANSACTION:
  1577. secondary_type = (uint) Alpm.HookWhen.POST_TRANSACTION;
  1578. break;
  1579. default:
  1580. break;
  1581. }
  1582. break;
  1583. case Alpm.Event.Type.HOOK_RUN_START:
  1584. details += data.hook_run_name;
  1585. details += data.hook_run_desc ?? "";
  1586. details += data.hook_run_position.to_string ();
  1587. details += data.hook_run_total.to_string ();
  1588. break;
  1589. case Alpm.Event.Type.PACKAGE_OPERATION_START:
  1590. switch (data.package_operation_operation) {
  1591. case Alpm.Package.Operation.REMOVE:
  1592. details += data.package_operation_oldpkg.name;
  1593. details += data.package_operation_oldpkg.version;
  1594. secondary_type = (uint) Alpm.Package.Operation.REMOVE;
  1595. break;
  1596. case Alpm.Package.Operation.INSTALL:
  1597. details += data.package_operation_newpkg.name;
  1598. details += data.package_operation_newpkg.version;
  1599. secondary_type = (uint) Alpm.Package.Operation.INSTALL;
  1600. break;
  1601. case Alpm.Package.Operation.REINSTALL:
  1602. details += data.package_operation_newpkg.name;
  1603. details += data.package_operation_newpkg.version;
  1604. secondary_type = (uint) Alpm.Package.Operation.REINSTALL;
  1605. break;
  1606. case Alpm.Package.Operation.UPGRADE:
  1607. details += data.package_operation_oldpkg.name;
  1608. details += data.package_operation_oldpkg.version;
  1609. details += data.package_operation_newpkg.version;
  1610. secondary_type = (uint) Alpm.Package.Operation.UPGRADE;
  1611. break;
  1612. case Alpm.Package.Operation.DOWNGRADE:
  1613. details += data.package_operation_oldpkg.name;
  1614. details += data.package_operation_oldpkg.version;
  1615. details += data.package_operation_newpkg.version;
  1616. secondary_type = (uint) Alpm.Package.Operation.DOWNGRADE;
  1617. break;
  1618. default:
  1619. break;
  1620. }
  1621. break;
  1622. case Alpm.Event.Type.PACKAGE_OPERATION_DONE:
  1623. switch (data.package_operation_operation) {
  1624. case Alpm.Package.Operation.INSTALL:
  1625. string log = "Installed %s (%s)\n".printf (data.package_operation_newpkg.name, data.package_operation_newpkg.version);
  1626. write_log_file (log);
  1627. break;
  1628. case Alpm.Package.Operation.REMOVE:
  1629. string log = "Removed %s (%s)\n".printf (data.package_operation_oldpkg.name, data.package_operation_oldpkg.version);
  1630. write_log_file (log);
  1631. break;
  1632. case Alpm.Package.Operation.REINSTALL:
  1633. string log = "Reinstalled %s (%s)\n".printf (data.package_operation_newpkg.name, data.package_operation_newpkg.version);
  1634. write_log_file (log);
  1635. break;
  1636. case Alpm.Package.Operation.UPGRADE:
  1637. string log = "Upgraded %s (%s -> %s)\n".printf (data.package_operation_oldpkg.name, data.package_operation_oldpkg.version, data.package_operation_newpkg.version);
  1638. write_log_file (log);
  1639. break;
  1640. case Alpm.Package.Operation.DOWNGRADE:
  1641. string log = "Downgraded %s (%s -> %s)\n".printf (data.package_operation_oldpkg.name, data.package_operation_oldpkg.version, data.package_operation_newpkg.version);
  1642. write_log_file (log);
  1643. break;
  1644. }
  1645. break;
  1646. case Alpm.Event.Type.DELTA_PATCH_START:
  1647. details += data.delta_patch_delta.to;
  1648. details += data.delta_patch_delta.delta;
  1649. break;
  1650. case Alpm.Event.Type.SCRIPTLET_INFO:
  1651. details += data.scriptlet_info_line;
  1652. write_log_file (data.scriptlet_info_line);
  1653. break;
  1654. case Alpm.Event.Type.PKGDOWNLOAD_START:
  1655. details += data.pkgdownload_file;
  1656. break;
  1657. case Alpm.Event.Type.OPTDEP_REMOVAL:
  1658. details += data.optdep_removal_pkg.name;
  1659. details += data.optdep_removal_optdep.compute_string ();
  1660. break;
  1661. case Alpm.Event.Type.DATABASE_MISSING:
  1662. details += data.database_missing_dbname;
  1663. break;
  1664. case Alpm.Event.Type.PACNEW_CREATED:
  1665. details += data.pacnew_created_file;
  1666. break;
  1667. case Alpm.Event.Type.PACSAVE_CREATED:
  1668. details += data.pacsave_created_file;
  1669. break;
  1670. default:
  1671. break;
  1672. }
  1673. pamac_daemon.emit_event ((uint) data.type, secondary_type, details);
  1674. }
  1675. private void cb_question (Alpm.Question.Data data) {
  1676. switch (data.type) {
  1677. case Alpm.Question.Type.INSTALL_IGNOREPKG:
  1678. // Do not install package in IgnorePkg/IgnoreGroup
  1679. data.install_ignorepkg_install = 0;
  1680. break;
  1681. case Alpm.Question.Type.REPLACE_PKG:
  1682. // Auto-remove conflicts in case of replaces
  1683. data.replace_replace = 1;
  1684. break;
  1685. case Alpm.Question.Type.CONFLICT_PKG:
  1686. // Auto-remove conflicts
  1687. data.conflict_remove = 1;
  1688. break;
  1689. case Alpm.Question.Type.REMOVE_PKGS:
  1690. // Do not upgrade packages which have unresolvable dependencies
  1691. data.remove_pkgs_skip = 1;
  1692. break;
  1693. case Alpm.Question.Type.SELECT_PROVIDER:
  1694. string depend_str = data.select_provider_depend.compute_string ();
  1695. string[] providers_str = {};
  1696. unowned Alpm.List<unowned Alpm.Package> list = data.select_provider_providers;
  1697. while (list != null) {
  1698. unowned Alpm.Package pkg = list.data;
  1699. providers_str += pkg.name;
  1700. list.next ();
  1701. }
  1702. pamac_daemon.provider_cond = Cond ();
  1703. pamac_daemon.provider_mutex = Mutex ();
  1704. pamac_daemon.choosen_provider = null;
  1705. pamac_daemon.emit_providers (depend_str, providers_str);
  1706. pamac_daemon.provider_mutex.lock ();
  1707. while (pamac_daemon.choosen_provider == null) {
  1708. pamac_daemon.provider_cond.wait (pamac_daemon.provider_mutex);
  1709. }
  1710. data.select_provider_use_index = pamac_daemon.choosen_provider;
  1711. pamac_daemon.provider_mutex.unlock ();
  1712. break;
  1713. case Alpm.Question.Type.CORRUPTED_PKG:
  1714. // Auto-remove corrupted pkgs in cache
  1715. data.corrupted_remove = 1;
  1716. break;
  1717. case Alpm.Question.Type.IMPORT_KEY:
  1718. if (data.import_key_key.revoked == 1) {
  1719. // Do not get revoked key
  1720. data.import_key_import = 0;
  1721. } else {
  1722. // Auto get not revoked key
  1723. data.import_key_import = 1;
  1724. }
  1725. break;
  1726. default:
  1727. data.any_answer = 0;
  1728. break;
  1729. }
  1730. }
  1731. private void cb_progress (Alpm.Progress progress, string pkgname, int percent, uint n_targets, uint current_target) {
  1732. if (percent == 0) {
  1733. pamac_daemon.emit_progress ((uint) progress, pkgname, (uint) percent, n_targets, current_target);
  1734. pamac_daemon.timer.start ();
  1735. } else if (percent == 100) {
  1736. pamac_daemon.emit_progress ((uint) progress, pkgname, (uint) percent, n_targets, current_target);
  1737. pamac_daemon.timer.stop ();
  1738. }else if (pamac_daemon.timer.elapsed () < 0.5) {
  1739. return;
  1740. } else {
  1741. pamac_daemon.emit_progress ((uint) progress, pkgname, (uint) percent, n_targets, current_target);
  1742. pamac_daemon.timer.start ();
  1743. }
  1744. }
  1745. private uint64 prevprogress;
  1746. private int cb_download (void* data, uint64 dltotal, uint64 dlnow, uint64 ultotal, uint64 ulnow) {
  1747. if (unlikely (pamac_daemon.cancellable.is_cancelled ())) {
  1748. return 1;
  1749. }
  1750. string filename = (string) data;
  1751. if (unlikely (dltotal == 0 || prevprogress == dltotal)) {
  1752. return 0;
  1753. } else if (unlikely (dlnow == 0)) {
  1754. pamac_daemon.emit_download (filename, dlnow, dltotal);
  1755. pamac_daemon.timer.start ();
  1756. } else if (unlikely (prevprogress == 0)) {
  1757. pamac_daemon.emit_download (filename, 0, dltotal);
  1758. pamac_daemon.emit_download (filename, dlnow, dltotal);
  1759. pamac_daemon.timer.start ();
  1760. } else if (unlikely (dlnow == dltotal)) {
  1761. pamac_daemon.emit_download (filename, dlnow, dltotal);
  1762. pamac_daemon.timer.stop ();
  1763. } else if (likely (pamac_daemon.timer.elapsed () < 0.5)) {
  1764. return 0;
  1765. } else {
  1766. pamac_daemon.emit_download (filename, dlnow, dltotal);
  1767. pamac_daemon.timer.start ();
  1768. }
  1769. //~ // avoid displaying progress for redirects with a body
  1770. //~ if (respcode >= 300) {
  1771. //~ return 0;
  1772. //~ }
  1773. prevprogress = dlnow;
  1774. return 0;
  1775. }
  1776. private int cb_fetch (string fileurl, string localpath, int force) {
  1777. if (pamac_daemon.cancellable.is_cancelled ()) {
  1778. return -1;
  1779. }
  1780. if (pamac_daemon.curl == null) {
  1781. pamac_daemon.curl = new Curl.Easy ();
  1782. }
  1783. char error_buffer[Curl.ERROR_SIZE];
  1784. var url = GLib.File.new_for_uri (fileurl);
  1785. var destfile = GLib.File.new_for_path (localpath + url.get_basename ());
  1786. var tempfile = GLib.File.new_for_path (destfile.get_path () + ".part");
  1787. pamac_daemon.curl.reset ();
  1788. pamac_daemon.curl.setopt (Curl.Option.URL, fileurl);
  1789. pamac_daemon.curl.setopt (Curl.Option.FAILONERROR, 1L);
  1790. pamac_daemon.curl.setopt (Curl.Option.ERRORBUFFER, error_buffer);
  1791. pamac_daemon.curl.setopt (Curl.Option.CONNECTTIMEOUT, 30L);
  1792. pamac_daemon.curl.setopt (Curl.Option.FILETIME, 1L);
  1793. pamac_daemon.curl.setopt (Curl.Option.NOPROGRESS, 0L);
  1794. pamac_daemon.curl.setopt (Curl.Option.FOLLOWLOCATION, 1L);
  1795. pamac_daemon.curl.setopt (Curl.Option.XFERINFOFUNCTION, cb_download);
  1796. pamac_daemon.curl.setopt (Curl.Option.XFERINFODATA, (void*) url.get_basename ());
  1797. pamac_daemon.curl.setopt (Curl.Option.LOW_SPEED_LIMIT, 1L);
  1798. pamac_daemon.curl.setopt (Curl.Option.LOW_SPEED_TIME, 30L);
  1799. pamac_daemon.curl.setopt (Curl.Option.NETRC, Curl.NetRCOption.OPTIONAL);
  1800. pamac_daemon.curl.setopt (Curl.Option.HTTPAUTH, Curl.CURLAUTH_ANY);
  1801. bool remove_partial_download = true;
  1802. if (fileurl.contains (".pkg.tar.") && !fileurl.has_suffix (".sig")) {
  1803. remove_partial_download = false;
  1804. }
  1805. string open_mode = "wb";
  1806. prevprogress = 0;
  1807. try {
  1808. if (force == 0) {
  1809. if (destfile.query_exists ()) {
  1810. // start from scratch only download if our local is out of date.
  1811. pamac_daemon.curl.setopt (Curl.Option.TIMECONDITION, Curl.TimeCond.IFMODSINCE);
  1812. FileInfo info = destfile.query_info ("time::modified", 0);
  1813. TimeVal time = info.get_modification_time ();
  1814. pamac_daemon.curl.setopt (Curl.Option.TIMEVALUE, time.tv_sec);
  1815. } else if (tempfile.query_exists ()) {
  1816. // a previous partial download exists, resume from end of file.
  1817. FileInfo info = tempfile.query_info ("standard::size", 0);
  1818. int64 size = info.get_size ();
  1819. pamac_daemon.curl.setopt (Curl.Option.RESUME_FROM_LARGE, size);
  1820. open_mode = "ab";
  1821. }
  1822. } else {
  1823. if (tempfile.query_exists ()) {
  1824. tempfile.delete ();
  1825. }
  1826. }
  1827. } catch (GLib.Error e) {
  1828. stderr.printf ("Error: %s\n", e.message);
  1829. }
  1830. Posix.FILE localf = Posix.FILE.open (tempfile.get_path (), open_mode);
  1831. if (localf == null) {
  1832. stdout.printf ("could not open file %s\n", tempfile.get_path ());
  1833. return -1;
  1834. }
  1835. pamac_daemon.curl.setopt (Curl.Option.WRITEDATA, localf);
  1836. // perform transfer
  1837. Curl.Code err = pamac_daemon.curl.perform ();
  1838. // disconnect relationships from the curl handle for things that might go out
  1839. // of scope, but could still be touched on connection teardown. This really
  1840. // only applies to FTP transfers.
  1841. pamac_daemon.curl.setopt (Curl.Option.NOPROGRESS, 1L);
  1842. pamac_daemon.curl.setopt (Curl.Option.ERRORBUFFER, null);
  1843. int ret;
  1844. // was it a success?
  1845. switch (err) {
  1846. case Curl.Code.OK:
  1847. long timecond, remote_time = -1;
  1848. double remote_size, bytes_dl;
  1849. unowned string effective_url;
  1850. // retrieve info about the state of the transfer
  1851. pamac_daemon.curl.getinfo (Curl.Info.FILETIME, out remote_time);
  1852. pamac_daemon.curl.getinfo (Curl.Info.CONTENT_LENGTH_DOWNLOAD, out remote_size);
  1853. pamac_daemon.curl.getinfo (Curl.Info.SIZE_DOWNLOAD, out bytes_dl);
  1854. pamac_daemon.curl.getinfo (Curl.Info.CONDITION_UNMET, out timecond);
  1855. pamac_daemon.curl.getinfo (Curl.Info.EFFECTIVE_URL, out effective_url);
  1856. if (timecond == 1 && bytes_dl == 0) {
  1857. // time condition was met and we didn't download anything. we need to
  1858. // clean up the 0 byte .part file that's left behind.
  1859. try {
  1860. if (tempfile.query_exists ()) {
  1861. tempfile.delete ();
  1862. }
  1863. } catch (GLib.Error e) {
  1864. stderr.printf ("Error: %s\n", e.message);
  1865. }
  1866. ret = 1;
  1867. }
  1868. // remote_size isn't necessarily the full size of the file, just what the
  1869. // server reported as remaining to download. compare it to what curl reported
  1870. // as actually being transferred during curl_easy_perform ()
  1871. else if (remote_size != -1 && bytes_dl != -1 && bytes_dl != remote_size) {
  1872. pamac_daemon.emit_log ((uint) Alpm.LogLevel.ERROR,
  1873. _("%s appears to be truncated: %jd/%jd bytes\n").printf (
  1874. fileurl, bytes_dl, remote_size));
  1875. if (remove_partial_download) {
  1876. try {
  1877. if (tempfile.query_exists ()) {
  1878. tempfile.delete ();
  1879. }
  1880. } catch (GLib.Error e) {
  1881. stderr.printf ("Error: %s\n", e.message);
  1882. }
  1883. }
  1884. ret = -1;
  1885. } else {
  1886. try {
  1887. tempfile.move (destfile, FileCopyFlags.OVERWRITE);
  1888. } catch (GLib.Error e) {
  1889. stderr.printf ("Error: %s\n", e.message);
  1890. }
  1891. ret = 0;
  1892. }
  1893. break;
  1894. case Curl.Code.ABORTED_BY_CALLBACK:
  1895. if (remove_partial_download) {
  1896. try {
  1897. if (tempfile.query_exists ()) {
  1898. tempfile.delete ();
  1899. }
  1900. } catch (GLib.Error e) {
  1901. stderr.printf ("Error: %s\n", e.message);
  1902. }
  1903. }
  1904. ret = -1;
  1905. break;
  1906. default:
  1907. // other cases are errors
  1908. try {
  1909. if (tempfile.query_exists ()) {
  1910. if (remove_partial_download) {
  1911. tempfile.delete ();
  1912. } else {
  1913. // delete zero length downloads
  1914. FileInfo info = tempfile.query_info ("standard::size", 0);
  1915. int64 size = info.get_size ();
  1916. if (size == 0) {
  1917. tempfile.delete ();
  1918. }
  1919. }
  1920. }
  1921. } catch (GLib.Error e) {
  1922. stderr.printf ("Error: %s\n", e.message);
  1923. }
  1924. // do not report error for missing sig with db
  1925. if (!fileurl.has_suffix ("db.sig")) {
  1926. string hostname = url.get_uri ().split("/")[2];
  1927. pamac_daemon.emit_log ((uint) Alpm.LogLevel.ERROR,
  1928. _("failed retrieving file '%s' from %s : %s\n").printf (
  1929. url.get_basename (), hostname, error_buffer));
  1930. }
  1931. ret = -1;
  1932. break;
  1933. }
  1934. return ret;
  1935. }
  1936. private void cb_totaldownload (uint64 total) {
  1937. pamac_daemon.emit_totaldownload (total);
  1938. }
  1939. private void cb_log (Alpm.LogLevel level, string fmt, va_list args) {
  1940. // do not log errors when download is cancelled
  1941. if (pamac_daemon.cancellable.is_cancelled ()) {
  1942. return;
  1943. }
  1944. Alpm.LogLevel logmask = Alpm.LogLevel.ERROR | Alpm.LogLevel.WARNING;
  1945. if ((level & logmask) == 0) {
  1946. return;
  1947. }
  1948. string? log = null;
  1949. log = fmt.vprintf (args);
  1950. if (log != null) {
  1951. pamac_daemon.emit_log ((uint) level, log);
  1952. }
  1953. }
  1954. void on_bus_acquired (DBusConnection conn) {
  1955. pamac_daemon = new Pamac.Daemon ();
  1956. try {
  1957. conn.register_object ("/org/manjaro/pamac", pamac_daemon);
  1958. }
  1959. catch (IOError e) {
  1960. stderr.printf ("Could not register service\n");
  1961. loop.quit ();
  1962. }
  1963. }
  1964. void main () {
  1965. // i18n
  1966. Intl.setlocale (LocaleCategory.ALL, "");
  1967. Intl.textdomain (GETTEXT_PACKAGE);
  1968. Bus.own_name (BusType.SYSTEM,
  1969. "org.manjaro.pamac",
  1970. BusNameOwnerFlags.NONE,
  1971. on_bus_acquired,
  1972. null,
  1973. () => {
  1974. stderr.printf ("Could not acquire name\n");
  1975. loop.quit ();
  1976. });
  1977. loop = new MainLoop ();
  1978. loop.run ();
  1979. }