1
<?php 
2
3
	/* 
4
	 * WiGit
5
	 * (c) Remko Tronçon (http://el-tramo.be)
6
	 * See COPYING for details
7
	 */
8
9
	require_once('classTextile.php');
10
11
	// --------------------------------------------------------------------------
12
	// Configuration
13
	// --------------------------------------------------------------------------
14
15
	if (file_exists('config.php')) {
16
		require_once('config.php');
17
	}
18
	if (!isset($GIT)) { $GIT = "git"; }
19
	if (!isset($BASE_URL)) { $BASE_URL = "/wigit"; }
20
	if (!isset($SCRIPT_URL)) { $SCRIPT_URL = "$BASE_URL/index.php?r="; }
21
	if (!isset($TITLE)) { $TITLE = "WiGit"; }
22
	if (!isset($DATA_DIR)) { $DATA_DIR = "data"; }
23
	if (!isset($DEFAULT_PAGE)) { $DEFAULT_PAGE = "Home"; }
24
	if (!isset($DEFAULT_AUTHOR)) { $DEFAULT_AUTHOR = 'Anonymous <anonymous@wigit>'; }
25
  if (!isset($AUTHORS)) { $AUTHORS = array(); }
26
  if (!isset($THEME)) { $THEME = "default"; }
27
28
29
	// --------------------------------------------------------------------------
30
	// Helpers
31
	// --------------------------------------------------------------------------
32
33
	function getGitHistory($file = "") {
34
		$output = array();
35
		// FIXME: Find a better way to find the files that changed than --name-only
36
		git("log --name-only --pretty=format:'%H>%T>%an>%ae>%aD>%s' -- $file", $output);
37
		$history = array();
38
		$historyItem = array();
39
		foreach ($output as $line) {
40
			$logEntry = explode(">", $line, 6);
41
			if (sizeof($logEntry) > 1) {
42
				// Populate history structure
43
				$historyItem = array(
44
						"author" => $logEntry[2], 
45
						"email" => $logEntry[3],
46
						"linked-author" => (
47
								$logEntry[3] == "" ? 
48
									$logEntry[2] 
49
									: "<a href=\"mailto:$logEntry[3]\">$logEntry[2]</a>"),
50
						"date" => $logEntry[4], 
51
						"message" => $logEntry[5],
52
						"commit" => $logEntry[0]
53
					);
54
			}
55
			else if (!isset($historyItem["page"])) {
56
				$historyItem["page"] = $line;
57
				$history[] = $historyItem;
58
			}
59
		}
60
		return $history;
61
	}
62
63
	function getAuthorForUser($user) {
64
		global $AUTHORS, $DEFAULT_AUTHOR;
65
66
		if (isset($AUTHORS[$user])) {
67
			return $AUTHORS[$user];
68
		}
69
		else if ($user != "") {
70
			return "$user <$user@wiggit>";
71
		}
72
		return $DEFAULT_AUTHOR;
73
	}
74
75
	function getHTTPUser() {
76
		// This code is copied from phpMyID. Thanks to the phpMyID dev(s).
77
		if (function_exists('apache_request_headers') && ini_get('safe_mode') == false) {
78
			$arh = apache_request_headers();
79
			$hdr = $arh['Authorization'];
80
		} elseif (isset($_SERVER['PHP_AUTH_DIGEST'])) {
81
			$hdr = $_SERVER['PHP_AUTH_DIGEST'];
82
		} elseif (isset($_SERVER['HTTP_AUTHORIZATION'])) {
83
			$hdr = $_SERVER['HTTP_AUTHORIZATION'];
84
		} elseif (isset($_ENV['PHP_AUTH_DIGEST'])) {
85
			$hdr = $_ENV['PHP_AUTH_DIGEST'];
86
		} elseif (isset($_REQUEST['auth'])) {
87
			$hdr = stripslashes(urldecode($_REQUEST['auth']));
88
		} else {
89
			$hdr = null;
90
		}
91
		$digest = substr($hdr,0,7) == 'Digest '
92
			?  substr($hdr, strpos($hdr, ' ') + 1)
93
			: $hdr;
94
		if (!is_null($digest)) {
95
			$hdr = array();
96
			preg_match_all('/(\w+)=(?:"([^"]+)"|([^\s,]+))/', $digest, $mtx, PREG_SET_ORDER);
97
			foreach ($mtx as $m) {
98
				if ($m[1] == "username") {
99
					return $m[2] ? $m[2] : str_replace("\\\"", "", $m[3]);
100
				}
101
			}
102
		}
103
		return $_SERVER['PHP_AUTH_USER'];
104
	}
105
106
	function git($command, &$output = "") {
107
		global $GIT, $DATA_DIR;
108
109
		$gitDir = dirname(__FILE__) . "/$DATA_DIR/.git";
110
		$gitWorkTree = dirname(__FILE__) . "/$DATA_DIR";
111
		$gitCommand = "$GIT --git-dir=$gitDir --work-tree=$gitWorkTree $command";
112
		$output = array();
113
		$result;
114
		// FIXME: Only do the escaping and the 2>&1 if we're not in safe mode 
115
		// (otherwise it will be escaped anyway).
116
		// FIXME: Removed escapeShellCmd because it clashed with author.
117
		$oldUMask = umask(0022);
118
		exec($gitCommand . " 2>&1", $output, $result);
119
		$umask = $oldUMask;
120
		// FIXME: The -1 is a hack to avoid 'commit' on an unchanged repo to
121
		// fail.
122
		if ($result != 0) {
123
			// FIXME: HTMLify these strings
124
			print "<h1>Error</h1>\n<pre>\n";
125
			print "$" . $gitCommand . "\n";
126
			print join("\n", $output) . "\n";
127
			//print "Error code: " . $result . "\n";
128
			print "</pre>";
129
			return 0;
130
		}
131
		return 1;
132
	}
133
134
	function sanitizeName($name) {
135
		return ereg_replace("[^A-Za-z0-9]", "_", $name);
136
	}
137
138
	function parseResource($resource) {
139
		global $DEFAULT_PAGE;
140
141
		$matches = array();
142
		$page = "";
143
		$type = "";
144
		if (ereg("/(.*)/(.*)", $resource, $matches)) {
145
			$page = sanitizeName($matches[1]);
146
			$type = $matches[2];
147
		}
148
		else if (ereg("/(.*)", $resource, $matches)) {
149
			$page = sanitizeName($matches[1]);
150
		}
151
		if ($page == "") {
152
			$page = $DEFAULT_PAGE;
153
		}
154
		if ($type == "") {
155
			$type = "view";
156
		}
157
		return array("page" => $page, "type" => $type);
158
	}
159
160
161
	// --------------------------------------------------------------------------
162
	// Wikify
163
	// --------------------------------------------------------------------------
164
165
	function wikify($text) {
166
		global $SCRIPT_URL;
167
168
		// FIXME: Do not apply this in <pre> and <notextile> blocks.
169
170
		// Linkify
171
		$text = preg_replace('@([^:])(https?://([-\w\.]+)+(:\d+)?(/([%-\w/_\.]*(\?\S+)?)?)?)@', '$1<a href="$2">$2</a>', $text);
172
173
		// WikiLinkify
174
		$text = preg_replace('@\[([A-Z]\w+)\]@', '<a href="' . $SCRIPT_URL . '/$1">$1</a>', $text);
175
		$text = preg_replace('@\[([A-Z]\w+)\|([\w\s]+)\]@', '<a href="' . $SCRIPT_URL . '/$1">$2</a>', $text);
176
177
		// Textilify
178
		$textile = new Textile();
179
		return $textile->TextileThis($text);
180
	}
181
182
	// --------------------------------------------------------------------------
183
	// Utility functions (for use inside templates)
184
	// --------------------------------------------------------------------------
185
	
186
	function getViewURL($page, $version = null) {
187
		global $SCRIPT_URL;
188
		if ($version) {
189
			return "$SCRIPT_URL/$page/$version";
190
		}
191
		else {
192
			return "$SCRIPT_URL/$page";
193
		}
194
	}
195
196
	function getPostURL() {
197
		global $SCRIPT_URL;
198
		$page = getPage();
199
		return "$SCRIPT_URL/$page";
200
	}
201
202
	function getEditURL() {
203
		global $SCRIPT_URL;
204
		$page = getPage();
205
		return "$SCRIPT_URL/$page/edit";
206
	}
207
208
	function getHistoryURL() {
209
		global $SCRIPT_URL;
210
		$page = getPage();
211
		return "$SCRIPT_URL/$page/history";
212
	}
213
	
214
	function getGlobalHistoryURL() {
215
		global $SCRIPT_URL;
216
		return "$SCRIPT_URL/history";
217
	}
218
219
	function getHomeURL() {
220
		global $SCRIPT_URL;
221
		return "$SCRIPT_URL/";
222
	}
223
224
	function getUser() {
225
		global $wikiUser;
226
		return $wikiUser;
227
	}
228
229
	function getTitle() {
230
		global $TITLE;
231
		return $TITLE;
232
	}
233
234
	function getPage() {
235
		global $wikiPage;
236
		return $wikiPage;
237
	}
238
239
	function getCSSURL() {
240
		global $BASE_URL;
241
		return "$BASE_URL/" . getThemeDir() . "/style.css";
242
	}
243
244
	function getThemeDir() {
245
		global $THEME;
246
		return "themes/$THEME";
247
	}
248
249
	function getFile() {
250
		global $wikiFile;
251
		return $wikiFile;
252
	}
253
254
	function getContent() {
255
		global $wikiContent;
256
		return $wikiContent;
257
	}
258
259
	function getRawData() {
260
		global $wikiData;
261
		return $wikiData;
262
	}
263
264
	// --------------------------------------------------------------------------
265
	// Initialize globals
266
	// --------------------------------------------------------------------------
267
268
	$wikiUser = getHTTPUser();
269
270
	$resource = parseResource($_GET['r']);
271
	$wikiPage = $resource["page"];
272
	$wikiSubPage = $resource["type"];
273
	$wikiFile = $DATA_DIR . "/" . $wikiPage;
274
275
276
	// --------------------------------------------------------------------------
277
	// Process request
278
	// --------------------------------------------------------------------------
279
280
	if (isset($_POST['data'])) {
281
		if (trim($_POST['data']) == "") {
282
			// Delete
283
			if (file_exists($wikiFile)) {
284
				if (!git("rm $wikiPage")) { return; }
285
286
				$commitMessage = addslashes("Deleted $wikiPage");
287
				$author = addslashes(getAuthorForUser(getUser()));
288
				if (!git("commit --allow-empty --no-verify --message='$commitMessage' --author='$author'")) { return; }
289
				if (!git("gc")) { return; }
290
			}
291
			header("Location: $wikiHome");
292
			return;
293
		}
294
		else {
295
			// Save
296
			$handle = fopen($wikiFile, "w");
297
			fputs($handle, stripslashes($_POST['data']));
298
			fclose($handle);
299
300
			$commitMessage = addslashes("Changed $wikiPage");
301
			$author = addslashes(getAuthorForUser(getUser()));
302
			if (!git("init")) { return; }
303
			if (!git("add $wikiPage")) { return; }
304
			if (!git("commit --allow-empty --no-verify --message='$commitMessage' --author='$author'")) { return; }
305
			if (!git("gc")) { return; }
306
			header("Location: " . getViewURL($wikiPage));
307
			return;
308
		}
309
	}
310
	// Get operation
311
	else {
312
		// Global history
313
		if ($wikiPage == "history") {
314
			$wikiHistory = getGitHistory();
315
			$wikiPage = "";
316
			include(getThemeDir() . "/history.php");
317
		}
318
		// Viewing
319
		else if ($wikiSubPage == "view") {
320
			if (!file_exists($wikiFile)) {
321
				header("Location: " . $SCRIPT_URL . "/" . $resource["page"] . "/edit");
322
				return;
323
			}
324
325
			// Open the file
326
			$handle = fopen($wikiFile, "r");
327
			$data = fread($handle, filesize($wikiFile));
328
			fclose($handle);
329
330
			// Put in template
331
			$wikiContent = wikify($data);
332
			include(getThemeDir() . "/view.php");
333
		}
334
		// Editing
335
		else if ($wikiSubPage == "edit") {
336
			if (file_exists($wikiFile)) {
337
				$handle = fopen($wikiFile, "r");
338
				$data = fread($handle, filesize($wikiFile));
339
			}
340
341
			// Put in template
342
			$wikiData = $data;
343
			include(getThemeDir() . "/edit.php");
344
		}
345
		// History
346
		else if ($wikiSubPage == "history") {
347
			$wikiHistory = getGitHistory($wikiPage);
348
			include(getThemeDir() . "/history.php");
349
		}
350
		// Specific version
351
		else if (eregi("[0-9A-F]{20,20}", $wikiSubPage)) {
352
			$output = array();
353
			if (!git("cat-file -p " . $wikiSubPage . ":$wikiPage", $output)) {
354
				return;
355
			}
356
			$wikiContent = wikify(join("\n", $output));
357
			include(getThemeDir() . "/view.php");
358
		}
359
		else {
360
			print "Unknow subpage: " . $wikiSubPage;
361
		}
362
	}
363
364
?>