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.
 
 
 

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