// // rhimportd // // The Radio Helsinki Rivendell Import Daemon // // // Copyright (C) 2015 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 rhimport import ( "bytes" "fmt" "github.com/golang-basic/go-curl" "io" "mime/multipart" "net/http" "os" "path" ) var ( bool2str = map[bool]string{false: "0", true: "1"} ) type ImportContext struct { *Config *RdDb UserName string Password string Trusted bool ShowId int GroupName string Cart int Cut int Channels int NormalizationLevel int AutotrimLevel int UseMetaData bool SourceUri string SourceFile string DeleteSourceFile bool DeleteSourceDir bool ProgressCallBack func(step int, step_name string, progress float64, userdata interface{}) ProgressCallBackData interface{} } func NewImportContext(conf *Config, rddb *RdDb, user string, group string) *ImportContext { ctx := new(ImportContext) ctx.Config = conf ctx.RdDb = rddb ctx.UserName = user ctx.Password = "" ctx.Trusted = false ctx.ShowId = 0 ctx.GroupName = group ctx.Cart = 0 ctx.Cut = 0 ctx.Channels = 2 ctx.NormalizationLevel = -1200 ctx.AutotrimLevel = 0 ctx.UseMetaData = false ctx.SourceFile = "" ctx.DeleteSourceFile = false ctx.DeleteSourceDir = false ctx.ProgressCallBack = nil return ctx } func (ctx *ImportContext) getPassword(cached bool) (err error) { req := getPasswordRequest{} req.username = ctx.UserName req.cached = cached req.response = make(chan getPasswordResult) ctx.RdDb.getPasswordChan <- req res := <-req.response if res.err != nil { err = res.err return } ctx.Password = res.password return } func (ctx *ImportContext) getShowInfo() (err error) { req := getShowInfoRequest{} req.showid = ctx.ShowId req.response = make(chan getShowInfoResult) ctx.RdDb.getShowInfoChan <- req res := <-req.response if res.err != nil { err = res.err return } rhdl.Printf("Title of show %d is '%s'", ctx.ShowId, res.title) return } func add_cart(ctx *ImportContext) (err error) { var b bytes.Buffer w := multipart.NewWriter(&b) if err = w.WriteField("COMMAND", "12"); err != nil { return } if err = w.WriteField("LOGIN_NAME", ctx.UserName); err != nil { return } if err = w.WriteField("PASSWORD", ctx.Password); err != nil { return } if err = w.WriteField("GRPOUP_NAME", ctx.GroupName); err != nil { return } if err = w.WriteField("TYPE", "audio"); err != nil { return } if ctx.Cart != 0 { if err = w.WriteField("CART_NUMBER", fmt.Sprintf("%d", ctx.Cart)); err != nil { return } } w.Close() return send_post_request(ctx.Config.RDXportEndpoint, &b, w.FormDataContentType()) } func add_cut(ctx *ImportContext) (err error) { var b bytes.Buffer w := multipart.NewWriter(&b) if err = w.WriteField("COMMAND", "10"); err != nil { return } if err = w.WriteField("LOGIN_NAME", ctx.UserName); err != nil { return } if err = w.WriteField("PASSWORD", ctx.Password); err != nil { return } if err = w.WriteField("CART_NUMBER", fmt.Sprintf("%d", ctx.Cart)); err != nil { return } w.Close() return send_post_request(ctx.Config.RDXportEndpoint, &b, w.FormDataContentType()) } func remove_cart(ctx *ImportContext) (err error) { var b bytes.Buffer w := multipart.NewWriter(&b) if err = w.WriteField("COMMAND", "13"); err != nil { return } if err = w.WriteField("LOGIN_NAME", ctx.UserName); err != nil { return } if err = w.WriteField("PASSWORD", ctx.Password); err != nil { return } if err = w.WriteField("CART_NUMBER", fmt.Sprintf("%d", ctx.Cart)); err != nil { return } w.Close() return send_post_request(ctx.Config.RDXportEndpoint, &b, w.FormDataContentType()) } func send_post_request(url string, b *bytes.Buffer, contenttype string) (err error) { var req *http.Request if req, err = http.NewRequest("POST", url, b); err != nil { return } if contenttype != "" { req.Header.Set("Content-Type", contenttype) } client := &http.Client{} var res *http.Response if res, err = client.Do(req); err != nil { return } defer res.Body.Close() fmt.Printf("Response Code: %d\nBody:\n", res.StatusCode) io.Copy(os.Stdout, res.Body) return } func import_audio_create_request(ctx *ImportContext, easy *curl.CURL) (form *curl.Form, err error) { form = curl.NewForm() if err = form.Add("COMMAND", "2"); err != nil { return } if err = form.Add("LOGIN_NAME", ctx.UserName); err != nil { return } if err = form.Add("PASSWORD", ctx.Password); err != nil { return } if err = form.Add("CART_NUMBER", fmt.Sprintf("%d", ctx.Cart)); err != nil { return } if err = form.Add("CUT_NUMBER", fmt.Sprintf("%d", ctx.Cut)); err != nil { return } if err = form.Add("CHANNELS", fmt.Sprintf("%d", ctx.Channels)); err != nil { return } if err = form.Add("NORMALIZATION_LEVEL", fmt.Sprintf("%d", ctx.NormalizationLevel)); err != nil { return } if err = form.Add("AUTOTRIM_LEVEL", fmt.Sprintf("%d", ctx.AutotrimLevel)); err != nil { return } if err = form.Add("USE_METADATA", bool2str[ctx.UseMetaData]); err != nil { return } if err = form.AddFile("FILENAME", ctx.SourceFile); err != nil { return } return } func import_audio(ctx *ImportContext) (err error) { easy := curl.EasyInit() if easy != nil { defer easy.Cleanup() easy.Setopt(curl.OPT_URL, ctx.Config.RDXportEndpoint) easy.Setopt(curl.OPT_POST, true) var form *curl.Form if form, err = import_audio_create_request(ctx, easy); err != nil { return } easy.Setopt(curl.OPT_HTTPPOST, form) easy.Setopt(curl.OPT_HTTPHEADER, []string{"Expect:"}) var resbody bytes.Buffer easy.Setopt(curl.OPT_WRITEFUNCTION, func(ptr []byte, userdata interface{}) bool { b := userdata.(*bytes.Buffer) b.Write(ptr) return true }) easy.Setopt(curl.OPT_WRITEDATA, &resbody) easy.Setopt(curl.OPT_NOPROGRESS, false) easy.Setopt(curl.OPT_PROGRESSFUNCTION, func(dltotal, dlnow, ultotal, ulnow float64, userdata interface{}) bool { ctx := userdata.(*ImportContext) if ctx.ProgressCallBack != nil { ctx.ProgressCallBack(2, "importing", ulnow/ultotal, ctx.ProgressCallBackData) } return true }) easy.Setopt(curl.OPT_PROGRESSDATA, ctx) if err = easy.Perform(); err != nil { return } var status_code interface{} if status_code, err = easy.Getinfo(curl.INFO_RESPONSE_CODE); err != nil { return } fmt.Printf("Response Code: %d\nBody:\n", status_code.(int)) resbody.WriteTo(os.Stdout) } else { err = fmt.Errorf("Error initializing libcurl") } return } func cleanup_files(ctx *ImportContext) { if ctx.DeleteSourceFile { rhdl.Printf("importer: removing file: %s", ctx.SourceFile) if err := os.Remove(ctx.SourceFile); err != nil { rhl.Printf("importer: error removing source file: %s", err) return } if ctx.DeleteSourceDir { dir := path.Dir(ctx.SourceFile) rhdl.Printf("importer: also removing directory: %s", dir) if err := os.Remove(dir); err != nil { rhl.Printf("importer: error removing source directory: %s", err) } } } return } func ImportFile(ctx *ImportContext) (err error) { rhl.Println("ImportFile called for", ctx.SourceFile) defer cleanup_files(ctx) if ctx.ProgressCallBack != nil { ctx.ProgressCallBack(2, "importing", 0.0, ctx.ProgressCallBackData) } if ctx.Trusted { if err = ctx.getPassword(true); err != nil { return } } if err = import_audio(ctx); err != nil { return } if ctx.ProgressCallBack != nil { ctx.ProgressCallBack(2, "importing", 1.0, ctx.ProgressCallBackData) } rhl.Println("ImportFile succesfully imported", ctx.SourceFile) return }