pkgfuncs.inc.php 46.2 KB
Newer Older
1
<?php
2
include_once("config.inc.php");
eric's avatar
eric committed
3

4
5
6
7
8
9
10
11
12
13
14
15
/**
 * Determine if the user can delete a specific package comment
 *
 * Only the comment submitter, Trusted Users, and Developers can delete
 * comments. This function is used for the backend side of comment deletion.
 *
 * @param string $comment_id The comment ID in the database
 * @param string $atype The account type of the user trying to delete a comment
 * @param string|int $uid The user ID of the individual trying to delete a comment
 *
 * @return bool True if the user can delete the comment, otherwise false
 */
16
function canDeleteComment($comment_id=0, $atype="", $uid=0) {
17
18
19
20
	if (!$uid) {
		/* Unauthenticated users cannot delete anything. */
		return false;
	}
21
	if ($atype == "Trusted User" || $atype == "Developer") {
22
		/* TUs and developers can delete any comment. */
23
		return true;
24
	}
Lukas Fleischer's avatar
Lukas Fleischer committed
25

26
	$dbh = DB::connect();
Lukas Fleischer's avatar
Lukas Fleischer committed
27
28
29

	$q = "SELECT COUNT(*) FROM PackageComments ";
	$q.= "WHERE ID = " . intval($comment_id) . " AND UsersID = " . $uid;
canyonknight's avatar
canyonknight committed
30
	$result = $dbh->query($q);
Lukas Fleischer's avatar
Lukas Fleischer committed
31
32
33

	if (!$result) {
		return false;
34
	}
Lukas Fleischer's avatar
Lukas Fleischer committed
35
36
37

	$row = $result->fetch(PDO::FETCH_NUM);
	return ($row[0] > 0);
38
}
eric's avatar
eric committed
39

40
41
42
43
44
45
46
47
48
49
50
51
/**
 * Determine if the user can delete a specific package comment using an array
 *
 * Only the comment submitter, Trusted Users, and Developers can delete
 * comments. This function is used for the frontend side of comment deletion.
 *
 * @param array $comment All database information relating a specific comment
 * @param string $atype The account type of the user trying to delete a comment
 * @param string|int $uid The user ID of the individual trying to delete a comment
 *
 * @return bool True if the user can delete the comment, otherwise false
 */
52
function canDeleteCommentArray($comment, $atype="", $uid=0) {
53
54
55
56
	if (!$uid) {
		/* Unauthenticated users cannot delete anything. */
		return false;
	} elseif ($atype == "Trusted User" || $atype == "Developer") {
57
		/* TUs and developers can delete any comment. */
58
		return true;
59
	} else if ($comment['UsersID'] == $uid) {
60
		/* Users can delete their own comments. */
61
		return true;
62
	}
63
	return false;
64
65
}

66
67
68
69
70
71
72
73
74
75
/**
 * Determine if the visitor can submit blacklisted packages.
 *
 * Only Trusted Users and Developers can delete blacklisted packages. Packages
 * are blacklisted if they are include in the official repositories.
 *
 * @param string $atype The account type of the user
 *
 * @return bool True if the user can submit blacklisted packages, otherwise false
 */
76
77
function canSubmitBlacklisted($atype = "") {
	if ($atype == "Trusted User" || $atype == "Developer") {
78
		/* Only TUs and developers can submit blacklisted packages. */
79
		return true;
80
81
	}
	else {
82
		return false;
83
84
85
	}
}

86
87
88
89
90
91
92
/**
 * Get all package categories stored in the database
 *
 * @param \PDO An already established database connection
 *
 * @return array All package categories
 */
93
function pkgCategories() {
eric's avatar
eric committed
94
	$cats = array();
95
	$dbh = DB::connect();
96
	$q = "SELECT * FROM PackageCategories WHERE ID != 1 ";
eric's avatar
eric committed
97
	$q.= "ORDER BY Category ASC";
canyonknight's avatar
canyonknight committed
98
	$result = $dbh->query($q);
eric's avatar
eric committed
99
	if ($result) {
canyonknight's avatar
canyonknight committed
100
		while ($row = $result->fetch(PDO::FETCH_NUM)) {
eric's avatar
eric committed
101
102
103
104
105
106
			$cats[$row[0]] = $row[1];
		}
	}
	return $cats;
}

107
108
109
110
111
112
113
/**
 * Check to see if the package name already exists in the database
 *
 * @param string $name The package name to check
 *
 * @return string|void Package name if it already exists
 */
114
function pkgid_from_name($name="") {
115
	if (!$name) {return NULL;}
116
	$dbh = DB::connect();
117
	$q = "SELECT ID FROM Packages ";
canyonknight's avatar
canyonknight committed
118
119
120
121
122
123
	$q.= "WHERE Name = " . $dbh->quote($name);
	$result = $dbh->query($q);
	if (!$result) {
		return;
	}
	$row = $result->fetch(PDO::FETCH_NUM);
124
125
126
	return $row[0];
}

127
128
129
130
131
132
133
/**
 * Get package dependencies for a specific package
 *
 * @param int $pkgid The package to get dependencies for
 *
 * @return array All package dependencies for the package
 */
134
function package_dependencies($pkgid) {
135
	$deps = array();
136
137
	$pkgid = intval($pkgid);
	if ($pkgid > 0) {
138
		$dbh = DB::connect();
Dan McGee's avatar
Dan McGee committed
139
140
141
142
		$q = "SELECT pd.DepName, pd.DepCondition, p.ID FROM PackageDepends pd ";
		$q.= "LEFT JOIN Packages p ON pd.DepName = p.Name ";
		$q.= "WHERE pd.PackageID = ". $pkgid . " ";
		$q.= "ORDER BY pd.DepName";
canyonknight's avatar
canyonknight committed
143
144
145
146
147
		$result = $dbh->query($q);
		if (!$result) {
			return array();
		}
		while ($row = $result->fetch(PDO::FETCH_NUM)) {
148
149
150
151
152
153
			$deps[] = $row;
		}
	}
	return $deps;
}

154
155
156
157
158
159
160
/**
 * Determine packages that depend on a package
 *
 * @param string $name The package name for the dependency search
 *
 * @return array All packages that depend on the specified package name
 */
161
function package_required($name="") {
162
	$deps = array();
Dan McGee's avatar
Dan McGee committed
163
	if ($name != "") {
164
		$dbh = DB::connect();
165
		$q = "SELECT DISTINCT p.Name, PackageID FROM PackageDepends pd ";
Dan McGee's avatar
Dan McGee committed
166
		$q.= "JOIN Packages p ON pd.PackageID = p.ID ";
canyonknight's avatar
canyonknight committed
167
		$q.= "WHERE DepName = " . $dbh->quote($name) . " ";
Dan McGee's avatar
Dan McGee committed
168
		$q.= "ORDER BY p.Name";
canyonknight's avatar
canyonknight committed
169
		$result = $dbh->query($q);
170
		if (!$result) {return array();}
canyonknight's avatar
canyonknight committed
171
		while ($row = $result->fetch(PDO::FETCH_NUM)) {
172
173
174
175
176
177
			$deps[] = $row;
		}
	}
	return $deps;
}

178
/**
179
 * Get the number of non-deleted comments for a specific package base
180
 *
181
 * @param string $pkgid The package base ID to get comment count for
182
183
184
 *
 * @return string The number of comments left for a specific package
 */
185
function package_comments_count($base_id) {
186
	$dbh = DB::connect();
canyonknight's avatar
canyonknight committed
187

188
189
	$base_id = intval($base_id);
	if ($base_id > 0) {
190
		$dbh = DB::connect();
191
		$q = "SELECT COUNT(*) FROM PackageComments ";
192
		$q.= "WHERE PackageBaseID = " . $base_id;
193
		$q.= " AND DelUsersID IS NULL";
194
	}
canyonknight's avatar
canyonknight committed
195
	$result = $dbh->query($q);
196
197
198
199
200

	if (!$result) {
		return;
	}

canyonknight's avatar
canyonknight committed
201
202
	$row = $result->fetch(PDO::FETCH_NUM);
	return $row[0];
203
204
}

205
/**
206
 * Get all package comment information for a specific package base
207
 *
208
 * @param int $pkgid The package base ID to get comments for
209
 *
210
 * @return array All package comment information for a specific package base
211
 */
212
213
function package_comments($base_id) {
	$base_id = intval($base_id);
eric's avatar
eric committed
214
	$comments = array();
215
	if ($base_id > 0) {
216
		$dbh = DB::connect();
217
		$q = "SELECT PackageComments.ID, UserName, UsersID, Comments, CommentTS ";
218
219
		$q.= "FROM PackageComments LEFT JOIN Users ";
		$q.= "ON PackageComments.UsersID = Users.ID ";
220
		$q.= "WHERE PackageBaseID = " . $base_id . " ";
221
		$q.= "AND DelUsersID IS NULL ";
222
		$q.= "ORDER BY CommentTS DESC";
223
224
225
226
227

		if (!isset($_GET['comments'])) {
			$q.= " LIMIT 10";
		}

canyonknight's avatar
canyonknight committed
228
		$result = $dbh->query($q);
Loui Chang's avatar
Loui Chang committed
229
230
231
232
233

		if (!$result) {
			return;
		}

canyonknight's avatar
canyonknight committed
234
		while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
eric's avatar
eric committed
235
236
237
238
239
240
			$comments[] = $row;
		}
	}
	return $comments;
}

241
242
243
244
/**
 * Add a comment to a package page and send out appropriate notifications
 *
 * @global string $AUR_LOCATION The AUR's URL used for notification e-mails
245
 * @param string $base_id The package base ID to add the comment on
246
247
248
249
250
 * @param string $uid The user ID of the individual who left the comment
 * @param string $comment The comment left on a package page
 *
 * @return void
 */
251
function add_package_comment($base_id, $uid, $comment) {
252
253
	global $AUR_LOCATION;

254
	$dbh = DB::connect();
255

canyonknight's avatar
canyonknight committed
256
	$q = "INSERT INTO PackageComments ";
257
258
	$q.= "(PackageBaseID, UsersID, Comments, CommentTS) VALUES (";
	$q.= intval($base_id) . ", " . $uid . ", ";
canyonknight's avatar
canyonknight committed
259
260
	$q.= $dbh->quote($comment) . ", UNIX_TIMESTAMP())";
	$dbh->exec($q);
261

262
263
264
265
	/*
	 * Send e-mail notifications.
	 * TODO: Move notification logic to separate function where it belongs.
	 */
canyonknight's avatar
canyonknight committed
266
267
268
269
	$q = "SELECT CommentNotify.*, Users.Email ";
	$q.= "FROM CommentNotify, Users ";
	$q.= "WHERE Users.ID = CommentNotify.UserID ";
	$q.= "AND CommentNotify.UserID != " . $uid . " ";
270
	$q.= "AND CommentNotify.PackageBaseID = " . intval($base_id);
canyonknight's avatar
canyonknight committed
271
	$result = $dbh->query($q);
272
273
	$bcc = array();

canyonknight's avatar
canyonknight committed
274
275
	if ($result) {
		while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
276
277
278
			array_push($bcc, $row['Email']);
		}

279
280
		$q = "SELECT Name FROM PackageBases WHERE ID = ";
		$q.= intval($base_id);
canyonknight's avatar
canyonknight committed
281
282
		$result = $dbh->query($q);
		$row = $result->fetch(PDO::FETCH_ASSOC);
283

284
285
286
287
288
289
		/*
		 * TODO: Add native language emails for users, based on their
		 * preferences. Simply making these strings translatable won't
		 * work, users would be getting emails in the language that the
		 * user who posted the comment was in.
		 */
290
		$body =
291
		'from ' . $AUR_LOCATION . get_pkg_uri($row['Name']) . "\n"
292
		. username_from_sid($_COOKIE['AURSID']) . " wrote:\n\n"
293
294
295
296
		. $comment
		. "\n\n---\nIf you no longer wish to receive notifications about this package, please go the the above package page and click the UnNotify button.";
		$body = wordwrap($body, 70);
		$bcc = implode(', ', $bcc);
297
298
299
		$headers = "MIME-Version: 1.0\r\n" .
			   "Content-type: text/plain; charset=UTF-8\r\n" .
			   "Bcc: $bcc\r\n" .
300
301
302
			   "Reply-to: nobody@archlinux.org\r\n" .
			   "From: aur-notify@archlinux.org\r\n" .
			   "X-Mailer: AUR";
303
304
305
306
		@mail('undisclosed-recipients: ;', "AUR Comment for " . $row['Name'], $body, $headers);
	}
}

307
308
309
310
311
312
313
/**
 * Get all package sources for a specific package
 *
 * @param string $pkgid The package ID to get the sources for
 *
 * @return array All sources associated with a specific package
 */
314
function package_sources($pkgid) {
315
	$sources = array();
316
317
	$pkgid = intval($pkgid);
	if ($pkgid > 0) {
318
		$dbh = DB::connect();
319
		$q = "SELECT Source FROM PackageSources ";
320
		$q.= "WHERE PackageID = " . $pkgid;
321
		$q.= " ORDER BY Source";
canyonknight's avatar
canyonknight committed
322
323
324
325
326
		$result = $dbh->query($q);
		if (!$result) {
			return array();
		}
		while ($row = $result->fetch(PDO::FETCH_NUM)) {
327
328
329
330
331
332
			$sources[] = $row[0];
		}
	}
	return $sources;
}

333
334
335
336
337
338
339
/**
 * Get a list of all packages a logged-in user has voted for
 *
 * @param string $sid The session ID of the visitor
 *
 * @return array All packages the visitor has voted for
 */
340
function pkgvotes_from_sid($sid="") {
341
342
	$pkgs = array();
	if (!$sid) {return $pkgs;}
343
	$dbh = DB::connect();
344
	$q = "SELECT PackageBaseID ";
345
346
347
	$q.= "FROM PackageVotes, Users, Sessions ";
	$q.= "WHERE Users.ID = Sessions.UsersID ";
	$q.= "AND Users.ID = PackageVotes.UsersID ";
canyonknight's avatar
canyonknight committed
348
349
	$q.= "AND Sessions.SessionID = " . $dbh->quote($sid);
	$result = $dbh->query($q);
350
	if ($result) {
canyonknight's avatar
canyonknight committed
351
		while ($row = $result->fetch(PDO::FETCH_NUM)) {
352
353
354
355
356
357
			$pkgs[$row[0]] = 1;
		}
	}
	return $pkgs;
}

358
359
360
361
362
363
364
/**
 * Determine package names from package IDs
 *
 * @param string|array $pkgids The package IDs to get names for
 *
 * @return array|string All names if multiple package IDs, otherwise package name
 */
365
function pkgname_from_id($pkgids) {
366
367
368
	if (is_array($pkgids)) {
		$pkgids = sanitize_ids($pkgids);
		$names = array();
369
		$dbh = DB::connect();
canyonknight's avatar
canyonknight committed
370
371
372
373
374
		$q = "SELECT Name FROM Packages WHERE ID IN (";
		$q.= implode(",", $pkgids) . ")";
		$result = $dbh->query($q);
		if ($result) {
			while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
375
376
377
378
379
380
				$names[] = $row['Name'];
			}
		}
		return $names;
	}
	elseif ($pkgids > 0) {
381
		$dbh = DB::connect();
382
		$q = "SELECT Name FROM Packages WHERE ID = " . $pkgids;
canyonknight's avatar
canyonknight committed
383
384
385
		$result = $dbh->query($q);
		if ($result) {
			$name = $result->fetch(PDO::FETCH_NUM);
386
		}
canyonknight's avatar
canyonknight committed
387
		return $name[0];
388
389
390
	}
	else {
		return NULL;
391
392
393
	}
}

394
395
396
397
398
399
400
/**
 * Determine if a package name is on the database blacklist
 *
 * @param string $name The package name to check
 *
 * @return bool True if the name is blacklisted, otherwise false
 */
401
function pkgname_is_blacklisted($name) {
402
	$dbh = DB::connect();
canyonknight's avatar
canyonknight committed
403
404
405
	$q = "SELECT COUNT(*) FROM PackageBlacklist ";
	$q.= "WHERE Name = " . $dbh->quote($name);
	$result = $dbh->query($q);
406
407

	if (!$result) return false;
408
	return ($result->fetchColumn() > 0);
409
410
}

411
/**
412
 * Get the package details
413
 *
414
 * @param string $id The package ID to get description for
415
 *
416
417
 * @return array The package's details OR error message
 **/
418
function get_package_details($id=0) {
419
	$dbh = DB::connect();
420

421
422
423
424
425
426
427
428
	$q = "SELECT Packages.*, PackageBases.Name AS BaseName, ";
	$q.= "PackageBases.CategoryID, PackageBases.NumVotes, ";
	$q.= "PackageBases.OutOfDateTS, PackageBases.SubmittedTS, ";
	$q.= "PackageBases.ModifiedTS, PackageBases.SubmitterUID, ";
	$q.= "PackageBases.MaintainerUID, PackageCategories.Category ";
	$q.= "FROM Packages, PackageBases, PackageCategories ";
	$q.= "WHERE PackageBases.ID = Packages.PackageBaseID ";
	$q.= "AND PackageBases.CategoryID = PackageCategories.ID ";
Loui Chang's avatar
Loui Chang committed
429
	$q.= "AND Packages.ID = " . intval($id);
canyonknight's avatar
canyonknight committed
430
	$result = $dbh->query($q);
eric's avatar
eric committed
431

432
433
	$row = array();

canyonknight's avatar
canyonknight committed
434
	if (!$result) {
435
		$row['error'] = __("Error retrieving package details.");
436
437
	}
	else {
canyonknight's avatar
canyonknight committed
438
		$row = $result->fetch(PDO::FETCH_ASSOC);
eric's avatar
eric committed
439
		if (empty($row)) {
440
			$row['error'] = __("Package details could not be found.");
441
		}
442
443
444
445
446
	}

	return $row;
}

447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
/**
 * Get the package base details
 *
 * @param string $id The package base ID to get description for
 *
 * @return array The package base's details OR error message
 **/
function get_pkgbase_details($base_id) {
	$dbh = DB::connect();

	$q = "SELECT PackageBases.ID, PackageBases.Name, ";
	$q.= "PackageBases.CategoryID, PackageBases.NumVotes, ";
	$q.= "PackageBases.OutOfDateTS, PackageBases.SubmittedTS, ";
	$q.= "PackageBases.ModifiedTS, PackageBases.SubmitterUID, ";
	$q.= "PackageBases.MaintainerUID, PackageCategories.Category ";
	$q.= "FROM PackageBases, PackageCategories ";
	$q.= "WHERE PackageBases.CategoryID = PackageCategories.ID ";
	$q.= "AND PackageBases.ID = " . intval($base_id);
	$result = $dbh->query($q);

	$row = array();

	if (!$result) {
		$row['error'] = __("Error retrieving package details.");
	}
	else {
		$row = $result->fetch(PDO::FETCH_ASSOC);
		if (empty($row)) {
			$row['error'] = __("Package details could not be found.");
		}
	}

	return $row;
}

482
483
484
485
486
487
488
489
490
491
492
/**
 * Display the package details page
 *
 * @global string $AUR_LOCATION The AUR's URL used for notification e-mails
 * @global bool $USE_VIRTUAL_URLS True if using URL rewriting, otherwise false
 * @param string $id The package ID to get details page for
 * @param array $row Package details retrieved by get_package_details
 * @param string $SID The session ID of the visitor
 *
 * @return void
 */
493
function display_package_details($id=0, $row, $SID="") {
494
495
496
	global $AUR_LOCATION;
	global $USE_VIRTUAL_URLS;

497
	$dbh = DB::connect();
498

499
500
501
502
503
504
505
506
507
508
509
	if (isset($row['error'])) {
		print "<p>" . $row['error'] . "</p>\n";
	}
	else {
		include('pkg_details.php');

		if ($SID) {
			include('actions_form.php');
			include('pkg_comment_form.php');
		}

510
511
		$base_id = pkgbase_from_pkgid($id);
		$comments = package_comments($base_id);
512
513
		if (!empty($comments)) {
			include('pkg_comments.php');
eric's avatar
eric committed
514
515
516
517
		}
	}
}

518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
/**
 * Display the package base details page
 *
 * @global string $AUR_LOCATION The AUR's URL used for notification e-mails
 * @global bool $USE_VIRTUAL_URLS True if using URL rewriting, otherwise false
 * @param string $id The package base ID to get details page for
 * @param array $row Package base details retrieved by get_pkgbase_details
 * @param string $SID The session ID of the visitor
 *
 * @return void
 */
function display_pkgbase_details($base_id, $row, $SID="") {
	global $AUR_LOCATION;
	global $USE_VIRTUAL_URLS;

	$dbh = DB::connect();

	if (isset($row['error'])) {
		print "<p>" . $row['error'] . "</p>\n";
	}
	else {
		include('pkgbase_details.php');

		if ($SID) {
			include('actions_form.php');
			include('pkg_comment_form.php');
		}

		$comments = package_comments($base_id);
		if (!empty($comments)) {
			include('pkg_comments.php');
		}
	}
}

eric's avatar
eric committed
553

Simo Leone's avatar
Simo Leone committed
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
/* pkg_search_page(SID)
 * outputs the body of search/search results page
 *
 * parameters:
 *  SID - current Session ID
 * preconditions:
 *  package search page has been accessed
 *  request variables have not been sanitized
 *
 *  request vars:
 *    O  - starting result number
 *    PP - number of search hits per page
 *    C  - package category ID number
 *    K  - package search string
 *    SO - search hit sort order:
 *          values: a - ascending
 *                  d - descending
 *    SB - sort search hits by:
572
 *          values: c - package category
Simo Leone's avatar
Simo Leone committed
573
574
575
576
 *                  n - package name
 *                  v - number of votes
 *                  m - maintainer username
 *    SeB- property that search string (K) represents
Andrea Scarpino's avatar
Andrea Scarpino committed
577
578
 *          values: n  - package name
 *                  nd - package name & description
579
 *                  x  - package name (exact match)
Simo Leone's avatar
Simo Leone committed
580
581
582
583
584
585
586
587
588
589
590
591
592
593
 *                  m  - package maintainer's username
 *                  s  - package submitter's username
 *    do_Orphans    - boolean. whether to search packages
 *                     without a maintainer
 *
 *
 *    These two are actually handled in packages.php.
 *
 *    IDs- integer array of ticked packages' IDs
 *    action - action to be taken on ticked packages
 *             values: do_Flag   - Flag out-of-date
 *                     do_UnFlag - Remove out-of-date flag
 *                     do_Adopt  - Adopt
 *                     do_Disown - Disown
594
 *                     do_Delete - Delete (requires confirm_Delete to be set)
595
596
 *                     do_Notify - Enable notification
 *                     do_UnNotify - Disable notification
Simo Leone's avatar
Simo Leone committed
597
 */
598
function pkg_search_page($SID="") {
599
	$dbh = DB::connect();
Loui Chang's avatar
Loui Chang committed
600

601
602
603
604
	/*
	 * Get commonly used variables.
	 * TODO: Reduce the number of database queries!
	 */
Loui Chang's avatar
Loui Chang committed
605
	if ($SID)
606
		$myuid = uid_from_sid($SID);
607
	$cats = pkgCategories($dbh);
Loui Chang's avatar
Loui Chang committed
608

609
	/* Sanitize paging variables. */
Loui Chang's avatar
Loui Chang committed
610
611
612
613
614
615
616
617
618
619
620
	if (isset($_GET['O'])) {
		$_GET['O'] = intval($_GET['O']);
		if ($_GET['O'] < 0)
			$_GET['O'] = 0;
	}
	else {
		$_GET['O'] = 0;
	}

	if (isset($_GET["PP"])) {
		$_GET["PP"] = intval($_GET["PP"]);
621
622
623
624
		if ($_GET["PP"] < 50)
			$_GET["PP"] = 50;
		else if ($_GET["PP"] > 250)
			$_GET["PP"] = 250;
Loui Chang's avatar
Loui Chang committed
625
626
	}
	else {
627
		$_GET["PP"] = 50;
Loui Chang's avatar
Loui Chang committed
628
629
	}

630
631
632
633
	/*
	 * FIXME: Pull out DB-related code. All of it! This one's worth a
	 * choco-chip cookie, one of those nice big soft ones.
	 */
Loui Chang's avatar
Loui Chang committed
634

635
	/* Build the package search query. */
636
	$q_select = "SELECT ";
Loui Chang's avatar
Loui Chang committed
637
	if ($SID) {
638
		$q_select .= "CommentNotify.UserID AS Notify,
639
			   PackageVotes.UsersID AS Voted, ";
Loui Chang's avatar
Loui Chang committed
640
	}
641
	$q_select .= "Users.Username AS Maintainer,
Loui Chang's avatar
Loui Chang committed
642
	PackageCategories.Category,
643
644
	Packages.Name, Packages.Version, Packages.Description,
	PackageBases.NumVotes, Packages.ID, PackageBases.OutOfDateTS ";
Loui Chang's avatar
Loui Chang committed
645

646
	$q_from = "FROM Packages
647
648
	LEFT JOIN PackageBases ON (PackageBases.ID = Packages.PackageBaseID)
	LEFT JOIN Users ON (PackageBases.MaintainerUID = Users.ID)
649
	LEFT JOIN PackageCategories
650
	ON (PackageBases.CategoryID = PackageCategories.ID) ";
Loui Chang's avatar
Loui Chang committed
651
	if ($SID) {
652
		/* This is not needed for the total row count query. */
653
		$q_from_extra = "LEFT JOIN PackageVotes
654
		ON (PackageBases.ID = PackageVotes.PackageBaseID AND PackageVotes.UsersID = $myuid)
Loui Chang's avatar
Loui Chang committed
655
		LEFT JOIN CommentNotify
656
		ON (PackageBases.ID = CommentNotify.PackageBaseID AND CommentNotify.UserID = $myuid) ";
657
658
	} else {
		$q_from_extra = "";
Loui Chang's avatar
Loui Chang committed
659
660
	}

Dan McGee's avatar
Dan McGee committed
661
	$q_where = "WHERE 1 = 1 ";
662
663
664
665
	/*
	 * TODO: Possibly do string matching on category to make request
	 * variable values more sensible.
	 */
666
	if (isset($_GET["C"]) && intval($_GET["C"])) {
667
		$q_where .= "AND Packages.CategoryID = ".intval($_GET["C"])." ";
Loui Chang's avatar
Loui Chang committed
668
669
	}

670
671
	if (isset($_GET['K'])) {
		if (isset($_GET["SeB"]) && $_GET["SeB"] == "m") {
672
			/* Search by maintainer. */
canyonknight's avatar
canyonknight committed
673
			$q_where .= "AND Users.Username = " . $dbh->quote($_GET['K']) . " ";
Loui Chang's avatar
Loui Chang committed
674
		}
675
		elseif (isset($_GET["SeB"]) && $_GET["SeB"] == "s") {
676
			/* Search by submitter. */
677
			$q_where .= "AND SubmitterUID = ".uid_from_username($_GET['K'])." ";
Loui Chang's avatar
Loui Chang committed
678
		}
679
		elseif (isset($_GET["SeB"]) && $_GET["SeB"] == "n") {
680
			/* Search by name. */
canyonknight's avatar
canyonknight committed
681
			$K = "%" . addcslashes($_GET['K'], '%_') . "%";
682
			$q_where .= "AND (Packages.Name LIKE " . $dbh->quote($K) . ") ";
Andrea Scarpino's avatar
Andrea Scarpino committed
683
		}
684
		elseif (isset($_GET["SeB"]) && $_GET["SeB"] == "x") {
685
			/* Search by name (exact match). */
686
			$q_where .= "AND (Packages.Name = " . $dbh->quote($_GET['K']) . ") ";
687
		}
Loui Chang's avatar
Loui Chang committed
688
		else {
689
			/* Search by name and description (default). */
canyonknight's avatar
canyonknight committed
690
			$K = "%" . addcslashes($_GET['K'], '%_') . "%";
691
			$q_where .= "AND (Packages.Name LIKE " . $dbh->quote($K) . " OR ";
canyonknight's avatar
canyonknight committed
692
			$q_where .= "Description LIKE " . $dbh->quote($K) . ") ";
Loui Chang's avatar
Loui Chang committed
693
694
695
		}
	}

696
	if (isset($_GET["do_Orphans"])) {
697
		$q_where .= "AND MaintainerUID IS NULL ";
Loui Chang's avatar
Loui Chang committed
698
	}
699

700
	if (isset($_GET['outdated'])) {
701
		if ($_GET['outdated'] == 'on') {
702
			$q_where .= "AND OutOfDateTS IS NOT NULL ";
703
704
		}
		elseif ($_GET['outdated'] == 'off') {
705
			$q_where .= "AND OutOfDateTS IS NULL ";
706
		}
707
708
	}

709
	$order = (isset($_GET["SO"]) && $_GET["SO"] == 'd') ? 'DESC' : 'ASC';
Loui Chang's avatar
Loui Chang committed
710

711
	$q_sort = "ORDER BY ";
712
713
	$sort_by = isset($_GET["SB"]) ? $_GET["SB"] : '';
	switch ($sort_by) {
Loui Chang's avatar
Loui Chang committed
714
	case 'c':
715
		$q_sort .= "CategoryID " . $order . ", ";
Loui Chang's avatar
Loui Chang committed
716
717
		break;
	case 'v':
718
		$q_sort .= "NumVotes " . $order . ", ";
Loui Chang's avatar
Loui Chang committed
719
		break;
720
721
	case 'w':
		if ($SID) {
722
			$q_sort .= "Voted " . $order . ", ";
723
724
725
726
		}
		break;
	case 'o':
		if ($SID) {
727
			$q_sort .= "Notify " . $order . ", ";
728
729
		}
		break;
Loui Chang's avatar
Loui Chang committed
730
	case 'm':
731
		$q_sort .= "Maintainer " . $order . ", ";
Loui Chang's avatar
Loui Chang committed
732
733
		break;
	case 'a':
734
		$q_sort .= "ModifiedTS " . $order . ", ";
Loui Chang's avatar
Loui Chang committed
735
736
737
738
		break;
	default:
		break;
	}
739
	$q_sort .= " Packages.Name " . $order . " ";
Loui Chang's avatar
Loui Chang committed
740

741
	$q_limit = "LIMIT ".$_GET["PP"]." OFFSET ".$_GET["O"];
742

743
744
	$q = $q_select . $q_from . $q_from_extra . $q_where . $q_sort . $q_limit;
	$q_total = "SELECT COUNT(*) " . $q_from . $q_where;
Loui Chang's avatar
Loui Chang committed
745

canyonknight's avatar
canyonknight committed
746
747
	$result = $dbh->query($q);
	$result_t = $dbh->query($q_total);
748
	if ($result_t) {
canyonknight's avatar
canyonknight committed
749
750
		$row = $result_t->fetch(PDO::FETCH_NUM);
		$total = $row[0];
751
752
753
754
	}
	else {
		$total = 0;
	}
Simo Leone's avatar
Simo Leone committed
755

756
	if ($result && $total > 0) {
757
758
		if (isset($_GET["SO"]) && $_GET["SO"] == "d"){
			$SO_next = "a";
Loui Chang's avatar
Loui Chang committed
759
760
		}
		else {
761
			$SO_next = "d";
Loui Chang's avatar
Loui Chang committed
762
		}
763
	}
Simo Leone's avatar
Simo Leone committed
764

765
	/* Calculate the results to use. */
Loui Chang's avatar
Loui Chang committed
766
	$first = $_GET['O'] + 1;
Simo Leone's avatar
Simo Leone committed
767

768
	/* Calculation of pagination links. */
769
770
771
772
	$per_page = ($_GET['PP'] > 0) ? $_GET['PP'] : 50;
	$current = ceil($first / $per_page);
	$pages = ceil($total / $per_page);
	$templ_pages = array();
773

774
	if ($current > 1) {
775
776
		$templ_pages['&laquo; ' . __('First')] = 0;
		$templ_pages['&lsaquo; ' . __('Previous')] = ($current - 2) * $per_page;
777
	}
778

779
780
	if ($current - 5 > 1)
		$templ_pages["..."] = false;
781

782
783
784
	for ($i = max($current - 5, 1); $i <= min($pages, $current + 5); $i++) {
		$templ_pages[$i] = ($i - 1) * $per_page;
	}
785

786
787
	if ($current + 5 < $pages)
		$templ_pages["... "] = false;
788

789
	if ($current < $pages) {
790
791
		$templ_pages[__('Next') . ' &rsaquo;'] = $current * $per_page;
		$templ_pages[__('Last') . ' &raquo;'] = ($pages - 1) * $per_page;
792
	}
Simo Leone's avatar
Simo Leone committed
793

794
	include('pkg_search_form.php');
795

canyonknight's avatar
canyonknight committed
796
797
798
799
	if ($result) {
		while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
			$searchresults[] = $row;
		}
800
801
	}

802
803
	include('pkg_search_results.php');

Loui Chang's avatar
Loui Chang committed
804
	return;
eric's avatar
eric committed
805
806
}

807
808
809
810
811
812
813
/**
 * Determine if a POST string has been sent by a visitor
 *
 * @param string $action String to check has been sent via POST
 *
 * @return bool True if the POST string was used, otherwise false
 */
Dan McGee's avatar
Dan McGee committed
814
815
816
817
818
function current_action($action) {
	return (isset($_POST['action']) && $_POST['action'] == $action) ||
		isset($_POST[$action]);
}

819
/**
820
821
822
823
824
 * Determine if sent IDs are valid integers
 *
 * @param array $ids IDs to validate
 *
 * @return array All sent IDs that are valid integers
825
826
827
828
829
830
831
832
833
834
835
836
 */
function sanitize_ids($ids) {
	$new_ids = array();
	foreach ($ids as $id) {
		$id = intval($id);
		if ($id > 0) {
			$new_ids[] = $id;
		}
	}
	return $new_ids;
}

837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
/**
 * Convert a list of package IDs into a list of corresponding package bases.
 *
 * @param array|int $ids Array of package IDs to convert
 *
 * @return array|int List of package base IDs
 */
function pkgbase_from_pkgid($ids) {
	$dbh = DB::connect();

	if (is_array($ids)) {
		$q = "SELECT PackageBaseID FROM Packages ";
		$q.= "WHERE ID IN (" . implode(",", $ids) . ")";
		$result = $dbh->query($q);
		return $result->fetchAll(PDO::FETCH_COLUMN, 0);
	} else {
		$q = "SELECT PackageBaseID FROM Packages ";
		$q.= "WHERE ID = " . $ids;
		$result = $dbh->query($q);
		return $result->fetch(PDO::FETCH_COLUMN, 0);
	}
}

860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
/**
 * Retrieve ID of a package base by name
 *
 * @param string $name The package base name to retrieve the ID for
 *
 * @return int The ID of the package base
 */
function pkgbase_from_name($name) {
	$dbh = DB::connect();
	$q = "SELECT ID FROM PackageBases WHERE Name = " . $dbh->quote($name);
	$result = $dbh->query($q);
	return $result->fetch(PDO::FETCH_COLUMN, 0);
}

/**
 * Retrieve the name of a package base given its ID
 *
 * @param int $base_id The ID of the package base to query
 *
 * @return string The name of the package base
 */
function pkgbase_name_from_id($base_id) {
	$dbh = DB::connect();
	$q = "SELECT Name FROM PackageBases WHERE ID = " . intval($base_id);
	$result = $dbh->query($q);
	return $result->fetch(PDO::FETCH_COLUMN, 0);
}

888
889
890
891
892
893
894
895
896
897
898
899
900
901
/**
 * Get the names of all packages belonging to a package base
 *
 * @param int $base_id The ID of the package base
 *
 * @return array The names of all packages belonging to the package base
 */
function pkgbase_get_pkgnames($base_id) {
	$dbh = DB::connect();
	$q = "SELECT Name FROM Packages WHERE PackageBaseID = " . intval($base_id);
	$result = $dbh->query($q);
	return $result->fetchAll(PDO::FETCH_COLUMN, 0);
}

902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
/**
 * Delete all packages belonging to a package base
 *
 * @param int $base_id The ID of the package base
 *
 * @return void
 */
function pkgbase_delete_packages($base_id) {
	$dbh = DB::connect();
	$q = "DELETE FROM Packages WHERE PackageBaseID = " . intval($base_id);
	$dbh->exec($q);
}

/**
 * Retrieve the maintainer of a package base given its ID
 *
 * @param int $base_id The ID of the package base to query
 *
 * @return int The user ID of the current package maintainer
 */
function pkgbase_maintainer_uid($base_id) {
	$dbh = DB::connect();
	$q = "SELECT MaintainerUID FROM PackageBases WHERE ID = " . intval($base_id);
	$result = $dbh->query($q);
	return $result->fetch(PDO::FETCH_COLUMN, 0);
}

929

930
/**
931
 * Flag package(s) as out-of-date
932
 *
933
 * @global string $AUR_LOCATION The AUR's URL used for notification e-mails
934
 * @param string $atype Account type, output of account_from_sid
935
 * @param array $ids Array of package IDs to flag/unflag
936
 *
937
 * @return array Tuple of success/failure indicator and error message
938
 */
939
function pkg_flag($atype, $ids) {
Dan McGee's avatar
Dan McGee committed
940
941
	global $AUR_LOCATION;

942
	if (!$atype) {
943
		return array(false, __("You must be logged in before you can flag packages."));
944
945
	}

946
	$ids = sanitize_ids($ids);
947
	$base_ids = pkgbase_from_pkgid($ids);
948
	if (empty($ids)) {
949
		return array(false, __("You did not select any packages to flag."));
950
951
	}

952
	$dbh = DB::connect();
953

954
	$q = "UPDATE PackageBases SET";
955
	$q.= " OutOfDateTS = UNIX_TIMESTAMP()";
956
	$q.= " WHERE ID IN (" . implode(",", $base_ids) . ")";
957
	$q.= " AND OutOfDateTS IS NULL";
958
959
960

	$affected_pkgs = $dbh->exec($q);

961
	if ($affected_pkgs > 0) {
962
		/* Notify of flagging by e-mail. */
963
964
965
		$f_name = username_from_sid($_COOKIE['AURSID']);
		$f_email = email_from_sid($_COOKIE['AURSID']);
		$f_uid = uid_from_sid($_COOKIE['AURSID']);
966
967
		$q = "SELECT Packages.Name, Users.Email, Packages.ID ";
		$q.= "FROM Packages, Users ";
968
		$q.= "WHERE Packages.ID IN (" . implode(",", $ids) .") ";
969
970
		$q.= "AND Users.ID = Packages.MaintainerUID ";
		$q.= "AND Users.ID != " . $f_uid;
canyonknight's avatar
canyonknight committed
971
972
973
		$result = $dbh->query($q);
		if ($result) {
			while ($row = $result->fetch(PDO::FETCH_ASSOC)) {
974
				$body = "Your package " . $row['Name'] . " has been flagged out of date by " . $f_name . " [1]. You may view your package at:\n" . $AUR_LOCATION . get_pkg_uri($row['Name']) . "\n\n[1] - " . $AUR_LOCATION . get_user_uri($f_name);
975
				$body = wordwrap($body, 70);
976
977
978
				$headers = "MIME-Version: 1.0\r\n" .
					   "Content-type: text/plain; charset=UTF-8\r\n" .
					   "Reply-to: nobody@archlinux.org\r\n" .
979
980
981
					   "From: aur-notify@archlinux.org\r\n" .
					   "X-Mailer: PHP\r\n" .
					   "X-MimeOLE: Produced By AUR";
982
				@mail($row['Email'], "AUR Out-of-date Notification for ".$row['Name'], $body, $headers);
983
984
985
986
			}
		}
	}

987
	return array(true, __("The selected packages have been flagged out-of-date."));
988
989
990
991
992
993
994
995
}

/**
 * Unflag package(s) as out-of-date
 *
 * @param string $atype Account type, output of account_from_sid
 * @param array $ids Array of package IDs to flag/unflag
 *
996
 * @return array Tuple of success/failure indicator and error message
997
 */
998
function pkg_unflag($atype, $ids) {
999
	if (!$atype) {
1000
		return array(false, __("You must be logged in before you can unflag packages."));
For faster browsing, not all history is shown. View entire blame