Example #1
0
func NewAuthenticatedServer(c *config.Configuration, strategy imageprocessor.ImageProcessorStrategy, auth Authenticator) *Server {
	factory := imagestore.NewFactory(c)
	httpclient := &http.Client{}
	stores := factory.NewImageStores()

	hashGenerator := factory.NewHashGenerator(stores)
	return &Server{c, httpclient, stores, hashGenerator, strategy, auth}
}
Example #2
0
func NewServer(c *config.Configuration, strategy imageprocessor.ImageProcessorStrategy) *Server {
	factory := imagestore.NewFactory(c)
	httpclient := &http.Client{}
	stores := factory.NewImageStores()
	store := stores[0]

	hashGenerator := factory.NewHashGenerator(store)
	authenticator := &PassthroughAuthenticator{}
	return &Server{c, httpclient, store, hashGenerator, strategy, authenticator}
}
Example #3
0
func (s *Server) buildThumbResponse(upload *uploadedfile.UploadedFile) (map[string]interface{}, error) {
	factory := imagestore.NewFactory(s.Config)
	thumbsResp := map[string]interface{}{}

	for _, t := range upload.GetThumbs() {
		thumbName := fmt.Sprintf("%s/%s", upload.GetHash(), t.GetName())
		tObj := factory.NewStoreObject(thumbName, upload.GetMime(), "thumbnail")
		err := tObj.Store(t, s.ImageStore)
		if err != nil {
			return nil, err
		}

		thumbsResp[t.GetName()] = tObj.Url
	}

	return thumbsResp, nil
}
Example #4
0
func (s *Server) uploadFile(uploadFile io.Reader, fileName string, thumbs []*uploadedfile.ThumbFile, user *AuthenticatedUser) ServerResponse {
	tmpFile, err := ioutil.TempFile(os.TempDir(), "image")
	if err != nil {
		fmt.Println(err)

		return ServerResponse{
			Error:  "Unable to write to /tmp",
			Status: http.StatusInternalServerError,
		}
	}

	defer tmpFile.Close()

	_, err = io.Copy(tmpFile, uploadFile)

	if err != nil {
		fmt.Println(err)

		return ServerResponse{
			Error:  "Unable to copy image to disk!",
			Status: http.StatusInternalServerError,
		}
	}

	upload, err := uploadedfile.NewUploadedFile(fileName, tmpFile.Name(), thumbs)
	defer upload.Clean()

	if err != nil {
		return ServerResponse{
			Error:  "Error detecting mime type!",
			Status: http.StatusInternalServerError,
		}
	}

	processor, err := s.processorStrategy(s.Config, upload)
	if err != nil {
		log.Printf("Error creating processor factory: %s", err.Error())
		return ServerResponse{
			Error:  "Unable to process image!",
			Status: http.StatusInternalServerError,
		}
	}

	err = processor.Run(upload)
	if err != nil {
		log.Printf("Error processing %+v: %s", upload, err.Error())
		return ServerResponse{
			Error:  "Unable to process image!",
			Status: http.StatusInternalServerError,
		}
	}

	upload.SetHash(s.hashGenerator.Get())

	factory := imagestore.NewFactory(s.Config)
	obj := factory.NewStoreObject(upload.GetHash(), upload.GetMime(), "original")

	uploadFilepath := upload.GetPath()
	uploadFileFd, err := os.Open(uploadFilepath)

	if err != nil {
		log.Printf("Error opening processed output %+v at %s: %s", upload, uploadFilepath, err.Error())
		return ServerResponse{
			Error:  "Unable to save image!",
			Status: http.StatusInternalServerError,
		}
	}

	obj, err = s.ImageStore.Save(uploadFileFd, obj)
	if err != nil {
		log.Printf("Error saving processed output to store: %s", err.Error())
		return ServerResponse{
			Error:  "Unable to save image!",
			Status: http.StatusInternalServerError,
		}
	}

	thumbsResp := map[string]interface{}{}
	for _, t := range upload.GetThumbs() {
		thumbName := fmt.Sprintf("%s/%s", upload.GetHash(), t.GetName())
		tObj := factory.NewStoreObject(thumbName, upload.GetMime(), "t")

		tPath := t.GetPath()
		tFile, err := os.Open(tPath)

		if err != nil {
			return ServerResponse{
				Error:  "Unable to save thumbnail!",
				Status: http.StatusInternalServerError,
			}
		}

		tObj, err = s.ImageStore.Save(tFile, tObj)
		if err != nil {
			return ServerResponse{
				Error:  "Unable to save thumbnail!",
				Status: http.StatusInternalServerError,
			}
		}

		thumbsResp[t.GetName()] = tObj.Url
	}

	size, err := upload.FileSize()
	if err != nil {
		return ServerResponse{
			Error:  "Unable to fetch image metadata!",
			Status: http.StatusInternalServerError,
		}
	}

	width, height, err := upload.Dimensions()

	if err != nil {
		return ServerResponse{
			Error:  "Error fetching upload dimensions: " + err.Error(),
			Status: http.StatusInternalServerError,
		}
	}

	var userID string
	if user != nil {
		userID = string(user.UserID)
	}

	resp := ImageResponse{
		Link:    obj.Url,
		Mime:    obj.MimeType,
		Hash:    upload.GetHash(),
		Name:    fileName,
		Size:    size,
		Width:   width,
		Height:  height,
		OCRText: upload.GetOCRText(),
		Thumbs:  thumbsResp,
		UserID:  userID,
	}

	return ServerResponse{
		Data:   resp,
		Status: http.StatusOK,
	}
}
Example #5
0
func (s *Server) uploadFile(uploadFile io.Reader, fileName string, thumbs []*uploadedfile.ThumbFile, user *AuthenticatedUser) ServerResponse {
	tmpFile, err := saveToTmp(uploadFile)
	if err != nil {
		return ServerResponse{
			Error:  "Error saving to disk!",
			Status: http.StatusInternalServerError,
		}
	}

	upload, err := uploadedfile.NewUploadedFile(fileName, tmpFile, thumbs)
	defer upload.Clean()

	if err != nil {
		return ServerResponse{
			Error:  "Error detecting mime type!",
			Status: http.StatusInternalServerError,
		}
	}

	processor, err := s.processorStrategy(s.Config, upload)
	if err != nil {
		log.Printf("Error creating processor factory: %s", err.Error())
		return ServerResponse{
			Error:  "Unable to process image!",
			Status: http.StatusInternalServerError,
		}
	}

	err = processor.Run(upload)
	if err != nil {
		log.Printf("Error processing %+v: %s", upload, err.Error())
		return ServerResponse{
			Error:  "Unable to process image!",
			Status: http.StatusInternalServerError,
		}
	}

	upload.SetHash(s.hashGenerator.Get())

	factory := imagestore.NewFactory(s.Config)
	obj := factory.NewStoreObject(upload.GetHash(), upload.GetMime(), "original")

	uploadFilepath := upload.GetPath()
	obj, err = s.ImageStore.Save(uploadFilepath, obj)
	if err != nil {
		log.Printf("Error saving processed output to store: %s", err.Error())
		return ServerResponse{
			Error:  "Unable to save image!",
			Status: http.StatusInternalServerError,
		}
	}

	thumbsResp, err := s.buildThumbResponse(upload)
	if err != nil {
		return ServerResponse{
			Error:  "Unable to process thumbnail!",
			Status: http.StatusInternalServerError,
		}
	}

	size, err := upload.FileSize()
	if err != nil {
		return ServerResponse{
			Error:  "Unable to fetch image metadata!",
			Status: http.StatusInternalServerError,
		}
	}

	width, height, err := upload.Dimensions()

	if err != nil {
		return ServerResponse{
			Error:  "Error fetching upload dimensions: " + err.Error(),
			Status: http.StatusInternalServerError,
		}
	}

	var userID string
	if user != nil {
		userID = string(user.UserID)
	}

	resp := ImageResponse{
		Link:    obj.Url,
		Mime:    obj.MimeType,
		Hash:    upload.GetHash(),
		Name:    fileName,
		Size:    size,
		Width:   width,
		Height:  height,
		OCRText: upload.GetOCRText(),
		Thumbs:  thumbsResp,
		UserID:  userID,
	}

	return ServerResponse{
		Data:   resp,
		Status: http.StatusOK,
	}
}
Example #6
0
func (s *Server) Configure(muxer *http.ServeMux) {

	var extractorFile fileExtractor = func(r *http.Request) (uploadFile io.Reader, filename string, uerr *UserError) {
		uploadFile, header, err := r.FormFile("image")

		if err != nil {
			return nil, "", &UserError{LogMessage: err, UserFacingMessage: errors.New("Error processing file")}
		}

		return uploadFile, header.Filename, nil
	}

	var extractorUrl fileExtractor = func(r *http.Request) (uploadFile io.Reader, filename string, uerr *UserError) {
		url := r.FormValue("image")
		uploadFile, err := s.download(url)

		if err != nil {
			return nil, "", &UserError{LogMessage: err, UserFacingMessage: errors.New("Error downloading URL!")}
		}

		return uploadFile, path.Base(url), nil
	}

	var extractorBase64 fileExtractor = func(r *http.Request) (uploadFile io.Reader, filename string, uerr *UserError) {
		input := r.FormValue("image")
		b64data := input[strings.IndexByte(input, ',')+1:]

		uploadFile = base64.NewDecoder(base64.StdEncoding, strings.NewReader(b64data))

		return uploadFile, "", nil
	}

	type uploadEndpoint func(fileExtractor, *AuthenticatedUser) http.HandlerFunc

	var uploadHandler uploadEndpoint = func(extractor fileExtractor, user *AuthenticatedUser) http.HandlerFunc {
		return func(w http.ResponseWriter, r *http.Request) {
			uploadFile, filename, uerr := extractor(r)
			if uerr != nil {
				log.Printf("Error extracting files: %s", uerr.LogMessage.Error())
				resp := ServerResponse{
					Status: http.StatusBadRequest,
					Error:  uerr.UserFacingMessage.Error(),
				}
				resp.Write(w)
				return
			}

			thumbs, err := parseThumbs(r)
			if err != nil {
				resp := ServerResponse{
					Status: http.StatusBadRequest,
					Error:  "Error parsing thumbnails!",
				}
				resp.Write(w)
				return
			}

			resp := s.uploadFile(uploadFile, filename, thumbs, user)

			switch uploadFile.(type) {
			case io.ReadCloser:
				defer uploadFile.(io.ReadCloser).Close()
				break
			default:
				break
			}

			resp.Write(w)
		}
	}

	// Wrap an existing upload endpoint with authentication, returning a new endpoint that 4xxs unless authentication is passed.
	authenticatedEndpoint := func(endpoint uploadEndpoint, extractor fileExtractor) http.HandlerFunc {
		return func(w http.ResponseWriter, r *http.Request) {
			requestVars := mux.Vars(r)
			attemptedUserIdString, ok := requestVars["user_id"]

			// They didn't send a user ID to a /user endpoint
			if !ok || attemptedUserIdString == "" {
				w.WriteHeader(http.StatusBadRequest)
				return
			}

			user, err := s.authenticator.GetUser(r)

			// Their HMAC was invalid or they are trying to upload to someone else's account
			if user == nil || err != nil || user.UserID != attemptedUserIdString {
				w.WriteHeader(http.StatusUnauthorized)
				log.Printf("Authentication error: %s", err.Error())
				return
			}

			handler := endpoint(extractor, user)
			handler(w, r)
		}
	}

	thumbnailHandler := func(w http.ResponseWriter, r *http.Request) {
		imageID := r.FormValue("uid")

		factory := imagestore.NewFactory(s.Config)
		tObj := factory.NewStoreObject(imageID, "", "original")

		thumbs, err := parseThumbs(r)
		if err != nil {
			resp := ServerResponse{
				Status: http.StatusBadRequest,
				Error:  "Error parsing thumbnails!",
			}
			resp.Write(w)
			return
		}

		if len(thumbs) != 1 {
			resp := ServerResponse{
				Status: http.StatusBadRequest,
				Error:  "Wrong number of thumbnails, expected 1",
			}
			resp.Write(w)
			return
		}

		storeReader, err := s.ImageStore.Get(tObj)
		if err != nil {
			resp := ServerResponse{
				Status: http.StatusBadRequest,
				Error:  fmt.Sprintf("Error retrieving image with ID: %s", imageID),
			}
			resp.Write(w)
			return
		}
		defer storeReader.Close()

		storeFile, err := saveToTmp(storeReader)
		if err != nil {
			resp := ServerResponse{
				Status: http.StatusBadRequest,
				Error:  "Error parsing thumbnails!",
			}
			resp.Write(w)
			return
		}
		defer os.Remove(storeFile)

		upload, err := uploadedfile.NewUploadedFile("", storeFile, thumbs)
		if err != nil {
			log.Printf("Error processing %+v: %s", storeFile, err.Error())
			resp := ServerResponse{
				Error:  "Unable to process thumbnail!",
				Status: http.StatusInternalServerError,
			}
			resp.Write(w)
			return
		}
		upload.SetHash(imageID)
		defer upload.Clean()

		processor, _ := imageprocessor.ThumbnailStrategy(s.Config, upload)
		err = processor.Run(upload)
		if err != nil {
			log.Printf("Error processing %+v: %s", upload, err.Error())
			resp := ServerResponse{
				Error:  "Unable to process thumbnail!",
				Status: http.StatusInternalServerError,
			}
			resp.Write(w)
			return
		}

		ts := upload.GetThumbs()
		t := ts[0]

		thumbName := fmt.Sprintf("%s/%s", upload.GetHash(), t.GetName())
		tObj = factory.NewStoreObject(thumbName, upload.GetMime(), "thumbnail")
		err = tObj.Store(t, s.ImageStore)
		if err != nil {
			log.Printf("Error storing %+v: %s", t, err.Error())
			resp := ServerResponse{
				Error:  "Unable to store thumbnail!",
				Status: http.StatusInternalServerError,
			}
			resp.Write(w)
			return
		}

		http.ServeFile(w, r, t.GetPath())
	}

	rootHandler := func(w http.ResponseWriter, r *http.Request) {
		fmt.Fprint(w, "<html><head><title>An open source image uploader by Imgur</title></head><body style=\"background-color: #2b2b2b; color: white\">")
		fmt.Fprint(w, "Congratulations! Your image upload server is up and running. Head over to the <a style=\"color: #85bf25 \" href=\"https://github.com/Imgur/mandible\">github</a> page for documentation")
		fmt.Fprint(w, "<br/><br/><br/><img src=\"http://i.imgur.com/YbfUjs5.png?2\" />")
		fmt.Fprint(w, "</body></html>")
	}

	router := mux.NewRouter()

	router.HandleFunc("/file", uploadHandler(extractorFile, nil))
	router.HandleFunc("/url", uploadHandler(extractorUrl, nil))
	router.HandleFunc("/base64", uploadHandler(extractorBase64, nil))

	router.HandleFunc("/user/{user_id}/file", authenticatedEndpoint(uploadHandler, extractorBase64))
	router.HandleFunc("/user/{user_id}/url", authenticatedEndpoint(uploadHandler, extractorUrl))
	router.HandleFunc("/user/{user_id}/base64", authenticatedEndpoint(uploadHandler, extractorBase64))

	router.HandleFunc("/thumbnail", thumbnailHandler)

	router.HandleFunc("/", rootHandler)

	muxer.Handle("/", router)
}