Ejemplo n.º 1
0
// 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
}
Ejemplo n.º 2
0
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)
}