po/POTFILES: Fix library file paths
[aur.git] / web / html / pkgsubmit.php
blob64281c7a611186485ea681cf52291a4bbd108279
1 <?php
3 set_include_path(get_include_path() . PATH_SEPARATOR . '../lib');
4 include_once("config.inc.php");
6 require_once('Archive/Tar.php');
8 include_once("aur.inc.php"); # access AUR common functions
9 include_once("pkgfuncs.inc.php"); # package functions
11 set_lang(); # this sets up the visitor's language
12 check_sid(); # see if they're still logged in
14 $cwd = getcwd();
16 if ($_COOKIE["AURSID"]) {
17 $uid = uid_from_sid($_COOKIE['AURSID']);
19 else {
20 $uid = NULL;
23 if ($uid):
25 # Track upload errors
26 $error = "";
28 if (isset($_REQUEST['pkgsubmit'])) {
30 # Before processing, make sure we even have a file
31 if ($_FILES['pfile']['size'] == 0){
32 $error = __("Error - No file uploaded");
35 # Check whether the file is gzip'ed
36 if (!$error) {
37 $fh = fopen($_FILES['pfile']['tmp_name'], 'rb');
38 fseek($fh, 0, SEEK_SET);
39 list(, $magic) = unpack('v', fread($fh, 2));
41 if ($magic != 0x8b1f) {
42 $error = __("Error - unsupported file format (please submit gzip'ed tarballs generated by makepkg(8) only).");
46 # Check uncompressed file size (ZIP bomb protection)
47 if (!$error && $MAX_FILESIZE_UNCOMPRESSED) {
48 fseek($fh, -4, SEEK_END);
49 list(, $filesize_uncompressed) = unpack('V', fread($fh, 4));
51 if ($filesize_uncompressed > $MAX_FILESIZE_UNCOMPRESSED) {
52 $error = __("Error - uncompressed file size too large.");
56 # Close file handle before extracting stuff
57 if (isset($fh) && is_resource($fh)) {
58 fclose($fh);
61 if (!$error) {
62 $tar = new Archive_Tar($_FILES['pfile']['tmp_name']);
64 # Extract PKGBUILD into a string
65 $pkgbuild_raw = '';
66 $dircount = 0;
67 foreach ($tar->listContent() as $tar_file) {
68 if (preg_match('/^[^\/]+\/PKGBUILD$/', $tar_file['filename'])) {
69 $pkgbuild_raw = $tar->extractInString($tar_file['filename']);
71 elseif (preg_match('/^[^\/]+\/$/', $tar_file['filename'])) {
72 if (++$dircount > 1) {
73 $error = __("Error - source tarball may not contain more than one directory.");
74 break;
77 elseif (preg_match('/^[^\/]+$/', $tar_file['filename'])) {
78 $error = __("Error - source tarball may not contain files outside a directory.");
79 break;
81 elseif (preg_match('/^[^\/]+\/[^\/]+\//', $tar_file['filename'])) {
82 $error = __("Error - source tarball may not contain nested subdirectories.");
83 break;
87 if (!$error && empty($pkgbuild_raw)) {
88 $error = __("Error trying to unpack upload - PKGBUILD does not exist.");
92 # if no error, get list of directory contents and process PKGBUILD
93 # TODO: This needs to be completely rewritten to support stuff like arrays
94 # and variable substitution among other things.
95 if (!$error) {
96 # process PKGBUILD - remove line concatenation
98 $pkgbuild = array();
99 $line_no = 0;
100 $lines = array();
101 $continuation_line = 0;
102 $current_line = "";
103 $paren_depth = 0;
104 foreach (explode("\n", $pkgbuild_raw) as $line) {
105 $line = trim($line);
106 # Remove comments
107 $line = preg_replace('/\s*#.*/', '', $line);
109 $char_counts = count_chars($line, 0);
110 $paren_depth += $char_counts[ord('(')] - $char_counts[ord(')')];
111 if (substr($line, strlen($line)-1) == "\\") {
112 # continue appending onto existing line_no
114 $current_line .= substr($line, 0, strlen($line)-1);
115 $continuation_line = 1;
116 } elseif ($paren_depth > 0) {
117 # assumed continuation
118 # continue appending onto existing line_no
120 $current_line .= $line . " ";
121 $continuation_line = 1;
122 } else {
123 # maybe the last line in a continuation, or a standalone line?
125 if ($continuation_line) {
126 # append onto existing line_no
128 $current_line .= $line;
129 $lines[$line_no] = $current_line;
130 $current_line = "";
131 } else {
132 # it's own line_no
134 $lines[$line_no] = $line;
136 $continuation_line = 0;
137 $line_no++;
141 # Now process the lines and put any var=val lines into the
142 # 'pkgbuild' array.
143 while (list($k, $line) = each($lines)) {
144 # Neutralize parameter substitution
145 $line = preg_replace('/\${(\w+)#(\w*)}?/', '$1$2', $line);
147 $lparts = Array();
148 # Match variable assignment only.
149 if (preg_match('/^\s*[_\w]+=[^=].*/', $line, $matches)) {
150 $lparts = explode("=", $matches[0], 2);
153 if (!empty($lparts)) {
154 # this is a variable/value pair, strip out
155 # array parens and any quoting, except in pkgdesc
156 # for pkgdesc, only remove start/end pairs of " or '
157 if ($lparts[0]=="pkgdesc") {
158 if ($lparts[1]{0} == '"' &&
159 $lparts[1]{strlen($lparts[1])-1} == '"') {
160 $pkgbuild[$lparts[0]] = substr($lparts[1], 1, -1);
162 elseif
163 ($lparts[1]{0} == "'" &&
164 $lparts[1]{strlen($lparts[1])-1} == "'") {
165 $pkgbuild[$lparts[0]] = substr($lparts[1], 1, -1);
166 } else {
167 $pkgbuild[$lparts[0]] = $lparts[1];
169 } else {
170 $pkgbuild[$lparts[0]] = str_replace(array("(",")","\"","'"), "",
171 $lparts[1]);
176 # some error checking on PKGBUILD contents - just make sure each
177 # variable has a value. This does not do any validity checking
178 # on the values, or attempts to fix line continuation/wrapping.
179 $req_vars = array("url", "pkgdesc", "license", "pkgrel", "pkgver", "arch", "pkgname");
180 foreach ($req_vars as $var) {
181 if (!array_key_exists($var, $pkgbuild)) {
182 $error = __('Missing %s variable in PKGBUILD.', $var);
183 break;
188 # TODO This is where other additional error checking can be
189 # performed. Examples: #md5sums == #sources?, md5sums of any
190 # included files match?, install scriptlet file exists?
193 # Check for http:// or other protocol in url
195 if (!$error) {
196 $parsed_url = parse_url($pkgbuild['url']);
197 if (!$parsed_url['scheme']) {
198 $error = __("Package URL is missing a protocol (ie. http:// ,ftp://)");
202 # Now, run through the pkgbuild array, and do "eval" and simple substituions.
203 if (!$error) {
204 while (list($k, $v) = each($pkgbuild)) {
205 if (strpos($k,'eval ') !== false) {
206 $k = preg_replace('/^eval[\s]*/', "", $k);
207 ##"eval" replacements
208 $pattern_eval = '/{\$({?)([\w]+)(}?)}/';
209 while (preg_match($pattern_eval,$v,$regs)) {
210 $pieces = explode(",",$pkgbuild["$regs[2]"]);
211 ## nongreedy matching! - preserving the order of "eval"
212 $pattern = '/([\S]*?){\$'.$regs[1].$regs[2].$regs[3].'}([\S]*)/';
213 while (preg_match($pattern,$v,$regs_replace)) {
214 $replacement = "";
215 for ($i = 0; $i < sizeof($pieces); $i++) {
216 $replacement .= $regs_replace[1].$pieces[$i].$regs_replace[2]." ";
218 $v=preg_replace($pattern, $replacement, $v, 1);
223 # Simple variable replacement
224 $pattern_var = '/\$({?)([_\w]+)(}?)/';
225 $offset = 0;
226 while (preg_match($pattern_var, $v, $regs, PREG_OFFSET_CAPTURE, $offset)) {
227 $var = $regs[2][0];
228 $pos = $regs[0][1];
229 $len = strlen($regs[0][0]);
231 if (isset($new_pkgbuild[$var])) {
232 $replacement = substr($new_pkgbuild[$var], strpos($new_pkgbuild[$var], " "));
234 else {
235 $replacement = '';
238 $v = substr_replace($v, $replacement, $pos, $len);
239 $offset = $pos + strlen($replacement);
241 $new_pkgbuild[$k] = $v;
245 # Now we've parsed the pkgbuild, let's move it to where it belongs
246 if (!$error) {
247 $pkg_name = str_replace("'", "", $new_pkgbuild['pkgname']);
248 $pkg_name = escapeshellarg($pkg_name);
249 $pkg_name = str_replace("'", "", $pkg_name);
251 $presult = preg_match("/^[a-z0-9][a-z0-9\.+_-]*$/", $pkg_name);
253 if (!$presult) {
254 $error = __("Invalid name: only lowercase letters are allowed.");
258 if (isset($pkg_name)) {
259 $incoming_pkgdir = INCOMING_DIR . substr($pkg_name, 0, 2) . "/" . $pkg_name;
262 if (!$error) {
263 # First, see if this package already exists, and if it can be overwritten
264 $pkg_id = pkgid_from_name($pkg_name);
265 if (can_submit_pkg($pkg_name, $_COOKIE["AURSID"])) {
266 if (file_exists($incoming_pkgdir)) {
267 # Blow away the existing file/dir and contents
268 rm_tree($incoming_pkgdir);
271 # The mode is masked by the current umask, so not as scary as it looks
272 if (!mkdir($incoming_pkgdir, 0777, true)) {
273 $error = __( "Could not create directory %s.", $incoming_pkgdir);
275 } else {
276 $error = __( "You are not allowed to overwrite the %h%s%h package.", "<b>", $pkg_name, "</b>");
279 if (!$error) {
280 # Check if package name is blacklisted.
281 if (!$pkg_id && pkgname_is_blacklisted($pkg_name)) {
282 if (!canSubmitBlacklisted(account_from_sid($_COOKIE["AURSID"]))) {
283 $error = __( "%s is on the package blacklist, please check if it's available in the official repos.", $pkg_name);
289 if (!$error) {
290 if (!chdir($incoming_pkgdir)) {
291 $error = __("Could not change directory to %s.", $incoming_pkgdir);
294 file_put_contents('PKGBUILD', $pkgbuild_raw);
295 move_uploaded_file($_FILES['pfile']['tmp_name'], $pkg_name . '.tar.gz');
298 # Update the backend database
299 if (!$error) {
301 $dbh = db_connect();
302 db_query("BEGIN", $dbh);
304 $q = "SELECT * FROM Packages WHERE Name = '" . mysql_real_escape_string($new_pkgbuild['pkgname']) . "'";
305 $result = db_query($q, $dbh);
306 $pdata = mysql_fetch_assoc($result);
308 if (isset($new_pkgbuild['epoch']) && (int)$new_pkgbuild['epoch'] > 0) {
309 $pkg_version = sprintf('%d:%s-%s', $new_pkgbuild['epoch'], $new_pkgbuild['pkgver'], $new_pkgbuild['pkgrel']);
310 } else {
311 $pkg_version = sprintf('%s-%s', $new_pkgbuild['pkgver'], $new_pkgbuild['pkgrel']);
314 if ($pdata) {
315 # This is an overwrite of an existing package, the database ID
316 # needs to be preserved so that any votes are retained. However,
317 # PackageDepends and PackageSources can be purged.
318 $packageID = $pdata["ID"];
320 # Flush out old data that will be replaced with new data
321 $q = "DELETE FROM PackageDepends WHERE PackageID = " . $packageID;
322 db_query($q, $dbh);
323 $q = "DELETE FROM PackageSources WHERE PackageID = " . $packageID;
324 db_query($q, $dbh);
326 # If a new category was chosen, change it to that
327 if ($_POST['category'] > 1) {
328 $q = sprintf( "UPDATE Packages SET CategoryID = %d WHERE ID = %d",
329 mysql_real_escape_string($_REQUEST['category']),
330 $packageID);
332 db_query($q, $dbh);
335 # Update package data
336 $q = sprintf("UPDATE Packages SET ModifiedTS = UNIX_TIMESTAMP(), Name = '%s', Version = '%s', License = '%s', Description = '%s', URL = '%s', OutOfDateTS = NULL, MaintainerUID = %d WHERE ID = %d",
337 mysql_real_escape_string($new_pkgbuild['pkgname']),
338 mysql_real_escape_string($pkg_version),
339 mysql_real_escape_string($new_pkgbuild['license']),
340 mysql_real_escape_string($new_pkgbuild['pkgdesc']),
341 mysql_real_escape_string($new_pkgbuild['url']),
342 $uid,
343 $packageID);
345 db_query($q, $dbh);
347 } else {
348 # This is a brand new package
349 $q = sprintf("INSERT INTO Packages (Name, License, Version, CategoryID, Description, URL, SubmittedTS, ModifiedTS, SubmitterUID, MaintainerUID) VALUES ('%s', '%s', '%s', %d, '%s', '%s', UNIX_TIMESTAMP(), UNIX_TIMESTAMP(), %d, %d)",
350 mysql_real_escape_string($new_pkgbuild['pkgname']),
351 mysql_real_escape_string($new_pkgbuild['license']),
352 mysql_real_escape_string($pkg_version),
353 mysql_real_escape_string($_REQUEST['category']),
354 mysql_real_escape_string($new_pkgbuild['pkgdesc']),
355 mysql_real_escape_string($new_pkgbuild['url']),
356 $uid,
357 $uid);
359 db_query($q, $dbh);
360 $packageID = mysql_insert_id($dbh);
364 # Update package depends
365 $depends = explode(" ", $new_pkgbuild['depends']);
366 if ($depends !== false) {
367 foreach ($depends as $dep) {
368 $deppkgname = preg_replace("/(<|<=|=|>=|>).*/", "", $dep);
369 $depcondition = str_replace($deppkgname, "", $dep);
371 if ($deppkgname == "") {
372 continue;
374 else if ($deppkgname == "#") {
375 break;
378 $q = sprintf("INSERT INTO PackageDepends (PackageID, DepName, DepCondition) VALUES (%d, '%s', '%s')",
379 $packageID,
380 mysql_real_escape_string($deppkgname),
381 mysql_real_escape_string($depcondition));
383 db_query($q, $dbh);
387 # Insert sources
388 $sources = explode(" ", $new_pkgbuild['source']);
389 foreach ($sources as $src) {
390 if ($src != "" ) {
391 $q = "INSERT INTO PackageSources (PackageID, Source) VALUES (";
392 $q .= $packageID . ", '" . mysql_real_escape_string($src) . "')";
393 db_query($q, $dbh);
397 # If we just created this package, or it was an orphan and we
398 # auto-adopted, add submitting user to the notification list.
399 if (!$pdata || $pdata["MaintainerUID"] === NULL) {
400 pkg_notify(account_from_sid($_COOKIE["AURSID"], $dbh), array($packageID), true, $dbh);
403 # Entire package creation process is atomic
404 db_query("COMMIT", $dbh);
406 header('Location: packages.php?ID=' . $packageID);
409 chdir($cwd);
412 # Logic over, let's do some output
414 html_header("Submit");
418 <?php if ($error): ?>
419 <p class="pkgoutput"><?php print $error ?></p>
420 <?php endif; ?>
422 <div class="pgbox">
423 <div class="pgboxtitle">
424 <span class="f3"><?php print __("Submit"); ?></span>
425 </div>
426 <div class="pgboxbody">
427 <p><?php echo __("Upload your source packages here. Create source packages with `makepkg --source`.") ?></p>
429 <?php
430 if (empty($_REQUEST['pkgsubmit']) || $error):
431 # User is not uploading, or there were errors uploading - then
432 # give the visitor the default upload form
433 if (ini_get("file_uploads")):
435 $pkg_categories = pkgCategories();
438 <form action='pkgsubmit.php' method='post' enctype='multipart/form-data'>
439 <div> <input type='hidden' name='pkgsubmit' value='1' /> </div>
440 <table border='0' cellspacing='5'>
441 <tr>
442 <td class='f4' align='right'><?php print __("Package Category"); ?>:</td>
443 <td class='f4' align='left'>
444 <select name='category'>
445 <option value='1'><?php print __("Select Category"); ?></option>
446 <?php
447 foreach ($pkg_categories as $num => $cat):
448 print "<option value='" . $num . "'";
449 if (isset($_POST['category']) && $_POST['category'] == $cat):
450 print " selected='selected'";
451 endif;
452 print ">" . $cat . "</option>";
453 endforeach;
455 </select>
456 </td>
457 </tr>
458 <tr>
459 <td class='f4' align='right'><?php print __("Upload package file"); ?>:</td>
460 <td class='f4' align='left'>
461 <input type='file' name='pfile' size='30' />
462 </td>
463 </tr>
464 <tr>
465 <td align='left'>
466 <input class='button' type='submit' value='<?php print __("Upload"); ?>' />
467 </td>
468 </tr>
469 </table>
470 </form>
472 <?php
473 else:
474 print __("Sorry, uploads are not permitted by this server.");
477 <br />
479 <?php
480 endif;
481 endif;
482 else:
483 # Visitor is not logged in
484 print __("You must create an account before you can upload packages.");
485 exit();
488 <br />
490 <?php
491 endif;
494 </div>
495 </div>
497 <?php
498 html_footer(AUR_VERSION);