| Server IP : 157.230.181.24 / Your IP : 216.73.217.11 Web Server : Apache/2.4.58 (Ubuntu) System : Linux conductive 6.8.0-117-generic #117-Ubuntu SMP PREEMPT_DYNAMIC Tue May 5 19:26:24 UTC 2026 x86_64 User : ( 1000) PHP Version : 8.3.31 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : OFF | Sudo : ON | Pkexec : OFF Directory : /var/www/vhosts/ceagon/open-oscar-server/state/ |
Upload File : |
package state
import (
"bytes"
"context"
"fmt"
"strings"
"text/template"
)
// relationshipSQLTpl defines the template for a SQL query used to query buddy
// list and privacy relationships between a user (`me`) and other users in the
// system.
//
// This query serves two purposes:
// 1. Retrieve all relationships for the user.
// 2. If filtering is enabled (`.DoFilter` is true), retrieve all relationships
// filtered on a specific list of users.
//
// The query creates a unified view of both server-side buddy lists and
// client-side buddy lists.
const relationshipSQLTpl = `
WITH myScreenName AS (SELECT ?),
{{ if .DoFilter }}filter AS (SELECT * FROM (VALUES%s) as t),{{ end }}
-- get all users who have ~you~ on their buddy list
theirBuddyLists AS (SELECT COALESCE(clientSide._screenName, feedbag._screenName) AS _screenName,
COALESCE(clientSide.isBuddy OR feedbag.isBuddy, FALSE) AS isBuddy,
COALESCE(clientSide.isPermit OR feedbag.isPermit, FALSE) AS isPermit,
COALESCE(clientSide.isDeny OR feedbag.isDeny, FALSE) AS isDeny
FROM (SELECT feedbag.screenName AS _screenName,
MAX(CASE WHEN feedbag.classId = 0 AND NOT feedbag.authPending THEN 1 ELSE 0 END) AS isBuddy,
MAX(CASE WHEN feedbag.classId = 2 THEN 1 ELSE 0 END) AS isPermit,
MAX(CASE WHEN feedbag.classId = 3 THEN 1 ELSE 0 END) AS isDeny
FROM feedbag
WHERE feedbag.name = (SELECT * FROM myScreenName)
{{ if .DoFilter }}AND feedbag.screenName IN (SELECT * FROM filter){{ end }}
AND feedbag.classId IN (0, 2, 3)
AND EXISTS(SELECT 1
FROM buddyListMode
WHERE buddyListMode.screenName = feedbag.screenName
AND useFeedbag IS TRUE)
GROUP BY feedbag.screenName) feedbag
FULL OUTER JOIN (SELECT me AS _screenName,
isBuddy AS isBuddy,
isPermit AS isPermit,
isDeny AS isDeny
FROM clientSideBuddyList
WHERE them = (SELECT * FROM myScreenName)
{{ if .DoFilter }}AND me IN (SELECT * FROM filter){{ end }}) clientSide
ON feedbag._screenName = clientSide._screenName),
-- get all users on ~your~ buddy list
yourBuddyList AS (SELECT COALESCE(clientSide._screenName, feedbag._screenName) AS _screenName,
COALESCE(clientSide.isBuddy OR feedbag.isBuddy, FALSE) AS isBuddy,
COALESCE(clientSide.isPermit OR feedbag.isPermit, FALSE) AS isPermit,
COALESCE(clientSide.isDeny OR feedbag.isDeny, FALSE) AS isDeny
FROM (SELECT feedbag.name AS _screenName,
MAX(CASE WHEN feedbag.classId = 0 AND NOT feedbag.authPending THEN 1 ELSE 0 END) AS isBuddy,
MAX(CASE WHEN feedbag.classId = 2 THEN 1 ELSE 0 END) AS isPermit,
MAX(CASE WHEN feedbag.classId = 3 THEN 1 ELSE 0 END) AS isDeny
FROM feedbag
WHERE feedbag.screenName = (SELECT * FROM myScreenName)
{{ if .DoFilter }}AND feedbag.name IN (SELECT * FROM filter){{ end }}
AND feedbag.classId IN (0, 2, 3)
AND EXISTS(SELECT 1
FROM buddyListMode
WHERE buddyListMode.screenName = feedbag.screenName
AND useFeedbag IS TRUE)
GROUP BY feedbag.name) feedbag
FULL OUTER JOIN (SELECT them AS _screenName,
isBuddy AS isBuddy,
isPermit AS isPermit,
isDeny AS isDeny
FROM clientSideBuddyList
WHERE me = (SELECT * FROM myScreenName)
{{ if .DoFilter }}AND them IN (SELECT * FROM filter){{ end }}) clientSide
ON feedbag._screenName = clientSide._screenName),
-- get privacy prefs of all users who have ~you~ on their buddy list
theirPrivacyPrefs AS (SELECT buddyListMode.screenName,
CASE
WHEN buddyListMode.useFeedbag IS TRUE THEN IFNULL(feedbagPrefs.pdMode, 1)
ELSE buddyListMode.clientSidePDMode END AS pdMode
FROM buddyListMode
LEFT JOIN feedbag feedbagPrefs
ON (feedbagPrefs.screenName == buddyListMode.screenName AND
feedbagPrefs.classID = 4)
WHERE EXISTS (SELECT 1
FROM theirBuddyLists
WHERE theirBuddyLists._screenName = buddyListMode.screenName)
OR EXISTS (SELECT 1
FROM yourBuddyList
WHERE yourBuddyList._screenName = buddyListMode.screenName)),
-- get privacy prefs of all users on ~your~ buddy list
yourPrivacyPrefs AS (SELECT buddyListMode.screenName,
CASE
WHEN buddyListMode.useFeedbag IS TRUE THEN IFNULL(feedbagPrefs.pdMode, 1)
ELSE buddyListMode.clientSidePDMode END AS pdMode
FROM buddyListMode
LEFT JOIN feedbag feedbagPrefs
ON (feedbagPrefs.screenName == buddyListMode.screenName AND
feedbagPrefs.classID = 4)
WHERE buddyListMode.screenName = (SELECT * FROM myScreenName))
-- create relationships between you and all combined users
SELECT COALESCE(yourBuddyList._screenName, theirBuddyLists._screenName) AS screenName,
CASE
WHEN yourPrivacyPrefs.pdMode = 1 THEN false
WHEN yourPrivacyPrefs.pdMode = 2 THEN true
WHEN yourPrivacyPrefs.pdMode = 3 THEN IFNULL(yourBuddyList.isPermit, false) = false
WHEN yourPrivacyPrefs.pdMode = 4 THEN IFNULL(yourBuddyList.isDeny, false)
WHEN yourPrivacyPrefs.pdMode = 5 THEN IFNULL(yourBuddyList.isBuddy, false) = false
ELSE false
END AS youBlock,
CASE
WHEN theirPrivacyPrefs.pdMode = 1 THEN false
WHEN theirPrivacyPrefs.pdMode = 2 THEN true
WHEN theirPrivacyPrefs.pdMode = 3 THEN IFNULL(theirBuddyLists.isPermit, false) = false
WHEN theirPrivacyPrefs.pdMode = 4 THEN IFNULL(theirBuddyLists.isDeny, false)
WHEN theirPrivacyPrefs.pdMode = 5 THEN IFNULL(theirBuddyLists.isBuddy, false) = false
ELSE false
END AS blocksYou,
IFNULL(theirBuddyLists.isBuddy, false) AS onTheirBuddyList,
IFNULL(yourBuddyList.isBuddy, false) AS onYourBuddyList
FROM theirBuddyLists
FULL OUTER JOIN yourBuddyList
ON (yourBuddyList._screenName = theirBuddyLists._screenName)
JOIN theirPrivacyPrefs
ON (theirPrivacyPrefs.screenName = COALESCE(theirBuddyLists._screenName, yourBuddyList._screenName))
JOIN yourPrivacyPrefs ON (1 = 1)
`
var (
queryWithoutFiltering = tmplMustCompile(struct{ DoFilter bool }{DoFilter: false})
queryWithFiltering = tmplMustCompile(struct{ DoFilter bool }{DoFilter: true})
)
// Relationship represents the relationship between two users.
// Users A and B are related if:
// - A has user B on their buddy list, or vice versa
// - A has user B on their deny list, or vice versa
// - A has user B on their permit list, or vice versa
type Relationship struct {
// User is the screen name of the user with whom you have a relationship.
User IdentScreenName
// BlocksYou indicates whether user blocks you. This is true when user has
// the following permit/deny modes set:
// - DenyAll
// - PermitSome (and you are not on permit list)
// - DenySome (and you are on deny list)
// - PermitOnList (and you are not on their buddy list)
BlocksYou bool
// YouBlock indicates whether you block user. This is true when user has
// the following permit/deny modes set:
// - DenyAll
// - PermitSome (and they are not on your permit list)
// - DenySome (and they are on your deny list)
// - PermitOnList (and they are not on your buddy list)
YouBlock bool
// IsOnTheirList indicates whether you are on user's buddy list.
IsOnTheirList bool
// IsOnYourList indicates whether this user is on your buddy list.
IsOnYourList bool
}
// Relationship retrieves the relationship between the specified user (`me`)
// and another user (`them`).
//
// This method always returns a usable [Relationship] value. If the user
// specified by `them` does not exist, the returned [Relationship] will have
// default boolean values.
func (f SQLiteUserStore) Relationship(ctx context.Context, me IdentScreenName, them IdentScreenName) (Relationship, error) {
rels, err := f.AllRelationships(ctx, me, []IdentScreenName{them})
if err != nil {
return Relationship{}, fmt.Errorf("error getting relationships: %w", err)
}
if len(rels) == 0 {
return Relationship{
User: them,
}, nil
}
return rels[0], nil
}
// AllRelationships retrieves the relationships between the specified user (`me`)
// and other users.
//
// A relationship is defined by the [Relationship] type, which describes the nature
// of the connection between users.
//
// This function only includes users who have activated their buddy list through
// a call to [SQLiteUserStore.RegisterBuddyList]. The results can be optionally
// filtered to include only specific users by providing their identifiers in
// the `filter` parameter.
func (f SQLiteUserStore) AllRelationships(ctx context.Context, me IdentScreenName, filter []IdentScreenName) ([]Relationship, error) {
tpl := queryWithoutFiltering
args := make([]any, 1, len(filter)+1)
args[0] = me.String()
if len(filter) > 0 {
// add placeholders to template
placeholders := strings.TrimRight(strings.Repeat("(?),", len(filter)), ",")
tpl = fmt.Sprintf(queryWithFiltering, placeholders)
// assemble arguments to match placeholders
for _, sn := range filter {
args = append(args, sn.String())
}
}
rows, err := f.db.QueryContext(ctx, tpl, args...)
if err != nil {
return nil, fmt.Errorf("error querying relationships: %w", err)
}
defer func() {
_ = rows.Close()
}()
var relationships []Relationship
for rows.Next() {
var screenName string
rel := Relationship{}
err = rows.Scan(
&screenName,
&rel.YouBlock,
&rel.BlocksYou,
&rel.IsOnTheirList,
&rel.IsOnYourList,
)
if err != nil {
return nil, fmt.Errorf("error scanning row: %w", err)
}
rel.User = NewIdentScreenName(screenName)
relationships = append(relationships, rel)
}
if err = rows.Err(); err != nil {
return nil, fmt.Errorf("error iterating over relationship rows: %w", err)
}
return relationships, nil
}
func tmplMustCompile(data any) string {
tmpl, err := template.New("").Parse(relationshipSQLTpl)
if err != nil {
panic(err)
}
buf := &bytes.Buffer{}
err = tmpl.Execute(buf, data)
if err != nil {
panic(err)
}
return buf.String()
}