You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

409 lines
9.4 KiB

  1. package cmd
  2. import (
  3. "fmt"
  4. "log"
  5. "os"
  6. "strings"
  7. "github.com/dutchcoders/transfer.sh/server"
  8. "github.com/dutchcoders/transfer.sh/server/storage"
  9. "github.com/fatih/color"
  10. "github.com/urfave/cli"
  11. "google.golang.org/api/googleapi"
  12. )
  13. var Version = "1.1.4"
  14. var helpTemplate = `NAME:
  15. {{.Name}} - {{.Usage}}
  16. DESCRIPTION:
  17. {{.Description}}
  18. USAGE:
  19. {{.Name}} {{if .Flags}}[flags] {{end}}command{{if .Flags}}{{end}} [arguments...]
  20. COMMANDS:
  21. {{range .Commands}}{{join .Names ", "}}{{ "\t" }}{{.Usage}}
  22. {{end}}{{if .Flags}}
  23. FLAGS:
  24. {{range .Flags}}{{.}}
  25. {{end}}{{end}}`
  26. var globalFlags = []cli.Flag{
  27. cli.StringFlag{
  28. Name: "listener",
  29. Usage: "127.0.0.1:8080",
  30. Value: "127.0.0.1:8080",
  31. },
  32. // redirect to https?
  33. // hostnames
  34. cli.StringFlag{
  35. Name: "profile-listener",
  36. Usage: "127.0.0.1:6060",
  37. Value: "",
  38. },
  39. cli.BoolFlag{
  40. Name: "force-https",
  41. Usage: "",
  42. },
  43. cli.StringFlag{
  44. Name: "tls-listener",
  45. Usage: "127.0.0.1:8443",
  46. Value: "",
  47. },
  48. cli.BoolFlag{
  49. Name: "tls-listener-only",
  50. Usage: "",
  51. },
  52. cli.StringFlag{
  53. Name: "tls-cert-file",
  54. Value: "",
  55. },
  56. cli.StringFlag{
  57. Name: "tls-private-key",
  58. Value: "",
  59. },
  60. cli.StringFlag{
  61. Name: "temp-path",
  62. Usage: "path to temp files",
  63. Value: os.TempDir(),
  64. },
  65. cli.StringFlag{
  66. Name: "web-path",
  67. Usage: "path to static web files",
  68. Value: "",
  69. },
  70. cli.StringFlag{
  71. Name: "proxy-path",
  72. Usage: "path prefix when service is run behind a proxy",
  73. Value: "",
  74. },
  75. cli.StringFlag{
  76. Name: "ga-key",
  77. Usage: "key for google analytics (front end)",
  78. Value: "",
  79. },
  80. cli.StringFlag{
  81. Name: "uservoice-key",
  82. Usage: "key for user voice (front end)",
  83. Value: "",
  84. },
  85. cli.IntFlag{
  86. Name: "lifetime",
  87. Usage: "default file lifetime in days",
  88. Value: 14,
  89. },
  90. cli.StringFlag{
  91. Name: "provider",
  92. Usage: "s3|gdrive|local",
  93. Value: "",
  94. },
  95. cli.StringFlag{
  96. Name: "s3-endpoint",
  97. Usage: "",
  98. Value: "",
  99. EnvVar: "S3_ENDPOINT",
  100. },
  101. cli.StringFlag{
  102. Name: "s3-region",
  103. Usage: "",
  104. Value: "eu-west-1",
  105. EnvVar: "S3_REGION",
  106. },
  107. cli.StringFlag{
  108. Name: "aws-access-key",
  109. Usage: "",
  110. Value: "",
  111. EnvVar: "AWS_ACCESS_KEY",
  112. },
  113. cli.StringFlag{
  114. Name: "aws-secret-key",
  115. Usage: "",
  116. Value: "",
  117. EnvVar: "AWS_SECRET_KEY",
  118. },
  119. cli.StringFlag{
  120. Name: "bucket",
  121. Usage: "",
  122. Value: "",
  123. EnvVar: "BUCKET",
  124. },
  125. cli.BoolFlag{
  126. Name: "s3-no-multipart",
  127. Usage: "Disables S3 Multipart Puts",
  128. },
  129. cli.BoolFlag{
  130. Name: "s3-path-style",
  131. Usage: "Forces path style URLs, required for Minio.",
  132. },
  133. cli.StringFlag{
  134. Name: "gdrive-client-json-filepath",
  135. Usage: "",
  136. Value: "",
  137. },
  138. cli.StringFlag{
  139. Name: "gdrive-local-config-path",
  140. Usage: "",
  141. Value: "",
  142. },
  143. cli.IntFlag{
  144. Name: "gdrive-chunk-size",
  145. Usage: "",
  146. Value: googleapi.DefaultUploadChunkSize / 1024 / 1024,
  147. },
  148. cli.IntFlag{
  149. Name: "rate-limit",
  150. Usage: "requests per minute",
  151. Value: 0,
  152. EnvVar: "",
  153. },
  154. cli.StringFlag{
  155. Name: "lets-encrypt-hosts",
  156. Usage: "host1, host2",
  157. Value: "",
  158. EnvVar: "HOSTS",
  159. },
  160. cli.StringFlag{
  161. Name: "log",
  162. Usage: "/var/log/transfersh.log",
  163. Value: "",
  164. },
  165. cli.StringFlag{
  166. Name: "basedir",
  167. Usage: "path to storage",
  168. Value: "",
  169. },
  170. cli.IntFlag{
  171. Name: "cleanup-interval",
  172. Usage: "interval to clean up expired files from local storage",
  173. Value: 1,
  174. },
  175. cli.StringFlag{
  176. Name: "clamav-host",
  177. Usage: "clamav-host",
  178. Value: "",
  179. EnvVar: "CLAMAV_HOST",
  180. },
  181. cli.StringFlag{
  182. Name: "virustotal-key",
  183. Usage: "virustotal-key",
  184. Value: "",
  185. EnvVar: "VIRUSTOTAL_KEY",
  186. },
  187. cli.BoolFlag{
  188. Name: "profiler",
  189. Usage: "enable profiling",
  190. },
  191. cli.StringFlag{
  192. Name: "http-auth-user",
  193. Usage: "user for http basic auth",
  194. Value: "",
  195. },
  196. cli.StringFlag{
  197. Name: "http-auth-pass",
  198. Usage: "pass for http basic auth",
  199. Value: "",
  200. },
  201. cli.StringFlag{
  202. Name: "ip-whitelist",
  203. Usage: "comma separated list of ips allowed to connect to the service",
  204. Value: "",
  205. },
  206. cli.StringFlag{
  207. Name: "ip-blacklist",
  208. Usage: "comma separated list of ips not allowed to connect to the service",
  209. Value: "",
  210. },
  211. }
  212. type Cmd struct {
  213. *cli.App
  214. }
  215. func VersionAction(c *cli.Context) {
  216. fmt.Println(color.YellowString(fmt.Sprintf("transfer.sh: Easy file sharing from the command line, version: %s", c.App.Version)))
  217. }
  218. func New() *Cmd {
  219. logger := log.New(os.Stdout, "[transfer.sh]", log.LstdFlags)
  220. app := cli.NewApp()
  221. app.Name = "transfer.sh"
  222. app.Author = ""
  223. app.Usage = "transfer.sh"
  224. app.Description = `Easy file sharing from the command line`
  225. app.Version = Version
  226. app.Flags = globalFlags
  227. app.CustomAppHelpTemplate = helpTemplate
  228. app.Commands = []cli.Command{
  229. {
  230. Name: "version",
  231. Action: VersionAction,
  232. },
  233. }
  234. app.Before = func(c *cli.Context) error {
  235. return nil
  236. }
  237. app.Action = func(c *cli.Context) {
  238. var options []server.OptionFn
  239. if v := c.String("listener"); v != "" {
  240. options = append(options, server.Listener(v))
  241. }
  242. if v := c.String("tls-listener"); v == "" {
  243. } else if c.Bool("tls-listener-only") {
  244. options = append(options, server.TLSListener(v, true))
  245. } else {
  246. options = append(options, server.TLSListener(v, false))
  247. }
  248. if v := c.String("profile-listener"); v != "" {
  249. options = append(options, server.ProfileListener(v))
  250. }
  251. if v := c.String("web-path"); v != "" {
  252. options = append(options, server.WebPath(v))
  253. }
  254. if v := c.String("proxy-path"); v != "" {
  255. options = append(options, server.ProxyPath(v))
  256. }
  257. if v := c.String("ga-key"); v != "" {
  258. options = append(options, server.GoogleAnalytics(v))
  259. }
  260. if v := c.String("uservoice-key"); v != "" {
  261. options = append(options, server.UserVoice(v))
  262. }
  263. if v := c.String("temp-path"); v != "" {
  264. options = append(options, server.TempPath(v))
  265. }
  266. if v := c.String("log"); v != "" {
  267. options = append(options, server.LogFile(logger, v))
  268. } else {
  269. options = append(options, server.Logger(logger))
  270. }
  271. if v := c.String("lets-encrypt-hosts"); v != "" {
  272. options = append(options, server.UseLetsEncrypt(strings.Split(v, ",")))
  273. }
  274. if v := c.String("virustotal-key"); v != "" {
  275. options = append(options, server.VirustotalKey(v))
  276. }
  277. if v := c.String("clamav-host"); v != "" {
  278. options = append(options, server.ClamavHost(v))
  279. }
  280. if v := c.Int("rate-limit"); v > 0 {
  281. options = append(options, server.RateLimit(v))
  282. }
  283. if cert := c.String("tls-cert-file"); cert == "" {
  284. } else if pk := c.String("tls-private-key"); pk == "" {
  285. } else {
  286. options = append(options, server.TLSConfig(cert, pk))
  287. }
  288. if c.Bool("profiler") {
  289. options = append(options, server.EnableProfiler())
  290. }
  291. if c.Bool("force-https") {
  292. options = append(options, server.ForceHTTPs())
  293. }
  294. if httpAuthUser := c.String("http-auth-user"); httpAuthUser == "" {
  295. } else if httpAuthPass := c.String("http-auth-pass"); httpAuthPass == "" {
  296. } else {
  297. options = append(options, server.HttpAuthCredentials(httpAuthUser, httpAuthPass))
  298. }
  299. applyIPFilter := false
  300. ipFilterOptions := server.IPFilterOptions{}
  301. if ipWhitelist := c.String("ip-whitelist"); ipWhitelist != "" {
  302. applyIPFilter = true
  303. ipFilterOptions.AllowedIPs = strings.Split(ipWhitelist, ",")
  304. ipFilterOptions.BlockByDefault = true
  305. }
  306. if ipBlacklist := c.String("ip-blacklist"); ipBlacklist != "" {
  307. applyIPFilter = true
  308. ipFilterOptions.BlockedIPs = strings.Split(ipBlacklist, ",")
  309. }
  310. if applyIPFilter {
  311. options = append(options, server.FilterOptions(ipFilterOptions))
  312. }
  313. if lifetime := c.Int("lifetime"); lifetime > 0 {
  314. options = append(options, server.LifeTime(lifetime))
  315. } else {
  316. panic("lifetime not greater than 0")
  317. }
  318. switch provider := c.String("provider"); provider {
  319. case "s3":
  320. if accessKey := c.String("aws-access-key"); accessKey == "" {
  321. panic("access-key not set.")
  322. } else if secretKey := c.String("aws-secret-key"); secretKey == "" {
  323. panic("secret-key not set.")
  324. } else if bucket := c.String("bucket"); bucket == "" {
  325. panic("bucket not set.")
  326. } else if awsStorage, err := storage.NewS3Storage(accessKey, secretKey, bucket, c.String("s3-region"),
  327. c.String("s3-endpoint"), logger, c.Bool("s3-no-multipart"), c.Bool("s3-path-style")); err != nil {
  328. panic(err)
  329. } else {
  330. options = append(options, server.UseStorage(awsStorage))
  331. }
  332. case "gdrive":
  333. chunkSize := c.Int("gdrive-chunk-size")
  334. if clientJsonFilepath := c.String("gdrive-client-json-filepath"); clientJsonFilepath == "" {
  335. panic("client-json-filepath not set.")
  336. } else if localConfigPath := c.String("gdrive-local-config-path"); localConfigPath == "" {
  337. panic("local-config-path not set.")
  338. } else if basedir := c.String("basedir"); basedir == "" {
  339. panic("basedir not set.")
  340. } else if gStorage, err := storage.NewGDriveStorage(clientJsonFilepath, localConfigPath, basedir, chunkSize, logger); err != nil {
  341. panic(err)
  342. } else {
  343. options = append(options, server.UseStorage(gStorage))
  344. }
  345. case "local":
  346. if v := c.String("basedir"); v == "" {
  347. panic("basedir not set.")
  348. } else if cleanUp := c.Int("cleanup-interval"); cleanUp <= 0 {
  349. panic("cleanup-interval invalid.")
  350. } else if localStorage, err := storage.NewLocalStorage(v, cleanUp, logger); err != nil {
  351. panic(err)
  352. } else {
  353. options = append(options, server.UseStorage(localStorage))
  354. }
  355. default:
  356. panic("Provider not set or invalid.")
  357. }
  358. srvr, err := server.New(
  359. options...,
  360. )
  361. if err != nil {
  362. logger.Println(color.RedString("Error starting server: %s", err.Error()))
  363. return
  364. }
  365. srvr.Run()
  366. }
  367. return &Cmd{
  368. App: app,
  369. }
  370. }