# Description of the server as it appears on the tracker
Description: A default configured Hotline server running Mobius
-# Server banner ID; currently implemented
-BannerID: 0
+# Path to server banner image. Only known to work in the 1.8 clients.
+# * The banner must be under 256K (262,140 bytes specifically)
+# * The standard size for a banner is 468 pixels wide and 60 pixels tall.
+# * The banner must be saved in the same folder this file.
+# * The banner must be a jpg
+BannerFile: "banner.jpg"
# Path to the Files directory, by default in a subdirectory of the config root named Files
FileRoot: Files
type Config struct {
Name string `yaml:"Name" validate:"required,max=50"` // Name used for Tracker registration
Description string `yaml:"Description" validate:"required,max=200"` // Description used for Tracker registration
- BannerID int `yaml:"BannerID"` // Unimplemented
+ BannerFile string `yaml:"BannerFile"` // Path to banner jpg
FileRoot string `yaml:"FileRoot" validate:"required"` // Path to Files
EnableTrackerRegistration bool `yaml:"EnableTrackerRegistration"` // Toggle Tracker Registration
Trackers []string `yaml:"Trackers" validate:"dive,hostname_port"` // List of trackers that the server should register with
const fieldChatID = 114
const fieldChatSubject = 115
const fieldWaitingCount = 116
+const fieldBannerType = 152
const fieldVersion = 160
const fieldCommunityBannerID = 161
const fieldServerName = 162
FileUpload = 1
FolderDownload = 2
FolderUpload = 3
+ bannerDownload = 4
)
type FileTransfer struct {
s.outbox <- c.NewReply(clientLogin,
NewField(fieldVersion, []byte{0x00, 0xbe}),
- NewField(fieldCommunityBannerID, []byte{0x00, 0x01}),
+ NewField(fieldCommunityBannerID, []byte{0, 0}),
NewField(fieldServerName, []byte(s.Config.Name)),
)
)
switch fileTransfer.Type {
+ case bannerDownload:
+ if err := s.bannerDownload(rwc); err != nil {
+ return err
+ }
case FileDownload:
s.Stats.DownloadCounter += 1
tranNotifyChatSubject = 119
tranSetChatSubject = 120
tranAgreed = 121
+ tranServerBanner = 122
tranGetFileNameList = 200
tranDownloadFile = 202
tranUploadFile = 203
tranMakeFileAlias = 209
tranDownloadFldr = 210
// tranDownloadInfo = 211 TODO: implement file transfer queue
- // tranDownloadBanner = 212 TODO: figure out what this is used for
+ tranDownloadBanner = 212
tranUploadFldr = 213
tranGetUserNameList = 300
tranNotifyChangeUser = 301
Name: "tranUserBroadcast",
Handler: HandleUserBroadcast,
},
+ tranDownloadBanner: {
+ Name: "tranDownloadBanner",
+ Handler: HandleDownloadBanner,
+ },
}
func HandleChatSend(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
cc.Server.outbox <- t
}
+ if cc.Server.Config.BannerFile != "" {
+ cc.Server.outbox <- *NewTransaction(tranServerBanner, cc.ID, NewField(fieldBannerType, []byte("JPEG")))
+ }
+
res = append(res, cc.NewReply(t))
return res, err
res = append(res, cc.NewReply(t))
return res, err
}
+
+func HandleDownloadBanner(cc *ClientConn, t *Transaction) (res []Transaction, err error) {
+ transactionRef := cc.Server.NewTransactionRef()
+ data := binary.BigEndian.Uint32(transactionRef)
+
+ ft := &FileTransfer{
+ ReferenceNumber: transactionRef,
+ Type: bannerDownload,
+ }
+
+ fi, err := cc.Server.FS.Stat(filepath.Join(cc.Server.ConfigDir, cc.Server.Config.BannerFile))
+ if err != nil {
+ return res, err
+ }
+
+ size := make([]byte, 4)
+ binary.BigEndian.PutUint32(size, uint32(fi.Size()))
+
+ cc.Server.mux.Lock()
+ defer cc.Server.mux.Unlock()
+ cc.Server.FileTransfers[data] = ft
+
+ res = append(res, cc.NewReply(t,
+ NewField(fieldRefNum, transactionRef),
+ NewField(fieldTransferSize, size),
+ ))
+
+ res = append(res, cc.NewReply(t))
+ return res, err
+}
"encoding/binary"
"errors"
"io"
+ "os"
+ "path/filepath"
)
type transfer struct {
return err
}
}
+}
+
+func (s *Server) bannerDownload(w io.Writer) error {
+ bannerBytes, err := os.ReadFile(filepath.Join(s.ConfigDir, s.Config.BannerFile))
+ if err != nil {
+ return err
+ }
+ _, err = w.Write(bannerBytes)
+ return err
}