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.

488 lines
11 KiB

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