// // rhimportd // // The Radio Helsinki Rivendell Import Daemon // // // Copyright (C) 2015-2016 Christian Pointner // // This file is part of rhimportd. // // rhimportd is free software: you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation, either version 3 of the License, or // any later version. // // rhimportd is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. // // You should have received a copy of the GNU General Public License // along with rhimportd. If not, see . // package main import ( "crypto/sha256" "encoding/hex" "encoding/json" "io" "net/http" "strings" "code.helsinki.at/rhrd-go/rddb" "code.helsinki.at/rhrd-go/rhimport" ) type webUploadResponseData struct { ResponseCode int `json:"RESPONSE_CODE"` ErrorString string `json:"ERROR_STRING"` } func webUploadErrorResponse(w http.ResponseWriter, code int, errStr string) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(code) encoder := json.NewEncoder(w) respdata := webUploadResponseData{code, errStr} encoder.Encode(respdata) } func webUploadSuccessResponse(w http.ResponseWriter) { w.Header().Set("Content-Type", "application/json") w.WriteHeader(http.StatusOK) encoder := json.NewEncoder(w) respdata := webUploadResponseData{http.StatusOK, "success"} encoder.Encode(respdata) } func webUploadHandler(conf *rhimport.Config, db *rddb.DBChan, sessions *rhimport.SessionStoreChan, trusted bool, w http.ResponseWriter, r *http.Request) { rhdl.Printf("WebUploadHandler: %s requests '%s'", r.RemoteAddr, r.URL.String()) if r.Method != "POST" { rhl.Printf("WebUploadHandler: got invalid request method") webUploadErrorResponse(w, http.StatusMethodNotAllowed, "only POST method is allowed") return } parts := strings.SplitN(r.URL.String(), "/", 2) if len(parts) != 2 { rhl.Printf("WebUploadHandler: got invalid request url") webUploadErrorResponse(w, http.StatusBadRequest, "invalid request URL, should be /") return } username := parts[0] sessionid := parts[1] s, _, code, _ := sessions.Get(username, sessionid) if code != http.StatusOK { rhl.Printf("WebUploadHandler: refusing file upload for '%s/%s': session not found", username, sessionid) webUploadErrorResponse(w, http.StatusUnauthorized, "session not found") return } cancel, attachmentChan := s.AttachUploader() if attachmentChan == nil || cancel == nil { rhl.Printf("WebUploadHandler: refusing file upload for '%s/%s': session already uploading", username, sessionid) webUploadErrorResponse(w, http.StatusForbidden, "upload already in progress") return } defer close(attachmentChan) rhl.Printf("WebUploadHandler: starting file upload for '%s/%s' (ContentLength: %d)", username, sessionid, r.ContentLength) sizeTotal := uint64(0) hash := sha256.New() body := io.TeeReader(r.Body, hash) for { chunk := rhimport.AttachmentChunk{} var data [128 * 1024]byte n, err := body.Read(data[:]) if n > 0 { sizeTotal += uint64(n) chunk.Data = data[:n] if err == io.EOF { err = nil } } chunk.Error = err if err == io.EOF { hashStr := hex.EncodeToString(hash.Sum(nil)) rhl.Printf("WebUploadHandler: file upload for '%s/%s' finished (%d bytes, SHA256: %s)", username, sessionid, sizeTotal, hashStr) webUploadSuccessResponse(w) return } select { case <-cancel: rhl.Printf("WebUploadHandler: file upload for '%s/%s' got canceld after %d bytes", username, sessionid, sizeTotal) webUploadErrorResponse(w, http.StatusNoContent, "canceled") return case attachmentChan <- chunk: } if err != nil { webUploadErrorResponse(w, http.StatusInternalServerError, err.Error()) return } } }