aim-oscar-server/main.go

282 lines
7.4 KiB
Go
Raw Permalink Normal View History

2021-11-10 13:46:52 -05:00
package main
import (
2021-12-16 17:41:17 -05:00
"aim-oscar/models"
2021-12-16 19:45:32 -05:00
"aim-oscar/oscar"
2021-12-24 12:41:16 -05:00
"aim-oscar/services"
"aim-oscar/util"
"bytes"
2021-12-16 17:41:17 -05:00
"context"
2021-12-26 14:01:55 -05:00
"crypto/tls"
2021-12-16 17:41:17 -05:00
"database/sql"
2021-12-24 13:38:22 -05:00
"flag"
2021-11-10 13:46:52 -05:00
"fmt"
"log"
"net"
"os"
"os/signal"
2021-12-26 14:01:55 -05:00
"strings"
2021-11-10 13:46:52 -05:00
"syscall"
2021-12-16 19:45:32 -05:00
"time"
2021-11-24 11:59:53 -05:00
2021-12-18 17:10:00 -05:00
"github.com/pkg/errors"
2021-12-16 17:41:17 -05:00
"github.com/uptrace/bun"
2021-12-26 14:01:55 -05:00
"github.com/uptrace/bun/dialect/pgdialect"
"github.com/uptrace/bun/driver/pgdriver"
2021-12-16 17:41:17 -05:00
"github.com/uptrace/bun/extra/bundebug"
2021-11-10 13:46:52 -05:00
)
2021-12-24 13:38:22 -05:00
var (
OSCAR_HOST = "0.0.0.0"
OSCAR_PORT = "5190"
OSCAR_ADDRESS = OSCAR_HOST + ":" + OSCAR_PORT
OSCAR_BOS_HOST = OSCAR_HOST
OSCAR_BOS_PORT = OSCAR_PORT
OSCAR_BOS_ADDRESS = OSCAR_BOS_HOST + ":" + OSCAR_BOS_PORT
2021-12-26 14:01:55 -05:00
DB_URL = ""
DB_USER = ""
DB_PASSWORD = ""
2023-03-04 13:34:28 -05:00
DB_NAME = ""
2021-11-10 13:46:52 -05:00
)
2021-12-24 13:38:22 -05:00
func init() {
if oscarHost, ok := os.LookupEnv("OSCAR_HOST"); ok {
OSCAR_HOST = oscarHost
}
var oscarHost string
flag.StringVar(&oscarHost, "host", OSCAR_HOST, "Server hostname")
if oscarPort, ok := os.LookupEnv("OSCAR_PORT"); ok {
OSCAR_PORT = oscarPort
}
var oscarPort string
flag.StringVar(&oscarPort, "port", OSCAR_PORT, "Server port")
if oscarBOSHost, ok := os.LookupEnv("OSCAR_BOS_HOST"); ok {
OSCAR_BOS_HOST = oscarBOSHost
}
var oscarBOSHost string
flag.StringVar(&oscarBOSHost, "boshost", OSCAR_BOS_HOST, "BOS hostname")
if oscarBOSPort, ok := os.LookupEnv("OSCAR_BOS_PORT"); ok {
OSCAR_BOS_PORT = oscarBOSPort
}
var oscarBOSPort string
flag.StringVar(&oscarBOSPort, "bosport", OSCAR_BOS_PORT, "BOS port")
2021-12-26 14:01:55 -05:00
if dbUrl, ok := os.LookupEnv("DB_URL"); ok {
2021-12-27 16:15:47 -05:00
DB_URL = strings.TrimSpace(dbUrl)
2021-12-26 14:01:55 -05:00
}
2021-12-27 16:15:47 -05:00
if dbUser, ok := os.LookupEnv("DB_USER"); ok {
DB_USER = strings.TrimSpace(dbUser)
2021-12-26 14:01:55 -05:00
}
2021-12-27 16:15:47 -05:00
if dbPassword, ok := os.LookupEnv("DB_PASSWORD"); ok {
DB_PASSWORD = strings.TrimSpace(dbPassword)
2021-12-26 14:01:55 -05:00
}
2023-03-04 13:34:28 -05:00
if dbName, ok := os.LookupEnv("DB_NAME"); ok {
DB_NAME = strings.TrimSpace(dbName)
}
2021-12-24 13:38:22 -05:00
flag.Parse()
OSCAR_HOST = oscarHost
OSCAR_PORT = oscarPort
OSCAR_ADDRESS = OSCAR_HOST + ":" + OSCAR_PORT
OSCAR_BOS_HOST = oscarBOSHost
OSCAR_BOS_PORT = oscarBOSPort
OSCAR_BOS_ADDRESS = OSCAR_BOS_HOST + ":" + OSCAR_BOS_PORT
2021-12-26 14:01:55 -05:00
if DB_URL == "" {
log.Fatalln("DB Url not specified")
}
if DB_USER == "" {
log.Fatalln("DB User not specified")
}
if DB_PASSWORD == "" {
log.Fatalln("DB password not specified")
}
2023-03-04 13:34:28 -05:00
if DB_NAME == "" {
log.Fatalln("DB name not specified")
}
2021-12-26 14:01:55 -05:00
}
func main() {
pgconn := pgdriver.NewConnector(
pgdriver.WithNetwork("tcp"),
pgdriver.WithAddr(DB_URL),
pgdriver.WithTLSConfig(&tls.Config{InsecureSkipVerify: true}),
pgdriver.WithUser(DB_USER),
pgdriver.WithPassword(DB_PASSWORD),
2023-03-04 13:34:28 -05:00
pgdriver.WithDatabase(DB_NAME),
2021-12-26 14:01:55 -05:00
pgdriver.WithInsecure(true),
pgdriver.WithTimeout(5*time.Second),
pgdriver.WithDialTimeout(5*time.Second),
pgdriver.WithReadTimeout(5*time.Second),
pgdriver.WithWriteTimeout(5*time.Second),
)
log.Printf("DB URL: %s", DB_URL)
2021-12-26 14:01:55 -05:00
// Set up the DB
sqldb := sql.OpenDB(pgconn)
db := bun.NewDB(sqldb, pgdialect.New())
2021-12-16 19:45:32 -05:00
db.SetConnMaxIdleTime(15 * time.Second)
db.SetConnMaxLifetime(1 * time.Minute)
2021-12-16 17:41:17 -05:00
// Print all queries to stdout.
db.AddQueryHook(bundebug.NewQueryHook(bundebug.WithVerbose(true)))
2021-12-16 19:45:32 -05:00
// Register our DB models
db.RegisterModel((*models.User)(nil), (*models.Message)(nil), (*models.Buddy)(nil), (*models.EmailVerification)(nil))
2021-12-16 17:41:17 -05:00
2021-12-24 13:38:22 -05:00
listener, err := net.Listen("tcp", OSCAR_ADDRESS)
2021-11-10 13:46:52 -05:00
if err != nil {
fmt.Println("Error listening: ", err.Error())
os.Exit(1)
}
defer listener.Close()
2021-12-24 12:41:16 -05:00
sessionManager := NewSessionManager()
2021-12-18 22:25:05 -05:00
// Goroutine that listens for messages to deliver and tries to find a user socket to push them to
2021-12-24 12:41:16 -05:00
commCh, messageRoutine := MessageDelivery(sessionManager)
2021-12-18 22:54:50 -05:00
go messageRoutine(db)
2021-12-18 22:25:05 -05:00
2021-12-19 01:14:25 -05:00
// Goroutine that listens for users who change their online status and notifies their buddies
2021-12-24 12:41:16 -05:00
onlineCh, onlineRoutine := OnlineNotification(sessionManager)
2021-12-19 01:14:25 -05:00
go onlineRoutine(db)
2021-12-24 12:41:16 -05:00
serviceManager := NewServiceManager()
2021-12-24 13:38:22 -05:00
serviceManager.RegisterService(0x01, &services.GenericServiceControls{OnlineCh: onlineCh, ServerHostname: OSCAR_ADDRESS})
2021-12-24 12:41:16 -05:00
serviceManager.RegisterService(0x02, &services.LocationServices{OnlineCh: onlineCh})
2022-02-11 13:28:01 -05:00
serviceManager.RegisterService(0x03, &services.BuddyListManagement{OnlineCh: onlineCh})
2021-12-24 12:41:16 -05:00
serviceManager.RegisterService(0x04, &services.ICBM{CommCh: commCh})
2021-12-24 13:38:22 -05:00
serviceManager.RegisterService(0x17, &services.AuthorizationRegistrationService{BOSAddress: OSCAR_BOS_ADDRESS})
2021-12-24 12:41:16 -05:00
2021-12-18 17:10:00 -05:00
handleCloseFn := func(ctx context.Context, session *oscar.Session) {
log.Printf("%v disconnected", session.RemoteAddr())
user := models.UserFromContext(ctx)
if user != nil {
2021-12-19 17:17:47 -05:00
user.Status = models.UserStatusAway
2021-12-27 16:15:47 -05:00
user.Cipher = ""
2021-12-18 17:10:00 -05:00
if err := user.Update(ctx, db); err != nil {
2021-12-19 01:14:25 -05:00
log.Print(errors.Wrap(err, "could not set user as inactive"))
2021-12-18 17:10:00 -05:00
}
2021-12-19 01:14:25 -05:00
2021-12-27 16:15:47 -05:00
onlineCh <- user
sessionManager.RemoveSession(user.ScreenName)
2021-12-18 17:10:00 -05:00
}
}
handleFn := func(ctx context.Context, flap *oscar.FLAP) context.Context {
session, err := oscar.SessionFromContext(ctx)
if err != nil {
2022-01-22 20:57:27 -05:00
// TODO
log.Printf("no session in context. FLAP dump:\n%s\n", flap)
return ctx
}
if user := models.UserFromContext(ctx); user != nil {
fmt.Printf("%s (%v) ->\n%+v\n", user.ScreenName, session.RemoteAddr(), flap)
2021-12-18 17:10:00 -05:00
user.LastActivityAt = time.Now()
ctx = models.NewContextWithUser(ctx, user)
sessionManager.SetSession(user.ScreenName, session)
} else {
fmt.Printf("%v ->\n%+v\n", session.RemoteAddr(), flap)
}
if flap.Header.Channel == 1 {
// Is this a hello?
if bytes.Equal(flap.Data.Bytes(), []byte{0, 0, 0, 1}) {
2022-01-23 13:22:05 -05:00
log.Println("this is a hello")
return ctx
}
2021-12-24 12:41:16 -05:00
user, err := services.AuthenticateFLAPCookie(ctx, db, flap)
if err != nil {
log.Printf("Could not authenticate cookie: %s", err)
return ctx
}
ctx = models.NewContextWithUser(ctx, user)
// Send available services
servicesSnac := oscar.NewSNAC(1, 3)
2021-12-24 12:41:16 -05:00
for family := range services.ServiceVersions {
2021-12-19 01:14:25 -05:00
servicesSnac.Data.WriteUint16(family)
}
servicesFlap := oscar.NewFLAP(2)
servicesFlap.Data.WriteBinary(servicesSnac)
session.Send(servicesFlap)
return ctx
} else if flap.Header.Channel == 2 {
snac := &oscar.SNAC{}
2022-02-03 14:06:33 -05:00
if err := snac.UnmarshalBinary(flap.Data.Bytes()); err != nil {
log.Printf("could not unmarshal FLAP data: %w", err)
session.Disconnect()
handleCloseFn(ctx, session)
return ctx
}
fmt.Printf("%+v\n", snac)
if tlvs, err := oscar.UnmarshalTLVs(snac.Data.Bytes()); err == nil {
for _, tlv := range tlvs {
fmt.Printf("%+v\n", tlv)
}
} else {
fmt.Printf("%s\n\n", util.PrettyBytes(snac.Data.Bytes()))
}
2021-12-24 12:41:16 -05:00
if service, ok := serviceManager.GetService(snac.Header.Family); ok {
2021-12-19 01:14:25 -05:00
newCtx, err := service.HandleSNAC(ctx, db, snac)
2022-01-18 16:20:51 -05:00
if err != nil {
log.Printf("encountered error: %s", err)
session.Disconnect()
handleCloseFn(ctx, session)
}
return newCtx
}
} else if flap.Header.Channel == 4 {
session.Disconnect()
2021-12-18 17:10:00 -05:00
handleCloseFn(ctx, session)
}
return ctx
2021-12-18 17:10:00 -05:00
}
handler := oscar.NewHandler(handleFn, handleCloseFn)
2021-11-10 13:46:52 -05:00
exitChan := make(chan os.Signal, 1)
signal.Notify(exitChan, os.Interrupt, syscall.SIGINT, syscall.SIGTERM, syscall.SIGABRT)
go func() {
<-exitChan
2021-12-18 22:25:05 -05:00
close(commCh)
2021-12-19 01:14:25 -05:00
close(onlineCh)
2021-11-10 13:46:52 -05:00
fmt.Println("Shutting down")
os.Exit(1)
}()
2021-12-24 13:38:22 -05:00
fmt.Println("OSCAR listening on " + OSCAR_ADDRESS)
fmt.Println("OSCAR BOS set to " + OSCAR_BOS_ADDRESS)
2021-11-10 13:46:52 -05:00
for {
conn, err := listener.Accept()
if err != nil {
fmt.Println("Error accepting connection: ", err.Error())
os.Exit(1)
}
log.Printf("Connection from %v", conn.RemoteAddr())
2021-12-16 19:45:32 -05:00
go handler.Handle(conn)
}
2021-11-10 13:46:52 -05:00
}