diff --git a/main.go b/main.go index 384c8c0..3d4abc0 100644 --- a/main.go +++ b/main.go @@ -47,7 +47,7 @@ func handle(c net.Conn) { } parts := strings.Fields(line) cmd := parts[0] - params := parts[1:] + params := strings.Join(parts[1:], "") switch cmd { case "USER": s.cmdServerUser(params) @@ -85,19 +85,29 @@ func handle(c net.Conn) { s.cmdServerLpsv(params) case "FEAT": s.cmdServerFeat(params) + case "DELE": + s.cmdServerDele(params) + case "MKD", "XMKD": + s.cmdServerMkd(params) + case "RMD", "XRMD": + s.cmdServerRmd(params) + case "STOR": + s.cmdServerStor(params) + case "SITE": + s.cmdServerSite(params) default: fmt.Printf("%s\n", line) } } } -func (s *Conn) cmdServerUser(args []string) { +func (s *Conn) cmdServerUser(args string) { fmt.Printf("cmdServerUser: %s\n", args) // s.user = args[0] - s.ctrl.PrintfLine("331 Password required for %s", args[0]) + s.ctrl.PrintfLine("331 Password required for %s", args) } -func (s *Conn) cmdServerPass(args []string) { +func (s *Conn) cmdServerPass(args string) { fmt.Printf("cmdServerPass: %s\n", args) // s.token = args[0] s.sw = &swift.Connection{UserName: s.user, ApiKey: s.token, AuthUrl: s.api, AuthVersion: 1} @@ -111,15 +121,12 @@ func (s *Conn) cmdServerPass(args []string) { s.ctrl.PrintfLine("230 Logged on") } -func (s *Conn) cmdServerPwd(args []string) { +func (s *Conn) cmdServerPwd(args string) { fmt.Printf("cmdServerPwd: %s\n", args) - if s.path == "" { - s.path = "/" - } s.ctrl.PrintfLine(`257 "%s" is current directory.`, s.path) } -func (s *Conn) cmdServerEpsv(args []string) { +func (s *Conn) cmdServerEpsv(args string) { fmt.Printf("cmdServerEpsv: %s\n", args) if s.ln == nil { ln, err := net.Listen("tcp4", fmt.Sprintf(":%d", s.port)) @@ -133,72 +140,89 @@ func (s *Conn) cmdServerEpsv(args []string) { _, port, _ := net.SplitHostPort(s.ln.Addr().String()) s.port, _ = strconv.Atoi(port) } + s.passive = true s.ctrl.PrintfLine("229 Entering Extended Passive Mode (|||%d|)", s.port) } -func (s *Conn) cmdServerType(args []string) { +func (s *Conn) cmdServerType(args string) { fmt.Printf("cmdServerType: %s\n", args) - s.mode = args[0] + s.mode = args s.ctrl.PrintfLine("200 Type set to %s", s.mode) } -func (s *Conn) cmdServerList(args []string) { +func (s *Conn) cmdServerList(args string) { fmt.Printf("cmdServerList: %s\n", args) - s.ctrl.PrintfLine(`150 Opening data channel for directory listing of "%s"`, strings.Join(args, "")) - c, err := s.newPassiveSocket() + s.ctrl.PrintfLine(`150 Opening data channel for directory listing of "%s"`, args) + c, err := s.newSocket() if err != nil { fmt.Printf(err.Error()) s.ctrl.PrintfLine("425 Data connection failed") return } - var n int var files []os.FileInfo + cnt := "" + p := "" + switch args { + case "-la", "-l", "-a", "-al": + args = "" + } + if len(args) > 0 && args[0] == '/' { + cnt = strings.Split(args, "/")[1] + p = strings.Join(strings.Split(args, "/")[2:], "/") + } - if s.path == "/" { + if cnt == "" && s.path == "/" { cnts, err := s.sw.ContainersAll(nil) if err != nil { fmt.Printf(err.Error()) return } files = append(files, NewDirItem(".", 4096, 0), NewDirItem("..", 4096, 0)) - for _, cnt := range cnts { - it := NewDirItem(cnt.Name, cnt.Bytes, cnt.Count) + for _, ct := range cnts { + it := NewDirItem(ct.Name, ct.Bytes, ct.Count) files = append(files, it) } } else { - if idx := strings.Index(s.path, "/"); idx != -1 { - s.container = s.path[idx+1:] - } else { - s.container = s.path + opts := &swift.ObjectsOpts{Delimiter: '/'} + cnt = strings.Split(s.path, "/")[1] + p = filepath.Clean(filepath.Join(strings.Join(strings.Split(s.path, "/")[2:], "/"), args)) + + if p != "." { + opts.Path = p } - objs, err := s.sw.ObjectsAll(s.container, nil) + objs, err := s.sw.ObjectsAll(cnt, opts) if err != nil { fmt.Printf(err.Error()) return } + files = append(files, NewDirItem(".", 4096, 0), NewDirItem("..", 4096, 0)) + var it os.FileInfo + for _, obj := range objs { - it := NewFileItem(obj.Name, obj.Bytes, 1) + if obj.PseudoDirectory || obj.ContentType == "application/directory" { + it = NewDirItem(obj.Name, obj.Bytes, 1) + } else { + it = NewFileItem(obj.Name, obj.Bytes, 1) + } files = append(files, it) } } - ls := newListFormatter(files) - n, err = c.Write([]byte(ls.Detailed())) + _, err = c.Write([]byte(ls.Detailed())) if err != nil { fmt.Printf(err.Error()) } c.Close() - s.ctrl.PrintfLine(`226 Closing data connection, sent %d bytes`, n) - // s.ctrl.PrintfLine("226 Transfer complete.") + s.ctrl.PrintfLine(`226 Closing data connection`) } -func (s *Conn) cmdServerSyst(args []string) { +func (s *Conn) cmdServerSyst(args string) { fmt.Printf("cmdServerSyst: %s\n", args) s.ctrl.PrintfLine("215 UNIX Type: L8") } -func (s *Conn) cmdServerQuit(args []string) { +func (s *Conn) cmdServerQuit(args string) { s.ctrl.PrintfLine("221 Goodbye") if s.ln != nil { s.ln.Close() @@ -208,7 +232,7 @@ func (s *Conn) cmdServerQuit(args []string) { } } -func (s *Conn) cmdServerPasv(args []string) { +func (s *Conn) cmdServerPasv(args string) { fmt.Printf("cmdServerPasv: %s\n", args) if s.ln == nil { ln, err := net.Listen("tcp4", fmt.Sprintf(":%d", s.port)) @@ -223,6 +247,7 @@ func (s *Conn) cmdServerPasv(args []string) { p1 := p0 / 256 p2 := p0 - p1*256 s.port, _ = strconv.Atoi(port) + s.passive = true s.ctrl.PrintfLine("227 Entering Passive Mode (%s,%d,%d)", strings.Replace(host, ".", ",", -1), p1, p2) } @@ -230,43 +255,86 @@ func (s *Conn) newPassiveSocket() (net.Conn, error) { return s.ln.Accept() } -func (s *Conn) cmdServerSize(args []string) { - fmt.Printf("cmdServerSize: %s\n", args) - s.ctrl.PrintfLine("213 %d", len([]byte("test\n"))) +func (s *Conn) newActiveSocket() (net.Conn, error) { + return net.Dial("tcp", fmt.Sprintf("%s:%d", s.host, s.port)) } -func (s *Conn) cmdServerCwd(args []string) { - fmt.Printf("cmdServerCwd: %s\n", args) - if len(args) == 0 { - s.path = "/" - } else { - s.path = filepath.Clean(args[0]) +func (s *Conn) newSocket() (net.Conn, error) { + if s.passive { + return s.newPassiveSocket() } + return s.newActiveSocket() +} + +func (s *Conn) cmdServerSize(args string) { + fmt.Printf("cmdServerSize: %s\n", args) + + if s.path == "/" && len(args) == 0 { + s.ctrl.PrintfLine("213 %d", 0) + return + } + + p := "" + cnt := "" + + if args[0] == '/' { + cnt = strings.Split(args, "/")[1] + p = strings.Join(strings.Split(args, "/")[2:], "") + } else { + if s.path != "/" { + cnt = strings.Split(s.path, "/")[1] + p = filepath.Clean(filepath.Join(strings.Join(strings.Split(s.path, "/")[2:], "/"), args)) + } else { + cnt = strings.Split(args, "/")[0] + } + } + if p != "" { + obj, _, err := s.sw.Object(cnt, p) + if err != nil { + fmt.Printf(err.Error()) + s.ctrl.PrintfLine("501 Error") + return + } + s.ctrl.PrintfLine("213 %d", obj.Bytes) + } else { + s.ctrl.PrintfLine("550 Could not get file size.") + } +} + +func (s *Conn) cmdServerCwd(args string) { + fmt.Printf("cmdServerCwd: %s\n", args) + s.path = filepath.Clean("/" + args) s.ctrl.PrintfLine(`250 "%s" is current directory.`, s.path) } -func (s *Conn) cmdServerPort(args []string) { +func (s *Conn) cmdServerPort(args string) { fmt.Printf("cmdServerPort: %s\n", args) - s.ctrl.PrintfLine("501 Data connection failed") + s.passive = false + s.ctrl.PrintfLine("522 Data connection failed") } -func (s *Conn) cmdServerEprt(args []string) { +func (s *Conn) cmdServerEprt(args string) { fmt.Printf("cmdServerEprt: %s\n", args) - s.ctrl.PrintfLine("501 Data connection failed") + parts := strings.Split(args, "|") + s.host = parts[2] + s.port, _ = strconv.Atoi(parts[3]) + + s.passive = false + s.ctrl.PrintfLine("200 Ok") } -func (s *Conn) cmdServerLprt(args []string) { +func (s *Conn) cmdServerLprt(args string) { fmt.Printf("cmdServerLprt: %s\n", args) - s.ctrl.PrintfLine("501 Data connection failed") + s.ctrl.PrintfLine("522 Data connection failed") } -func (s *Conn) cmdServerLpsv(args []string) { +func (s *Conn) cmdServerLpsv(args string) { fmt.Printf("cmdServerLpsv: %s\n", args) - s.ctrl.PrintfLine("501 Data connection failed") + s.ctrl.PrintfLine("522 Data connection failed") } -func (s *Conn) cmdServerFeat(args []string) { +func (s *Conn) cmdServerFeat(args string) { fmt.Printf("cmdServerFeat: %s\n", args) s.ctrl.PrintfLine("501 Data connection failed") return @@ -287,22 +355,162 @@ func (s *Conn) cmdServerFeat(args []string) { 211 END`) } -func (s *Conn) cmdServerRetr(args []string) { - fmt.Printf("cmdServerRetr: %s\n", args) - s.ctrl.PrintfLine(`150 Opening data channel for file contents "%s"`, args[0]) - c, err := s.newPassiveSocket() - if err != nil { - s.ctrl.PrintfLine("425 Data connection failed") - return - } - n, err := c.Write([]byte("test\n")) - if err != nil { - s.ctrl.PrintfLine("425 Data connection failed") - fmt.Printf(err.Error()) - return - } - c.Close() - s.ctrl.PrintfLine(`226 Closing data connection, sent %d bytes`, n) - s.ctrl.PrintfLine("226 Transfer complete.") +func (s *Conn) cmdServerDele(args string) { + fmt.Printf("cmdServerDele: %s\n", args) + p := "" + cnt := "" + var err error + if args[0] == '/' { + cnt = strings.Split(args, "/")[1] + p = filepath.Clean(strings.Join(strings.Split(args, "/")[2:], "/")) + } else { + if s.path != "/" { + cnt = strings.Split(s.path, "/")[1] + p = filepath.Clean(filepath.Join(strings.Join(strings.Split(s.path, "/")[2:], "/"), args)) + } + } + err = s.sw.ObjectDelete(cnt, p) + if err != nil { + fmt.Printf(err.Error()) + s.ctrl.PrintfLine(`450 Delete failed "%s"`, args) + return + } + s.ctrl.PrintfLine(`250 Delete success "%s"`, args) +} + +func (s *Conn) cmdServerRmd(args string) { + fmt.Printf("cmdServerRmd: %s\n", args) + p := "" + cnt := "" + var err error + + if args[0] == '/' { + cnt = strings.Split(args, "/")[1] + p = filepath.Clean(strings.Join(strings.Split(args, "/")[2:], "/")) + } else { + if s.path != "/" { + cnt = strings.Split(s.path, "/")[1] + p = filepath.Clean(filepath.Join(strings.Join(strings.Split(s.path, "/")[2:], "/"), args)) + } + } + if p != "" && p != "." { + err = s.sw.ObjectDelete(cnt, p) + } else { + err = s.sw.ContainerDelete(cnt) + } + if err != nil { + fmt.Printf(err.Error()) + s.ctrl.PrintfLine(`450 Delete failed "%s"`, args) + return + } + s.ctrl.PrintfLine(`250 Delete success "%s"`, args) +} + +func (s *Conn) cmdServerMkd(args string) { + fmt.Printf("cmdServerMkd: %s\n", args) + p := "" + cnt := "" + var err error + if args[0] == '/' { + cnt = strings.Split(args, "/")[1] + p = filepath.Clean(strings.Join(strings.Split(args, "/")[2:], "/")) + } else { + if s.path != "/" { + cnt = strings.Split(s.path, "/")[1] + p = filepath.Clean(filepath.Join(strings.Join(strings.Split(s.path, "/")[2:], "/"), args)) + } + } + + if p != "" && p != "." { + var f *swift.ObjectCreateFile + f, err = s.sw.ObjectCreate(cnt, p, false, "", "application/directory", nil) + if err != nil { + fmt.Printf(err.Error()) + s.ctrl.PrintfLine(`550 Create failed "%s"`, args) + return + } + err = f.Close() + } else { + err = s.sw.ContainerCreate(cnt, nil) + } + if err != nil { + fmt.Printf(err.Error()) + s.ctrl.PrintfLine(`550 Create failed "%s"`, args) + return + } + s.ctrl.PrintfLine(`250 Create success "%s"`, args) +} + +func (s *Conn) cmdServerRetr(args string) { + fmt.Printf("cmdServerRetr: %s\n", args) + + p := "" + cnt := "" + + if args[0] == '/' { + cnt = strings.Split(args, "/")[1] + p = filepath.Clean(strings.Join(strings.Split(args, "/")[2:], "/")) + } else { + cnt = strings.Split(s.path, "/")[1] + p = filepath.Clean(filepath.Join(strings.Join(strings.Split(s.path, "/")[2:], "/"), args)) + } + + _, _, err := s.sw.Object(cnt, p) + if err != nil { + s.ctrl.PrintfLine("550 Failed to open file") + return + } + + s.ctrl.PrintfLine(`150 Opening data channel for file contents "%s"`, args) + c, err := s.newSocket() + if err != nil { + s.ctrl.PrintfLine("425 Data connection failed") + return + } + defer c.Close() + + _, err = s.sw.ObjectGet(cnt, p, c, false, nil) + if err != nil { + s.ctrl.PrintfLine("550 Failed to open file") + return + } + + s.ctrl.PrintfLine(`226 Closing data connection`) +} + +func (s *Conn) cmdServerStor(args string) { + fmt.Printf("cmdServerRetr: %s\n", args) + + p := "" + cnt := "" + + if args[0] == '/' { + cnt = strings.Split(args, "/")[1] + p = filepath.Clean(strings.Join(strings.Split(args, "/")[2:], "/")) + } else { + cnt = strings.Split(s.path, "/")[1] + p = filepath.Clean(filepath.Join(strings.Join(strings.Split(s.path, "/")[2:], "/"), args)) + } + + s.ctrl.PrintfLine(`150 Opening data channel for file contents "%s"`, args) + c, err := s.newSocket() + if err != nil { + s.ctrl.PrintfLine("425 Data connection failed") + return + } + defer c.Close() + + _, err = s.sw.ObjectPut(cnt, p, c, false, "", "", nil) + if err != nil { + s.ctrl.PrintfLine("552 Failed to store file") + return + } + + s.ctrl.PrintfLine(`226 Closing data connection`) +} + +func (s *Conn) cmdServerSite(args string) { + s.ctrl.PrintfLine(`200 Success`) + //SITE CHMOD 0644 /public/.wgetpaste.conf } diff --git a/server.go b/server.go index 0d7659a..ee1569b 100644 --- a/server.go +++ b/server.go @@ -8,17 +8,17 @@ import ( ) type Conn struct { - ctrl *textproto.Conn - data net.Conn - ln net.Listener - port int - mode string - path string - sw *swift.Connection - user string - token string - container string - api string + ctrl *textproto.Conn + ln net.Listener + host string + port int + mode string + sw *swift.Connection + user string + token string + path string + api string + passive bool } func (c *Conn) Close() error { @@ -26,5 +26,5 @@ func (c *Conn) Close() error { } func NewServer(c net.Conn) (*Conn, error) { - return &Conn{api: "https://api.clodo.ru", user: "storage_21_1", token: "56652e9028ded5ea5d4772ba80e578ce", ctrl: textproto.NewConn(c)}, nil + return &Conn{api: "https://api.clodo.ru", user: "storage_21_1", token: "56652e9028ded5ea5d4772ba80e578ce", ctrl: textproto.NewConn(c), path: "/"}, nil }