// Start a publishing process. Accepted parameter: // jobname=<jobname> // vars=var1=foo,var2=bar (where all but the frist = is encoded as %3D) func v0PublishHandler(w http.ResponseWriter, r *http.Request) { var files map[string]interface{} data, err := ioutil.ReadAll(r.Body) if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintln(protocolFile, "Internal error 011:") fmt.Fprintln(protocolFile, err) fmt.Fprintln(w, "Internal error 011") return } err = json.Unmarshal(data, &files) if err != nil { w.WriteHeader(http.StatusBadRequest) fmt.Fprintln(w, "JSON error:", err) return } err = makePublisherTemp() if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintln(protocolFile, "Internal error 012:") fmt.Fprintln(protocolFile, err) fmt.Fprintln(w, "Internal error 012") return } // Always start with non-0 to avoid problems out of scope of the publisher tmpdir, err := ioutil.TempDir(serverTemp, "1") if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintln(protocolFile, "Internal error 013:") fmt.Fprintln(protocolFile, err) fmt.Fprintln(w, "Internal error 013") return } id, err := filepath.Rel(serverTemp, tmpdir) if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintln(protocolFile, "Internal error 014:") fmt.Fprintln(protocolFile, err) fmt.Fprintln(w, "Internal error 014") return } fmt.Fprintf(protocolFile, "%s: Publishing request from %s with id %s\n", time.Now().Format("2006-01-02 15:04:05"), r.RemoteAddr, id) for k, v := range files { bb := bytes.NewBuffer([]byte(v.(string))) b64reader := base64.NewDecoder(base64.StdEncoding, bb) f, err := os.Create(filepath.Join(tmpdir, k)) if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintln(protocolFile, "Internal error 015:") fmt.Fprintln(protocolFile, err) fmt.Fprintln(w, "Internal error 015") return } _, err = io.Copy(f, b64reader) if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintln(protocolFile, "Internal error 016:") fmt.Fprintln(protocolFile, err) fmt.Fprintln(w, "Internal error 016") return } f.Close() } jobname := r.FormValue("jobname") if jobname == "" { // let's try the config file cd, err := configurator.ReadFiles(filepath.Join(tmpdir, "publisher.cfg")) if err == nil { jobname = cd.String("DEFAULT", "jobname") } } if jobname != "" { err = ioutil.WriteFile(filepath.Join(tmpdir, "jobname.txt"), []byte(jobname), 0644) if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintln(protocolFile, "Internal error 017:") fmt.Fprintln(protocolFile, err) fmt.Fprintln(w, "Internal error 017") return } } if vars := r.FormValue("vars"); vars != "" { f, err := os.OpenFile(filepath.Join(tmpdir, "extravars"), os.O_RDWR|os.O_CREATE, 0644) if err != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintln(protocolFile, "Internal error 018:") fmt.Fprintln(protocolFile, err) fmt.Fprintln(w, "Internal error 018") return } for _, v := range strings.Split(vars, ",") { f.Write([]byte(v + "\n")) } f.Close() } addPublishrequestToQueue(id) jsonid := struct { Id string `json:"id"` }{ Id: id, } buf, marshallerr := json.Marshal(jsonid) if marshallerr != nil { w.WriteHeader(http.StatusInternalServerError) fmt.Fprintln(protocolFile, "Internal error 019:") fmt.Fprintln(protocolFile, marshallerr) fmt.Fprintln(w, "Internal error 019") return } w.WriteHeader(http.StatusCreated) w.Write(buf) return }
func main() { op := optionparser.NewOptionParser() op.On("--address IPADDRESS", "Address to be used for the server mode. Defaults to 127.0.0.1", options) op.On("--autoopen", "Open the PDF file (MacOS X and Linux only)", options) op.On("-c NAME", "--config", "Read the config file with the given NAME. Default: 'publisher.cfg'", &configfilename) op.On("--credits", "Show credits and exit", showCredits) op.On("--no-cutmarks", "Display cutmarks in the document", layoutoptions) op.On("--data NAME", "Name of the XML data file. Defaults to 'data.xml'. Use '-' for STDIN", options) op.On("--dummy", "Don't read a data file, use '<data />' as input", options) op.On("-x", "--extra-dir DIR", "Additional directory for file search", extradir) op.On("--extra-xml NAME", "Add this file to the layout file", extraXML) op.On("--filter FILTER", "Run XPROC filter before publishing starts", options) op.On("--grid", "Display background grid. Disable with --no-grid", options) op.On("--ignore-case", "Ignore case when accessing files (on a case-insensitive file system)", options) op.On("--no-local", "Add local directory to the search path. Default is true", &add_local_path) op.On("--layout NAME", "Name of the layout file. Defaults to 'layout.xml'", options) op.On("--jobname NAME", "The name of the resulting PDF file (without extension), default is 'publisher'", options) op.On("--mainlanguage NAME", "The document's main language in locale format, for example 'en' or 'en_US'.", &mainlanguage) op.On("--outputdir=DIR", "Copy PDF and protocol to this directory", options) op.On("--port PORT", "Port to be used for the server mode. Defaults to 5266", options) op.On("--profile", "Run publisher with profiling on (internal use)", options) op.On("--quiet", "Run publisher in silent mode", options) op.On("--runs NUM", "Number of publishing runs ", options) op.On("--startpage NUM", "The first page number", layoutoptions) op.On("--show-gridallocation", "Show the allocated grid cells", layoutoptions) op.On("--systemfonts", "Use system fonts (not Win XP)", &useSystemFonts) op.On("--tempdir=DIR", "Use this directory instead of the system temporary directory", options) op.On("--trace", "Show debug messages and some tracing PDF output", layoutoptions) op.On("--timeout SEC", "Exit after SEC seconds", options) op.On("-v", "--var VAR=VALUE", "Set a variable for the publishing run", setVariable) op.On("--varsfile NAME", "Set variables for the publishing run from key=value... file", options) op.On("--verbose", "Print a bit of debugging output", options) op.On("--version", "Show version information", versioninfo) op.On("--wd DIR", "Change working directory", options) op.On("--xml", "Output as (pseudo-)XML (for list-fonts)", options) op.Command("clean", "Remove publisher generated files") op.Command("compare", "Compare files for quality assurance") op.Command("doc", "Open documentation") op.Command("list-fonts", "List installed fonts (use together with --xml for copy/paste)") op.Command("run", "Start publishing (default)") op.Command("server", "Run as http-api server on localhost port 5266 (configure with --address and --port)") op.Command("watch", "Start watchdog / hotfolder") err := op.Parse() if err != nil { log.Fatal("Parse error: ", err) } var command string switch len(op.Extra) { case 0: // no command given, run is the default command command = "run" case 1: // great command = op.Extra[0] default: // more than one command given, what should I do? command = op.Extra[0] } cfg, err = configurator.ReadFiles(filepath.Join(homedir, ".publisher.cfg"), "/etc/speedata/publisher.cfg") if err != nil { log.Fatal(err) } // When the user requests another working directory, we should // change into the given wd first, before reading the local // options wdIsSet := false if wd := getOption("wd"); wd != "" { wdIsSet = true err := os.Chdir(wd) if err != nil { log.Fatal(err) } log.Printf("Working directory now: %s", wd) pwd = wd } cfg.ReadFile(filepath.Join(pwd, configfilename)) // ... but if the local config file has a wd=... option, we should honor this // and also honor the settings in that publisher.cfg file if !wdIsSet { if wd := getOption("wd"); wd != "" { err := os.Chdir(wd) if err != nil { log.Fatal(err) } log.Printf("Working directory now: %s", wd) pwd = wd cfg.ReadFile(filepath.Join(pwd, configfilename)) } } if add_local_path { extra_dir = append(extra_dir, pwd) } if useSystemFonts { // FontFolder() is system dependent and defined in extra files ff, err := FontFolder() if err != nil { log.Fatal(err) } defaults["fontpath"] = ff } os.Setenv("SP_MAINLANGUAGE", mainlanguage) os.Setenv("SP_FONT_PATH", getOption("fontpath")) os.Setenv("SP_PATH_REWRITE", getOption("pathrewrite")) os.Setenv("IMGCACHE", getOption("imagecache")) if ed := cfg.String("DEFAULT", "extra-dir"); ed != "" { abspath, err := filepath.Abs(ed) if err != nil { log.Fatal("Cannot find directory", ed) } extra_dir = append(extra_dir, abspath) } os.Setenv("SD_EXTRA_DIRS", strings.Join(extra_dir, string(filepath.ListSeparator))) if extraxmloption := getOption("extraxml"); extraxmloption != "" { for _, xmlfile := range strings.Split(extraxmloption, ",") { extraxml = append(extraxml, xmlfile) } } os.Setenv("SD_EXTRA_XML", strings.Join(extraxml, ",")) verbose := false if getOption("verbose") != "" { verbose = true os.Setenv("SP_VERBOSITY", "1") fmt.Println("SD_EXTRA_DIRS:", os.Getenv("SD_EXTRA_DIRS")) fmt.Println("SD_EXTRA_XML:", os.Getenv("SD_EXTRA_XML")) } if getOption("ignore-case") == "true" { os.Setenv("SP_IGNORECASE", "1") if verbose { fmt.Println("Ignore case for file system access") } } var exitstatus int if getOption("profile") != "" { fmt.Println("Profiling publisher run. Removing lprof_* now.") os.Setenv("SD_PROFILER", "true") files, err := filepath.Glob("lprof_*") if err != nil { log.Fatal(err) } for _, filename := range files { err = os.Remove(filename) if err != nil { log.Fatal(err) } } } if seconds := getOption("timeout"); seconds != "" { num, err := strconv.Atoi(seconds) if err != nil { log.Fatal(err) } log.Printf("Setting timeout to %d seconds", num) go timeoutCatcher(num) } // There is no need for the internal daemon when we do the other commands switch command { case "run", "server": daemon = comm.NewServer() } switch command { case "run": jobname := getOption("jobname") finishedfilename := fmt.Sprintf("%s.finished", jobname) os.Remove(finishedfilename) if filter := getOption("filter"); filter != "" { if filepath.Ext(filter) != ".xpl" { filter = filter + ".xpl" } log.Println("Run filter: ", filter) os.Setenv("CLASSPATH", libdir+"/calabash.jar:"+libdir+"/saxon9he.jar") cmdline := "java com.xmlcalabash.drivers.Main " + filter run(cmdline) } exitstatus = runPublisher() // profiler requested? if getOption("profile") != "" { fmt.Println("Run 'summary.lua' on resulting lprof_* file.") files, err := filepath.Glob("lprof_*") if err != nil { log.Fatal(err) } if len(files) != 1 { log.Println("Profiling not done, expecting exactly one file matching lprof_*.") } else { cmdline := fmt.Sprintf(`"%s" --luaonly "%s/lua/summary.lua" -v %s`, getExecutablePath(), srcdir, files[0]) run(cmdline) } } ioutil.WriteFile(finishedfilename, []byte("finished\n"), 0600) // open PDF if necessary if getOption("autoopen") == "true" { openFile(jobname + ".pdf") } case "compare": if len(op.Extra) > 1 { dir := op.Extra[1] fi, err := os.Stat(dir) if err != nil { log.Fatal(err) } if !fi.IsDir() { log.Fatalf("%q must be a directory", dir) } absDir, err := filepath.Abs(dir) if err != nil { log.Fatal(err) } sp.DoCompare(absDir) } else { log.Println("Please give one directory") } case "clean": jobname := getOption("jobname") files, err := filepath.Glob(jobname + "*") if err != nil { log.Fatal(err) } for _, v := range files { switch filepath.Ext(v) { case ".vars", ".log", ".protocol", ".dataxml", ".status", ".finished": log.Printf("Removing %s", v) err = os.Remove(v) if err != nil { log.Println(err) } } if v == jobname+"-aux.xml" { log.Printf("Removing %s", v) err = os.Remove(v) if err != nil { log.Println(err) } } } case "doc": openFile(path_to_documentation) os.Exit(0) case "list-fonts": var xml string if getOption("xml") == "true" { xml = "xml" } cmdline := fmt.Sprintf(`"%s" --luaonly "%s/lua/sdscripts.lua" "%s" list-fonts %s`, getExecutablePath(), srcdir, inifile, xml) run(cmdline) case "watch": watch_dir := getOptionSection("hotfolder", "hotfolder") events := getOptionSection("events", "hotfolder") var hotfolder_events []hotfolder.Event for _, v := range strings.Split(events, ";") { pattern_command := strings.Split(v, ":") if len(pattern_command) < 2 { log.Fatal("Something is wrong with the configuration file. hotfolder section correct?") } hotfolder_event := new(hotfolder.Event) hotfolder_event.Pattern = regexp.MustCompile(pattern_command[0]) hotfolder_event.Command = &pattern_command[1] hotfolder_events = append(hotfolder_events, *hotfolder_event) } if watch_dir != "" { hotfolder.Watch(watch_dir, hotfolder_events) } else { log.Fatal("Problem with watch dir in section [hotfolder].") } case "server": runServer(getOption("port"), getOption("address"), getOption("tempdir")) default: log.Fatal("unknown command:", command) } if daemon != nil { daemon.Close() } showDuration() os.Exit(exitstatus) }