X-Git-Url: https://git.r.bdr.sh/rbdr/mobius/blobdiff_plain/b129b7cbc9fd9a9c11a77e5922861ef08893efa1..a55350daaf83498b7a237c027ad0dd2377f06fee:/hotline/transaction_handlers.go diff --git a/hotline/transaction_handlers.go b/hotline/transaction_handlers.go index 793bb74..3d7ba14 100644 --- a/hotline/transaction_handlers.go +++ b/hotline/transaction_handlers.go @@ -1,6 +1,7 @@ package hotline import ( + "bufio" "bytes" "encoding/binary" "errors" @@ -372,6 +373,8 @@ func HandleSendInstantMsg(cc *ClientConn, t *Transaction) (res []Transaction, er return res, err } +var fileTypeFLDR = [4]byte{0x66, 0x6c, 0x64, 0x72} + func HandleGetFileInfo(cc *ClientConn, t *Transaction) (res []Transaction, err error) { fileName := t.GetField(FieldFileName).Data filePath := t.GetField(FieldFilePath).Data @@ -395,9 +398,9 @@ func HandleGetFileInfo(cc *ClientConn, t *Transaction) (res []Transaction, err e NewField(FieldFileName, []byte(encodedName)), NewField(FieldFileTypeString, fw.ffo.FlatFileInformationFork.friendlyType()), NewField(FieldFileCreatorString, fw.ffo.FlatFileInformationFork.friendlyCreator()), - NewField(FieldFileType, fw.ffo.FlatFileInformationFork.TypeSignature), - NewField(FieldFileCreateDate, fw.ffo.FlatFileInformationFork.CreateDate), - NewField(FieldFileModifyDate, fw.ffo.FlatFileInformationFork.ModifyDate), + NewField(FieldFileType, fw.ffo.FlatFileInformationFork.TypeSignature[:]), + NewField(FieldFileCreateDate, fw.ffo.FlatFileInformationFork.CreateDate[:]), + NewField(FieldFileModifyDate, fw.ffo.FlatFileInformationFork.ModifyDate[:]), } // Include the optional FileComment field if there is a comment. @@ -406,7 +409,7 @@ func HandleGetFileInfo(cc *ClientConn, t *Transaction) (res []Transaction, err e } // Include the FileSize field for files. - if !bytes.Equal(fw.ffo.FlatFileInformationFork.TypeSignature, []byte{0x66, 0x6c, 0x64, 0x72}) { + if fw.ffo.FlatFileInformationFork.TypeSignature != fileTypeFLDR { fields = append(fields, NewField(FieldFileSize, fw.totalSize())) } @@ -414,11 +417,11 @@ func HandleGetFileInfo(cc *ClientConn, t *Transaction) (res []Transaction, err e return res, err } -// HandleSetFileInfo updates a file or folder name and/or comment from the Get Info window +// HandleSetFileInfo updates a file or folder Name and/or comment from the Get Info window // Fields used in the request: -// * 201 File name +// * 201 File Name // * 202 File path Optional -// * 211 File new name Optional +// * 211 File new Name Optional // * 210 File comment Optional // Fields used in the reply: None func HandleSetFileInfo(cc *ClientConn, t *Transaction) (res []Transaction, err error) { @@ -460,7 +463,7 @@ func HandleSetFileInfo(cc *ClientConn, t *Transaction) (res []Transaction, err e if err != nil { return res, err } - _, err = w.Write(hlFile.ffo.FlatFileInformationFork.MarshalBinary()) + _, err = io.Copy(w, &hlFile.ffo.FlatFileInformationFork) if err != nil { return res, err } @@ -516,7 +519,7 @@ func HandleSetFileInfo(cc *ClientConn, t *Transaction) (res []Transaction, err e // HandleDeleteFile deletes a file or folder // Fields used in the request: -// * 201 File name +// * 201 File Name // * 202 File path // Fields used in the reply: none func HandleDeleteFile(cc *ClientConn, t *Transaction) (res []Transaction, err error) { @@ -574,7 +577,7 @@ func HandleMoveFile(cc *ClientConn, t *Transaction) (res []Transaction, err erro return res, err } - cc.logger.Infow("Move file", "src", filePath+"/"+fileName, "dst", fileNewPath+"/"+fileName) + cc.logger.Info("Move file", "src", filePath+"/"+fileName, "dst", fileNewPath+"/"+fileName) hlFile, err := newFileWrapper(cc.Server.FS, filePath, 0) if err != nil { @@ -636,10 +639,10 @@ func HandleNewFolder(cc *ClientConn, t *Transaction) (res []Transaction, err err return res, fmt.Errorf("invalid filepath encoding: %w", err) } - // TODO: check path and folder name lengths + // TODO: check path and folder Name lengths if _, err := cc.Server.FS.Stat(newFolderPath); !os.IsNotExist(err) { - msg := fmt.Sprintf("Cannot create folder \"%s\" because there is already a file or folder with that name.", folderName) + msg := fmt.Sprintf("Cannot create folder \"%s\" because there is already a file or folder with that Name.", folderName) return []Transaction{cc.NewErrReply(t, msg)}, nil } @@ -658,7 +661,7 @@ func HandleSetUser(cc *ClientConn, t *Transaction) (res []Transaction, err error return res, err } - login := decodeString(t.GetField(FieldUserLogin).Data) + login := string(encodeString(t.GetField(FieldUserLogin).Data)) userName := string(t.GetField(FieldUserName).Data) newAccessLvl := t.GetField(FieldUserAccess).Data @@ -748,7 +751,8 @@ func HandleListUsers(cc *ClientConn, t *Transaction) (res []Transaction, err err var userFields []Field for _, acc := range cc.Server.Accounts { - b, err := io.ReadAll(acc) + accCopy := *acc + b, err := io.ReadAll(&accCopy) if err != nil { return res, err } @@ -771,9 +775,20 @@ func HandleListUsers(cc *ClientConn, t *Transaction) (res []Transaction, err err // performed. This seems to be the only place in the Hotline protocol where a data field contains another data field. func HandleUpdateUser(cc *ClientConn, t *Transaction) (res []Transaction, err error) { for _, field := range t.Fields { - subFields, err := ReadFields(field.Data[0:2], field.Data[2:]) - if err != nil { - return res, err + var subFields []Field + + // Create a new scanner for parsing incoming bytes into transaction tokens + scanner := bufio.NewScanner(bytes.NewReader(field.Data[2:])) + scanner.Split(fieldScanner) + + for i := 0; i < int(binary.BigEndian.Uint16(field.Data[0:2])); i++ { + scanner.Scan() + + var field Field + if _, err := field.Write(scanner.Bytes()); err != nil { + return res, fmt.Errorf("error reading field: %w", err) + } + subFields = append(subFields, field) } // If there's only one subfield, that indicates this is a delete operation for the login in FieldData @@ -783,8 +798,8 @@ func HandleUpdateUser(cc *ClientConn, t *Transaction) (res []Transaction, err er return res, err } - login := decodeString(getField(FieldData, &subFields).Data) - cc.logger.Infow("DeleteUser", "login", login) + login := string(encodeString(getField(FieldData, &subFields).Data)) + cc.logger.Info("DeleteUser", "login", login) if err := cc.Server.DeleteUser(login); err != nil { return res, err @@ -798,9 +813,9 @@ func HandleUpdateUser(cc *ClientConn, t *Transaction) (res []Transaction, err er // If FieldData is included, this is a rename operation where FieldData contains the login of the existing // account and FieldUserLogin contains the new login. if getField(FieldData, &subFields) != nil { - loginToRename = decodeString(getField(FieldData, &subFields).Data) + loginToRename = string(encodeString(getField(FieldData, &subFields).Data)) } - userLogin := decodeString(getField(FieldUserLogin, &subFields).Data) + userLogin := string(encodeString(getField(FieldUserLogin, &subFields).Data)) if loginToRename != "" { accountToUpdate = loginToRename } else { @@ -810,9 +825,9 @@ func HandleUpdateUser(cc *ClientConn, t *Transaction) (res []Transaction, err er // Check if accountToUpdate has an existing account. If so, we know we are updating an existing user. if acc, ok := cc.Server.Accounts[accountToUpdate]; ok { if loginToRename != "" { - cc.logger.Infow("RenameUser", "prevLogin", accountToUpdate, "newLogin", userLogin) + cc.logger.Info("RenameUser", "prevLogin", accountToUpdate, "newLogin", userLogin) } else { - cc.logger.Infow("UpdateUser", "login", accountToUpdate) + cc.logger.Info("UpdateUser", "login", accountToUpdate) } // account exists, so this is an update action @@ -842,8 +857,8 @@ func HandleUpdateUser(cc *ClientConn, t *Transaction) (res []Transaction, err er } err = cc.Server.UpdateUser( - decodeString(getField(FieldData, &subFields).Data), - decodeString(getField(FieldUserLogin, &subFields).Data), + string(encodeString(getField(FieldData, &subFields).Data)), + string(encodeString(getField(FieldUserLogin, &subFields).Data)), string(getField(FieldUserName, &subFields).Data), acc.Password, acc.Access, @@ -857,7 +872,7 @@ func HandleUpdateUser(cc *ClientConn, t *Transaction) (res []Transaction, err er return res, nil } - cc.logger.Infow("CreateUser", "login", userLogin) + cc.logger.Info("CreateUser", "login", userLogin) newAccess := accessBitmap{} copy(newAccess[:], getField(FieldUserAccess, &subFields).Data) @@ -889,7 +904,7 @@ func HandleNewUser(cc *ClientConn, t *Transaction) (res []Transaction, err error return res, err } - login := decodeString(t.GetField(FieldUserLogin).Data) + login := string(encodeString(t.GetField(FieldUserLogin).Data)) // If the account already dataFile, reply with an error if _, ok := cc.Server.Accounts[login]; ok { @@ -925,7 +940,7 @@ func HandleDeleteUser(cc *ClientConn, t *Transaction) (res []Transaction, err er return res, nil } - login := decodeString(t.GetField(FieldUserLogin).Data) + login := string(encodeString(t.GetField(FieldUserLogin).Data)) if err := cc.Server.DeleteUser(login); err != nil { return res, err @@ -958,7 +973,7 @@ func HandleUserBroadcast(cc *ClientConn, t *Transaction) (res []Transaction, err // 103 User ID // // Fields used in the reply: -// 102 User name +// 102 User Name // 101 Data User info text string func HandleGetClientInfoText(cc *ClientConn, t *Transaction) (res []Transaction, err error) { if !cc.Authorize(accessGetClientInfo) { @@ -981,9 +996,7 @@ func HandleGetClientInfoText(cc *ClientConn, t *Transaction) (res []Transaction, } func HandleGetUserNameList(cc *ClientConn, t *Transaction) (res []Transaction, err error) { - res = append(res, cc.NewReply(t, cc.Server.connectedUsers()...)) - - return res, err + return []Transaction{cc.NewReply(t, cc.Server.connectedUsers()...)}, nil } func HandleTranAgreed(cc *ClientConn, t *Transaction) (res []Transaction, err error) { @@ -997,8 +1010,8 @@ func HandleTranAgreed(cc *ClientConn, t *Transaction) (res []Transaction, err er cc.Icon = t.GetField(FieldUserIconID).Data - cc.logger = cc.logger.With("name", string(cc.UserName)) - cc.logger.Infow("Login successful", "clientVersion", fmt.Sprintf("%v", func() int { i, _ := byteToInt(cc.Version); return i }())) + cc.logger = cc.logger.With("Name", string(cc.UserName)) + cc.logger.Info("Login successful", "clientVersion", fmt.Sprintf("%v", func() int { i, _ := byteToInt(cc.Version); return i }())) options := t.GetField(FieldOptions).Data optBitmap := big.NewInt(int64(binary.BigEndian.Uint16(options))) @@ -1006,19 +1019,19 @@ func HandleTranAgreed(cc *ClientConn, t *Transaction) (res []Transaction, err er flagBitmap := big.NewInt(int64(binary.BigEndian.Uint16(cc.Flags))) // Check refuse private PM option - if optBitmap.Bit(refusePM) == 1 { + if optBitmap.Bit(UserOptRefusePM) == 1 { flagBitmap.SetBit(flagBitmap, UserFlagRefusePM, 1) binary.BigEndian.PutUint16(cc.Flags, uint16(flagBitmap.Int64())) } // Check refuse private chat option - if optBitmap.Bit(refuseChat) == 1 { + if optBitmap.Bit(UserOptRefuseChat) == 1 { flagBitmap.SetBit(flagBitmap, UserFlagRefusePChat, 1) binary.BigEndian.PutUint16(cc.Flags, uint16(flagBitmap.Int64())) } // Check auto response - if optBitmap.Bit(autoResponse) == 1 { + if optBitmap.Bit(UserOptAutoResponse) == 1 { cc.AutoReply = t.GetField(FieldAutomaticResponse).Data } else { cc.AutoReply = []byte{} @@ -1107,7 +1120,7 @@ func HandleDisconnectUser(cc *ClientConn, t *Transaction) (res []Transaction, er switch t.GetField(FieldOptions).Data[1] { case 1: // send message: "You are temporarily banned on this server" - cc.logger.Infow("Disconnect & temporarily ban " + string(clientConn.UserName)) + cc.logger.Info("Disconnect & temporarily ban " + string(clientConn.UserName)) res = append(res, *NewTransaction( TranServerMsg, @@ -1120,7 +1133,7 @@ func HandleDisconnectUser(cc *ClientConn, t *Transaction) (res []Transaction, er cc.Server.banList[strings.Split(clientConn.RemoteAddr, ":")[0]] = &banUntil case 2: // send message: "You are permanently banned on this server" - cc.logger.Infow("Disconnect & ban " + string(clientConn.UserName)) + cc.logger.Info("Disconnect & ban " + string(clientConn.UserName)) res = append(res, *NewTransaction( TranServerMsg, @@ -1194,7 +1207,7 @@ func HandleNewNewsCat(cc *ClientConn, t *Transaction) (res []Transaction, err er cats := cc.Server.GetNewsCatByPath(pathStrs) cats[name] = NewsCategoryListData15{ Name: name, - Type: []byte{0, 3}, + Type: [2]byte{0, 3}, Articles: map[uint32]*NewsArtData{}, SubCats: make(map[string]NewsCategoryListData15), } @@ -1207,7 +1220,7 @@ func HandleNewNewsCat(cc *ClientConn, t *Transaction) (res []Transaction, err er } // Fields used in the request: -// 322 News category name +// 322 News category Name // 325 News path func HandleNewNewsFldr(cc *ClientConn, t *Transaction) (res []Transaction, err error) { if !cc.Authorize(accessNewsCreateFldr) { @@ -1218,12 +1231,10 @@ func HandleNewNewsFldr(cc *ClientConn, t *Transaction) (res []Transaction, err e name := string(t.GetField(FieldFileName).Data) pathStrs := ReadNewsPath(t.GetField(FieldNewsPath).Data) - cc.logger.Infof("Creating new news folder %s", name) - cats := cc.Server.GetNewsCatByPath(pathStrs) cats[name] = NewsCategoryListData15{ Name: name, - Type: []byte{0, 2}, + Type: [2]byte{0, 2}, Articles: map[uint32]*NewsArtData{}, SubCats: make(map[string]NewsCategoryListData15), } @@ -1244,8 +1255,9 @@ func HandleNewNewsFldr(cc *ClientConn, t *Transaction) (res []Transaction, err e func HandleGetNewsArtNameList(cc *ClientConn, t *Transaction) (res []Transaction, err error) { if !cc.Authorize(accessNewsReadArt) { res = append(res, cc.NewErrReply(t, "You are not allowed to read news.")) - return res, err + return res, nil } + pathStrs := ReadNewsPath(t.GetField(FieldNewsPath).Data) var cat NewsCategoryListData15 @@ -1258,8 +1270,13 @@ func HandleGetNewsArtNameList(cc *ClientConn, t *Transaction) (res []Transaction nald := cat.GetNewsArtListData() - res = append(res, cc.NewReply(t, NewField(FieldNewsArtListData, nald.Payload()))) - return res, err + b, err := io.ReadAll(&nald) + if err != nil { + return res, fmt.Errorf("error loading news articles: %w", err) + } + + res = append(res, cc.NewReply(t, NewField(FieldNewsArtListData, b))) + return res, nil } // HandleGetNewsArtData requests information about the specific news article. @@ -1310,11 +1327,11 @@ func HandleGetNewsArtData(cc *ClientConn, t *Transaction) (res []Transaction, er res = append(res, cc.NewReply(t, NewField(FieldNewsArtTitle, []byte(art.Title)), NewField(FieldNewsArtPoster, []byte(art.Poster)), - NewField(FieldNewsArtDate, art.Date), - NewField(FieldNewsArtPrevArt, art.PrevArt), - NewField(FieldNewsArtNextArt, art.NextArt), - NewField(FieldNewsArtParentArt, art.ParentArt), - NewField(FieldNewsArt1stChildArt, art.FirstChildArt), + NewField(FieldNewsArtDate, art.Date[:]), + NewField(FieldNewsArtPrevArt, art.PrevArt[:]), + NewField(FieldNewsArtNextArt, art.NextArt[:]), + NewField(FieldNewsArtParentArt, art.ParentArt[:]), + NewField(FieldNewsArt1stChildArt, art.FirstChildArt[:]), NewField(FieldNewsArtDataFlav, []byte("text/plain")), NewField(FieldNewsArtData, []byte(art.Data)), )) @@ -1337,7 +1354,7 @@ func HandleDelNewsItem(cc *ClientConn, t *Transaction) (res []Transaction, err e } } - if bytes.Equal(cats[delName].Type, []byte{0, 3}) { + if cats[delName].Type == [2]byte{0, 3} { if !cc.Authorize(accessNewsDeleteCat) { return append(res, cc.NewErrReply(t, "You are not allowed to delete news categories.")), nil } @@ -1416,14 +1433,17 @@ func HandlePostNewsArt(cc *ClientConn, t *Transaction) (res []Transaction, err e bs := make([]byte, 4) binary.BigEndian.PutUint32(bs, convertedArtID) + cc.Server.mux.Lock() + defer cc.Server.mux.Unlock() + newArt := NewsArtData{ Title: string(t.GetField(FieldNewsArtTitle).Data), Poster: string(cc.UserName), Date: toHotlineTime(time.Now()), - PrevArt: []byte{0, 0, 0, 0}, - NextArt: []byte{0, 0, 0, 0}, - ParentArt: bs, - FirstChildArt: []byte{0, 0, 0, 0}, + PrevArt: [4]byte{}, + NextArt: [4]byte{}, + ParentArt: [4]byte(bs), + FirstChildArt: [4]byte{}, DataFlav: []byte("text/plain"), Data: string(t.GetField(FieldNewsArtData).Data), } @@ -1439,10 +1459,10 @@ func HandlePostNewsArt(cc *ClientConn, t *Transaction) (res []Transaction, err e prevID := uint32(keys[len(keys)-1]) nextID = prevID + 1 - binary.BigEndian.PutUint32(newArt.PrevArt, prevID) + binary.BigEndian.PutUint32(newArt.PrevArt[:], prevID) // Set next article ID - binary.BigEndian.PutUint32(cat.Articles[prevID].NextArt, nextID) + binary.BigEndian.PutUint32(cat.Articles[prevID].NextArt[:], nextID) } // Update parent article with first child reply @@ -1450,8 +1470,8 @@ func HandlePostNewsArt(cc *ClientConn, t *Transaction) (res []Transaction, err e if parentID != 0 { parentArt := cat.Articles[parentID] - if bytes.Equal(parentArt.FirstChildArt, []byte{0, 0, 0, 0}) { - binary.BigEndian.PutUint32(parentArt.FirstChildArt, nextID) + if parentArt.FirstChildArt == [4]byte{0, 0, 0, 0} { + binary.BigEndian.PutUint32(parentArt.FirstChildArt[:], nextID) } } @@ -1579,7 +1599,7 @@ func HandleDownloadFolder(cc *ClientConn, t *Transaction) (res []Transaction, er // Upload all files from the local folder and its subfolders to the specified path on the server // Fields used in the request -// 201 File name +// 201 File Name // 202 File path // 108 transfer size Total size of all items in the folder // 220 Folder item count @@ -1614,7 +1634,7 @@ func HandleUploadFolder(cc *ClientConn, t *Transaction) (res []Transaction, err // HandleUploadFile // Fields used in the request: -// 201 File name +// 201 File Name // 202 File path // 204 File transfer options "Optional // Used only to resume download, currently has value 2" @@ -1650,7 +1670,7 @@ func HandleUploadFile(cc *ClientConn, t *Transaction) (res []Transaction, err er } if _, err := cc.Server.FS.Stat(fullFilePath); err == nil { - res = append(res, cc.NewErrReply(t, fmt.Sprintf("Cannot accept upload because there is already a file named \"%v\". Try choosing a different name.", string(fileName)))) + res = append(res, cc.NewErrReply(t, fmt.Sprintf("Cannot accept upload because there is already a file named \"%v\". Try choosing a different Name.", string(fileName)))) return res, err } @@ -1699,14 +1719,14 @@ func HandleSetClientUserInfo(cc *ClientConn, t *Transaction) (res []Transaction, optBitmap := big.NewInt(int64(binary.BigEndian.Uint16(options))) flagBitmap := big.NewInt(int64(binary.BigEndian.Uint16(cc.Flags))) - flagBitmap.SetBit(flagBitmap, UserFlagRefusePM, optBitmap.Bit(refusePM)) + flagBitmap.SetBit(flagBitmap, UserFlagRefusePM, optBitmap.Bit(UserOptRefusePM)) binary.BigEndian.PutUint16(cc.Flags, uint16(flagBitmap.Int64())) - flagBitmap.SetBit(flagBitmap, UserFlagRefusePChat, optBitmap.Bit(refuseChat)) + flagBitmap.SetBit(flagBitmap, UserFlagRefusePChat, optBitmap.Bit(UserOptRefuseChat)) binary.BigEndian.PutUint16(cc.Flags, uint16(flagBitmap.Int64())) // Check auto response - if optBitmap.Bit(autoResponse) == 1 { + if optBitmap.Bit(UserOptAutoResponse) == 1 { cc.AutoReply = t.GetField(FieldAutomaticResponse).Data } else { cc.AutoReply = []byte{} @@ -1743,13 +1763,13 @@ func HandleGetFileNameList(cc *ClientConn, t *Transaction) (res []Transaction, e nil, ) if err != nil { - return res, err + return res, fmt.Errorf("error reading file path: %w", err) } var fp FilePath if t.GetField(FieldFilePath).Data != nil { if _, err = fp.Write(t.GetField(FieldFilePath).Data); err != nil { - return res, err + return res, fmt.Errorf("error writing file path: %w", err) } } @@ -1761,7 +1781,7 @@ func HandleGetFileNameList(cc *ClientConn, t *Transaction) (res []Transaction, e fileNames, err := getFileNameList(fullPath, cc.Server.Config.IgnoreFiles) if err != nil { - return res, err + return res, fmt.Errorf("getFileNameList: %w", err) } res = append(res, cc.NewReply(t, fileNames...)) @@ -1891,7 +1911,7 @@ func HandleRejectChatInvite(cc *ClientConn, t *Transaction) (res []Transaction, // HandleJoinChat is sent from a v1.8+ Hotline client when the joins a private chat // Fields used in the reply: // * 115 Chat subject -// * 300 User name with info (Optional) +// * 300 User Name with info (Optional) // * 300 (more user names with info) func HandleJoinChat(cc *ClientConn, t *Transaction) (res []Transaction, err error) { chatID := t.GetField(FieldChatID).Data @@ -1918,14 +1938,16 @@ func HandleJoinChat(cc *ClientConn, t *Transaction) (res []Transaction, err erro replyFields := []Field{NewField(FieldChatSubject, []byte(privChat.Subject))} for _, c := range sortedClients(privChat.ClientConn) { - user := User{ + b, err := io.ReadAll(&User{ ID: *c.ID, Icon: c.Icon, Flags: c.Flags, Name: string(c.UserName), + }) + if err != nil { + return res, nil } - - replyFields = append(replyFields, NewField(FieldUsernameWithInfo, user.Payload())) + replyFields = append(replyFields, NewField(FieldUsernameWithInfo, b)) } res = append(res, cc.NewReply(t, replyFields...)) @@ -1991,7 +2013,7 @@ func HandleSetChatSubject(cc *ClientConn, t *Transaction) (res []Transaction, er // HandleMakeAlias makes a file alias using the specified path. // Fields used in the request: -// 201 File name +// 201 File Name // 202 File path // 212 File new path Destination path // @@ -2016,7 +2038,7 @@ func HandleMakeAlias(cc *ClientConn, t *Transaction) (res []Transaction, err err return res, err } - cc.logger.Debugw("Make alias", "src", fullFilePath, "dst", fullNewFilePath) + cc.logger.Debug("Make alias", "src", fullFilePath, "dst", fullNewFilePath) if err := cc.Server.FS.Symlink(fullFilePath, fullNewFilePath); err != nil { res = append(res, cc.NewErrReply(t, "Error creating alias")) @@ -2034,19 +2056,11 @@ func HandleMakeAlias(cc *ClientConn, t *Transaction) (res []Transaction, err err // 107 FieldRefNum Used later for transfer // 108 FieldTransferSize Size of data to be downloaded func HandleDownloadBanner(cc *ClientConn, t *Transaction) (res []Transaction, err error) { - fi, err := cc.Server.FS.Stat(filepath.Join(cc.Server.ConfigDir, cc.Server.Config.BannerFile)) - if err != nil { - return res, err - } - ft := cc.newFileTransfer(bannerDownload, []byte{}, []byte{}, make([]byte, 4)) + binary.BigEndian.PutUint32(ft.TransferSize, uint32(len(cc.Server.banner))) - binary.BigEndian.PutUint32(ft.TransferSize, uint32(fi.Size())) - - res = append(res, cc.NewReply(t, + return append(res, cc.NewReply(t, NewField(FieldRefNum, ft.refNum[:]), NewField(FieldTransferSize, ft.TransferSize), - )) - - return res, err + )), err }