diff --git a/AGENTS.md b/AGENTS.md
new file mode 100644
index 0000000..d380d49
--- /dev/null
+++ b/AGENTS.md
@@ -0,0 +1 @@
+im gonna make my own xmpp client with blackjack and hookers
diff --git a/CLAUDE.md b/CLAUDE.md
new file mode 100644
index 0000000..f41d112
--- /dev/null
+++ b/CLAUDE.md
@@ -0,0 +1 @@
+i farted
diff --git a/TODO.md b/TODO.md
index c8baaeb..6c7fb73 100644
--- a/TODO.md
+++ b/TODO.md
@@ -1,6 +1,4 @@
# TODO
-- XEP-0393: Message Styling 0%
-- XEP-0402: PEP Native Bookmarks 50% (often lags out client if enabled)
- XEP-0461: Message Replies 0%
- XEP-0444: Message Reactions partial
diff --git a/cache.go b/cache.go
index 4de8f29..d1210fd 100644
--- a/cache.go
+++ b/cache.go
@@ -71,10 +71,10 @@ func newImageFromPath(path string) (*gtk.Image, error) {
return gtk.NewImageFromPaintable(tex), nil
}
-func setImageFromPath(path string, i *gtk.Image) (error) {
+func setImageFromPath(path string, i *gtk.Image) error {
tex, err := getTexture(path)
if err != nil {
- return err
+ return err
}
i.SetFromPaintable(tex)
diff --git a/gtk-helpers.go b/gtk-helpers.go
index 2733833..28056b7 100644
--- a/gtk-helpers.go
+++ b/gtk-helpers.go
@@ -718,9 +718,9 @@ func createIdenticon(word string, always_create bool, i *gtk.Image) { // This fu
p := loader.Pixbuf()
gt := gdk.NewTextureForPixbuf(p)
/*
- i := gtk.NewImageFromPaintable(gt)
- i.AddCSSClass(loadedConfig.CVD.String() + "_CVD")
- return i
+ i := gtk.NewImageFromPaintable(gt)
+ i.AddCSSClass(loadedConfig.CVD.String() + "_CVD")
+ return i
*/
i.SetFromPaintable(gt)
diff --git a/gtk-message.go b/gtk-message.go
index 0d46c20..a8adb72 100644
--- a/gtk-message.go
+++ b/gtk-message.go
@@ -307,9 +307,9 @@ func generateMessageWidget(p stanza.Packet, blocked bool) (gtk.Widgetter, bool)
} else {
createIdenticon(m.From, false, im)
/*
- im.SetPixelSize(40)
- im.AddCSSClass("author_img")
- authorBox.Append(im)
+ im.SetPixelSize(40)
+ im.AddCSSClass("author_img")
+ authorBox.Append(im)
*/
}
} else if m.Type == stanza.MessageTypeChat {
@@ -464,16 +464,18 @@ func getAvatar(j, hash string, i *gtk.Image) { // TODO: This function probably s
_, err = os.ReadFile(hash)
if err == nil {
/*
- i, err = newImageFromPath(hash)
- if err != nil {
- invalidImages.Store(oghash, true)
- createIdenticon(j, false, i)
- }
+ i, err = newImageFromPath(hash)
+ if err != nil {
+ invalidImages.Store(oghash, true)
+ createIdenticon(j, false, i)
+ }
*/
- setImageFromPath(hash, i)
+ glib.IdleAdd(func() {
+ setImageFromPath(hash, i)
+ })
return
- }
+ }
iqResp, err := stanza.NewIQ(stanza.Attrs{
Type: "get",
@@ -519,12 +521,12 @@ func getAvatar(j, hash string, i *gtk.Image) { // TODO: This function probably s
}
glib.IdleAdd(func() {
- err = setImageFromPath(hash, i)
- if err != nil {
- invalidImages.Store(oghash, true)
- createIdenticon(j, false, i)
- }
+ err = setImageFromPath(hash, i)
+ if err != nil {
+ invalidImages.Store(oghash, true)
+ createIdenticon(j, false, i)
+ }
- i.AddCSSClass(loadedConfig.CVD.String() + "_CVD")
+ i.AddCSSClass(loadedConfig.CVD.String() + "_CVD")
})
}
diff --git a/gtk-xmpp-markup.go b/gtk-xmpp-markup.go
index 2bf57f8..0f8acaf 100644
--- a/gtk-xmpp-markup.go
+++ b/gtk-xmpp-markup.go
@@ -15,15 +15,27 @@ var (
)
func XEPToPango(text string) string {
- format := func(s string) string {
+ formatInline := func(s string) string {
s = html.EscapeString(s)
s = boldRE.ReplaceAllString(s, "$1")
s = italicRE.ReplaceAllString(s, "$1")
s = strikeRE.ReplaceAllString(s, "$1")
- s = codeRE.ReplaceAllString(s, "$1")
+ s = codeRE.ReplaceAllString(s, `$1`)
return s
}
+
+ format := func(s string) string {
+ lines := strings.Split(s, "\n")
+
+ for i, line := range lines {
+
+ lines[i] = formatInline(line)
+ }
+
+ return strings.Join(lines, "\n")
+ }
+
var b strings.Builder
last := 0
diff --git a/main.go b/main.go
index 69d6cea..19c8186 100644
--- a/main.go
+++ b/main.go
@@ -82,6 +82,9 @@ var pingTimes = [][]float64{}
var xmlLog *os.File
+// Basic roster
+var Roster = make(map[string]bool)
+
func init() {
beeep.AppName = loadedLocale["appName"]
@@ -98,7 +101,6 @@ func init() {
}
func main() {
-
pingTimes = append(pingTimes, []float64{})
p, err := ensureConfig()
if err != nil {
@@ -132,6 +134,11 @@ func main() {
xmlLog = os.Stdout
}
+ res := "/" + loadedConfig.Resource
+ if loadedConfig.RandomizeResource {
+ res = ""
+ }
+
config := xmpp.Config{
TransportConfiguration: xmpp.TransportConfiguration{
Address: loadedConfig.Server,
@@ -141,7 +148,7 @@ func main() {
ConnectTimeout: 300,
TLSConfig: &tls.Config{InsecureSkipVerify: loadedConfig.Insecure},
},
- Jid: loadedConfig.Username + "/" + loadedConfig.Resource,
+ Jid: loadedConfig.Username + res,
Credential: xmpp.Password(loadedConfig.Password),
Insecure: loadedConfig.Insecure,
StreamManagementEnable: true,
@@ -161,6 +168,29 @@ func main() {
panic(err)
}
+ // All requests from either ourselves or our server will always be accepted regardless of user settings.
+
+
+ if !(loadedConfig.DiscoPrivacy == 0) && !((JidMustParse(iq.From).Bare() == JidMustParse(clientroot.Session.BindJid).Bare()) || (JidMustParse(iq.From).Domain == JidMustParse(clientroot.Session.BindJid).Domain)) {
+ if loadedConfig.DiscoPrivacy == None || (loadedConfig.DiscoPrivacy == OnlyRoster && !Roster[JidMustParse(iq.From).Bare()]) {
+ fmt.Println("blocking", loadedConfig.DiscoPrivacy)
+ if loadedConfig.IgnoreInsteadOfReturnErrorUponPrivacyViolation {
+ return
+ }
+
+ iqResp.Type = "error"
+
+ stanza_err := new(stanza.Err)
+ stanza_err.Type = stanza.ErrorTypeCancel
+ stanza_err.Text = "service-unavailable"
+
+ iqResp.MakeError(*stanza_err)
+ s.Send(iqResp)
+ return
+ }
+ }
+
+
identity := stanza.Identity{
Name: "Lambda",
Category: "client", // TODO: Allow spoofing on user request
@@ -182,6 +212,8 @@ func main() {
{Var: "urn:xmpp:attention:0"},
{Var: "urn:xmpp:carbons:2"},
{Var: "urn:xmpp:ping"},
+ {Var: "jabber:iq:oob"},
+ {Var: "jabber:x:oob"},
},
}
iqResp.Payload = &payload
@@ -192,7 +224,6 @@ func main() {
iq, ok := p.(*stanza.IQ)
if !ok || iq.Type != stanza.IQTypeGet {
return
-
}
iqResp, err := stanza.NewIQ(stanza.Attrs{Type: "result", From: iq.To, To: iq.From, Id: iq.Id})
@@ -200,6 +231,27 @@ func main() {
panic(err)
}
+ // All requests from either ourselves or our server will always be accepted regardless of user settings.
+
+ if !(loadedConfig.VersionPrivacy == 0) && !((JidMustParse(iq.From).Bare() == JidMustParse(clientroot.Session.BindJid).Bare()) || (JidMustParse(iq.From).Domain == JidMustParse(clientroot.Session.BindJid).Domain)) {
+ if loadedConfig.VersionPrivacy == None || (loadedConfig.VersionPrivacy == OnlyRoster && !Roster[JidMustParse(iq.From).Bare()]) {
+ fmt.Println("blocking", loadedConfig.DiscoPrivacy)
+ if loadedConfig.IgnoreInsteadOfReturnErrorUponPrivacyViolation {
+ return
+ }
+
+ iqResp.Type = "error"
+
+ stanza_err := new(stanza.Err)
+ stanza_err.Type = stanza.ErrorTypeCancel
+ stanza_err.Text = "service-unavailable"
+
+ iqResp.MakeError(*stanza_err)
+ s.Send(iqResp)
+ return
+ }
+ }
+
v := &stanza.Version{}
v = v.SetInfo(loadedLocale["appName"], lambda_version, runtime.GOOS) // TODO: Allow spoofing on user request
@@ -484,37 +536,6 @@ func main() {
}
}()
- // Throughput
- /*
- var oldsize int64
- var newsize int64
- var diff float64
- go func() {
- for {
- time.Sleep(5 * time.Second)
- stat, err := xmlLog.Stat()
- if err != nil {
- panic(err)
- }
-
- newsize = stat.Size()
- diff = float64(newsize-oldsize) / 1000
-
- ic := clientAssets["car"]
- if diff >= 25 {
- ic = clientAssets["car_high"]
- }
-
- glib.IdleAdd(func() {
- sStatus.SetText(fmt.Sprintf("%.2f%s", diff, loadedLocale["KBPerSecond"]))
- sIcon.SetFromPaintable(ic)
- })
-
- oldsize = newsize
- }
- }()
- */
-
glib.IdleAdd(func() {
connectionStatus.SetText(fmt.Sprintf("%s%s", loadedLocale["connectedAs"], JidMustParse(clientroot.Session.BindJid).Bare()))
connectionStatus.SetTooltipText(fmt.Sprintf("%s%s\n%s%t", loadedLocale["bindedJid"], clientroot.Session.BindJid, loadedLocale["usingTLS"], clientroot.Session.TlsEnabled))
@@ -583,6 +604,7 @@ func main() {
for _, v := range items.Items {
name := v.Name
jid := v.Jid
+ Roster[jid] = true
if name == "" {
name = jid
@@ -613,13 +635,13 @@ func main() {
go getAvatar(jid, jid, im)
/*
- go func() {
- new_im := getAvatar(jid, jid) // TODO: Use PEP avatar and do not use JID as hash
- glib.IdleAdd(func() {
- new_im.SetPixelSize(40)
- box.Prepend(new_im)
- })
- }()
+ go func() {
+ new_im := getAvatar(jid, jid) // TODO: Use PEP avatar and do not use JID as hash
+ glib.IdleAdd(func() {
+ new_im.SetPixelSize(40)
+ box.Prepend(new_im)
+ })
+ }()
*/
box.AddController(gesture1)
@@ -710,13 +732,13 @@ func main() {
go getAvatar(jid, jid, im)
/*
- go func() {
- new_im := getAvatar(jid, jid)
- glib.IdleAdd(func() {
- new_im.SetPixelSize(40)
- box.Prepend(new_im)
- })
- }()
+ go func() {
+ new_im := getAvatar(jid, jid)
+ glib.IdleAdd(func() {
+ new_im.SetPixelSize(40)
+ box.Prepend(new_im)
+ })
+ }()
*/
box.AddController(gesture1)
@@ -1014,13 +1036,13 @@ func activate(app *gtk.Application) {
go getAvatar(t, t, im)
/*
- go func() {
- new_im := getAvatar(t, t)
- glib.IdleAdd(func() {
- new_im.SetPixelSize(40)
- box.Prepend(new_im)
- })
- }()
+ go func() {
+ new_im := getAvatar(t, t)
+ glib.IdleAdd(func() {
+ new_im.SetPixelSize(40)
+ box.Prepend(new_im)
+ })
+ }()
*/
b := gtk.NewLabel(n)
@@ -1208,7 +1230,7 @@ func activate(app *gtk.Application) {
the_menuBar := gtk.NewPopoverMenuBarFromModel(the_menu)
app.SetMenubar(gio.NewMenu())
- window.SetTitle("Lambda")
+ window.SetTitle(loadedLocale["appName"])
window.Window.AddCSSClass("ssd")
window.Window.SetDefaultSize(500, 500)
menu = gtk.NewBox(gtk.OrientationVertical, 0)
diff --git a/types.go b/types.go
index d1d0b90..505ae1b 100644
--- a/types.go
+++ b/types.go
@@ -18,9 +18,17 @@ type chatTab struct {
type BlockingMode int
const (
- Drop BlockingMode = iota
- Hide
- Highlight
+ Drop BlockingMode = iota // Do not even process if the entity is blocked
+ Hide // Hide the message but allow the user to expand it if they wish (discord style)
+ Highlight // Just highlight the message in orange
+)
+
+type DiscoveryMode int
+
+const (
+ Everyone DiscoveryMode = iota // All entities can query
+ OnlyRoster // Only people on your roster can query
+ None // Nobody can query
)
type lambdaConfig struct {
@@ -48,6 +56,18 @@ type lambdaConfig struct {
BlockingMode BlockingMode
CheckUpdate string
+
+ RandomizeResource bool // If this is set to true, then the resource will be completely randomized by the server on every single connect
+
+ // Privacy settings
+ DiscoPrivacy DiscoveryMode // Whether or not Lambda will report its service discovery features and Identity. Reccomended mode is Everyone, Default is Everyone
+ VersionPrivacy DiscoveryMode // Whether or not Lambda will report its version information. Reccomended mode is Roster, Default is Everyone
+
+ IgnoreInsteadOfReturnErrorUponPrivacyViolation bool // If set to true, Lambda will simply ignore Disco and Query Version requests from entities that are untrusted as per the user's discovery mode. If set to false, a service-unavailable error will be reported.
+
+ // Spoofing
+
+ // TODO
}
type mucUnit struct {
diff --git a/version.go b/version.go
index 3307953..feb7fed 100644
--- a/version.go
+++ b/version.go
@@ -1,3 +1,3 @@
package main
-var lambda_version string = "6"
+var lambda_version string = "7"