1<?php
2/**
3 * Poldek Plugin: query poldek for package info
4 *
5 * Add poldek tags to dokuwiki
6 *
7 * @license    GPL 2 (http://www.gnu.org/licenses/gpl.html)
8 * @author     Elan Ruusamäe <glen@delfi.ee>
9 */
10// must be run within Dokuwiki
11if(!defined('DOKU_INC')) die();
12
13/**
14 * All DokuWiki plugins to extend the parser/rendering mechanism
15 * need to inherit from this class
16 *
17 * @author     Elan Ruusamäe <glen@delfi.ee>
18 */
19class helper_plugin_poldek extends DokuWiki_Plugin {
20	/*
21	 * Path to package list cache file
22	 * Filled by sync() method.
23	 * @var string $cache
24	 */
25	private $cache;
26
27	/**
28	 * Update poldek indexes for active repos
29	 * Save down list of packages.
30	 *
31	 * We create two cache objects, one for updating indexes, which is mtime based
32	 * And other for package list, which depends on index file.
33	 * And each page depends on the package list file
34	 *
35	 * Without force, cache is attempted to be used, even if it's stale
36	 *
37	 * Called by ls command, or from cron
38	 */
39	public function sync($force = false) {
40		// do this once per request
41		if ($this->cache) {
42			return;
43		}
44
45		global $conf;
46
47		$idx_cache = new cache($this->getPluginName(), '.idx');
48		$pkg_cache = new cache($this->getPluginName(), '.txt');
49		$cache_exists = file_exists($pkg_cache->cache);
50
51		// check poldek indexes
52		if (!$cache_exists || !$idx_cache->useCache(array('age' => $conf['locktime'], 'files' => getConfigFiles('main')))) {
53
54			// cache is ok, if it exists and is not empty and does not contain errors
55			$cache_ok = $cache_exists && filesize($pkg_cache->cache) && !preg_grep('/^error:/', file($pkg_cache->cache));
56
57			// without force update indexes only if cache is missing
58			if ($force || !$cache_exists) {
59				$lines = $this->exec("--up");
60				// process output, if we find "Writing ..." line, means we should update ls output as well
61				// Writing /root/.poldek-cache/[...]/packages.ndir.gz...
62				// Index patches size too big
63				// Retrieving whole index ...
64				if (!$cache_exists || !$cache_ok || preg_grep('/^(Writing|Retrieving whole index) /', $lines)) {
65					$idx_cache->storeCache(time());
66				} else {
67					// freshen timestamp or we keep updating indexes if index
68					// is older than locktime
69					touch($idx_cache->cache);
70					if ($force) {
71						// sleep, so packages cache be newer
72						sleep(1);
73					}
74					// touch also package file, not to trigger it's update
75					touch($pkg_cache->cache);
76					clearstatcache();
77				}
78			}
79		}
80
81		// do not update listing, if cache exists and not in cron mode
82		if (($force || !$cache_exists) && !$pkg_cache->useCache(array('files' => array($idx_cache->cache)))) {
83			$lines = $this->shcmd("ls", $rc);
84			// write cache, unless there was an error
85			if (!$rc) {
86				$pkg_cache->storeCache(join("\n", $lines));
87			}
88		}
89
90		$this->cache = $pkg_cache->cache;
91	}
92
93	public function ls($package) {
94		static $cache;
95
96		if (!$cache) {
97			$cache = array();
98
99			// regexp matching is slow.
100			// cache this for cases having more than one instance of our plugin on page
101			foreach (file($this->getCache()) as $line) {
102				if (preg_match('/^(?P<name>.+)-(?P<version>[^-]+)-(?P<release>[^-]+)\.(?P<arch>[^.]+)$/', $line, $m)) {
103					$cache[$m['name']] = $line;
104				} elseif (preg_match('/error: (?P<name>.+): no such package or directory/', $line, $m)) {
105					$cache[$m['name']] = $line;
106				}
107			}
108		}
109
110		if (isset($cache[$package])) {
111			return $cache[$package];
112		}
113
114		return "error: $package: no such package";
115	}
116
117	/**
118	 * Get filename for package list
119	 * It ensures that cache file is created, if missing
120	 */
121	public function getCache() {
122		$this->sync();
123		return $this->cache;
124	}
125
126	/**
127	 * Run command in poldek
128	 */
129	private function shcmd($cmd, &$rc = null) {
130		return $this->exec('-q -Q --shcmd='.escapeshellarg($cmd), $rc);
131	}
132
133	/**
134	 * Run poldek with configured repositories and cachedir
135	 * Setups proxies if needed
136	 */
137	private function exec($cmd, &$rc = null) {
138		global $conf;
139		$cachedir = $conf['cachedir'].'/'.$this->getPluginName();
140
141		// base poldek command
142		$poldek = 'exec poldek --skip-installed --cachedir=' . escapeshellarg($cachedir);
143
144		$poldek_conf = DOKU_CONF . 'poldek.conf';
145		if (file_exists($poldek_conf)) {
146			$poldek .= ' --conf '.escapeshellarg($poldek_conf);
147		}
148
149		$repos = $this->getConf('repos');
150		foreach (explode(',', $repos) as $repo) {
151			$poldek .= ' --sn '.escapeshellarg(trim($repo));
152		}
153
154		// proxies setup
155		$http_proxy = $this->getConf('http_proxy');
156		if ($http_proxy) {
157			$poldek .= ' -O '.escapeshellarg("proxy=http: {$http_proxy}");
158		}
159		$ftp_proxy = $this->getConf('ftp_proxy');
160		if ($ftp_proxy) {
161			$poldek.= ' -O '.escapeshellarg("proxy=ftp: {$ftp_proxy}");
162		}
163
164		$poldek .= " $cmd";
165
166		exec($poldek, $lines, $rc);
167		return $lines;
168	}
169}
170
171//Setup VIM: ex: noet ts=4 enc=utf-8 :
172