Re: [PATCH] Exclude suspended Users from being notified

2020-07-03 Thread Kevin Morris
Alright, that's the final patch revision. This change ultimately just
removes suspended users from the sql results in `notify.py`, which excludes
them from all email notifications.

On Fri, Jul 3, 2020 at 6:29 PM Kevin Morris  wrote:

> The existing notify.py script was grabbing entries regardless
> of user suspension. This has been modified to only send notifications
> to unsuspended users.
>
> This change was written as a solution to
> https://bugs.archlinux.org/task/65554.
>
> Signed-off-by: Kevin Morris 
> ---
>  aurweb/scripts/notify.py | 31 ---
>  1 file changed, 20 insertions(+), 11 deletions(-)
>
> diff --git a/aurweb/scripts/notify.py b/aurweb/scripts/notify.py
> index 5b18a476..223ed61f 100755
> --- a/aurweb/scripts/notify.py
> +++ b/aurweb/scripts/notify.py
> @@ -124,7 +124,7 @@ class ResetKeyNotification(Notification):
>  def __init__(self, conn, uid):
>  cur = conn.execute('SELECT UserName, Email, BackupEmail, ' +
> 'LangPreference, ResetKey ' +
> -   'FROM Users WHERE ID = ?', [uid])
> +   'FROM Users WHERE ID = ? AND Suspended = 0',
> [uid])
>  self._username, self._to, self._backup, self._lang,
> self._resetkey = cur.fetchone()
>  super().__init__()
>
> @@ -171,7 +171,8 @@ class CommentNotification(Notification):
> 'ON PackageNotifications.UserID = Users.ID
> WHERE ' +
> 'Users.CommentNotify = 1 AND ' +
> 'PackageNotifications.UserID != ? AND ' +
> -   'PackageNotifications.PackageBaseID = ?',
> +   'PackageNotifications.PackageBaseID = ? AND ' +
> +   'Users.Suspended = 0' +,
> [uid, pkgbase_id])
>  self._recipients = cur.fetchall()
>  cur = conn.execute('SELECT Comments FROM PackageComments WHERE ID
> = ?',
> @@ -218,7 +219,8 @@ class UpdateNotification(Notification):
> 'ON PackageNotifications.UserID = Users.ID
> WHERE ' +
> 'Users.UpdateNotify = 1 AND ' +
> 'PackageNotifications.UserID != ? AND ' +
> -   'PackageNotifications.PackageBaseID = ?',
> +   'PackageNotifications.PackageBaseID = ? AND ' +
> +   'Users.Suspended = 0',
> [uid, pkgbase_id])
>  self._recipients = cur.fetchall()
>  super().__init__()
> @@ -264,7 +266,8 @@ class FlagNotification(Notification):
> 'INNER JOIN PackageBases ' +
> 'ON PackageBases.MaintainerUID = Users.ID OR '
> +
> 'PackageBases.ID =
> PackageComaintainers.PackageBaseID ' +
> -   'WHERE PackageBases.ID = ?', [pkgbase_id])
> +   'WHERE PackageBases.ID = ? AND ' +
> +   'Users.Suspended = 0', [pkgbase_id])
>  self._recipients = cur.fetchall()
>  cur = conn.execute('SELECT FlaggerComment FROM PackageBases WHERE
> ' +
> 'ID = ?', [pkgbase_id])
> @@ -302,7 +305,8 @@ class OwnershipEventNotification(Notification):
> 'ON PackageNotifications.UserID = Users.ID
> WHERE ' +
> 'Users.OwnershipNotify = 1 AND ' +
> 'PackageNotifications.UserID != ? AND ' +
> -   'PackageNotifications.PackageBaseID = ?',
> +   'PackageNotifications.PackageBaseID = ? AND ' +
> +   'Users.Suspended = 0',
> [uid, pkgbase_id])
>  self._recipients = cur.fetchall()
>  cur = conn.execute('SELECT FlaggerComment FROM PackageBases WHERE
> ' +
> @@ -341,7 +345,7 @@ class ComaintainershipEventNotification(Notification):
>  def __init__(self, conn, uid, pkgbase_id):
>  self._pkgbase = pkgbase_from_id(conn, pkgbase_id)
>  cur = conn.execute('SELECT Email, LangPreference FROM Users ' +
> -   'WHERE ID = ?', [uid])
> +   'WHERE ID = ? AND Suspended = 0', [uid])
>  self._to, self._lang = cur.fetchone()
>  super().__init__()
>
> @@ -384,7 +388,8 @@ class DeleteNotification(Notification):
> 'INNER JOIN PackageNotifications ' +
> 'ON PackageNotifications.UserID = Users.ID
> WHERE ' +
> 'PackageNotifications.UserID != ? AND ' +
> -   'PackageNotifications.PackageBaseID = ?',
> +   'PackageNotifications.PackageBaseID = ? AND ' +
> +   'Users.Suspended = 0',
> [uid, old_pkgbase_id])
>  self._recipients = cur.fetchall()
> 

[PATCH] Exclude suspended Users from being notified

2020-07-03 Thread Kevin Morris
The existing notify.py script was grabbing entries regardless
of user suspension. This has been modified to only send notifications
to unsuspended users.

This change was written as a solution to
https://bugs.archlinux.org/task/65554.

Signed-off-by: Kevin Morris 
---
 aurweb/scripts/notify.py | 31 ---
 1 file changed, 20 insertions(+), 11 deletions(-)

diff --git a/aurweb/scripts/notify.py b/aurweb/scripts/notify.py
index 5b18a476..223ed61f 100755
--- a/aurweb/scripts/notify.py
+++ b/aurweb/scripts/notify.py
@@ -124,7 +124,7 @@ class ResetKeyNotification(Notification):
 def __init__(self, conn, uid):
 cur = conn.execute('SELECT UserName, Email, BackupEmail, ' +
'LangPreference, ResetKey ' +
-   'FROM Users WHERE ID = ?', [uid])
+   'FROM Users WHERE ID = ? AND Suspended = 0', [uid])
 self._username, self._to, self._backup, self._lang, self._resetkey = 
cur.fetchone()
 super().__init__()
 
@@ -171,7 +171,8 @@ class CommentNotification(Notification):
'ON PackageNotifications.UserID = Users.ID WHERE ' +
'Users.CommentNotify = 1 AND ' +
'PackageNotifications.UserID != ? AND ' +
-   'PackageNotifications.PackageBaseID = ?',
+   'PackageNotifications.PackageBaseID = ? AND ' +
+   'Users.Suspended = 0' +,
[uid, pkgbase_id])
 self._recipients = cur.fetchall()
 cur = conn.execute('SELECT Comments FROM PackageComments WHERE ID = ?',
@@ -218,7 +219,8 @@ class UpdateNotification(Notification):
'ON PackageNotifications.UserID = Users.ID WHERE ' +
'Users.UpdateNotify = 1 AND ' +
'PackageNotifications.UserID != ? AND ' +
-   'PackageNotifications.PackageBaseID = ?',
+   'PackageNotifications.PackageBaseID = ? AND ' +
+   'Users.Suspended = 0',
[uid, pkgbase_id])
 self._recipients = cur.fetchall()
 super().__init__()
@@ -264,7 +266,8 @@ class FlagNotification(Notification):
'INNER JOIN PackageBases ' +
'ON PackageBases.MaintainerUID = Users.ID OR ' +
'PackageBases.ID = 
PackageComaintainers.PackageBaseID ' +
-   'WHERE PackageBases.ID = ?', [pkgbase_id])
+   'WHERE PackageBases.ID = ? AND ' +
+   'Users.Suspended = 0', [pkgbase_id])
 self._recipients = cur.fetchall()
 cur = conn.execute('SELECT FlaggerComment FROM PackageBases WHERE ' +
'ID = ?', [pkgbase_id])
@@ -302,7 +305,8 @@ class OwnershipEventNotification(Notification):
'ON PackageNotifications.UserID = Users.ID WHERE ' +
'Users.OwnershipNotify = 1 AND ' +
'PackageNotifications.UserID != ? AND ' +
-   'PackageNotifications.PackageBaseID = ?',
+   'PackageNotifications.PackageBaseID = ? AND ' +
+   'Users.Suspended = 0',
[uid, pkgbase_id])
 self._recipients = cur.fetchall()
 cur = conn.execute('SELECT FlaggerComment FROM PackageBases WHERE ' +
@@ -341,7 +345,7 @@ class ComaintainershipEventNotification(Notification):
 def __init__(self, conn, uid, pkgbase_id):
 self._pkgbase = pkgbase_from_id(conn, pkgbase_id)
 cur = conn.execute('SELECT Email, LangPreference FROM Users ' +
-   'WHERE ID = ?', [uid])
+   'WHERE ID = ? AND Suspended = 0', [uid])
 self._to, self._lang = cur.fetchone()
 super().__init__()
 
@@ -384,7 +388,8 @@ class DeleteNotification(Notification):
'INNER JOIN PackageNotifications ' +
'ON PackageNotifications.UserID = Users.ID WHERE ' +
'PackageNotifications.UserID != ? AND ' +
-   'PackageNotifications.PackageBaseID = ?',
+   'PackageNotifications.PackageBaseID = ? AND ' +
+   'Users.Suspended = 0',
[uid, old_pkgbase_id])
 self._recipients = cur.fetchall()
 super().__init__()
@@ -431,7 +436,8 @@ class RequestOpenNotification(Notification):
'INNER JOIN Users ' +
'ON Users.ID = PackageRequests.UsersID ' +
'OR Users.ID = PackageBases.MaintainerUID ' +
-   'WHERE PackageRequests.ID = ?', [reqid])
+   'WHERE PackageRequests.ID = ? 

Re: [PATCH] Remove PackageNotifications when User is suspended

2020-07-03 Thread Kevin Morris
Updated patch inc which only modifies `scripts/notify.py` and preserves
notification records.

On Fri, Jul 3, 2020 at 12:02 PM Kevin Morris  wrote:

> This path will only trigger when suspending a user through an
> account page; if the database is managed manually or through
> another script, PackageNotifications will also need manual
> modifications if intended.
>
> This change was written as a solution to
> https://bugs.archlinux.org/task/65554.
>
> Signed-off-by: Kevin Morris 
> ---
>  web/lib/acctfuncs.inc.php | 4 
>  1 file changed, 4 insertions(+)
>
> diff --git a/web/lib/acctfuncs.inc.php b/web/lib/acctfuncs.inc.php
> index d238c0e0..fb7cca8c 100644
> --- a/web/lib/acctfuncs.inc.php
> +++ b/web/lib/acctfuncs.inc.php
> @@ -403,6 +403,10 @@ function
> process_account_form($TYPE,$A,$U="",$T="",$S="",$E="",$BE="",$H="",$P="
> $q.= " WHERE ID = ".intval($UID);
> $result = $dbh->exec($q);
>
> +   // Clean up PackageNotifications for $UID as well.
> +   $q = "DELETE FROM PackageNotifications WHERE UserID =
> ".intval($UID);
> +   $result = $dbh->exec($q);
> +
> if (isset($ssh_keys) && count($ssh_keys) > 0) {
> $ssh_key_result = account_set_ssh_keys($UID,
> $ssh_keys, $ssh_fingerprints);
> } else {
> --
> 2.20.1
>
>

-- 
Kevin Morris
Software Developer

Personal Inquiries: kevr.gt...@gmail.com
Personal Phone: (415) 583-9687

Technologies: C, C++, Python, Django, Ruby, Rails, ReactJS, jQuery,
Javascript, SQL, Redux


[PATCH] Exclude suspended Users from being notified

2020-07-03 Thread Kevin Morris
The existing notify.py script was grabbing entries regardless
of user suspension. This has been modified to only send notifications
to unsuspended users, except for a Trusted User Vote Notification.

This change was written as a solution to
https://bugs.archlinux.org/task/65554.

Signed-off-by: Kevin Morris 
---
 aurweb/scripts/notify.py | 22 +++---
 1 file changed, 15 insertions(+), 7 deletions(-)

diff --git a/aurweb/scripts/notify.py b/aurweb/scripts/notify.py
index 5b18a476..94ca1936 100755
--- a/aurweb/scripts/notify.py
+++ b/aurweb/scripts/notify.py
@@ -170,6 +170,7 @@ class CommentNotification(Notification):
'FROM Users INNER JOIN PackageNotifications ' +
'ON PackageNotifications.UserID = Users.ID WHERE ' +
'Users.CommentNotify = 1 AND ' +
+   'Users.Suspended = 0 AND ' +
'PackageNotifications.UserID != ? AND ' +
'PackageNotifications.PackageBaseID = ?',
[uid, pkgbase_id])
@@ -217,6 +218,7 @@ class UpdateNotification(Notification):
'INNER JOIN PackageNotifications ' +
'ON PackageNotifications.UserID = Users.ID WHERE ' +
'Users.UpdateNotify = 1 AND ' +
+   'Users.Suspended = 0 AND ' +
'PackageNotifications.UserID != ? AND ' +
'PackageNotifications.PackageBaseID = ?',
[uid, pkgbase_id])
@@ -264,7 +266,8 @@ class FlagNotification(Notification):
'INNER JOIN PackageBases ' +
'ON PackageBases.MaintainerUID = Users.ID OR ' +
'PackageBases.ID = 
PackageComaintainers.PackageBaseID ' +
-   'WHERE PackageBases.ID = ?', [pkgbase_id])
+   'WHERE PackageBases.ID = ? AND ' +
+   'Users.Suspended = 0', [pkgbase_id])
 self._recipients = cur.fetchall()
 cur = conn.execute('SELECT FlaggerComment FROM PackageBases WHERE ' +
'ID = ?', [pkgbase_id])
@@ -302,7 +305,8 @@ class OwnershipEventNotification(Notification):
'ON PackageNotifications.UserID = Users.ID WHERE ' +
'Users.OwnershipNotify = 1 AND ' +
'PackageNotifications.UserID != ? AND ' +
-   'PackageNotifications.PackageBaseID = ?',
+   'PackageNotifications.PackageBaseID = ? AND ' +
+   'Users.Suspended = 0',
[uid, pkgbase_id])
 self._recipients = cur.fetchall()
 cur = conn.execute('SELECT FlaggerComment FROM PackageBases WHERE ' +
@@ -341,7 +345,7 @@ class ComaintainershipEventNotification(Notification):
 def __init__(self, conn, uid, pkgbase_id):
 self._pkgbase = pkgbase_from_id(conn, pkgbase_id)
 cur = conn.execute('SELECT Email, LangPreference FROM Users ' +
-   'WHERE ID = ?', [uid])
+   'WHERE ID = ? AND Suspended = 0', [uid])
 self._to, self._lang = cur.fetchone()
 super().__init__()
 
@@ -384,7 +388,8 @@ class DeleteNotification(Notification):
'INNER JOIN PackageNotifications ' +
'ON PackageNotifications.UserID = Users.ID WHERE ' +
'PackageNotifications.UserID != ? AND ' +
-   'PackageNotifications.PackageBaseID = ?',
+   'PackageNotifications.PackageBaseID = ? AND ' +
+   'Users.Suspended = 0',
[uid, old_pkgbase_id])
 self._recipients = cur.fetchall()
 super().__init__()
@@ -431,7 +436,8 @@ class RequestOpenNotification(Notification):
'INNER JOIN Users ' +
'ON Users.ID = PackageRequests.UsersID ' +
'OR Users.ID = PackageBases.MaintainerUID ' +
-   'WHERE PackageRequests.ID = ?', [reqid])
+   'WHERE PackageRequests.ID = ? AND ' +
+   'Users.Suspended = 0', [reqid])
 self._to = aurweb.config.get('options', 'aur_request_ml')
 self._cc = [row[0] for row in cur.fetchall()]
 cur = conn.execute('SELECT Comments FROM PackageRequests WHERE ID = ?',
@@ -485,7 +491,8 @@ class RequestCloseNotification(Notification):
'INNER JOIN Users ' +
'ON Users.ID = PackageRequests.UsersID ' +
'OR Users.ID = PackageBases.MaintainerUID ' +
-   'WHERE PackageRequests.ID = ?', [reqid])
+   'WHERE 

[PATCH] Remove PackageNotifications when User is suspended

2020-07-03 Thread Kevin Morris
This path will only trigger when suspending a user through an
account page; if the database is managed manually or through
another script, PackageNotifications will also need manual
modifications if intended.

This change was written as a solution to
https://bugs.archlinux.org/task/65554.

Signed-off-by: Kevin Morris 
---
 web/lib/acctfuncs.inc.php | 4 
 1 file changed, 4 insertions(+)

diff --git a/web/lib/acctfuncs.inc.php b/web/lib/acctfuncs.inc.php
index d238c0e0..fb7cca8c 100644
--- a/web/lib/acctfuncs.inc.php
+++ b/web/lib/acctfuncs.inc.php
@@ -403,6 +403,10 @@ function 
process_account_form($TYPE,$A,$U="",$T="",$S="",$E="",$BE="",$H="",$P="
$q.= " WHERE ID = ".intval($UID);
$result = $dbh->exec($q);
 
+   // Clean up PackageNotifications for $UID as well.
+   $q = "DELETE FROM PackageNotifications WHERE UserID = 
".intval($UID);
+   $result = $dbh->exec($q);
+
if (isset($ssh_keys) && count($ssh_keys) > 0) {
$ssh_key_result = account_set_ssh_keys($UID, $ssh_keys, 
$ssh_fingerprints);
} else {
-- 
2.20.1


Re: [PATCH] Add POST support for RPC API

2020-07-03 Thread Kevin Morris
Apologies, `application/data` in the commit message's example should be
`application/json`, and will need to be updated after review(s) in the
final commit.

On Fri, Jul 3, 2020 at 10:53 AM Kevin Morris  wrote:

> When we received a POST method from a client, deduce our `$data`
> by determining which `Content-Type` is used.
>
> Supported `Content-Type`s
> =
>
> * `application/json`
> * `multipart/form-data`
>
> `application/json` POST Example, searching for 'test':
> ```
> curl -X POST -H 'Content-Type: application/data' \
> --data '{"v": "5", "type": "search", "arg": "test"}' \
> https://aur.archlinux.org/rpc/
> ```
>
> `multipart/form-data` POST Example, search for 'test':
> ```
> curl -X POST -H 'Content-Type: multipart/form-data' \
> -F v=5 -F type=search -F arg=test \
> https://aur.archlinux.org/rpc/
> ```
>
> This change was written as a solution to
> https://bugs.archlinux.org/task/49089.
>
> NOTE: This commit is not final; would like a review and a bit more
> consideration on `multipart/form-data` and ensuring that we're
> handling everything we need to.
>
> Signed-off-by: Kevin Morris 
> ---
>  web/html/rpc.php | 37 ++---
>  1 file changed, 34 insertions(+), 3 deletions(-)
>
> diff --git a/web/html/rpc.php b/web/html/rpc.php
> index 64c95622..41762aa3 100644
> --- a/web/html/rpc.php
> +++ b/web/html/rpc.php
> @@ -2,16 +2,47 @@
>  set_include_path(get_include_path() . PATH_SEPARATOR . '../lib');
>  include_once("aurjson.class.php");
>
> -if ( $_SERVER['REQUEST_METHOD'] != 'GET' ) {
> +$exposed_methods = array('GET', 'POST');
> +$request_method = strtoupper($_SERVER['REQUEST_METHOD']);
> +
> +if ( !in_array($request_method, $exposed_methods) ) {
> header('HTTP/1.1 405 Method Not Allowed');
> exit();
>  }
>
> -if ( isset($_GET['type']) ) {
> +if ( $request_method === 'GET' ) {
> +   $data = $_GET;
> +} else {
> +   // Otherwise, we were given a POST method, and we'll do some more
> +   // work to deduce our data input.
> +
> +   // Extract Content-Type; remove any trailing arguments from the
> string.
> +   $content_type = $_SERVER['CONTENT_TYPE'];
> +   if ( strpos($content_type, ';') ) {
> +   // Example: `multipart/form-data; boundary blahblah` is
> extracted as
> +   // `multipart/form-data`.
> +   $content_type = explode(';', $content_type)[0];
> +   }
> +   $content_type = rtrim(trim($content_type));
> +
> +   if ($content_type === 'application/json') {
> +   $json = file_get_contents('php://input');
> +   $data = json_decode($json, true);
> +   } elseif ($content_type === 'multipart/form-data') {
> +   $data = $_POST;
> +   } else {
> +   header('HTTP/1.1 400 Bad Request');
> +   echo "Error: Unsupported Content-Type header.\n";
> +   exit();
> +   }
> +}
> +
> +if ( isset($data['type']) ) {
> $rpc_o = new AurJSON();
> -   echo $rpc_o->handle($_GET);
> +   echo $rpc_o->handle($data);
>  }
>  else {
> echo file_get_contents('../../doc/rpc.html');
>  }
> +
>  ?>
> --
> 2.20.1
>
>

-- 
Kevin Morris
Software Developer

Personal Inquiries: kevr.gt...@gmail.com
Personal Phone: (415) 583-9687

Technologies: C, C++, Python, Django, Ruby, Rails, ReactJS, jQuery,
Javascript, SQL, Redux


[PATCH] Add POST support for RPC API

2020-07-03 Thread Kevin Morris
When we received a POST method from a client, deduce our `$data`
by determining which `Content-Type` is used.

Supported `Content-Type`s
=

* `application/json`
* `multipart/form-data`

`application/json` POST Example, searching for 'test':
```
curl -X POST -H 'Content-Type: application/data' \
--data '{"v": "5", "type": "search", "arg": "test"}' \
https://aur.archlinux.org/rpc/
```

`multipart/form-data` POST Example, search for 'test':
```
curl -X POST -H 'Content-Type: multipart/form-data' \
-F v=5 -F type=search -F arg=test \
https://aur.archlinux.org/rpc/
```

This change was written as a solution to
https://bugs.archlinux.org/task/49089.

NOTE: This commit is not final; would like a review and a bit more
consideration on `multipart/form-data` and ensuring that we're
handling everything we need to.

Signed-off-by: Kevin Morris 
---
 web/html/rpc.php | 37 ++---
 1 file changed, 34 insertions(+), 3 deletions(-)

diff --git a/web/html/rpc.php b/web/html/rpc.php
index 64c95622..41762aa3 100644
--- a/web/html/rpc.php
+++ b/web/html/rpc.php
@@ -2,16 +2,47 @@
 set_include_path(get_include_path() . PATH_SEPARATOR . '../lib');
 include_once("aurjson.class.php");
 
-if ( $_SERVER['REQUEST_METHOD'] != 'GET' ) {
+$exposed_methods = array('GET', 'POST');
+$request_method = strtoupper($_SERVER['REQUEST_METHOD']);
+
+if ( !in_array($request_method, $exposed_methods) ) {
header('HTTP/1.1 405 Method Not Allowed');
exit();
 }
 
-if ( isset($_GET['type']) ) {
+if ( $request_method === 'GET' ) {
+   $data = $_GET;
+} else {
+   // Otherwise, we were given a POST method, and we'll do some more
+   // work to deduce our data input.
+
+   // Extract Content-Type; remove any trailing arguments from the string.
+   $content_type = $_SERVER['CONTENT_TYPE'];
+   if ( strpos($content_type, ';') ) {
+   // Example: `multipart/form-data; boundary blahblah` is 
extracted as
+   // `multipart/form-data`.
+   $content_type = explode(';', $content_type)[0];
+   }
+   $content_type = rtrim(trim($content_type));
+
+   if ($content_type === 'application/json') {
+   $json = file_get_contents('php://input');
+   $data = json_decode($json, true);
+   } elseif ($content_type === 'multipart/form-data') {
+   $data = $_POST;
+   } else {
+   header('HTTP/1.1 400 Bad Request');
+   echo "Error: Unsupported Content-Type header.\n";
+   exit();
+   }
+}
+
+if ( isset($data['type']) ) {
$rpc_o = new AurJSON();
-   echo $rpc_o->handle($_GET);
+   echo $rpc_o->handle($data);
 }
 else {
echo file_get_contents('../../doc/rpc.html');
 }
+
 ?>
-- 
2.20.1