github.com/jancarloviray/community@v0.41.1-0.20170124221257-33a66c87cf2f/cmd/wordconvert/wordconvert.go (about)

     1  // Copyright 2016 Documize Inc. <legal@documize.com>. All rights reserved.
     2  //
     3  // This software (Documize Community Edition) is licensed under
     4  // GNU AGPL v3 http://www.gnu.org/licenses/agpl-3.0.en.html
     5  //
     6  // You can operate outside the AGPL restrictions by purchasing
     7  // Documize Enterprise Edition and obtaining a commercial license
     8  // by contacting <sales@documize.com>.
     9  //
    10  // https://documize.com
    11  
    12  // Package main contains a command line utility to convert multiple word documents using api.documize.com
    13  package main
    14  
    15  import (
    16  	"archive/zip"
    17  	"bytes"
    18  	"crypto/tls"
    19  	"errors"
    20  	"flag"
    21  	"fmt"
    22  	"io"
    23  	"io/ioutil"
    24  	"mime/multipart"
    25  	"net"
    26  	"net/http"
    27  	"os"
    28  	"path"
    29  	"strings"
    30  )
    31  
    32  const serverURLfmt = "https://%s/api/1/word"
    33  
    34  var server = flag.String("s", "api.documize.com:443", "the server")
    35  var outputDir = flag.String("o", ".", "specify the directory to hold the output")
    36  var ignoreBadCert = flag.Bool("k", false, "ignore bad certificate errors")
    37  var verbose = flag.Bool("v", false, "verbose progress messages")
    38  var stayziped = flag.Bool("z", false, "do not automatically unzip content")
    39  var token = flag.String("t", "", "authorization token (if you use your e-mail address here during preview period, we will tell you before changes are made)")
    40  var ignoreErrs = flag.Bool("e", false, "report errors on individual files, but continue")
    41  var version = flag.Bool("version", false, "display the version of this code")
    42  
    43  // does the file have a valid extension
    44  func validXtn(fn string) bool {
    45  	lcfn := strings.ToLower(fn)
    46  	for _, xtn := range []string{".doc", ".docx", ".pdf"} {
    47  		if strings.HasSuffix(lcfn, xtn) {
    48  			return true
    49  		}
    50  	}
    51  	return false
    52  }
    53  
    54  //  errCanContinue is the mechanism to print errors yet continue, if that command line option is chosen
    55  func errCanContinue(can bool, err error) bool {
    56  	if err == nil {
    57  		return false
    58  	}
    59  	fmt.Fprintln(os.Stderr, err)
    60  	if *ignoreErrs && can {
    61  		return true
    62  	}
    63  	os.Exit(0)
    64  	return true // never reached
    65  }
    66  
    67  func main() {
    68  
    69  	flag.Parse()
    70  
    71  	if *version {
    72  		fmt.Println("Version: 0.1 preview")
    73  	}
    74  
    75  	if *outputDir != "." {
    76  		if err := os.Mkdir(*outputDir, 0777); err != nil && !os.IsExist(err) {
    77  			errCanContinue(false, err)
    78  		}
    79  	}
    80  
    81  	host, _, err := net.SplitHostPort(*server)
    82  	errCanContinue(false, err)
    83  
    84  	tlc := &tls.Config{
    85  		InsecureSkipVerify: *ignoreBadCert,
    86  		ServerName:         host,
    87  	}
    88  
    89  	transport := &http.Transport{TLSClientConfig: tlc}
    90  	hclient := &http.Client{Transport: transport}
    91  
    92  	processFiles(hclient)
    93  
    94  	os.Exit(1)
    95  }
    96  
    97  func processFiles(hclient *http.Client) {
    98  
    99  	for _, fileName := range flag.Args() {
   100  
   101  		if validXtn(fileName) {
   102  
   103  			if *verbose {
   104  				fmt.Println("processing", fileName)
   105  			}
   106  
   107  			content, err := ioutil.ReadFile(fileName)
   108  			if errCanContinue(true, err) {
   109  				continue
   110  			}
   111  
   112  			bodyBuf := &bytes.Buffer{}
   113  			bodyWriter := multipart.NewWriter(bodyBuf)
   114  
   115  			_, fn := path.Split(fileName)
   116  			fileWriter, err := bodyWriter.CreateFormFile("wordfile", fn) // name as expected by the API
   117  			if errCanContinue(true, err) {
   118  				continue
   119  			}
   120  
   121  			_, err = io.Copy(fileWriter, bytes.NewReader(content))
   122  			if errCanContinue(true, err) {
   123  				continue
   124  			}
   125  
   126  			contentType := bodyWriter.FormDataContentType()
   127  			err = bodyWriter.Close()
   128  			if errCanContinue(true, err) {
   129  				continue
   130  			}
   131  
   132  			target := fmt.Sprintf(serverURLfmt, *server)
   133  			if *token != "" {
   134  				target += "?token=" + *token // NOTE: after the preview phase, token will not be optional
   135  			}
   136  
   137  			req, err := http.NewRequest("POST",
   138  				target,
   139  				bodyBuf)
   140  			if errCanContinue(true, err) {
   141  				continue
   142  			}
   143  
   144  			req.Header.Set("Content-Type", contentType)
   145  			resp, err := hclient.Do(req)
   146  			if errCanContinue(true, err) {
   147  				continue
   148  			}
   149  
   150  			zipdata, err := ioutil.ReadAll(resp.Body)
   151  			if errCanContinue(true, err) {
   152  				continue
   153  			}
   154  
   155  			resp.Body.Close() // ignore error
   156  
   157  			if resp.StatusCode != http.StatusOK {
   158  				if errCanContinue(true, errors.New("server returned status: "+resp.Status)) {
   159  					continue
   160  				}
   161  			}
   162  
   163  			targetDir := *outputDir + "/" + fn + ".content"
   164  			if *stayziped {
   165  				if err := ioutil.WriteFile(targetDir+".zip", zipdata, 0666); err != nil {
   166  					if errCanContinue(true, err) {
   167  						continue
   168  					}
   169  				}
   170  			} else {
   171  				if errCanContinue(true, unzipFiles(zipdata, targetDir)) {
   172  					continue
   173  				}
   174  			}
   175  
   176  		} else {
   177  
   178  			if *verbose {
   179  				fmt.Println("ignored", fileName)
   180  			}
   181  
   182  		}
   183  	}
   184  }
   185  
   186  // simple unzip
   187  func unzipFiles(zipdata []byte, targetDir string) error {
   188  
   189  	rdr, err := zip.NewReader(bytes.NewReader(zipdata), int64(len(zipdata)))
   190  	if err != nil {
   191  		return err
   192  	}
   193  
   194  	if err := os.Mkdir(targetDir, 0777); err != nil && !os.IsExist(err) { // make sure the target directory exists
   195  		return err
   196  	}
   197  
   198  fileLoop:
   199  	for _, zf := range rdr.File {
   200  		frc, err := zf.Open()
   201  		if errCanContinue(true, err) {
   202  			continue
   203  		}
   204  
   205  		filedata, err := ioutil.ReadAll(frc)
   206  		if errCanContinue(true, err) {
   207  			continue
   208  		}
   209  
   210  		subTarget := targetDir + "/" + zf.Name
   211  
   212  		subDir := path.Dir(subTarget)
   213  
   214  		if subDir != targetDir {
   215  			rump := strings.TrimPrefix(subDir, targetDir)
   216  			tree := strings.Split(rump, "/")
   217  			built := ""
   218  			for _, thisPart := range tree[1:] { // make sure we have a directory at each level of the tree
   219  				built += "/" + thisPart
   220  				if err := os.Mkdir(targetDir+built, 0777); err != nil && !os.IsExist(err) {
   221  					if errCanContinue(true, err) {
   222  						continue fileLoop
   223  					}
   224  				}
   225  			}
   226  		}
   227  
   228  		if err := ioutil.WriteFile(subTarget, filedata, 0666); err != nil {
   229  			if errCanContinue(true, err) {
   230  				continue
   231  			}
   232  		}
   233  
   234  		if *verbose {
   235  			fmt.Println("wrote", subTarget)
   236  		}
   237  		frc.Close()
   238  	}
   239  
   240  	return nil
   241  }