pkgsubmit.php 13 KB
Newer Older
1
<?php
pjmattal's avatar
pjmattal committed
2

3
set_include_path(get_include_path() . PATH_SEPARATOR . '../lib');
4
include_once("config.inc.php");
5

Dan McGee's avatar
Dan McGee committed
6
require_once('Archive/Tar.php');
pjmattal's avatar
pjmattal committed
7

8
9
include_once("aur.inc.php");         # access AUR common functions
include_once("pkgfuncs.inc.php");    # package functions
10

eric's avatar
eric committed
11
12
set_lang();                 # this sets up the visitor's language
check_sid();                # see if they're still logged in
eric's avatar
eric committed
13

14
15
$cwd = getcwd();

16
17
18
19
20
21
22
23
if ($_COOKIE["AURSID"]) {
	$uid = uid_from_sid($_COOKIE['AURSID']);
}
else {
	$uid = NULL;
}

if ($uid):
24

25
	# Track upload errors
eric's avatar
eric committed
26
	$error = "";
27
	$ignore_missing_aurinfo = 0;
eric's avatar
eric committed
28

29
	if (isset($_REQUEST['pkgsubmit'])) {
30

31
32
33
34
35
		# Make sure authenticated user submitted the package themselves
		if (!check_token()) {
			$error = __("Invalid token for user action.");
		}

36
		# Before processing, make sure we even have a file
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
		switch($_FILES['pfile']['error']) {
			case UPLOAD_ERR_INI_SIZE:
				$maxsize =  ini_get('upload_max_filesize');
				$error = __("Error - Uploaded file larger than maximum allowed size (%s)", $maxsize);
				break;
			case UPLOAD_ERR_PARTIAL:
				$error = __("Error - File partially uploaded");
				break;
			case UPLOAD_ERR_NO_FILE:
				$error = __("Error - No file uploaded");
				break;
			case UPLOAD_ERR_NO_TMP_DIR:
				$error = __("Error - Could not locate temporary upload folder");
				break;
			case UPLOAD_ERR_CANT_WRITE:
				$error = __("Error - File could not be written");
				break;
dsa's avatar
dsa committed
54
		}
55

56
57
58
59
		# Check whether the file is gzip'ed
		if (!$error) {
			$fh = fopen($_FILES['pfile']['tmp_name'], 'rb');
			fseek($fh, 0, SEEK_SET);
60
			list(, $magic) = unpack('v', fread($fh, 2));
61
62
63
64
65
66

			if ($magic != 0x8b1f) {
				$error = __("Error - unsupported file format (please submit gzip'ed tarballs generated by makepkg(8) only).");
			}
		}

67
68
69
		# Check uncompressed file size (ZIP bomb protection)
		if (!$error && $MAX_FILESIZE_UNCOMPRESSED) {
			fseek($fh, -4, SEEK_END);
70
			list(, $filesize_uncompressed) = unpack('V', fread($fh, 4));
71
72
73
74
75
76

			if ($filesize_uncompressed > $MAX_FILESIZE_UNCOMPRESSED) {
				$error = __("Error - uncompressed file size too large.");
			}
		}

77
		# Close file handle before extracting stuff
78
		if (isset($fh) && is_resource($fh)) {
79
80
81
			fclose($fh);
		}

eric's avatar
eric committed
82
		if (!$error) {
83
			$tar = new Archive_Tar($_FILES['pfile']['tmp_name']);
84

85
			/* Extract PKGBUILD and .AURINFO into a string. */
86
			$pkgbuild_raw = $srcinfo_raw = '';
87
			$dircount = 0;
88
			foreach ($tar->listContent() as $tar_file) {
89
90
91
92
93
94
95
96
				if ($tar_file['typeflag'] == 0) {
					if (strchr($tar_file['filename'], '/') === false) {
						$error = __("Error - source tarball may not contain files outside a directory.");
						break;
					}
					elseif (substr($tar_file['filename'], -9) == '/PKGBUILD') {
						$pkgbuild_raw = $tar->extractInString($tar_file['filename']);
					}
97
98
99
					elseif (substr($tar_file['filename'], -9) == '/.AURINFO') {
						$srcinfo_raw = $tar->extractInString($tar_file['filename']);
					}
100
				}
101
102
103
104
105
106
				elseif ($tar_file['typeflag'] == 5) {
					if (substr_count($tar_file['filename'], "/") > 1) {
						$error = __("Error - source tarball may not contain nested subdirectories.");
						break;
					}
					elseif (++$dircount > 1) {
107
108
109
110
						$error = __("Error - source tarball may not contain more than one directory.");
						break;
					}
				}
111
			}
112
		}
113

114
115
116
		if (!$error && $dircount !== 1) {
			$error = __("Error - source tarball may not contain files outside a directory.");
		}
117

118
119
120
		if (empty($pkgbuild_raw)) {
			$pkgbuild_raw = '';
			if (!$error) {
121
122
				$error = __("Error trying to unpack upload - PKGBUILD does not exist.");
			}
123
		}
124

125
126
127
		if (empty($srcinfo_raw)) {
			$srcinfo_raw = '';
			if (!$error && (!isset($_POST['ignore_missing_aurinfo']) || $_POST['ignore_missing_aurinfo'] != 1)) {
128
129
130
				$ignore_missing_aurinfo = 1;
				$error = __("The source package does not contain any meta data. Please use `mkaurball` to create AUR source packages. Support for source packages without .AURINFO entries will be removed in an upcoming release! You can resubmit the package if you want to proceed anyway.");
			}
131
		}
jchu's avatar
jchu committed
132

133
134
135
136
		/* Parse .AURINFO and extract meta data. */
		$pkgbase_info = array();
		$pkginfo = array();
		$section_info = array();
137
		foreach (explode("\n", $srcinfo_raw) as $line) {
138
			$line = trim($line);
139
140
141
142
143
			if (empty($line) || $line[0] == '#') {
				continue;
			}
			list($key, $value) = explode(' = ', $line, 2);
			switch ($key) {
144
			case 'pkgbase':
145
			case 'pkgname':
146
147
148
149
150
151
				if (!empty($section_info)) {
					if (isset($section_info['pkgbase'])) {
						$pkgbase_info = $section_info;
					} elseif (isset($section_info['pkgname'])) {
						$pkginfo[] = array_merge($pkgbase_info, $section_info);
					}
152
				}
153
154
155
156
157
158
159
				$section_info = array(
					'depends' => array(),
					'makedepends' => array(),
					'checkdepends' => array(),
					'optdepends' => array(),
					'source' => array()
				);
160
				/* Fall-through case. */
161
			case 'epoch':
162
			case 'pkgdesc':
163
164
			case 'pkgver':
			case 'pkgrel':
165
166
			case 'url':
			case 'license':
167
				$section_info[$key] = $value;
168
				break;
169
			case 'source':
170
			case 'depends':
171
172
173
			case 'makedepends':
			case 'checkdepends':
			case 'optdepends':
174
				$section_info[$key][] = $value;
175
176
177
178
				break;
			}
		}

179
180
181
182
183
184
185
186
		if (!empty($section_info)) {
			if (isset($section_info['pkgbase'])) {
				$pkgbase_info = $section_info;
			} elseif (isset($section_info['pkgname'])) {
				$pkginfo[] = array_merge($pkgbase_info, $section_info);
			}
		} else {
			/* Use data from the PKGBUILD parser (deprecated!) */
187
188
			include('pkgbuild-parser.inc.php');

189
190
191
192
			$pkgbase_info = $new_pkgbuild;
			if (!isset($pkgbase_info['pkgbase'])) {
				$pkgbase_info['pkgbase'] = $pkgbase_info['pkgname'];
			}
193
194
195
196
197
198
			foreach (array('source', 'depends', 'makedepends', 'checkdepends', 'optdepends') as $array_opt) {
				if (empty($pkgbase_info[$array_opt])) {
					$pkgbase_info[$array_opt] = array();
				} else {
					$pkgbase_info[$array_opt] = explode(" ", $pkgbase_info[$array_opt]);
				}
199
200
201
202
203
			}
			$pkginfo[] = $pkgbase_info;
		}

		/* Validate package base name. */
204
		if (!$error) {
205
206
			$pkgbase_name = $pkgbase_info['pkgbase'];
			if (!preg_match("/^[a-z0-9][a-z0-9\.+_-]*$/", $pkgbase_name)) {
207
208
				$error = __("Invalid name: only lowercase letters are allowed.");
			}
209
210
211

			/* Check whether the package base already exists. */
			$base_id = pkgbase_from_name($pkgbase_name);
212
213
		}

214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
		foreach ($pkginfo as $key => $pi) {
			/* Bail out early if an error has occurred. */
			if ($error) {
				break;
			}

			/* Validate package names. */
			$pkg_name = $pi['pkgname'];
			if (!preg_match("/^[a-z0-9][a-z0-9\.+_-]*$/", $pkg_name)) {
				$error = __("Invalid name: only lowercase letters are allowed.");
				break;
			}

			/* Determine the full package versions with epoch. */
			if (isset($pi['epoch']) && (int)$pi['epoch'] > 0) {
				$pkginfo[$key]['full-version'] = sprintf('%d:%s-%s', $pi['epoch'], $pi['pkgver'], $pi['pkgrel']);
230
			} else {
231
				$pkginfo[$key]['full-version'] = sprintf('%s-%s', $pi['pkgver'], $pi['pkgrel']);
232
233
			}

234
235
			/* Check for http:// or other protocols in the URL. */
			$parsed_url = parse_url($pi['url']);
236
237
			if (!$parsed_url['scheme']) {
				$error = __("Package URL is missing a protocol (ie. http:// ,ftp://)");
238
				break;
239
240
			}

241
242
243
244
245
246
			/*
			 * The DB schema imposes limitations on number of
			 * allowed characters. Print error message when these
			 * limitations are exceeded.
			 */
			if (strlen($pi['pkgname']) > 64) {
247
				$error = __("Error - Package name cannot be greater than %d characters", 64);
248
				break;
249
			}
250
			if (strlen($pi['url']) > 255) {
251
				$error = __("Error - Package URL cannot be greater than %d characters", 255);
252
				break;
253
			}
254
			if (strlen($pi['pkgdesc']) > 255) {
255
				$error = __("Error - Package description cannot be greater than %d characters", 255);
256
				break;
257
			}
258
			if (strlen($pi['license']) > 40) {
259
				$error = __("Error - Package license cannot be greater than %d characters", 40);
260
				break;
261
			}
262
			if (strlen($pkginfo[$key]['full-version']) > 32) {
263
				$error = __("Error - Package version cannot be greater than %d characters", 32);
264
265
266
267
				break;
			}

			/* Check if package name is blacklisted. */
268
			if (!$base_id && pkg_name_is_blacklisted($pi['pkgname']) && !can_submit_blacklisted(account_from_sid($_COOKIE["AURSID"]))) {
269
270
				$error = __( "%s is on the package blacklist, please check if it's available in the official repos.", $pi['pkgname']);
				break;
271
272
273
			}
		}

274
275
		if (isset($pkgbase_name)) {
			$incoming_pkgdir = INCOMING_DIR . substr($pkgbase_name, 0, 2) . "/" . $pkgbase_name;
276
		}
277

278
		/* Upload PKGBUILD and tarball. */
279
280
281
		if (!$error && !can_submit_pkgbase($pkgbase_name, $_COOKIE["AURSID"])) {
			$error = __( "You are not allowed to overwrite the %s%s%s package.", "<strong>", $pkgbase_name, "</strong>");
		}
282

283
284
285
286
287
		if (!$error) {
			foreach ($pkginfo as $pi) {
				if (!can_submit_pkg($pi['pkgname'], $base_id)) {
					$error = __( "You are not allowed to overwrite the %s%s%s package.", "<strong>", $pi['pkgname'], "</strong>");
					break;
288
289
290
291
				}
			}
		}

292
		if (!$error) {
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
			/*
			 * Blow away the existing directory and its contents.
			 */
			if (file_exists($incoming_pkgdir)) {
				rm_tree($incoming_pkgdir);
			}

			/*
			 * The mode is masked by the current umask, so not as
			 * scary as it looks.
			 */
			if (!mkdir($incoming_pkgdir, 0777, true)) {
				$error = __( "Could not create directory %s.", $incoming_pkgdir);
			}

308
			if (!chdir($incoming_pkgdir)) {
309
				$error = __("Could not change directory to %s.", $incoming_pkgdir);
310
			}
311

312
			file_put_contents('PKGBUILD', $pkgbuild_raw);
313
			move_uploaded_file($_FILES['pfile']['tmp_name'], $pkgbase_name . '.tar.gz');
314
315
		}

316
		/* Update the backend database. */
317
		if (!$error) {
318
			begin_atomic_commit();
319

320
321
322
323
			/*
			 * Check the category to use, "1" meaning "none" (or
			 * "keep category" for existing packages).
			 */
324
			if (isset($_POST['category'])) {
325
326
				$category_id = max(1, intval($_POST['category']));
			} else {
327
328
329
				$category_id = 1;
			}

330
331
332
333
334
335
336
			if ($base_id) {
				/*
				 * This is an overwrite of an existing package
				 * base, the database ID needs to be preserved
				 * so that any votes are retained.
				 */
				$was_orphan = (pkgbase_maintainer_uid($base_id) === NULL);
337

338
				pkgbase_update($base_id, $pkgbase_info['pkgbase'], $uid);
339

340
				if ($category_id > 1) {
341
					pkgbase_update_category($base_id, $category_id);
342
343
				}

344
				pkgbase_delete_packages($base_id);
345
			} else {
346
347
				/* This is a brand new package. */
				$was_orphan = true;
348
				$base_id = pkgbase_create($pkgbase_name, $category_id, $uid);
Dan McGee's avatar
Dan McGee committed
349
			}
350

351
			foreach ($pkginfo as $pi) {
352
				$pkgid = pkg_create($base_id, $pi['pkgname'], $pi['license'], $pi['full-version'], $pi['pkgdesc'], $pi['url']);
353

354
355
356
357
358
359
				foreach (array('depends', 'makedepends', 'checkdepends', 'optdepends') as $deptype) {
					foreach ($pi[$deptype] as $dep) {
						$deppkgname = preg_replace("/(<|=|>).*/", "", $dep);
						$depcondition = str_replace($deppkgname, "", $dep);
						pkg_add_dep($pkgid, $deptype, $deppkgname, $depcondition);
					}
360
				}
361

362
				foreach ($pi['source'] as $src) {
363
					pkg_add_src($pkgid, $src);
Dan McGee's avatar
Dan McGee committed
364
365
				}
			}
366

367
368
369
370
371
372
			/*
			 * If we just created this package, or it was an orphan
			 * and we auto-adopted, add submitting user to the
			 * notification list.
			 */
			if ($was_orphan) {
373
				pkgbase_notify(account_from_sid($_COOKIE["AURSID"]), array($base_id), true);
374
			}
Dan McGee's avatar
Dan McGee committed
375

376
			end_atomic_commit();
377

378
			header('Location: ' . get_pkg_uri($pi[0]['pkgname']));
379
		}
380

381
		chdir($cwd);
eric's avatar
eric committed
382
383
	}

384
html_header("Submit");
eric's avatar
eric committed
385

386
387
?>

388
<div class="box">
Lukas Fleischer's avatar
Lukas Fleischer committed
389
	<h2><?= __("Submit"); ?></h2>
Lukas Fleischer's avatar
Lukas Fleischer committed
390
	<p><?= __("Upload your source packages here. Create source packages with `mkaurball`.") ?></p>
eric's avatar
eric committed
391

392
<?php
393
	if (empty($_REQUEST['pkgsubmit']) || $error):
394
		# User is not uploading, or there were errors uploading - then
eric's avatar
eric committed
395
		# give the visitor the default upload form
Callan Barrett's avatar
Callan Barrett committed
396
397
		if (ini_get("file_uploads")):

398
			$pkgbase_categories = pkgbase_categories();
399
?>
400

401
402
403
404
<?php if ($error): ?>
	<ul class="errorlist"><li><?= $error ?></li></ul>
<?php endif; ?>

Lukas Fleischer's avatar
Lukas Fleischer committed
405
<form action="<?= get_uri('/submit/'); ?>" method="post" enctype="multipart/form-data">
406
407
408
	<fieldset>
		<div>
			<input type="hidden" name="pkgsubmit" value="1" />
409
			<input type="hidden" name="ignore_missing_aurinfo" value="<?= $ignore_missing_aurinfo ?>" />
Lukas Fleischer's avatar
Lukas Fleischer committed
410
			<input type="hidden" name="token" value="<?= htmlspecialchars($_COOKIE['AURSID']) ?>" />
411
412
		</div>
		<p>
Lukas Fleischer's avatar
Lukas Fleischer committed
413
			<label for="id_category"><?= __("Package Category"); ?>:</label>
414
			<select id="id_category" name="category">
Lukas Fleischer's avatar
Lukas Fleischer committed
415
				<option value="1"><?= __("Select Category"); ?></option>
416
				<?php
417
					foreach ($pkgbase_categories as $num => $cat):
418
						print '<option value="' . $num . '"';
Callan Barrett's avatar
Callan Barrett committed
419
						if (isset($_POST['category']) && $_POST['category'] == $cat):
420
							print ' selected="selected"';
Callan Barrett's avatar
Callan Barrett committed
421
						endif;
422
						print '>' . $cat . '</option>';
Callan Barrett's avatar
Callan Barrett committed
423
					endforeach;
424
425
				?>
			</select>
426
427
		</p>
		<p>
Lukas Fleischer's avatar
Lukas Fleischer committed
428
			<label for="id_file"><?= __("Upload package file"); ?>:</label>
429
430
431
432
			<input id="id_file" type="file" name="pfile" size='30' />
		</p>
		<p>
			<label></label>
Lukas Fleischer's avatar
Lukas Fleischer committed
433
			<input class="button" type="submit" value="<?= __("Upload"); ?>" />
434
435
		</p>
	</fieldset>
436
</form>
437
</div>
438
<?php
Callan Barrett's avatar
Callan Barrett committed
439
		else:
eric's avatar
eric committed
440
			print __("Sorry, uploads are not permitted by this server.");
Callan Barrett's avatar
Callan Barrett committed
441
442
443
?>

<br />
444
</div>
Callan Barrett's avatar
Callan Barrett committed
445
446
447
448
449
<?php
		endif;
	endif;
else:
	# Visitor is not logged in
450
	html_header("Submit");
eric's avatar
eric committed
451
	print __("You must create an account before you can upload packages.");
Callan Barrett's avatar
Callan Barrett committed
452
?>
453

Callan Barrett's avatar
Callan Barrett committed
454
<br />
455

Callan Barrett's avatar
Callan Barrett committed
456
457
<?php
endif;
458
459
?>

460

461
462

<?php
tardo's avatar
tardo committed
463
html_footer(AUR_VERSION);
464