Implement DCC streaming
This commit is contained in:
parent
973578bb49
commit
e33b9f05e4
File diff suppressed because one or more lines are too long
@ -134,6 +134,22 @@ export default function handleSocket({
|
||||
}
|
||||
},
|
||||
|
||||
dcc_send({ server, from, filename, url }) {
|
||||
const serverName = getState().servers[server]?.name || server;
|
||||
|
||||
dispatch(
|
||||
openModal('confirm', {
|
||||
question: `${from} on ${serverName} is sending you: ${filename}`,
|
||||
confirmation: 'Download',
|
||||
onConfirm: () => {
|
||||
const a = document.createElement('a');
|
||||
a.href = url;
|
||||
a.click();
|
||||
}
|
||||
})
|
||||
);
|
||||
},
|
||||
|
||||
_connected(connected) {
|
||||
dispatch(setConnected(connected));
|
||||
}
|
||||
|
@ -109,8 +109,14 @@ func init() {
|
||||
viper.BindPFlags(rootCmd.PersistentFlags())
|
||||
viper.BindPFlags(rootCmd.Flags())
|
||||
|
||||
viper.SetDefault("hexIP", false)
|
||||
viper.SetDefault("verify_certificates", true)
|
||||
viper.SetDefault("https.enabled", true)
|
||||
viper.SetDefault("https.port", 443)
|
||||
viper.SetDefault("auth.anonymous", true)
|
||||
viper.SetDefault("auth.login", true)
|
||||
viper.SetDefault("auth.registration", true)
|
||||
viper.SetDefault("dcc.enabled", true)
|
||||
viper.SetDefault("dcc.autoget.delete", true)
|
||||
}
|
||||
|
||||
func initConfig(configPath string, overwrite bool) {
|
||||
|
@ -4,8 +4,6 @@ port = 80
|
||||
# Hex encode the users IP and use it as the ident
|
||||
hexIP = false
|
||||
verify_certificates = true
|
||||
# Automatically download files from DCC SENDs
|
||||
autoget = false
|
||||
|
||||
# Defaults for the client connect form
|
||||
[defaults]
|
||||
@ -63,6 +61,20 @@ secret = ""
|
||||
key = ""
|
||||
secret = ""
|
||||
|
||||
[dcc]
|
||||
# Receive files through DCC, the user gets to choose if they want to accept the file,
|
||||
# the file then gets streamed to the user
|
||||
enabled = true
|
||||
|
||||
[dcc.autoget]
|
||||
# Instead of streaming the file to the user, dispatch automatically downloads
|
||||
# DCC files and sends a download link to the user once its done
|
||||
enabled = false
|
||||
# Delete the file after the user has downloaded it once
|
||||
delete = true
|
||||
# Delete the file after a certain time period of inactivity, not implemented yet
|
||||
delete_after = "30m"
|
||||
|
||||
# Strict-Transport-Security
|
||||
[https.hsts]
|
||||
enabled = false
|
||||
|
@ -14,12 +14,12 @@ type Config struct {
|
||||
Dev bool
|
||||
HexIP bool
|
||||
VerifyCertificates bool `mapstructure:"verify_certificates"`
|
||||
Autoget bool
|
||||
Headers map[string]string
|
||||
Defaults Defaults
|
||||
HTTPS HTTPS
|
||||
LetsEncrypt LetsEncrypt
|
||||
Auth Auth
|
||||
DCC DCC
|
||||
}
|
||||
|
||||
type Defaults struct {
|
||||
@ -65,6 +65,17 @@ type Provider struct {
|
||||
Secret string
|
||||
}
|
||||
|
||||
type DCC struct {
|
||||
Enabled bool
|
||||
Autoget Autoget
|
||||
}
|
||||
|
||||
type Autoget struct {
|
||||
Enabled bool
|
||||
Delete bool
|
||||
DeleteAfter time.Duration `mapstructure:"delete_after"`
|
||||
}
|
||||
|
||||
func LoadConfig() (*Config, chan *Config) {
|
||||
viper.SetConfigName("config")
|
||||
viper.AddConfigPath(storage.Path.ConfigRoot())
|
||||
|
@ -41,7 +41,6 @@ func Serve(handler http.Handler, cfg Config) error {
|
||||
httpsSrv := &http.Server{
|
||||
Addr: net.JoinHostPort(cfg.Addr, cfg.PortHTTPS),
|
||||
ReadTimeout: 5 * time.Second,
|
||||
WriteTimeout: 10 * time.Second,
|
||||
IdleTimeout: 120 * time.Second,
|
||||
Handler: handler,
|
||||
}
|
||||
@ -101,7 +100,6 @@ func Serve(handler http.Handler, cfg Config) error {
|
||||
}
|
||||
} else {
|
||||
httpSrv.ReadTimeout = 5 * time.Second
|
||||
httpSrv.WriteTimeout = 10 * time.Second
|
||||
httpSrv.IdleTimeout = 120 * time.Second
|
||||
httpSrv.Handler = handler
|
||||
|
||||
|
@ -26,12 +26,8 @@ type Client struct {
|
||||
// Source is the reply to SOURCE CTCP messages
|
||||
Source string
|
||||
|
||||
DownloadFolder string
|
||||
Autoget bool
|
||||
|
||||
Messages chan *Message
|
||||
ConnectionChanged chan ConnectionState
|
||||
Progress chan DownloadProgress
|
||||
Features *Features
|
||||
nick string
|
||||
channels []string
|
||||
@ -59,7 +55,6 @@ func NewClient(nick, username string) *Client {
|
||||
Realname: nick,
|
||||
Messages: make(chan *Message, 32),
|
||||
ConnectionChanged: make(chan ConnectionState, 4),
|
||||
Progress: make(chan DownloadProgress, 4),
|
||||
out: make(chan string, 32),
|
||||
quit: make(chan struct{}),
|
||||
reconnect: make(chan struct{}),
|
||||
|
@ -47,13 +47,6 @@ func (c *Client) handleCTCP(ctcp *CTCP, msg *Message) {
|
||||
case "CLIENTINFO":
|
||||
c.ReplyCTCP(msg.Nick, ctcp.Command, ClientInfo)
|
||||
|
||||
case "DCC":
|
||||
if strings.HasPrefix(ctcp.Params, "SEND") {
|
||||
if dccSend := ParseDCCSend(ctcp); dccSend != nil {
|
||||
go c.Download(dccSend)
|
||||
}
|
||||
}
|
||||
|
||||
case "FINGER", "VERSION":
|
||||
if c.Version != "" {
|
||||
c.ReplyCTCP(msg.Nick, ctcp.Command, c.Version)
|
||||
|
@ -2,14 +2,11 @@ package irc
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"net"
|
||||
"os"
|
||||
"path"
|
||||
"path/filepath"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
@ -52,27 +49,16 @@ func ParseDCCSend(ctcp *CTCP) *DCCSend {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Client) Download(pack *DCCSend) {
|
||||
if !c.Autoget {
|
||||
// TODO: ask user if he/she wants to download the file
|
||||
return
|
||||
}
|
||||
|
||||
c.Progress <- DownloadProgress{
|
||||
func DownloadDCC(w io.Writer, pack *DCCSend, progress chan DownloadProgress) error {
|
||||
if progress != nil {
|
||||
progress <- DownloadProgress{
|
||||
File: pack.File,
|
||||
}
|
||||
|
||||
file, err := os.OpenFile(filepath.Join(c.DownloadFolder, pack.File), os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
c.downloadFailed(pack, err)
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
conn, err := net.Dial("tcp", net.JoinHostPort(pack.IP, pack.Port))
|
||||
if err != nil {
|
||||
c.downloadFailed(pack, err)
|
||||
return
|
||||
return err
|
||||
}
|
||||
defer conn.Close()
|
||||
|
||||
@ -87,24 +73,26 @@ func (c *Client) Download(pack *DCCSend) {
|
||||
n, err := conn.Read(buf)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
c.downloadFailed(pack, err)
|
||||
return
|
||||
return err
|
||||
}
|
||||
if n == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if _, err := file.Write(buf[:n]); err != nil {
|
||||
c.downloadFailed(pack, err)
|
||||
return
|
||||
if _, err := w.Write(buf[:n]); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
accBytes += uint64(n)
|
||||
totalBytes += uint64(n)
|
||||
|
||||
conn.Write(uint64Bytes(totalBytes))
|
||||
_, err = conn.Write(uint64Bytes(totalBytes))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if progress != nil {
|
||||
if dt := time.Since(prevUpdate); dt >= time.Second {
|
||||
prevUpdate = time.Now()
|
||||
|
||||
@ -119,7 +107,7 @@ func (c *Client) Download(pack *DCCSend) {
|
||||
bytesRemaining := float64(pack.Length - totalBytes)
|
||||
percentage := 100 * (float64(totalBytes) / float64(pack.Length))
|
||||
|
||||
c.Progress <- DownloadProgress{
|
||||
progress <- DownloadProgress{
|
||||
Speed: humanReadableByteCount(averageSpeed, true),
|
||||
PercCompletion: percentage,
|
||||
BytesRemaining: humanReadableByteCount(bytesRemaining, false),
|
||||
@ -130,21 +118,18 @@ func (c *Client) Download(pack *DCCSend) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
c.Progress <- DownloadProgress{
|
||||
if progress != nil {
|
||||
progress <- DownloadProgress{
|
||||
PercCompletion: 100,
|
||||
BytesCompleted: humanReadableByteCount(float64(totalBytes), false),
|
||||
SecondsElapsed: secondsSince(start),
|
||||
File: pack.File,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *Client) downloadFailed(pack *DCCSend, err error) {
|
||||
c.Progress <- DownloadProgress{
|
||||
PercCompletion: -1,
|
||||
File: pack.File,
|
||||
Error: err,
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type DownloadProgress struct {
|
||||
@ -158,14 +143,6 @@ type DownloadProgress struct {
|
||||
SecondsToGo float64 `json:"eta"`
|
||||
}
|
||||
|
||||
func (p DownloadProgress) ToJSON() string {
|
||||
progress, err := json.Marshal(p)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
return string(progress)
|
||||
}
|
||||
|
||||
func intToIP(n int) string {
|
||||
var byte1 = n & 255
|
||||
var byte2 = ((n >> 8) & 255)
|
||||
|
@ -73,9 +73,6 @@ func connectIRC(server *storage.Server, state *State, srcIP []byte) *irc.Client
|
||||
}
|
||||
}
|
||||
|
||||
i.DownloadFolder = storage.Path.Downloads(state.user.Username)
|
||||
i.Autoget = cfg.Autoget
|
||||
|
||||
state.setIRC(server.Host, i)
|
||||
i.Connect(address)
|
||||
go newIRCHandler(i, state).run()
|
||||
|
@ -3,8 +3,10 @@ package server
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
"unicode"
|
||||
|
||||
"github.com/kjk/betterguid"
|
||||
@ -26,6 +28,7 @@ type ircHandler struct {
|
||||
userBuffers map[string][]string
|
||||
motdBuffer MOTD
|
||||
listBuffer storage.ChannelListIndex
|
||||
dccProgress chan irc.DownloadProgress
|
||||
|
||||
handlers map[string]func(*irc.Message)
|
||||
}
|
||||
@ -35,6 +38,7 @@ func newIRCHandler(client *irc.Client, state *State) *ircHandler {
|
||||
client: client,
|
||||
state: state,
|
||||
userBuffers: make(map[string][]string),
|
||||
dccProgress: make(chan irc.DownloadProgress, 4),
|
||||
}
|
||||
i.initHandlers()
|
||||
return i
|
||||
@ -64,7 +68,7 @@ func (i *ircHandler) run() {
|
||||
i.log("Connected")
|
||||
}
|
||||
|
||||
case progress := <-i.client.Progress:
|
||||
case progress := <-i.dccProgress:
|
||||
if progress.Error != nil {
|
||||
i.sendDCCInfo("%s: Download failed (%s)", true, progress.File, progress.Error)
|
||||
} else if progress.PercCompletion == 100 {
|
||||
@ -175,10 +179,46 @@ func (i *ircHandler) mode(msg *irc.Message) {
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ircHandler) message(msg *irc.Message) {
|
||||
if ctcp := msg.ToCTCP(); ctcp != nil && ctcp.Command != "ACTION" {
|
||||
func (i *ircHandler) receiveDCCSend(pack *irc.DCCSend, msg *irc.Message) {
|
||||
cfg := i.state.srv.Config()
|
||||
|
||||
if cfg.DCC.Enabled {
|
||||
if cfg.DCC.Autoget.Enabled {
|
||||
file, err := os.OpenFile(storage.Path.DownloadedFile(i.state.user.Username, pack.File), os.O_CREATE|os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
defer file.Close()
|
||||
|
||||
irc.DownloadDCC(file, pack, i.dccProgress)
|
||||
} else {
|
||||
i.state.setPendingDCC(pack.File, pack)
|
||||
|
||||
i.state.sendJSON("dcc_send", DCCSend{
|
||||
Server: i.client.Host,
|
||||
From: msg.Nick,
|
||||
Filename: pack.File,
|
||||
URL: fmt.Sprintf("%s://%s/downloads/%s/%s",
|
||||
i.state.String("scheme"), i.state.String("host"), i.state.user.Username, pack.File),
|
||||
})
|
||||
|
||||
time.Sleep(150 * time.Second)
|
||||
i.state.deletePendingDCC(pack.File)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (i *ircHandler) message(msg *irc.Message) {
|
||||
if ctcp := msg.ToCTCP(); ctcp != nil {
|
||||
if ctcp.Command == "DCC" && strings.HasPrefix(ctcp.Params, "SEND") {
|
||||
if pack := irc.ParseDCCSend(ctcp); pack != nil {
|
||||
go i.receiveDCCSend(pack, msg)
|
||||
return
|
||||
}
|
||||
} else if ctcp.Command != "ACTION" {
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
message := Message{
|
||||
ID: betterguid.New(),
|
||||
@ -431,8 +471,7 @@ func (i *ircHandler) initHandlers() {
|
||||
}
|
||||
|
||||
func (i *ircHandler) log(v ...interface{}) {
|
||||
s := fmt.Sprintln(v...)
|
||||
log.Println("[IRC]", i.state.user.ID, i.client.Host, s[:len(s)-1])
|
||||
log.Println("[IRC]", i.state.user.ID, i.client.Host, fmt.Sprint(v...))
|
||||
}
|
||||
|
||||
func (i *ircHandler) sendDCCInfo(message string, log bool, a ...interface{}) {
|
||||
|
@ -222,6 +222,13 @@ type ChannelForward struct {
|
||||
New string
|
||||
}
|
||||
|
||||
type DCCSend struct {
|
||||
Server string
|
||||
From string
|
||||
Filename string
|
||||
URL string
|
||||
}
|
||||
|
||||
type Tab struct {
|
||||
storage.Tab
|
||||
}
|
||||
|
@ -3060,7 +3060,110 @@ func (v *Error) UnmarshalJSON(data []byte) error {
|
||||
func (v *Error) UnmarshalEasyJSON(l *jlexer.Lexer) {
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer27(l, v)
|
||||
}
|
||||
func easyjson42239ddeDecodeGithubComKhliengDispatchServer28(in *jlexer.Lexer, out *ConnectionUpdate) {
|
||||
func easyjson42239ddeDecodeGithubComKhliengDispatchServer28(in *jlexer.Lexer, out *DCCSend) {
|
||||
isTopLevel := in.IsStart()
|
||||
if in.IsNull() {
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
in.Skip()
|
||||
return
|
||||
}
|
||||
in.Delim('{')
|
||||
for !in.IsDelim('}') {
|
||||
key := in.UnsafeFieldName(false)
|
||||
in.WantColon()
|
||||
if in.IsNull() {
|
||||
in.Skip()
|
||||
in.WantComma()
|
||||
continue
|
||||
}
|
||||
switch key {
|
||||
case "server":
|
||||
out.Server = string(in.String())
|
||||
case "from":
|
||||
out.From = string(in.String())
|
||||
case "filename":
|
||||
out.Filename = string(in.String())
|
||||
case "url":
|
||||
out.URL = string(in.String())
|
||||
default:
|
||||
in.SkipRecursive()
|
||||
}
|
||||
in.WantComma()
|
||||
}
|
||||
in.Delim('}')
|
||||
if isTopLevel {
|
||||
in.Consumed()
|
||||
}
|
||||
}
|
||||
func easyjson42239ddeEncodeGithubComKhliengDispatchServer28(out *jwriter.Writer, in DCCSend) {
|
||||
out.RawByte('{')
|
||||
first := true
|
||||
_ = first
|
||||
if in.Server != "" {
|
||||
const prefix string = ",\"server\":"
|
||||
first = false
|
||||
out.RawString(prefix[1:])
|
||||
out.String(string(in.Server))
|
||||
}
|
||||
if in.From != "" {
|
||||
const prefix string = ",\"from\":"
|
||||
if first {
|
||||
first = false
|
||||
out.RawString(prefix[1:])
|
||||
} else {
|
||||
out.RawString(prefix)
|
||||
}
|
||||
out.String(string(in.From))
|
||||
}
|
||||
if in.Filename != "" {
|
||||
const prefix string = ",\"filename\":"
|
||||
if first {
|
||||
first = false
|
||||
out.RawString(prefix[1:])
|
||||
} else {
|
||||
out.RawString(prefix)
|
||||
}
|
||||
out.String(string(in.Filename))
|
||||
}
|
||||
if in.URL != "" {
|
||||
const prefix string = ",\"url\":"
|
||||
if first {
|
||||
first = false
|
||||
out.RawString(prefix[1:])
|
||||
} else {
|
||||
out.RawString(prefix)
|
||||
}
|
||||
out.String(string(in.URL))
|
||||
}
|
||||
out.RawByte('}')
|
||||
}
|
||||
|
||||
// MarshalJSON supports json.Marshaler interface
|
||||
func (v DCCSend) MarshalJSON() ([]byte, error) {
|
||||
w := jwriter.Writer{}
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer28(&w, v)
|
||||
return w.Buffer.BuildBytes(), w.Error
|
||||
}
|
||||
|
||||
// MarshalEasyJSON supports easyjson.Marshaler interface
|
||||
func (v DCCSend) MarshalEasyJSON(w *jwriter.Writer) {
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer28(w, v)
|
||||
}
|
||||
|
||||
// UnmarshalJSON supports json.Unmarshaler interface
|
||||
func (v *DCCSend) UnmarshalJSON(data []byte) error {
|
||||
r := jlexer.Lexer{Data: data}
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer28(&r, v)
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
|
||||
func (v *DCCSend) UnmarshalEasyJSON(l *jlexer.Lexer) {
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer28(l, v)
|
||||
}
|
||||
func easyjson42239ddeDecodeGithubComKhliengDispatchServer29(in *jlexer.Lexer, out *ConnectionUpdate) {
|
||||
isTopLevel := in.IsStart()
|
||||
if in.IsNull() {
|
||||
if isTopLevel {
|
||||
@ -3097,7 +3200,7 @@ func easyjson42239ddeDecodeGithubComKhliengDispatchServer28(in *jlexer.Lexer, ou
|
||||
in.Consumed()
|
||||
}
|
||||
}
|
||||
func easyjson42239ddeEncodeGithubComKhliengDispatchServer28(out *jwriter.Writer, in ConnectionUpdate) {
|
||||
func easyjson42239ddeEncodeGithubComKhliengDispatchServer29(out *jwriter.Writer, in ConnectionUpdate) {
|
||||
out.RawByte('{')
|
||||
first := true
|
||||
_ = first
|
||||
@ -3143,27 +3246,27 @@ func easyjson42239ddeEncodeGithubComKhliengDispatchServer28(out *jwriter.Writer,
|
||||
// MarshalJSON supports json.Marshaler interface
|
||||
func (v ConnectionUpdate) MarshalJSON() ([]byte, error) {
|
||||
w := jwriter.Writer{}
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer28(&w, v)
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer29(&w, v)
|
||||
return w.Buffer.BuildBytes(), w.Error
|
||||
}
|
||||
|
||||
// MarshalEasyJSON supports easyjson.Marshaler interface
|
||||
func (v ConnectionUpdate) MarshalEasyJSON(w *jwriter.Writer) {
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer28(w, v)
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer29(w, v)
|
||||
}
|
||||
|
||||
// UnmarshalJSON supports json.Unmarshaler interface
|
||||
func (v *ConnectionUpdate) UnmarshalJSON(data []byte) error {
|
||||
r := jlexer.Lexer{Data: data}
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer28(&r, v)
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer29(&r, v)
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
|
||||
func (v *ConnectionUpdate) UnmarshalEasyJSON(l *jlexer.Lexer) {
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer28(l, v)
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer29(l, v)
|
||||
}
|
||||
func easyjson42239ddeDecodeGithubComKhliengDispatchServer29(in *jlexer.Lexer, out *ClientCert) {
|
||||
func easyjson42239ddeDecodeGithubComKhliengDispatchServer30(in *jlexer.Lexer, out *ClientCert) {
|
||||
isTopLevel := in.IsStart()
|
||||
if in.IsNull() {
|
||||
if isTopLevel {
|
||||
@ -3196,7 +3299,7 @@ func easyjson42239ddeDecodeGithubComKhliengDispatchServer29(in *jlexer.Lexer, ou
|
||||
in.Consumed()
|
||||
}
|
||||
}
|
||||
func easyjson42239ddeEncodeGithubComKhliengDispatchServer29(out *jwriter.Writer, in ClientCert) {
|
||||
func easyjson42239ddeEncodeGithubComKhliengDispatchServer30(out *jwriter.Writer, in ClientCert) {
|
||||
out.RawByte('{')
|
||||
first := true
|
||||
_ = first
|
||||
@ -3222,27 +3325,27 @@ func easyjson42239ddeEncodeGithubComKhliengDispatchServer29(out *jwriter.Writer,
|
||||
// MarshalJSON supports json.Marshaler interface
|
||||
func (v ClientCert) MarshalJSON() ([]byte, error) {
|
||||
w := jwriter.Writer{}
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer29(&w, v)
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer30(&w, v)
|
||||
return w.Buffer.BuildBytes(), w.Error
|
||||
}
|
||||
|
||||
// MarshalEasyJSON supports easyjson.Marshaler interface
|
||||
func (v ClientCert) MarshalEasyJSON(w *jwriter.Writer) {
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer29(w, v)
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer30(w, v)
|
||||
}
|
||||
|
||||
// UnmarshalJSON supports json.Unmarshaler interface
|
||||
func (v *ClientCert) UnmarshalJSON(data []byte) error {
|
||||
r := jlexer.Lexer{Data: data}
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer29(&r, v)
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer30(&r, v)
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
|
||||
func (v *ClientCert) UnmarshalEasyJSON(l *jlexer.Lexer) {
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer29(l, v)
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer30(l, v)
|
||||
}
|
||||
func easyjson42239ddeDecodeGithubComKhliengDispatchServer30(in *jlexer.Lexer, out *ChannelSearchResult) {
|
||||
func easyjson42239ddeDecodeGithubComKhliengDispatchServer31(in *jlexer.Lexer, out *ChannelSearchResult) {
|
||||
isTopLevel := in.IsStart()
|
||||
if in.IsNull() {
|
||||
if isTopLevel {
|
||||
@ -3308,7 +3411,7 @@ func easyjson42239ddeDecodeGithubComKhliengDispatchServer30(in *jlexer.Lexer, ou
|
||||
in.Consumed()
|
||||
}
|
||||
}
|
||||
func easyjson42239ddeEncodeGithubComKhliengDispatchServer30(out *jwriter.Writer, in ChannelSearchResult) {
|
||||
func easyjson42239ddeEncodeGithubComKhliengDispatchServer31(out *jwriter.Writer, in ChannelSearchResult) {
|
||||
out.RawByte('{')
|
||||
first := true
|
||||
_ = first
|
||||
@ -3367,25 +3470,25 @@ func easyjson42239ddeEncodeGithubComKhliengDispatchServer30(out *jwriter.Writer,
|
||||
// MarshalJSON supports json.Marshaler interface
|
||||
func (v ChannelSearchResult) MarshalJSON() ([]byte, error) {
|
||||
w := jwriter.Writer{}
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer30(&w, v)
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer31(&w, v)
|
||||
return w.Buffer.BuildBytes(), w.Error
|
||||
}
|
||||
|
||||
// MarshalEasyJSON supports easyjson.Marshaler interface
|
||||
func (v ChannelSearchResult) MarshalEasyJSON(w *jwriter.Writer) {
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer30(w, v)
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer31(w, v)
|
||||
}
|
||||
|
||||
// UnmarshalJSON supports json.Unmarshaler interface
|
||||
func (v *ChannelSearchResult) UnmarshalJSON(data []byte) error {
|
||||
r := jlexer.Lexer{Data: data}
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer30(&r, v)
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer31(&r, v)
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
|
||||
func (v *ChannelSearchResult) UnmarshalEasyJSON(l *jlexer.Lexer) {
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer30(l, v)
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer31(l, v)
|
||||
}
|
||||
func easyjson42239ddeDecodeGithubComKhliengDispatchStorage1(in *jlexer.Lexer, out *storage.ChannelListItem) {
|
||||
isTopLevel := in.IsStart()
|
||||
@ -3454,7 +3557,7 @@ func easyjson42239ddeEncodeGithubComKhliengDispatchStorage1(out *jwriter.Writer,
|
||||
}
|
||||
out.RawByte('}')
|
||||
}
|
||||
func easyjson42239ddeDecodeGithubComKhliengDispatchServer31(in *jlexer.Lexer, out *ChannelSearch) {
|
||||
func easyjson42239ddeDecodeGithubComKhliengDispatchServer32(in *jlexer.Lexer, out *ChannelSearch) {
|
||||
isTopLevel := in.IsStart()
|
||||
if in.IsNull() {
|
||||
if isTopLevel {
|
||||
@ -3489,7 +3592,7 @@ func easyjson42239ddeDecodeGithubComKhliengDispatchServer31(in *jlexer.Lexer, ou
|
||||
in.Consumed()
|
||||
}
|
||||
}
|
||||
func easyjson42239ddeEncodeGithubComKhliengDispatchServer31(out *jwriter.Writer, in ChannelSearch) {
|
||||
func easyjson42239ddeEncodeGithubComKhliengDispatchServer32(out *jwriter.Writer, in ChannelSearch) {
|
||||
out.RawByte('{')
|
||||
first := true
|
||||
_ = first
|
||||
@ -3525,27 +3628,27 @@ func easyjson42239ddeEncodeGithubComKhliengDispatchServer31(out *jwriter.Writer,
|
||||
// MarshalJSON supports json.Marshaler interface
|
||||
func (v ChannelSearch) MarshalJSON() ([]byte, error) {
|
||||
w := jwriter.Writer{}
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer31(&w, v)
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer32(&w, v)
|
||||
return w.Buffer.BuildBytes(), w.Error
|
||||
}
|
||||
|
||||
// MarshalEasyJSON supports easyjson.Marshaler interface
|
||||
func (v ChannelSearch) MarshalEasyJSON(w *jwriter.Writer) {
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer31(w, v)
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer32(w, v)
|
||||
}
|
||||
|
||||
// UnmarshalJSON supports json.Unmarshaler interface
|
||||
func (v *ChannelSearch) UnmarshalJSON(data []byte) error {
|
||||
r := jlexer.Lexer{Data: data}
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer31(&r, v)
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer32(&r, v)
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
|
||||
func (v *ChannelSearch) UnmarshalEasyJSON(l *jlexer.Lexer) {
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer31(l, v)
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer32(l, v)
|
||||
}
|
||||
func easyjson42239ddeDecodeGithubComKhliengDispatchServer32(in *jlexer.Lexer, out *ChannelForward) {
|
||||
func easyjson42239ddeDecodeGithubComKhliengDispatchServer33(in *jlexer.Lexer, out *ChannelForward) {
|
||||
isTopLevel := in.IsStart()
|
||||
if in.IsNull() {
|
||||
if isTopLevel {
|
||||
@ -3580,7 +3683,7 @@ func easyjson42239ddeDecodeGithubComKhliengDispatchServer32(in *jlexer.Lexer, ou
|
||||
in.Consumed()
|
||||
}
|
||||
}
|
||||
func easyjson42239ddeEncodeGithubComKhliengDispatchServer32(out *jwriter.Writer, in ChannelForward) {
|
||||
func easyjson42239ddeEncodeGithubComKhliengDispatchServer33(out *jwriter.Writer, in ChannelForward) {
|
||||
out.RawByte('{')
|
||||
first := true
|
||||
_ = first
|
||||
@ -3616,27 +3719,27 @@ func easyjson42239ddeEncodeGithubComKhliengDispatchServer32(out *jwriter.Writer,
|
||||
// MarshalJSON supports json.Marshaler interface
|
||||
func (v ChannelForward) MarshalJSON() ([]byte, error) {
|
||||
w := jwriter.Writer{}
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer32(&w, v)
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer33(&w, v)
|
||||
return w.Buffer.BuildBytes(), w.Error
|
||||
}
|
||||
|
||||
// MarshalEasyJSON supports easyjson.Marshaler interface
|
||||
func (v ChannelForward) MarshalEasyJSON(w *jwriter.Writer) {
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer32(w, v)
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer33(w, v)
|
||||
}
|
||||
|
||||
// UnmarshalJSON supports json.Unmarshaler interface
|
||||
func (v *ChannelForward) UnmarshalJSON(data []byte) error {
|
||||
r := jlexer.Lexer{Data: data}
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer32(&r, v)
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer33(&r, v)
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
|
||||
func (v *ChannelForward) UnmarshalEasyJSON(l *jlexer.Lexer) {
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer32(l, v)
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer33(l, v)
|
||||
}
|
||||
func easyjson42239ddeDecodeGithubComKhliengDispatchServer33(in *jlexer.Lexer, out *Away) {
|
||||
func easyjson42239ddeDecodeGithubComKhliengDispatchServer34(in *jlexer.Lexer, out *Away) {
|
||||
isTopLevel := in.IsStart()
|
||||
if in.IsNull() {
|
||||
if isTopLevel {
|
||||
@ -3669,7 +3772,7 @@ func easyjson42239ddeDecodeGithubComKhliengDispatchServer33(in *jlexer.Lexer, ou
|
||||
in.Consumed()
|
||||
}
|
||||
}
|
||||
func easyjson42239ddeEncodeGithubComKhliengDispatchServer33(out *jwriter.Writer, in Away) {
|
||||
func easyjson42239ddeEncodeGithubComKhliengDispatchServer34(out *jwriter.Writer, in Away) {
|
||||
out.RawByte('{')
|
||||
first := true
|
||||
_ = first
|
||||
@ -3695,23 +3798,23 @@ func easyjson42239ddeEncodeGithubComKhliengDispatchServer33(out *jwriter.Writer,
|
||||
// MarshalJSON supports json.Marshaler interface
|
||||
func (v Away) MarshalJSON() ([]byte, error) {
|
||||
w := jwriter.Writer{}
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer33(&w, v)
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer34(&w, v)
|
||||
return w.Buffer.BuildBytes(), w.Error
|
||||
}
|
||||
|
||||
// MarshalEasyJSON supports easyjson.Marshaler interface
|
||||
func (v Away) MarshalEasyJSON(w *jwriter.Writer) {
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer33(w, v)
|
||||
easyjson42239ddeEncodeGithubComKhliengDispatchServer34(w, v)
|
||||
}
|
||||
|
||||
// UnmarshalJSON supports json.Unmarshaler interface
|
||||
func (v *Away) UnmarshalJSON(data []byte) error {
|
||||
r := jlexer.Lexer{Data: data}
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer33(&r, v)
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer34(&r, v)
|
||||
return r.Error()
|
||||
}
|
||||
|
||||
// UnmarshalEasyJSON supports easyjson.Unmarshaler interface
|
||||
func (v *Away) UnmarshalEasyJSON(l *jlexer.Lexer) {
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer33(l, v)
|
||||
easyjson42239ddeDecodeGithubComKhliengDispatchServer34(l, v)
|
||||
}
|
||||
|
@ -3,6 +3,7 @@ package server
|
||||
import (
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@ -10,6 +11,7 @@ import (
|
||||
"github.com/gorilla/websocket"
|
||||
"github.com/khlieng/dispatch/config"
|
||||
"github.com/khlieng/dispatch/pkg/https"
|
||||
"github.com/khlieng/dispatch/pkg/irc"
|
||||
"github.com/khlieng/dispatch/pkg/session"
|
||||
"github.com/khlieng/dispatch/storage"
|
||||
)
|
||||
@ -202,9 +204,21 @@ func (d *Dispatch) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
|
||||
filename := params[2]
|
||||
|
||||
w.Header().Set("Content-Disposition", "attachment; filename="+filename)
|
||||
http.ServeFile(w, r, storage.Path.DownloadedFile(state.user.Username, filename))
|
||||
|
||||
if pack, ok := state.getPendingDCC(filename); ok {
|
||||
state.deletePendingDCC(filename)
|
||||
|
||||
w.Header().Set("Content-Length", strconv.FormatUint(pack.Length, 10))
|
||||
irc.DownloadDCC(w, pack, nil)
|
||||
} else {
|
||||
file := storage.Path.DownloadedFile(state.user.Username, filename)
|
||||
http.ServeFile(w, r, file)
|
||||
|
||||
if d.Config().DCC.Autoget.Delete {
|
||||
os.Remove(file)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
fail(w, http.StatusNotFound)
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ type State struct {
|
||||
|
||||
irc map[string]*irc.Client
|
||||
connectionState map[string]irc.ConnectionState
|
||||
pendingDCCSends map[string]*irc.DCCSend
|
||||
ircLock sync.Mutex
|
||||
|
||||
ws map[string]*wsConn
|
||||
@ -39,6 +40,7 @@ func NewState(user *storage.User, srv *Dispatch) *State {
|
||||
stateData: stateData{m: map[string]interface{}{}},
|
||||
irc: make(map[string]*irc.Client),
|
||||
connectionState: make(map[string]irc.ConnectionState),
|
||||
pendingDCCSends: make(map[string]*irc.DCCSend),
|
||||
ws: make(map[string]*wsConn),
|
||||
broadcast: make(chan WSResponse, 32),
|
||||
srv: srv,
|
||||
@ -102,6 +104,25 @@ func (s *State) setConnectionState(server string, state irc.ConnectionState) {
|
||||
s.ircLock.Unlock()
|
||||
}
|
||||
|
||||
func (s *State) getPendingDCC(filename string) (*irc.DCCSend, bool) {
|
||||
s.ircLock.Lock()
|
||||
pack, ok := s.pendingDCCSends[filename]
|
||||
s.ircLock.Unlock()
|
||||
return pack, ok
|
||||
}
|
||||
|
||||
func (s *State) setPendingDCC(filename string, pack *irc.DCCSend) {
|
||||
s.ircLock.Lock()
|
||||
s.pendingDCCSends[filename] = pack
|
||||
s.ircLock.Unlock()
|
||||
}
|
||||
|
||||
func (s *State) deletePendingDCC(filename string) {
|
||||
s.ircLock.Lock()
|
||||
delete(s.pendingDCCSends, filename)
|
||||
s.ircLock.Unlock()
|
||||
}
|
||||
|
||||
func (s *State) setWS(addr string, w *wsConn) {
|
||||
s.wsLock.Lock()
|
||||
s.ws[addr] = w
|
||||
|
Loading…
Reference in New Issue
Block a user