dkforest

A forum and chat platform (onion)
git clone https://git.dasho.dev/n0tr1v/dkforest.git
Log | Files | Refs | LICENSE

topBarHandler.go (9784B)


      1 package v1
      2 
      3 import (
      4 	"dkforest/pkg/clockwork"
      5 	"dkforest/pkg/config"
      6 	"dkforest/pkg/database"
      7 	dutils "dkforest/pkg/database/utils"
      8 	"dkforest/pkg/utils"
      9 	"dkforest/pkg/web/handlers/interceptors"
     10 	"dkforest/pkg/web/handlers/interceptors/command"
     11 	"dkforest/pkg/web/handlers/streamModals"
     12 	"dkforest/pkg/web/handlers/utils/stream"
     13 	"errors"
     14 	"fmt"
     15 	"github.com/dustin/go-humanize"
     16 	"github.com/labstack/echo"
     17 	"net/http"
     18 	"net/url"
     19 	"strings"
     20 	"time"
     21 )
     22 
     23 func getDataMessagePrefix(db *database.DkfDB, c echo.Context, roomKey string, room database.ChatRoom, authUser *database.User) (out string, err error) {
     24 	pm := c.QueryParam(command.RedirectPmQP)
     25 	edit := c.QueryParam(command.RedirectEditQP)
     26 	group := c.QueryParam(command.RedirectGroupQP)
     27 	mod := c.QueryParam(command.RedirectModQP)
     28 	hbm := c.QueryParam(command.RedirectHbmQP)
     29 	tag := c.QueryParam(command.RedirectTagQP)
     30 	htag := c.QueryParam(command.RedirectHTagQP)
     31 	mtag := c.QueryParam(command.RedirectMTagQP)
     32 	quote := c.QueryParam(command.RedirectQuoteQP)
     33 
     34 	if pm != "" {
     35 		out = "/pm " + pm + " "
     36 	} else if hbm != "" {
     37 		out = "/hbm "
     38 	} else if mod != "" {
     39 		out = "/m "
     40 	} else if group != "" {
     41 		out = "/g " + group + " "
     42 	} else if tag != "" {
     43 		out = "@" + tag + " "
     44 	} else if htag != "" {
     45 		out = "/hbm @" + htag + " "
     46 	} else if mtag != "" {
     47 		out = "/m @" + mtag + " "
     48 	} else if edit != "" {
     49 		out, err = handleGetEdit(db, edit, roomKey, room, authUser)
     50 		if err != nil {
     51 			return
     52 		}
     53 	} else if quote != "" {
     54 		out, err = handleGetQuote(db, quote, roomKey, room, authUser)
     55 		if err != nil {
     56 			return
     57 		}
     58 	}
     59 	return
     60 }
     61 
     62 func buildCommandsList(authUser *database.User, room database.ChatRoom) (commandsList []string) {
     63 	if !authUser.AutocompleteCommandsEnabled {
     64 		return
     65 	}
     66 	commandsList = append(commandsList, "/pm ")
     67 	commandsList = append(commandsList, "/pmw ")
     68 	commandsList = append(commandsList, "/pmb ")
     69 	if authUser.IsModerator() {
     70 		commandsList = append(commandsList, "/m ")
     71 	}
     72 	commandsList = append(commandsList, "/me ")
     73 	commandsList = append(commandsList, "/e ")
     74 	commandsList = append(commandsList, "/chess ")
     75 	commandsList = append(commandsList, "/ignore ")
     76 	commandsList = append(commandsList, "/unignore ")
     77 	commandsList = append(commandsList, "/inbox ")
     78 	commandsList = append(commandsList, "/toggle-autocomplete")
     79 	commandsList = append(commandsList, "/d")
     80 	commandsList = append(commandsList, "/hide")
     81 	commandsList = append(commandsList, "/unhide")
     82 	commandsList = append(commandsList, "/pmwhitelist")
     83 	commandsList = append(commandsList, "/setpmmode whitelist")
     84 	commandsList = append(commandsList, "/setpmmode standard")
     85 	commandsList = append(commandsList, "/g ")
     86 	commandsList = append(commandsList, "/subscribe")
     87 	commandsList = append(commandsList, "/unsubscribe")
     88 	commandsList = append(commandsList, "/p ")
     89 	commandsList = append(commandsList, "/token")
     90 	commandsList = append(commandsList, "/md5 ")
     91 	commandsList = append(commandsList, "/sha1 ")
     92 	commandsList = append(commandsList, "/sha256 ")
     93 	commandsList = append(commandsList, "/sha512 ")
     94 	commandsList = append(commandsList, "/dice")
     95 	commandsList = append(commandsList, "/choice ")
     96 	if authUser.CanSeeHB() {
     97 		commandsList = append(commandsList, "/hbm") // CanSeeHB
     98 	}
     99 	// Private room
    100 	if room.IsOwned() {
    101 		commandsList = append(commandsList, "/mode")
    102 		commandsList = append(commandsList, "/wl")
    103 	}
    104 	// Private room owner
    105 	if room.IsRoomOwner(authUser.ID) {
    106 		commandsList = append(commandsList, "/addgroup")
    107 		commandsList = append(commandsList, "/rmgroup")
    108 		commandsList = append(commandsList, "/glock")
    109 		commandsList = append(commandsList, "/gunlock")
    110 		commandsList = append(commandsList, "/gusers")
    111 		commandsList = append(commandsList, "/groups")
    112 		commandsList = append(commandsList, "/gadduser")
    113 		commandsList = append(commandsList, "/grmuser")
    114 		commandsList = append(commandsList, "/mode user-whitelist")
    115 		commandsList = append(commandsList, "/mode standard")
    116 		commandsList = append(commandsList, "/wl groupName")
    117 	}
    118 	// Moderators
    119 	if authUser.IsModerator() {
    120 		commandsList = append(commandsList, "/moderators")
    121 		commandsList = append(commandsList, "/kick ")
    122 		commandsList = append(commandsList, "/unkick ")
    123 		commandsList = append(commandsList, "/logout ")
    124 		commandsList = append(commandsList, "/captcha ")
    125 		commandsList = append(commandsList, "/rtuto ")
    126 		commandsList = append(commandsList, "/hellban ")
    127 		commandsList = append(commandsList, "/unhellban ")
    128 	}
    129 	return commandsList
    130 }
    131 
    132 func ChatTopBarHandler(c echo.Context) error {
    133 	authUser := c.Get("authUser").(*database.User)
    134 	db := c.Get("database").(*database.DkfDB)
    135 	csrf, _ := c.Get("csrf").(string)
    136 	var data chatTopBarData
    137 	data.RoomName = c.Param("roomName")
    138 
    139 	redirectPmUsernameQP := command.RedirectPmUsernameQP
    140 	redirectMultilineQP := command.RedirectMultilineQP
    141 	queryParams := c.QueryParams()
    142 	pmUsername := c.QueryParam(redirectPmUsernameQP)
    143 	origMl := utils.DoParseBool(c.QueryParam(redirectMultilineQP))
    144 	data.QueryParams = queryParams.Encode()
    145 	queryParams.Set(redirectMultilineQP, "1")
    146 	data.QueryParamsMl = queryParams.Encode()
    147 	queryParams.Del(redirectMultilineQP)
    148 	data.QueryParamsNml = queryParams.Encode()
    149 
    150 	redirectQP := url.Values{}
    151 	if authUser.CanUseMultiline {
    152 		data.Multiline = origMl
    153 		if data.Multiline {
    154 			redirectQP.Set(redirectMultilineQP, "1")
    155 		}
    156 	}
    157 	if pmUsername != "" {
    158 		redirectQP.Set(redirectPmUsernameQP, pmUsername)
    159 	}
    160 
    161 	room, roomKey, err := dutils.GetRoomAndKey(db, c, data.RoomName)
    162 	if err != nil {
    163 		return c.NoContent(http.StatusForbidden)
    164 	}
    165 
    166 	// If the tutorial is not completed, just render the chat top-bar, no matter what.
    167 	if (room.IsOfficialRoom() || (room.IsListed && !room.IsProtected())) && !authUser.TutorialCompleted() {
    168 		return c.Render(http.StatusOK, "chat-top-bar", data)
    169 	}
    170 
    171 	data.Message, err = getDataMessagePrefix(db, c, roomKey, room, authUser)
    172 	if err != nil {
    173 		return c.Redirect(http.StatusFound, "/api/v1/chat/top-bar/"+room.Name)
    174 	}
    175 
    176 	data.CommandsList = buildCommandsList(authUser, room)
    177 
    178 	// GET requests stops here
    179 	if c.Request().Method != http.MethodPost {
    180 		if authUser.UseStreamTopBar {
    181 			if streamItem, err := stream.SetStreaming(c, authUser.ID, ""); err == nil {
    182 				defer streamItem.Cleanup()
    183 				_, _ = c.Response().Write([]byte(RenderTopBar(csrf, data, authUser)))
    184 				c.Response().Flush()
    185 				<-streamItem.Quit
    186 			}
    187 			return c.NoContent(http.StatusOK)
    188 		}
    189 		return c.Render(http.StatusOK, "chat-top-bar", data)
    190 	}
    191 
    192 	// ------------------------------------------------------------------------
    193 
    194 	if room.Name == config.AnnouncementsRoomName && authUser.ID != config.RootAdminID {
    195 		data.Error = "read only room"
    196 		return c.Render(http.StatusOK, "chat-top-bar", data)
    197 	}
    198 
    199 	if c.Request().ContentLength > config.MaxUserFileUploadSize {
    200 		data.Error = fmt.Sprintf("The maximum file size is %s", humanize.Bytes(config.MaxUserFileUploadSize))
    201 		return c.Render(http.StatusOK, "chat-top-bar", data)
    202 	}
    203 
    204 	origMessage := strings.TrimSpace(c.Request().PostFormValue("message"))
    205 
    206 	cmd := command.NewCommand(c, origMessage, room, roomKey)
    207 	cmd.RedirectQP = redirectQP
    208 
    209 	interceptorsArr := []interceptors.Interceptor{
    210 		interceptors.SnippetInterceptor{},
    211 		interceptors.SpamInterceptor{},
    212 		interceptors.BattleshipInstance,
    213 		interceptors.WWInstance,
    214 		interceptors.BangInterceptor{},
    215 		interceptors.UploadInterceptor{},
    216 		interceptors.SlashInterceptor{},
    217 		streamModals.CodeModal{},
    218 		streamModals.PurgeModal{},
    219 		interceptors.MsgInterceptor{},
    220 	}
    221 	for _, interceptor := range interceptorsArr {
    222 		interceptor.InterceptMsg(cmd)
    223 		data.Message = cmd.DataMessage
    224 		if cmd.Err != nil {
    225 			break
    226 		}
    227 	}
    228 	return redirectCmd(c, cmd, data)
    229 }
    230 
    231 func redirectCmd(c echo.Context, cmd *command.Command, data chatTopBarData) error {
    232 	err := cmd.Err
    233 	redirectURL := cmd.RedirectURL()
    234 	if err != nil {
    235 		var successErr *command.ErrSuccess
    236 		if errors.Is(err, command.ErrRedirect) {
    237 			return c.Redirect(http.StatusFound, redirectURL)
    238 		} else if errors.Is(err, command.ErrStop) {
    239 			return c.Render(http.StatusOK, "chat-top-bar", data)
    240 		} else if errors.As(err, &successErr) {
    241 			data.Success = successErr.Error()
    242 			return c.Render(http.StatusOK, "chat-top-bar", data)
    243 		}
    244 		data.Message = cmd.OrigMessage
    245 		data.Error = err.Error()
    246 		return c.Render(http.StatusOK, "chat-top-bar", data)
    247 	}
    248 	return c.Redirect(http.StatusFound, redirectURL)
    249 }
    250 
    251 func handleGetQuote(db *database.DkfDB, msgUUID, roomKey string, room database.ChatRoom, authUser *database.User) (dataMessage string, err error) {
    252 	quoted, err := db.GetRoomChatMessageByUUID(room.ID, msgUUID)
    253 	if err != nil {
    254 		return
    255 	}
    256 
    257 	// Build prefix for /m | /pm | /g | /hbm
    258 	prefix := ""
    259 	if quoted.IsPm() {
    260 		toUsername := utils.Ternary(quoted.OwnMessage(authUser.ID), quoted.ToUser.Username, quoted.User.Username)
    261 		prefix = fmt.Sprintf(`/pm %s `, toUsername)
    262 	} else if quoted.GroupID != nil {
    263 		prefix = fmt.Sprintf(`/g %s `, quoted.Group.Name)
    264 	} else if quoted.Moderators {
    265 		prefix = fmt.Sprintf(`/m `)
    266 	} else if (quoted.IsHellbanned || quoted.User.IsHellbanned) && authUser.IsModerator() {
    267 		prefix = fmt.Sprintf(`/hbm `)
    268 	}
    269 
    270 	// Append the actual quoted text
    271 	dataMessage = prefix + dutils.GetQuoteTxt(db, roomKey, quoted) + " "
    272 	return
    273 }
    274 
    275 func handleGetEdit(db *database.DkfDB, hourMinSec, roomKey string, room database.ChatRoom, authUser *database.User) (dataMessage string, err error) {
    276 	if dt, err := utils.ParsePrevDatetimeAt(hourMinSec, clockwork.NewRealClock()); err == nil {
    277 		if time.Since(dt) <= config.EditMessageTimeLimit {
    278 			if msg, err := db.GetRoomChatMessageByDate(room.ID, authUser.ID, dt.UTC()); err == nil {
    279 				decrypted, err := msg.GetRawMessage(roomKey)
    280 				if err != nil {
    281 					return "", err
    282 				}
    283 				dataMessage = "/e " + hourMinSec + " " + decrypted
    284 			}
    285 		}
    286 	}
    287 	return dataMessage, nil
    288 }