github.com/decred/politeia@v1.4.0/politeiawww/cmd/shared/client.go (about)

     1  // Copyright (c) 2017-2020 The Decred developers
     2  // Use of this source code is governed by an ISC
     3  // license that can be found in the LICENSE file.
     4  
     5  package shared
     6  
     7  import (
     8  	"bytes"
     9  	"context"
    10  	"encoding/json"
    11  	"fmt"
    12  	"net/http"
    13  	"net/http/cookiejar"
    14  	"net/url"
    15  	"os"
    16  	"reflect"
    17  	"strings"
    18  
    19  	"decred.org/dcrwallet/rpc/walletrpc"
    20  	cms "github.com/decred/politeia/politeiawww/api/cms/v1"
    21  	www "github.com/decred/politeia/politeiawww/api/www/v1"
    22  	"github.com/decred/politeia/util"
    23  	"github.com/gorilla/schema"
    24  	"golang.org/x/net/publicsuffix"
    25  	"google.golang.org/grpc"
    26  	"google.golang.org/grpc/credentials"
    27  )
    28  
    29  // Client is a politeiawww client.
    30  type Client struct {
    31  	http *http.Client
    32  	cfg  *Config
    33  
    34  	// wallet grpc
    35  	ctx    context.Context
    36  	creds  credentials.TransportCredentials
    37  	conn   *grpc.ClientConn
    38  	wallet walletrpc.WalletServiceClient
    39  }
    40  
    41  func prettyPrintJSON(v interface{}) error {
    42  	b, err := json.MarshalIndent(v, "", "  ")
    43  	if err != nil {
    44  		return fmt.Errorf("MarshalIndent: %v", err)
    45  	}
    46  	fmt.Fprintf(os.Stdout, "%s\n", b)
    47  	return nil
    48  }
    49  
    50  // userWWWErrorStatus retrieves the human readable error message for an error
    51  // status code. The status code error message comes from the www api.
    52  func userWWWErrorStatus(e www.ErrorStatusT) string {
    53  	s, ok := www.ErrorStatus[e]
    54  	if ok {
    55  		return s
    56  	}
    57  	s, ok = cms.ErrorStatus[e]
    58  	if ok {
    59  		return s
    60  	}
    61  	return ""
    62  }
    63  
    64  // wwwError unmarshals the response body from makeRequest, and handles any
    65  // status code errors from the server. Parses the error code and error context
    66  // from the www api, in case of user error.
    67  func wwwError(body []byte, statusCode int) error {
    68  	switch statusCode {
    69  	case http.StatusBadRequest:
    70  		// User Error
    71  		var ue www.UserError
    72  		err := json.Unmarshal(body, &ue)
    73  		if err != nil {
    74  			return fmt.Errorf("unmarshal UserError: %v", err)
    75  		}
    76  		if ue.ErrorCode != 0 {
    77  			var e error
    78  			errMsg := userWWWErrorStatus(ue.ErrorCode)
    79  			if len(ue.ErrorContext) == 0 {
    80  				// Error format when an ErrorContext is not included
    81  				e = fmt.Errorf("%v, %v", statusCode, errMsg)
    82  			} else {
    83  				// Error format when an ErrorContext is included
    84  				e = fmt.Errorf("%v, %v: %v", statusCode, errMsg,
    85  					strings.Join(ue.ErrorContext, ", "))
    86  			}
    87  			return e
    88  		}
    89  	case http.StatusInternalServerError:
    90  		// Server Error
    91  		var er www.ErrorReply
    92  		err := json.Unmarshal(body, &er)
    93  		if err != nil {
    94  			return fmt.Errorf("unmarshal Error: %v", err)
    95  		}
    96  		var e error
    97  		if len(er.ErrorContext) == 0 {
    98  			// Error format when an ErrorContext is not included
    99  			e = fmt.Errorf("ServerError timestamp: %v", er.ErrorCode)
   100  		} else {
   101  			// Error format when an ErrorContext is included
   102  			e = fmt.Errorf("ServerError timestamp: %v context: %v",
   103  				er.ErrorCode, er.ErrorContext)
   104  		}
   105  		return e
   106  	default:
   107  		// Default Status Code Error
   108  		return fmt.Errorf("%v", statusCode)
   109  	}
   110  
   111  	return nil
   112  }
   113  
   114  // makeRequest sends the provided request to the politeiawww backend specified
   115  // by the Client config. This function handles verbose printing when specified
   116  // by the Client config since verbose printing includes details such as the
   117  // full route and http response codes. Caller functions handle status code
   118  // validation and error checks.
   119  func (c *Client) makeRequest(method, routeVersion, route string, body interface{}) (int, []byte, error) {
   120  	// Setup request
   121  	var requestBody []byte
   122  	var queryParams string
   123  	if body != nil {
   124  		switch {
   125  		case method == http.MethodGet:
   126  			// Use reflection in case the interface value is nil
   127  			// but the interface type is not. This can happen when
   128  			// query params exist but are not used.
   129  			if reflect.ValueOf(body).IsNil() {
   130  				break
   131  			}
   132  
   133  			// GET requests don't have a request body; instead we
   134  			// will populate the query params.
   135  			form := url.Values{}
   136  			if err := schema.NewEncoder().Encode(body, form); err != nil {
   137  				return 0, nil, err
   138  			}
   139  			queryParams = "?" + form.Encode()
   140  
   141  		case method == http.MethodPost || method == http.MethodPut:
   142  			var err error
   143  			requestBody, err = json.Marshal(body)
   144  			if err != nil {
   145  				return 0, nil, err
   146  			}
   147  
   148  		default:
   149  			return 0, nil, fmt.Errorf("unknown http method '%v'", method)
   150  		}
   151  	}
   152  
   153  	fullRoute := c.cfg.Host + routeVersion + route + queryParams
   154  
   155  	// Print request details
   156  	switch {
   157  	case c.cfg.Verbose && method == http.MethodGet:
   158  		fmt.Printf("Request: GET %v\n", fullRoute)
   159  	case c.cfg.Verbose && method == http.MethodPost:
   160  		fmt.Printf("Request: POST %v\n", fullRoute)
   161  		err := prettyPrintJSON(body)
   162  		if err != nil {
   163  			return 0, nil, err
   164  		}
   165  	case c.cfg.Verbose && method == http.MethodPut:
   166  		fmt.Printf("Request: PUT %v\n", fullRoute)
   167  		err := prettyPrintJSON(body)
   168  		if err != nil {
   169  			return 0, nil, err
   170  		}
   171  	}
   172  
   173  	// Create http request
   174  	req, err := http.NewRequest(method, fullRoute, bytes.NewReader(requestBody))
   175  	if err != nil {
   176  		return 0, nil, err
   177  	}
   178  	req.Header.Add(www.CsrfToken, c.cfg.CSRF)
   179  
   180  	// Send request
   181  	r, err := c.http.Do(req)
   182  	if err != nil {
   183  		return 0, nil, err
   184  	}
   185  	defer func() {
   186  		r.Body.Close()
   187  	}()
   188  
   189  	responseBody := util.ConvertBodyToByteArray(r.Body, false)
   190  
   191  	// Print response details
   192  	if c.cfg.Verbose {
   193  		fmt.Printf("Response: %v\n", r.StatusCode)
   194  	}
   195  
   196  	return r.StatusCode, responseBody, nil
   197  }
   198  
   199  // Version returns the version information for the politeiawww instance.
   200  func (c *Client) Version() (*www.VersionReply, error) {
   201  	fullRoute := c.cfg.Host + www.PoliteiaWWWAPIRoute + www.RouteVersion
   202  
   203  	// Print request details
   204  	if c.cfg.Verbose {
   205  		fmt.Printf("Request: GET %v\n", fullRoute)
   206  	}
   207  
   208  	// Create new http request instead of using makeRequest()
   209  	// so that we can save the CSRF tokens to disk.
   210  	req, err := http.NewRequest(http.MethodGet, fullRoute, nil)
   211  	if err != nil {
   212  		return nil, err
   213  	}
   214  	req.Header.Add(www.CsrfToken, c.cfg.CSRF)
   215  
   216  	// Send request
   217  	r, err := c.http.Do(req)
   218  	if err != nil {
   219  		return nil, err
   220  	}
   221  	defer func() {
   222  		r.Body.Close()
   223  	}()
   224  
   225  	respBody := util.ConvertBodyToByteArray(r.Body, false)
   226  
   227  	// Validate response status
   228  	if r.StatusCode != http.StatusOK {
   229  		var ue www.UserError
   230  		err = json.Unmarshal(respBody, &ue)
   231  		if err == nil {
   232  			return nil, fmt.Errorf("%v, %v %v", r.StatusCode,
   233  				userWWWErrorStatus(ue.ErrorCode),
   234  				strings.Join(ue.ErrorContext, ", "))
   235  		}
   236  
   237  		return nil, fmt.Errorf("%v", r.StatusCode)
   238  	}
   239  
   240  	// Unmarshal response
   241  	var vr www.VersionReply
   242  	err = json.Unmarshal(respBody, &vr)
   243  	if err != nil {
   244  		return nil, fmt.Errorf("unmarshal VersionReply: %v", err)
   245  	}
   246  
   247  	// Print response details
   248  	if c.cfg.Verbose {
   249  		fmt.Printf("Response: %v\n", r.StatusCode)
   250  		err := prettyPrintJSON(vr)
   251  		if err != nil {
   252  			return nil, err
   253  		}
   254  	}
   255  
   256  	// CSRF protection works via the double-submit method.
   257  	// One token is sent in the cookie. A second token is
   258  	// sent in the header. Both tokens must be persisted
   259  	// between CLI commands.
   260  
   261  	// Persist CSRF header token
   262  	c.cfg.CSRF = r.Header.Get(www.CsrfToken)
   263  	err = c.cfg.SaveCSRF(c.cfg.CSRF)
   264  	if err != nil {
   265  		return nil, err
   266  	}
   267  
   268  	// Persist CSRF cookie token
   269  	err = c.cfg.SaveCookies(c.http.Jar.Cookies(req.URL))
   270  	if err != nil {
   271  		return nil, err
   272  	}
   273  
   274  	return &vr, nil
   275  }
   276  
   277  // Login logs a user into politeiawww.
   278  func (c *Client) Login(l *www.Login) (*www.LoginReply, error) {
   279  	// Setup request
   280  	requestBody, err := json.Marshal(l)
   281  	if err != nil {
   282  		return nil, err
   283  	}
   284  
   285  	fullRoute := c.cfg.Host + www.PoliteiaWWWAPIRoute + www.RouteLogin
   286  
   287  	// Print request details
   288  	if c.cfg.Verbose {
   289  		fmt.Printf("Request: POST %v\n", fullRoute)
   290  		err := prettyPrintJSON(l)
   291  		if err != nil {
   292  			return nil, err
   293  		}
   294  	}
   295  
   296  	// Create new http request instead of using makeRequest()
   297  	// so that we can save the session data for subsequent
   298  	// commands
   299  	req, err := http.NewRequest(http.MethodPost, fullRoute,
   300  		bytes.NewReader(requestBody))
   301  	if err != nil {
   302  		return nil, err
   303  	}
   304  	req.Header.Add(www.CsrfToken, c.cfg.CSRF)
   305  
   306  	// Send request
   307  	r, err := c.http.Do(req)
   308  	if err != nil {
   309  		return nil, err
   310  	}
   311  	defer func() {
   312  		r.Body.Close()
   313  	}()
   314  
   315  	respBody := util.ConvertBodyToByteArray(r.Body, false)
   316  
   317  	// Validate response status
   318  	if r.StatusCode != http.StatusOK {
   319  		var ue www.UserError
   320  		err = json.Unmarshal(respBody, &ue)
   321  		if err == nil {
   322  			return nil, fmt.Errorf("%v, %v %v", r.StatusCode,
   323  				userWWWErrorStatus(ue.ErrorCode),
   324  				strings.Join(ue.ErrorContext, ", "))
   325  		}
   326  
   327  		return nil, fmt.Errorf("%v", r.StatusCode)
   328  	}
   329  
   330  	// Unmarshal response
   331  	var lr www.LoginReply
   332  	err = json.Unmarshal(respBody, &lr)
   333  	if err != nil {
   334  		return nil, fmt.Errorf("unmarshal LoginReply: %v", err)
   335  	}
   336  
   337  	// Print response details
   338  	if c.cfg.Verbose {
   339  		fmt.Printf("Response: %v\n", r.StatusCode)
   340  		err := prettyPrintJSON(lr)
   341  		if err != nil {
   342  			return nil, err
   343  		}
   344  	}
   345  
   346  	// Persist session data
   347  	ck := c.http.Jar.Cookies(req.URL)
   348  	if err = c.cfg.SaveCookies(ck); err != nil {
   349  		return nil, err
   350  	}
   351  
   352  	return &lr, nil
   353  }
   354  
   355  // Logout logs out a user from politeiawww.
   356  func (c *Client) Logout() (*www.LogoutReply, error) {
   357  	fullRoute := c.cfg.Host + www.PoliteiaWWWAPIRoute + www.RouteLogout
   358  
   359  	// Print request details
   360  	if c.cfg.Verbose {
   361  		fmt.Printf("Request: POST  %v\n", fullRoute)
   362  	}
   363  
   364  	// Create new http request instead of using makeRequest()
   365  	// so that we can save the updated cookies to disk
   366  	req, err := http.NewRequest(http.MethodPost, fullRoute, nil)
   367  	if err != nil {
   368  		return nil, err
   369  	}
   370  	req.Header.Add(www.CsrfToken, c.cfg.CSRF)
   371  
   372  	// Send request
   373  	r, err := c.http.Do(req)
   374  	if err != nil {
   375  		return nil, err
   376  	}
   377  	defer func() {
   378  		r.Body.Close()
   379  	}()
   380  
   381  	respBody := util.ConvertBodyToByteArray(r.Body, false)
   382  
   383  	// Validate response status
   384  	if r.StatusCode != http.StatusOK {
   385  		var ue www.UserError
   386  		err = json.Unmarshal(respBody, &ue)
   387  		if err == nil {
   388  			return nil, fmt.Errorf("%v, %v %v", r.StatusCode,
   389  				userWWWErrorStatus(ue.ErrorCode),
   390  				strings.Join(ue.ErrorContext, ", "))
   391  		}
   392  
   393  		return nil, fmt.Errorf("%v", r.StatusCode)
   394  	}
   395  
   396  	// Unmarshal response
   397  	var lr www.LogoutReply
   398  	err = json.Unmarshal(respBody, &lr)
   399  	if err != nil {
   400  		return nil, fmt.Errorf("unmarshal LogoutReply: %v", err)
   401  	}
   402  
   403  	// Print response details
   404  	if c.cfg.Verbose {
   405  		fmt.Printf("Response: %v\n", r.StatusCode)
   406  		err := prettyPrintJSON(lr)
   407  		if err != nil {
   408  			return nil, err
   409  		}
   410  	}
   411  
   412  	// Persist cookies
   413  	ck := c.http.Jar.Cookies(req.URL)
   414  	if err = c.cfg.SaveCookies(ck); err != nil {
   415  		return nil, err
   416  	}
   417  
   418  	return &lr, nil
   419  }
   420  
   421  // Policy returns the politeiawww policy information.
   422  func (c *Client) Policy() (*www.PolicyReply, error) {
   423  	statusCode, respBody, err := c.makeRequest(http.MethodGet,
   424  		www.PoliteiaWWWAPIRoute, www.RoutePolicy, nil)
   425  	if err != nil {
   426  		return nil, err
   427  	}
   428  
   429  	if statusCode != http.StatusOK {
   430  		return nil, wwwError(respBody, statusCode)
   431  	}
   432  
   433  	var pr www.PolicyReply
   434  	err = json.Unmarshal(respBody, &pr)
   435  	if err != nil {
   436  		return nil, fmt.Errorf("unmarshal PolicyReply: %v", err)
   437  	}
   438  
   439  	if c.cfg.Verbose {
   440  		err := prettyPrintJSON(pr)
   441  		if err != nil {
   442  			return nil, err
   443  		}
   444  	}
   445  
   446  	return &pr, nil
   447  }
   448  
   449  // CMSPolicy returns the politeiawww policy information.
   450  func (c *Client) CMSPolicy() (*cms.PolicyReply, error) {
   451  	statusCode, respBody, err := c.makeRequest(http.MethodGet,
   452  		www.PoliteiaWWWAPIRoute, www.RoutePolicy, nil)
   453  	if err != nil {
   454  		return nil, err
   455  	}
   456  
   457  	if statusCode != http.StatusOK {
   458  		return nil, wwwError(respBody, statusCode)
   459  	}
   460  
   461  	var pr cms.PolicyReply
   462  	err = json.Unmarshal(respBody, &pr)
   463  	if err != nil {
   464  		return nil, fmt.Errorf("unmarshal CMSPolicyReply: %v", err)
   465  	}
   466  
   467  	if c.cfg.Verbose {
   468  		err := prettyPrintJSON(pr)
   469  		if err != nil {
   470  			return nil, err
   471  		}
   472  	}
   473  
   474  	return &pr, nil
   475  }
   476  
   477  // InviteNewUser creates a new cmswww user.
   478  func (c *Client) InviteNewUser(inu *cms.InviteNewUser) (*cms.InviteNewUserReply, error) {
   479  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
   480  		cms.APIRoute, cms.RouteInviteNewUser, inu)
   481  	if err != nil {
   482  		return nil, err
   483  	}
   484  
   485  	if statusCode != http.StatusOK {
   486  		return nil, wwwError(respBody, statusCode)
   487  	}
   488  
   489  	var inur cms.InviteNewUserReply
   490  	err = json.Unmarshal(respBody, &inur)
   491  	if err != nil {
   492  		return nil, fmt.Errorf("unmarshal InviteNewUserReply: %v", err)
   493  	}
   494  
   495  	if c.cfg.Verbose {
   496  		err := prettyPrintJSON(inur)
   497  		if err != nil {
   498  			return nil, err
   499  		}
   500  	}
   501  
   502  	return &inur, nil
   503  }
   504  
   505  // RegisterUser finalizes the signup process for a new cmswww user.
   506  func (c *Client) RegisterUser(ru *cms.RegisterUser) (*cms.RegisterUserReply, error) {
   507  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
   508  		cms.APIRoute, cms.RouteRegisterUser, ru)
   509  	if err != nil {
   510  		return nil, err
   511  	}
   512  
   513  	if statusCode != http.StatusOK {
   514  		return nil, wwwError(respBody, statusCode)
   515  	}
   516  
   517  	var rur cms.RegisterUserReply
   518  	err = json.Unmarshal(respBody, &rur)
   519  	if err != nil {
   520  		return nil, fmt.Errorf("unmarshal RegisterUserReply: %v", err)
   521  	}
   522  
   523  	if c.cfg.Verbose {
   524  		err := prettyPrintJSON(rur)
   525  		if err != nil {
   526  			return nil, err
   527  		}
   528  	}
   529  
   530  	return &rur, nil
   531  }
   532  
   533  // NewUser creates a new politeiawww user.
   534  func (c *Client) NewUser(nu *www.NewUser) (*www.NewUserReply, error) {
   535  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
   536  		www.PoliteiaWWWAPIRoute, www.RouteNewUser, nu)
   537  	if err != nil {
   538  		return nil, err
   539  	}
   540  
   541  	if statusCode != http.StatusOK {
   542  		return nil, wwwError(respBody, statusCode)
   543  	}
   544  
   545  	var nur www.NewUserReply
   546  	err = json.Unmarshal(respBody, &nur)
   547  	if err != nil {
   548  		return nil, fmt.Errorf("unmarshal NewUserReply: %v", err)
   549  	}
   550  
   551  	if c.cfg.Verbose {
   552  		err := prettyPrintJSON(nur)
   553  		if err != nil {
   554  			return nil, err
   555  		}
   556  	}
   557  
   558  	return &nur, nil
   559  }
   560  
   561  // VerifyNewUser verifies a user's email address.
   562  func (c *Client) VerifyNewUser(vnu *www.VerifyNewUser) (*www.VerifyNewUserReply, error) {
   563  	statusCode, respBody, err := c.makeRequest(http.MethodGet,
   564  		www.PoliteiaWWWAPIRoute, www.RouteVerifyNewUser, vnu)
   565  	if err != nil {
   566  		return nil, err
   567  	}
   568  
   569  	if statusCode != http.StatusOK {
   570  		return nil, wwwError(respBody, statusCode)
   571  	}
   572  
   573  	var vnur www.VerifyNewUserReply
   574  	err = json.Unmarshal(respBody, &vnur)
   575  	if err != nil {
   576  		return nil, fmt.Errorf("unmarshal VerifyNewUserReply: %v", err)
   577  	}
   578  
   579  	if c.cfg.Verbose {
   580  		err := prettyPrintJSON(vnur)
   581  		if err != nil {
   582  			return nil, err
   583  		}
   584  	}
   585  
   586  	return &vnur, nil
   587  }
   588  
   589  // Me returns user details for the logged in user.
   590  func (c *Client) Me() (*www.LoginReply, error) {
   591  	statusCode, respBody, err := c.makeRequest(http.MethodGet,
   592  		www.PoliteiaWWWAPIRoute, www.RouteUserMe, nil)
   593  	if err != nil {
   594  		return nil, err
   595  	}
   596  
   597  	if statusCode != http.StatusOK {
   598  		return nil, wwwError(respBody, statusCode)
   599  	}
   600  
   601  	var lr www.LoginReply
   602  	err = json.Unmarshal(respBody, &lr)
   603  	if err != nil {
   604  		return nil, fmt.Errorf("unmarshal LoginReply: %v", err)
   605  	}
   606  
   607  	if c.cfg.Verbose {
   608  		err := prettyPrintJSON(lr)
   609  		if err != nil {
   610  			return nil, err
   611  		}
   612  	}
   613  
   614  	return &lr, nil
   615  }
   616  
   617  // Secret pings politeiawww.
   618  func (c *Client) Secret() (*www.UserError, error) {
   619  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
   620  		www.PoliteiaWWWAPIRoute, www.RouteSecret, nil)
   621  	if err != nil {
   622  		return nil, err
   623  	}
   624  
   625  	if statusCode != http.StatusOK {
   626  		return nil, wwwError(respBody, statusCode)
   627  	}
   628  
   629  	var ue www.UserError
   630  	err = json.Unmarshal(respBody, &ue)
   631  	if err != nil {
   632  		return nil, fmt.Errorf("unmarshal UserError: %v", err)
   633  	}
   634  
   635  	if c.cfg.Verbose {
   636  		err := prettyPrintJSON(ue)
   637  		if err != nil {
   638  			return nil, err
   639  		}
   640  	}
   641  
   642  	return &ue, nil
   643  }
   644  
   645  // ChangeUsername changes the username of the logged in user.
   646  func (c *Client) ChangeUsername(cu *www.ChangeUsername) (*www.ChangeUsernameReply, error) {
   647  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
   648  		www.PoliteiaWWWAPIRoute, www.RouteChangeUsername, cu)
   649  	if err != nil {
   650  		return nil, err
   651  	}
   652  
   653  	if statusCode != http.StatusOK {
   654  		return nil, wwwError(respBody, statusCode)
   655  	}
   656  
   657  	var cur www.ChangeUsernameReply
   658  	err = json.Unmarshal(respBody, &cur)
   659  	if err != nil {
   660  		return nil, fmt.Errorf("unmarshal ChangeUsernameReply: %v", err)
   661  	}
   662  
   663  	if c.cfg.Verbose {
   664  		err := prettyPrintJSON(cur)
   665  		if err != nil {
   666  			return nil, err
   667  		}
   668  	}
   669  
   670  	return &cur, nil
   671  }
   672  
   673  // ChangePassword changes the password for the logged in user.
   674  func (c *Client) ChangePassword(cp *www.ChangePassword) (*www.ChangePasswordReply, error) {
   675  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
   676  		www.PoliteiaWWWAPIRoute, www.RouteChangePassword, cp)
   677  	if err != nil {
   678  		return nil, err
   679  	}
   680  
   681  	if statusCode != http.StatusOK {
   682  		return nil, wwwError(respBody, statusCode)
   683  	}
   684  
   685  	var cpr www.ChangePasswordReply
   686  	err = json.Unmarshal(respBody, &cpr)
   687  	if err != nil {
   688  		return nil, fmt.Errorf("unmarshal ChangePasswordReply: %v", err)
   689  	}
   690  
   691  	if c.cfg.Verbose {
   692  		err := prettyPrintJSON(cpr)
   693  		if err != nil {
   694  			return nil, err
   695  		}
   696  	}
   697  
   698  	return &cpr, nil
   699  }
   700  
   701  // ResetPassword resets the password of the specified user.
   702  func (c *Client) ResetPassword(rp *www.ResetPassword) (*www.ResetPasswordReply, error) {
   703  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
   704  		www.PoliteiaWWWAPIRoute, www.RouteResetPassword, rp)
   705  	if err != nil {
   706  		return nil, err
   707  	}
   708  
   709  	if statusCode != http.StatusOK {
   710  		return nil, wwwError(respBody, statusCode)
   711  	}
   712  
   713  	var rpr www.ResetPasswordReply
   714  	err = json.Unmarshal(respBody, &rpr)
   715  	if err != nil {
   716  		return nil, fmt.Errorf("unmarshal ResetPasswordReply: %v", err)
   717  	}
   718  
   719  	if c.cfg.Verbose {
   720  		err := prettyPrintJSON(rpr)
   721  		if err != nil {
   722  			return nil, err
   723  		}
   724  	}
   725  
   726  	return &rpr, nil
   727  }
   728  
   729  // VerifyResetPassword sends the VerifyResetPassword command to politeiawww.
   730  func (c *Client) VerifyResetPassword(vrp www.VerifyResetPassword) (*www.VerifyResetPasswordReply, error) {
   731  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
   732  		www.PoliteiaWWWAPIRoute, www.RouteVerifyResetPassword, vrp)
   733  	if err != nil {
   734  		return nil, err
   735  	}
   736  
   737  	if statusCode != http.StatusOK {
   738  		return nil, wwwError(respBody, statusCode)
   739  	}
   740  
   741  	var reply www.VerifyResetPasswordReply
   742  	err = json.Unmarshal(respBody, &reply)
   743  	if err != nil {
   744  		return nil, fmt.Errorf("unmarshal VerifyResetPasswordReply: %v", err)
   745  	}
   746  
   747  	if c.cfg.Verbose {
   748  		err := prettyPrintJSON(reply)
   749  		if err != nil {
   750  			return nil, err
   751  		}
   752  	}
   753  
   754  	return &reply, nil
   755  }
   756  
   757  // UserProposalPaywall retrieves proposal credit paywall information for the
   758  // logged in user.
   759  func (c *Client) UserProposalPaywall() (*www.UserProposalPaywallReply, error) {
   760  	statusCode, respBody, err := c.makeRequest(http.MethodGet,
   761  		www.PoliteiaWWWAPIRoute, www.RouteUserProposalPaywall, nil)
   762  	if err != nil {
   763  		return nil, err
   764  	}
   765  
   766  	if statusCode != http.StatusOK {
   767  		return nil, wwwError(respBody, statusCode)
   768  	}
   769  
   770  	var ppdr www.UserProposalPaywallReply
   771  	err = json.Unmarshal(respBody, &ppdr)
   772  	if err != nil {
   773  		return nil, fmt.Errorf("unmarshal ProposalPaywalDetailsReply: %v", err)
   774  	}
   775  
   776  	if c.cfg.Verbose {
   777  		err := prettyPrintJSON(ppdr)
   778  		if err != nil {
   779  			return nil, err
   780  		}
   781  	}
   782  
   783  	return &ppdr, nil
   784  }
   785  
   786  // NewInvoice submits the specified invoice to politeiawww for the logged in
   787  // user.
   788  func (c *Client) NewInvoice(ni *cms.NewInvoice) (*cms.NewInvoiceReply, error) {
   789  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
   790  		cms.APIRoute, cms.RouteNewInvoice, ni)
   791  	if err != nil {
   792  		return nil, err
   793  	}
   794  
   795  	if statusCode != http.StatusOK {
   796  		return nil, wwwError(respBody, statusCode)
   797  	}
   798  
   799  	var nir cms.NewInvoiceReply
   800  	err = json.Unmarshal(respBody, &nir)
   801  	if err != nil {
   802  		return nil, fmt.Errorf("unmarshal NewInvoiceReply: %v", err)
   803  	}
   804  
   805  	if c.cfg.Verbose {
   806  		err := prettyPrintJSON(nir)
   807  		if err != nil {
   808  			return nil, err
   809  		}
   810  	}
   811  
   812  	return &nir, nil
   813  }
   814  
   815  // EditInvoice edits the specified invoice with the logged in user.
   816  func (c *Client) EditInvoice(ei *cms.EditInvoice) (*cms.EditInvoiceReply, error) {
   817  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
   818  		cms.APIRoute, cms.RouteEditInvoice, ei)
   819  	if err != nil {
   820  		return nil, err
   821  	}
   822  
   823  	if statusCode != http.StatusOK {
   824  		return nil, wwwError(respBody, statusCode)
   825  	}
   826  
   827  	var eir cms.EditInvoiceReply
   828  	err = json.Unmarshal(respBody, &eir)
   829  	if err != nil {
   830  		return nil, fmt.Errorf("unmarshal EditInvoiceReply: %v", err)
   831  	}
   832  
   833  	if c.cfg.Verbose {
   834  		err := prettyPrintJSON(eir)
   835  		if err != nil {
   836  			return nil, err
   837  		}
   838  	}
   839  
   840  	return &eir, nil
   841  }
   842  
   843  // ProposalDetails retrieves the specified proposal.
   844  func (c *Client) ProposalDetails(token string, pd *www.ProposalsDetails) (*www.ProposalDetailsReply, error) {
   845  	route := "/proposals/" + token
   846  	statusCode, respBody, err := c.makeRequest(http.MethodGet,
   847  		www.PoliteiaWWWAPIRoute, route, pd)
   848  	if err != nil {
   849  		return nil, err
   850  	}
   851  
   852  	if statusCode != http.StatusOK {
   853  		return nil, wwwError(respBody, statusCode)
   854  	}
   855  
   856  	var pr www.ProposalDetailsReply
   857  	err = json.Unmarshal(respBody, &pr)
   858  	if err != nil {
   859  		return nil, fmt.Errorf("unmarshal ProposalDetailsReply: %v", err)
   860  	}
   861  
   862  	if c.cfg.Verbose {
   863  		err := prettyPrintJSON(pr)
   864  		if err != nil {
   865  			return nil, err
   866  		}
   867  	}
   868  
   869  	return &pr, nil
   870  }
   871  
   872  // UserInvoices retrieves the proposals that have been submitted by the
   873  // specified user.
   874  func (c *Client) UserInvoices(up *cms.UserInvoices) (*cms.UserInvoicesReply, error) {
   875  	statusCode, respBody, err := c.makeRequest(http.MethodGet,
   876  		cms.APIRoute, cms.RouteUserInvoices, up)
   877  	if err != nil {
   878  		return nil, err
   879  	}
   880  
   881  	if statusCode != http.StatusOK {
   882  		return nil, wwwError(respBody, statusCode)
   883  	}
   884  
   885  	var upr cms.UserInvoicesReply
   886  	err = json.Unmarshal(respBody, &upr)
   887  	if err != nil {
   888  		return nil, fmt.Errorf("unmarshal UserInvoicesReply: %v", err)
   889  	}
   890  
   891  	if c.cfg.Verbose {
   892  		err := prettyPrintJSON(upr)
   893  		if err != nil {
   894  			return nil, err
   895  		}
   896  	}
   897  
   898  	return &upr, nil
   899  }
   900  
   901  // ProposalBilling retrieves the billing for the requested proposal
   902  func (c *Client) ProposalBilling(pb *cms.ProposalBilling) (*cms.ProposalBillingReply, error) {
   903  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
   904  		cms.APIRoute, cms.RouteProposalBilling, pb)
   905  	if err != nil {
   906  		return nil, err
   907  	}
   908  
   909  	if statusCode != http.StatusOK {
   910  		return nil, wwwError(respBody, statusCode)
   911  	}
   912  
   913  	var pbr cms.ProposalBillingReply
   914  	err = json.Unmarshal(respBody, &pbr)
   915  	if err != nil {
   916  		return nil, fmt.Errorf("unmarshal ProposalBillingReply: %v", err)
   917  	}
   918  
   919  	if c.cfg.Verbose {
   920  		err := prettyPrintJSON(pbr)
   921  		if err != nil {
   922  			return nil, err
   923  		}
   924  	}
   925  
   926  	return &pbr, nil
   927  }
   928  
   929  // ProposalBillingDetails retrieves the billing for the requested proposal
   930  func (c *Client) ProposalBillingDetails(pbd *cms.ProposalBillingDetails) (*cms.ProposalBillingDetailsReply, error) {
   931  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
   932  		cms.APIRoute, cms.RouteProposalBillingDetails, pbd)
   933  	if err != nil {
   934  		return nil, err
   935  	}
   936  
   937  	if statusCode != http.StatusOK {
   938  		return nil, wwwError(respBody, statusCode)
   939  	}
   940  
   941  	var pbdr cms.ProposalBillingDetailsReply
   942  	err = json.Unmarshal(respBody, &pbdr)
   943  	if err != nil {
   944  		return nil, fmt.Errorf("unmarshal ProposalBillingDetailsReply: %v", err)
   945  	}
   946  
   947  	if c.cfg.Verbose {
   948  		err := prettyPrintJSON(pbdr)
   949  		if err != nil {
   950  			return nil, err
   951  		}
   952  	}
   953  
   954  	return &pbdr, nil
   955  }
   956  
   957  // ProposalBillingSummary retrieves the billing for all approved proposals.
   958  func (c *Client) ProposalBillingSummary(pbd *cms.ProposalBillingSummary) (*cms.ProposalBillingSummaryReply, error) {
   959  	statusCode, respBody, err := c.makeRequest(http.MethodGet,
   960  		cms.APIRoute, cms.RouteProposalBillingSummary, pbd)
   961  	if err != nil {
   962  		return nil, err
   963  	}
   964  
   965  	if statusCode != http.StatusOK {
   966  		return nil, wwwError(respBody, statusCode)
   967  	}
   968  
   969  	var pbdr cms.ProposalBillingSummaryReply
   970  	err = json.Unmarshal(respBody, &pbdr)
   971  	if err != nil {
   972  		return nil, fmt.Errorf("unmarshal ProposalBillingSummaryReply: %v", err)
   973  	}
   974  
   975  	if c.cfg.Verbose {
   976  		err := prettyPrintJSON(pbdr)
   977  		if err != nil {
   978  			return nil, err
   979  		}
   980  	}
   981  
   982  	return &pbdr, nil
   983  }
   984  
   985  // Invoices retrieves invoices base on possible field set in the request
   986  // month/year and/or status
   987  func (c *Client) Invoices(ai *cms.Invoices) (*cms.InvoicesReply, error) {
   988  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
   989  		cms.APIRoute, cms.RouteInvoices, ai)
   990  	if err != nil {
   991  		return nil, err
   992  	}
   993  
   994  	if statusCode != http.StatusOK {
   995  		return nil, wwwError(respBody, statusCode)
   996  	}
   997  
   998  	var air cms.InvoicesReply
   999  	err = json.Unmarshal(respBody, &air)
  1000  	if err != nil {
  1001  		return nil, fmt.Errorf("unmarshal InvoicesReply: %v", err)
  1002  	}
  1003  
  1004  	if c.cfg.Verbose {
  1005  		err := prettyPrintJSON(air)
  1006  		if err != nil {
  1007  			return nil, err
  1008  		}
  1009  	}
  1010  
  1011  	return &air, nil
  1012  }
  1013  
  1014  // GeneratePayouts generates a list of payouts for all approved invoices that
  1015  // contain an address and amount for an admin to the process
  1016  func (c *Client) GeneratePayouts(gp *cms.GeneratePayouts) (*cms.GeneratePayoutsReply, error) {
  1017  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
  1018  		cms.APIRoute, cms.RouteGeneratePayouts, gp)
  1019  	if err != nil {
  1020  		return nil, err
  1021  	}
  1022  
  1023  	if statusCode != http.StatusOK {
  1024  		return nil, wwwError(respBody, statusCode)
  1025  	}
  1026  
  1027  	var gpr cms.GeneratePayoutsReply
  1028  	err = json.Unmarshal(respBody, &gpr)
  1029  	if err != nil {
  1030  		return nil, fmt.Errorf("unmarshal GeneratePayoutsReply: %v", err)
  1031  	}
  1032  
  1033  	if c.cfg.Verbose {
  1034  		err := prettyPrintJSON(gpr)
  1035  		if err != nil {
  1036  			return nil, err
  1037  		}
  1038  	}
  1039  
  1040  	return &gpr, nil
  1041  }
  1042  
  1043  // PayInvoices is a temporary command that allows an administrator to set all
  1044  // approved invoices to the paid status. This will be removed once the
  1045  // address watching for payment is complete and working.
  1046  func (c *Client) PayInvoices(pi *cms.PayInvoices) (*cms.PayInvoicesReply, error) {
  1047  	statusCode, respBody, err := c.makeRequest(http.MethodGet,
  1048  		cms.APIRoute, cms.RoutePayInvoices, pi)
  1049  	if err != nil {
  1050  		return nil, err
  1051  	}
  1052  
  1053  	if statusCode != http.StatusOK {
  1054  		return nil, wwwError(respBody, statusCode)
  1055  	}
  1056  
  1057  	var pir cms.PayInvoicesReply
  1058  	err = json.Unmarshal(respBody, &pir)
  1059  	if err != nil {
  1060  		return nil, fmt.Errorf("unmarshal PayInvoiceReply: %v", err)
  1061  	}
  1062  
  1063  	return &pir, nil
  1064  }
  1065  
  1066  // BatchProposals retrieves a list of proposals
  1067  func (c *Client) BatchProposals(bp *www.BatchProposals) (*www.BatchProposalsReply, error) {
  1068  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
  1069  		www.PoliteiaWWWAPIRoute, www.RouteBatchProposals, bp)
  1070  	if err != nil {
  1071  		return nil, err
  1072  	}
  1073  
  1074  	if statusCode != http.StatusOK {
  1075  		return nil, wwwError(respBody, statusCode)
  1076  	}
  1077  
  1078  	var bpr www.BatchProposalsReply
  1079  	err = json.Unmarshal(respBody, &bpr)
  1080  	if err != nil {
  1081  		return nil, fmt.Errorf("unmarshal BatchProposals: %v", err)
  1082  	}
  1083  
  1084  	if c.cfg.Verbose {
  1085  		err := prettyPrintJSON(bpr)
  1086  		if err != nil {
  1087  			return nil, err
  1088  		}
  1089  	}
  1090  
  1091  	return &bpr, nil
  1092  }
  1093  
  1094  // GetAllVetted retrieves a page of vetted proposals.
  1095  func (c *Client) GetAllVetted(gav *www.GetAllVetted) (*www.GetAllVettedReply, error) {
  1096  	statusCode, respBody, err := c.makeRequest(http.MethodGet,
  1097  		www.PoliteiaWWWAPIRoute, www.RouteAllVetted, gav)
  1098  	if err != nil {
  1099  		return nil, err
  1100  	}
  1101  
  1102  	if statusCode != http.StatusOK {
  1103  		return nil, wwwError(respBody, statusCode)
  1104  	}
  1105  
  1106  	var gavr www.GetAllVettedReply
  1107  	err = json.Unmarshal(respBody, &gavr)
  1108  	if err != nil {
  1109  		return nil, fmt.Errorf("unmarshal GetAllVettedReply: %v", err)
  1110  	}
  1111  
  1112  	if c.cfg.Verbose {
  1113  		err := prettyPrintJSON(gavr)
  1114  		if err != nil {
  1115  			return nil, err
  1116  		}
  1117  	}
  1118  
  1119  	return &gavr, nil
  1120  }
  1121  
  1122  // WWWNewComment submits a new proposal comment for the logged in user.
  1123  func (c *Client) WWWNewComment(nc *www.NewComment) (*www.NewCommentReply, error) {
  1124  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
  1125  		www.PoliteiaWWWAPIRoute, www.RouteNewComment, nc)
  1126  	if err != nil {
  1127  		return nil, err
  1128  	}
  1129  
  1130  	if statusCode != http.StatusOK {
  1131  		return nil, wwwError(respBody, statusCode)
  1132  	}
  1133  
  1134  	var ncr www.NewCommentReply
  1135  	err = json.Unmarshal(respBody, &ncr)
  1136  	if err != nil {
  1137  		return nil, fmt.Errorf("unmarshal NewCommentReply: %v", err)
  1138  	}
  1139  
  1140  	if c.cfg.Verbose {
  1141  		err := prettyPrintJSON(ncr)
  1142  		if err != nil {
  1143  			return nil, err
  1144  		}
  1145  	}
  1146  
  1147  	return &ncr, nil
  1148  }
  1149  
  1150  // InvoiceComments retrieves the comments for the specified proposal.
  1151  func (c *Client) InvoiceComments(token string) (*www.GetCommentsReply, error) {
  1152  	route := "/invoices/" + token + "/comments"
  1153  	statusCode, respBody, err := c.makeRequest(http.MethodGet,
  1154  		cms.APIRoute, route, nil)
  1155  	if err != nil {
  1156  		return nil, err
  1157  	}
  1158  
  1159  	if statusCode != http.StatusOK {
  1160  		return nil, wwwError(respBody, statusCode)
  1161  	}
  1162  
  1163  	var gcr www.GetCommentsReply
  1164  	err = json.Unmarshal(respBody, &gcr)
  1165  	if err != nil {
  1166  		return nil, fmt.Errorf("unmarshal InvoiceCommentsReply: %v", err)
  1167  	}
  1168  
  1169  	if c.cfg.Verbose {
  1170  		err := prettyPrintJSON(gcr)
  1171  		if err != nil {
  1172  			return nil, err
  1173  		}
  1174  	}
  1175  
  1176  	return &gcr, nil
  1177  }
  1178  
  1179  // WWWCensorComment censors the specified proposal comment.
  1180  func (c *Client) WWWCensorComment(cc *www.CensorComment) (*www.CensorCommentReply, error) {
  1181  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
  1182  		www.PoliteiaWWWAPIRoute, www.RouteCensorComment, cc)
  1183  	if err != nil {
  1184  		return nil, err
  1185  	}
  1186  
  1187  	if statusCode != http.StatusOK {
  1188  		return nil, wwwError(respBody, statusCode)
  1189  	}
  1190  
  1191  	var ccr www.CensorCommentReply
  1192  	err = json.Unmarshal(respBody, &ccr)
  1193  	if err != nil {
  1194  		return nil, fmt.Errorf("unmarshal CensorCommentReply: %v", err)
  1195  	}
  1196  
  1197  	if c.cfg.Verbose {
  1198  		err := prettyPrintJSON(ccr)
  1199  		if err != nil {
  1200  			return nil, err
  1201  		}
  1202  	}
  1203  
  1204  	return &ccr, nil
  1205  }
  1206  
  1207  // UserRegistrationPayment checks whether the logged in user has paid their user
  1208  // registration fee.
  1209  func (c *Client) UserRegistrationPayment() (*www.UserRegistrationPaymentReply, error) {
  1210  	statusCode, respBody, err := c.makeRequest(http.MethodGet,
  1211  		www.PoliteiaWWWAPIRoute, www.RouteUserRegistrationPayment, nil)
  1212  	if err != nil {
  1213  		return nil, err
  1214  	}
  1215  
  1216  	if statusCode != http.StatusOK {
  1217  		return nil, wwwError(respBody, statusCode)
  1218  	}
  1219  
  1220  	var urpr www.UserRegistrationPaymentReply
  1221  	err = json.Unmarshal(respBody, &urpr)
  1222  	if err != nil {
  1223  		return nil, fmt.Errorf("unmarshal UserRegistrationPaymentReply: %v",
  1224  			err)
  1225  	}
  1226  
  1227  	if c.cfg.Verbose {
  1228  		err := prettyPrintJSON(urpr)
  1229  		if err != nil {
  1230  			return nil, err
  1231  		}
  1232  	}
  1233  
  1234  	return &urpr, nil
  1235  }
  1236  
  1237  // UserDetails retrieves the user details for the specified user.
  1238  func (c *Client) UserDetails(userID string) (*www.UserDetailsReply, error) {
  1239  	route := "/user/" + userID
  1240  	statusCode, respBody, err := c.makeRequest(http.MethodGet,
  1241  		www.PoliteiaWWWAPIRoute, route, nil)
  1242  	if err != nil {
  1243  		return nil, err
  1244  	}
  1245  
  1246  	if statusCode != http.StatusOK {
  1247  		return nil, wwwError(respBody, statusCode)
  1248  	}
  1249  
  1250  	var udr www.UserDetailsReply
  1251  	err = json.Unmarshal(respBody, &udr)
  1252  	if err != nil {
  1253  		return nil, fmt.Errorf("unmarshal UserDetailsReply: %v", err)
  1254  	}
  1255  
  1256  	if c.cfg.Verbose {
  1257  		err := prettyPrintJSON(udr)
  1258  		if err != nil {
  1259  			return nil, err
  1260  		}
  1261  	}
  1262  
  1263  	return &udr, nil
  1264  }
  1265  
  1266  // Users retrieves a list of users that adhere to the specified filtering
  1267  // parameters.
  1268  func (c *Client) Users(u *www.Users) (*www.UsersReply, error) {
  1269  	statusCode, respBody, err := c.makeRequest(http.MethodGet,
  1270  		www.PoliteiaWWWAPIRoute, www.RouteUsers, u)
  1271  	if err != nil {
  1272  		return nil, err
  1273  	}
  1274  
  1275  	if statusCode != http.StatusOK {
  1276  		return nil, wwwError(respBody, statusCode)
  1277  	}
  1278  
  1279  	var ur www.UsersReply
  1280  	err = json.Unmarshal(respBody, &ur)
  1281  	if err != nil {
  1282  		return nil, fmt.Errorf("unmarshal UsersReply: %v", err)
  1283  	}
  1284  
  1285  	if c.cfg.Verbose {
  1286  		err := prettyPrintJSON(ur)
  1287  		if err != nil {
  1288  			return nil, err
  1289  		}
  1290  	}
  1291  
  1292  	return &ur, nil
  1293  }
  1294  
  1295  // CMSUsers retrieves a list of cms users that adhere to the specified filtering
  1296  // parameters.
  1297  func (c *Client) CMSUsers(cu *cms.CMSUsers) (*cms.CMSUsersReply, error) {
  1298  	statusCode, respBody, err := c.makeRequest(http.MethodGet, cms.APIRoute,
  1299  		cms.RouteCMSUsers, cu)
  1300  	if err != nil {
  1301  		return nil, err
  1302  	}
  1303  
  1304  	if statusCode != http.StatusOK {
  1305  		return nil, wwwError(respBody, statusCode)
  1306  	}
  1307  
  1308  	var cur cms.CMSUsersReply
  1309  	err = json.Unmarshal(respBody, &cur)
  1310  	if err != nil {
  1311  		return nil, fmt.Errorf("unmarshal CMSUsersReply: %v", err)
  1312  	}
  1313  
  1314  	if c.cfg.Verbose {
  1315  		err := prettyPrintJSON(cur)
  1316  		if err != nil {
  1317  			return nil, err
  1318  		}
  1319  	}
  1320  
  1321  	return &cur, nil
  1322  }
  1323  
  1324  // ManageUser allows an admin to edit certain attributes of the specified user.
  1325  func (c *Client) ManageUser(mu *www.ManageUser) (*www.ManageUserReply, error) {
  1326  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
  1327  		www.PoliteiaWWWAPIRoute, www.RouteManageUser, mu)
  1328  	if err != nil {
  1329  		return nil, err
  1330  	}
  1331  
  1332  	if statusCode != http.StatusOK {
  1333  		return nil, wwwError(respBody, statusCode)
  1334  	}
  1335  
  1336  	var mur www.ManageUserReply
  1337  	err = json.Unmarshal(respBody, &mur)
  1338  	if err != nil {
  1339  		return nil, fmt.Errorf("unmarshal ManageUserReply: %v", err)
  1340  	}
  1341  
  1342  	if c.cfg.Verbose {
  1343  		err := prettyPrintJSON(mur)
  1344  		if err != nil {
  1345  			return nil, err
  1346  		}
  1347  	}
  1348  
  1349  	return &mur, nil
  1350  }
  1351  
  1352  // EditUser allows the logged in user to update their user settings.
  1353  func (c *Client) EditUser(eu *www.EditUser) (*www.EditUserReply, error) {
  1354  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
  1355  		www.PoliteiaWWWAPIRoute, www.RouteEditUser, eu)
  1356  	if err != nil {
  1357  		return nil, err
  1358  	}
  1359  
  1360  	if statusCode != http.StatusOK {
  1361  		return nil, wwwError(respBody, statusCode)
  1362  	}
  1363  
  1364  	var eur www.EditUserReply
  1365  	err = json.Unmarshal(respBody, &eur)
  1366  	if err != nil {
  1367  		return nil, fmt.Errorf("unmarshal EditUserReply: %v", err)
  1368  	}
  1369  
  1370  	if c.cfg.Verbose {
  1371  		err := prettyPrintJSON(eur)
  1372  		if err != nil {
  1373  			return nil, err
  1374  		}
  1375  	}
  1376  
  1377  	return &eur, nil
  1378  }
  1379  
  1380  // VoteStatus retrieves the vote status for the specified proposal.
  1381  func (c *Client) VoteStatus(token string) (*www.VoteStatusReply, error) {
  1382  	route := "/proposals/" + token + "/votestatus"
  1383  	statusCode, respBody, err := c.makeRequest(http.MethodGet,
  1384  		www.PoliteiaWWWAPIRoute, route, nil)
  1385  	if err != nil {
  1386  		return nil, err
  1387  	}
  1388  
  1389  	if statusCode != http.StatusOK {
  1390  		return nil, wwwError(respBody, statusCode)
  1391  	}
  1392  
  1393  	var vsr www.VoteStatusReply
  1394  	err = json.Unmarshal(respBody, &vsr)
  1395  	if err != nil {
  1396  		return nil, fmt.Errorf("unmarshal VoteStatusReply: %v", err)
  1397  	}
  1398  
  1399  	if c.cfg.Verbose {
  1400  		err := prettyPrintJSON(vsr)
  1401  		if err != nil {
  1402  			return nil, err
  1403  		}
  1404  	}
  1405  
  1406  	return &vsr, nil
  1407  }
  1408  
  1409  // VoteResults retrieves the vote results for a proposal.
  1410  func (c *Client) VoteResults(token string) (*www.VoteResultsReply, error) {
  1411  	route := "/proposals/" + token + "/votes"
  1412  	statusCode, respBody, err := c.makeRequest(http.MethodGet,
  1413  		www.PoliteiaWWWAPIRoute, route, nil)
  1414  	if err != nil {
  1415  		return nil, err
  1416  	}
  1417  	if statusCode != http.StatusOK {
  1418  		return nil, wwwError(respBody, statusCode)
  1419  	}
  1420  
  1421  	var vsr www.VoteResultsReply
  1422  	err = json.Unmarshal(respBody, &vsr)
  1423  	if err != nil {
  1424  		return nil, fmt.Errorf("unmarshal VoteResultsReply: %v", err)
  1425  	}
  1426  
  1427  	if c.cfg.Verbose {
  1428  		err := prettyPrintJSON(vsr)
  1429  		if err != nil {
  1430  			return nil, err
  1431  		}
  1432  	}
  1433  
  1434  	return &vsr, nil
  1435  }
  1436  
  1437  // GetAllVoteStatus retreives the vote status of all public proposals.
  1438  func (c *Client) GetAllVoteStatus() (*www.GetAllVoteStatusReply, error) {
  1439  	statusCode, respBody, err := c.makeRequest(http.MethodGet,
  1440  		www.PoliteiaWWWAPIRoute, www.RouteAllVoteStatus, nil)
  1441  	if err != nil {
  1442  		return nil, err
  1443  	}
  1444  
  1445  	if statusCode != http.StatusOK {
  1446  		return nil, wwwError(respBody, statusCode)
  1447  	}
  1448  
  1449  	var avsr www.GetAllVoteStatusReply
  1450  	err = json.Unmarshal(respBody, &avsr)
  1451  	if err != nil {
  1452  		return nil, fmt.Errorf("unmarshal GetAllVoteStatusReply: %v", err)
  1453  	}
  1454  
  1455  	if c.cfg.Verbose {
  1456  		err := prettyPrintJSON(avsr)
  1457  		if err != nil {
  1458  			return nil, fmt.Errorf("prettyPrintJSON: %v", err)
  1459  		}
  1460  	}
  1461  
  1462  	return &avsr, nil
  1463  }
  1464  
  1465  // ActiveVotes retreives the vote status of all public proposals.
  1466  func (c *Client) ActiveVotes() (*www.ActiveVoteReply, error) {
  1467  	statusCode, respBody, err := c.makeRequest(http.MethodGet,
  1468  		www.PoliteiaWWWAPIRoute, www.RouteActiveVote, nil)
  1469  	if err != nil {
  1470  		return nil, err
  1471  	}
  1472  	if statusCode != http.StatusOK {
  1473  		return nil, wwwError(respBody, statusCode)
  1474  	}
  1475  
  1476  	var avr www.ActiveVoteReply
  1477  	err = json.Unmarshal(respBody, &avr)
  1478  	if err != nil {
  1479  		return nil, err
  1480  	}
  1481  
  1482  	if c.cfg.Verbose {
  1483  		err := prettyPrintJSON(avr)
  1484  		if err != nil {
  1485  			return nil, err
  1486  		}
  1487  	}
  1488  
  1489  	return &avr, nil
  1490  }
  1491  
  1492  // ActiveVotesDCC retreives all dccs that are currently being voted on.
  1493  func (c *Client) ActiveVotesDCC() (*cms.ActiveVoteReply, error) {
  1494  	statusCode, respBody, err := c.makeRequest(http.MethodGet,
  1495  		www.PoliteiaWWWAPIRoute, cms.RouteActiveVotesDCC, nil)
  1496  	if err != nil {
  1497  		return nil, err
  1498  	}
  1499  
  1500  	if statusCode != http.StatusOK {
  1501  		return nil, wwwError(respBody, statusCode)
  1502  	}
  1503  
  1504  	var avr cms.ActiveVoteReply
  1505  	err = json.Unmarshal(respBody, &avr)
  1506  	if err != nil {
  1507  		return nil, fmt.Errorf("unmarshal ActiveVoteDCCReply: %v", err)
  1508  	}
  1509  
  1510  	if c.cfg.Verbose {
  1511  		err := prettyPrintJSON(avr)
  1512  		if err != nil {
  1513  			return nil, err
  1514  		}
  1515  	}
  1516  
  1517  	return &avr, nil
  1518  }
  1519  
  1520  // UpdateUserKey updates the identity of the logged in user.
  1521  func (c *Client) UpdateUserKey(uuk *www.UpdateUserKey) (*www.UpdateUserKeyReply, error) {
  1522  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
  1523  		www.PoliteiaWWWAPIRoute, www.RouteUpdateUserKey, &uuk)
  1524  	if err != nil {
  1525  		return nil, err
  1526  	}
  1527  
  1528  	if statusCode != http.StatusOK {
  1529  		return nil, wwwError(respBody, statusCode)
  1530  	}
  1531  
  1532  	var uukr www.UpdateUserKeyReply
  1533  	err = json.Unmarshal(respBody, &uukr)
  1534  	if err != nil {
  1535  		return nil, fmt.Errorf("unmarshal UpdateUserKeyReply: %v", err)
  1536  	}
  1537  
  1538  	if c.cfg.Verbose {
  1539  		err := prettyPrintJSON(uukr)
  1540  		if err != nil {
  1541  			return nil, err
  1542  		}
  1543  	}
  1544  
  1545  	return &uukr, nil
  1546  }
  1547  
  1548  // VerifyUpdateUserKey is used to verify a new user identity.
  1549  func (c *Client) VerifyUpdateUserKey(vuuk *www.VerifyUpdateUserKey) (*www.VerifyUpdateUserKeyReply, error) {
  1550  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
  1551  		www.PoliteiaWWWAPIRoute, www.RouteVerifyUpdateUserKey, &vuuk)
  1552  	if err != nil {
  1553  		return nil, err
  1554  	}
  1555  
  1556  	if statusCode != http.StatusOK {
  1557  		return nil, wwwError(respBody, statusCode)
  1558  	}
  1559  
  1560  	var vuukr www.VerifyUpdateUserKeyReply
  1561  	err = json.Unmarshal(respBody, &vuukr)
  1562  	if err != nil {
  1563  		return nil, fmt.Errorf("unmarshal VerifyUpdateUserKeyReply: %v", err)
  1564  	}
  1565  
  1566  	if c.cfg.Verbose {
  1567  		err := prettyPrintJSON(vuukr)
  1568  		if err != nil {
  1569  			return nil, err
  1570  		}
  1571  	}
  1572  
  1573  	return &vuukr, nil
  1574  }
  1575  
  1576  // UserProposalPaywallTx retrieves payment details of any pending proposal
  1577  // credit payment from the logged in user.
  1578  func (c *Client) UserProposalPaywallTx() (*www.UserProposalPaywallTxReply, error) {
  1579  	statusCode, respBody, err := c.makeRequest(http.MethodGet,
  1580  		www.PoliteiaWWWAPIRoute, www.RouteUserProposalPaywallTx, nil)
  1581  	if err != nil {
  1582  		return nil, err
  1583  	}
  1584  
  1585  	if statusCode != http.StatusOK {
  1586  		return nil, wwwError(respBody, statusCode)
  1587  	}
  1588  
  1589  	var upptxr www.UserProposalPaywallTxReply
  1590  	err = json.Unmarshal(respBody, &upptxr)
  1591  	if err != nil {
  1592  		return nil, fmt.Errorf("unmarshal ProposalPaywallPaymentReply: %v", err)
  1593  	}
  1594  
  1595  	if c.cfg.Verbose {
  1596  		err := prettyPrintJSON(upptxr)
  1597  		if err != nil {
  1598  			return nil, err
  1599  		}
  1600  	}
  1601  
  1602  	return &upptxr, nil
  1603  }
  1604  
  1605  // UserPaymentsRescan scans the specified user's paywall address and makes sure
  1606  // that the user's account has been properly credited with all payments.
  1607  func (c *Client) UserPaymentsRescan(upr *www.UserPaymentsRescan) (*www.UserPaymentsRescanReply, error) {
  1608  	statusCode, respBody, err := c.makeRequest(http.MethodPut,
  1609  		www.PoliteiaWWWAPIRoute, www.RouteUserPaymentsRescan, upr)
  1610  	if err != nil {
  1611  		return nil, err
  1612  	}
  1613  
  1614  	if statusCode != http.StatusOK {
  1615  		return nil, wwwError(respBody, statusCode)
  1616  	}
  1617  
  1618  	var uprr www.UserPaymentsRescanReply
  1619  	err = json.Unmarshal(respBody, &uprr)
  1620  	if err != nil {
  1621  		return nil, fmt.Errorf("unmarshal UserPaymentsRescanReply: %v", err)
  1622  	}
  1623  
  1624  	if c.cfg.Verbose {
  1625  		err := prettyPrintJSON(uprr)
  1626  		if err != nil {
  1627  			return nil, err
  1628  		}
  1629  	}
  1630  
  1631  	return &uprr, nil
  1632  }
  1633  
  1634  // UserProposalCredits retrieves the proposal credit history for the logged
  1635  // in user.
  1636  func (c *Client) UserProposalCredits() (*www.UserProposalCreditsReply, error) {
  1637  	statusCode, respBody, err := c.makeRequest(http.MethodGet,
  1638  		www.PoliteiaWWWAPIRoute, www.RouteUserProposalCredits, nil)
  1639  	if err != nil {
  1640  		return nil, err
  1641  	}
  1642  
  1643  	if statusCode != http.StatusOK {
  1644  		return nil, wwwError(respBody, statusCode)
  1645  	}
  1646  
  1647  	var upcr www.UserProposalCreditsReply
  1648  	err = json.Unmarshal(respBody, &upcr)
  1649  	if err != nil {
  1650  		return nil, fmt.Errorf("unmarshal UserProposalCreditsReply: %v", err)
  1651  	}
  1652  
  1653  	if c.cfg.Verbose {
  1654  		err := prettyPrintJSON(upcr)
  1655  		if err != nil {
  1656  			return nil, err
  1657  		}
  1658  	}
  1659  
  1660  	return &upcr, nil
  1661  }
  1662  
  1663  // ResendVerification re-sends the user verification email for an unverified
  1664  // user.
  1665  func (c *Client) ResendVerification(rv www.ResendVerification) (*www.ResendVerificationReply, error) {
  1666  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
  1667  		www.PoliteiaWWWAPIRoute, www.RouteResendVerification, rv)
  1668  	if err != nil {
  1669  		return nil, err
  1670  	}
  1671  
  1672  	if statusCode != http.StatusOK {
  1673  		return nil, wwwError(respBody, statusCode)
  1674  	}
  1675  
  1676  	var rvr www.ResendVerificationReply
  1677  	err = json.Unmarshal(respBody, &rvr)
  1678  	if err != nil {
  1679  		return nil, fmt.Errorf("unmarshal ResendVerificationReply: %v", err)
  1680  	}
  1681  
  1682  	if c.cfg.Verbose {
  1683  		err := prettyPrintJSON(rvr)
  1684  		if err != nil {
  1685  			return nil, err
  1686  		}
  1687  	}
  1688  
  1689  	return &rvr, nil
  1690  }
  1691  
  1692  // InvoiceDetails retrieves the specified invoice.
  1693  func (c *Client) InvoiceDetails(token string, id *cms.InvoiceDetails) (*cms.InvoiceDetailsReply, error) {
  1694  	route := "/invoices/" + token
  1695  	statusCode, respBody, err := c.makeRequest(http.MethodGet, cms.APIRoute,
  1696  		route, id)
  1697  	if err != nil {
  1698  		return nil, err
  1699  	}
  1700  
  1701  	if statusCode != http.StatusOK {
  1702  		return nil, wwwError(respBody, statusCode)
  1703  	}
  1704  
  1705  	var idr cms.InvoiceDetailsReply
  1706  	err = json.Unmarshal(respBody, &idr)
  1707  	if err != nil {
  1708  		return nil, fmt.Errorf("unmarshal InvoiceDetailsReply: %v", err)
  1709  	}
  1710  
  1711  	if c.cfg.Verbose {
  1712  		err := prettyPrintJSON(idr)
  1713  		if err != nil {
  1714  			return nil, err
  1715  		}
  1716  	}
  1717  
  1718  	return &idr, nil
  1719  }
  1720  
  1721  // SetInvoiceStatus changes the status of the specified invoice.
  1722  func (c *Client) SetInvoiceStatus(sis *cms.SetInvoiceStatus) (*cms.SetInvoiceStatusReply, error) {
  1723  	route := "/invoices/" + sis.Token + "/status"
  1724  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
  1725  		cms.APIRoute, route, sis)
  1726  	if err != nil {
  1727  		return nil, err
  1728  	}
  1729  
  1730  	if statusCode != http.StatusOK {
  1731  		return nil, wwwError(respBody, statusCode)
  1732  	}
  1733  
  1734  	var sisr cms.SetInvoiceStatusReply
  1735  	err = json.Unmarshal(respBody, &sisr)
  1736  	if err != nil {
  1737  		return nil, fmt.Errorf("unmarshal SetInvoiceStatusReply: %v", err)
  1738  	}
  1739  
  1740  	if c.cfg.Verbose {
  1741  		err := prettyPrintJSON(sisr)
  1742  		if err != nil {
  1743  			return nil, err
  1744  		}
  1745  	}
  1746  
  1747  	return &sisr, nil
  1748  }
  1749  
  1750  // TokenInventory retrieves the censorship record tokens of all proposals in
  1751  // the inventory.
  1752  func (c *Client) TokenInventory() (*www.TokenInventoryReply, error) {
  1753  	statusCode, respBody, err := c.makeRequest(http.MethodGet,
  1754  		www.PoliteiaWWWAPIRoute, www.RouteTokenInventory, nil)
  1755  	if err != nil {
  1756  		return nil, err
  1757  	}
  1758  
  1759  	if statusCode != http.StatusOK {
  1760  		return nil, wwwError(respBody, statusCode)
  1761  	}
  1762  
  1763  	var tir www.TokenInventoryReply
  1764  	err = json.Unmarshal(respBody, &tir)
  1765  	if err != nil {
  1766  		return nil, fmt.Errorf("unmarshal TokenInventoryReply: %v", err)
  1767  	}
  1768  
  1769  	if c.cfg.Verbose {
  1770  		err := prettyPrintJSON(tir)
  1771  		if err != nil {
  1772  			return nil, err
  1773  		}
  1774  	}
  1775  
  1776  	return &tir, nil
  1777  }
  1778  
  1779  // InvoiceExchangeRate changes the status of the specified invoice.
  1780  func (c *Client) InvoiceExchangeRate(ier *cms.InvoiceExchangeRate) (*cms.InvoiceExchangeRateReply, error) {
  1781  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
  1782  		cms.APIRoute, cms.RouteInvoiceExchangeRate, ier)
  1783  	if err != nil {
  1784  		return nil, err
  1785  	}
  1786  
  1787  	if statusCode != http.StatusOK {
  1788  		return nil, wwwError(respBody, statusCode)
  1789  	}
  1790  
  1791  	var ierr cms.InvoiceExchangeRateReply
  1792  	err = json.Unmarshal(respBody, &ierr)
  1793  	if err != nil {
  1794  		return nil, fmt.Errorf("unmarshal SetInvoiceStatusReply: %v", err)
  1795  	}
  1796  
  1797  	if c.cfg.Verbose {
  1798  		err := prettyPrintJSON(ierr)
  1799  		if err != nil {
  1800  			return nil, err
  1801  		}
  1802  	}
  1803  	return &ierr, nil
  1804  }
  1805  
  1806  // InvoicePayouts retrieves invoices base on possible field set in the request
  1807  // month/year and/or status
  1808  func (c *Client) InvoicePayouts(lip *cms.InvoicePayouts) (*cms.InvoicePayoutsReply, error) {
  1809  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
  1810  		cms.APIRoute, cms.RouteInvoicePayouts, lip)
  1811  	if err != nil {
  1812  		return nil, err
  1813  	}
  1814  
  1815  	if statusCode != http.StatusOK {
  1816  		return nil, wwwError(respBody, statusCode)
  1817  	}
  1818  
  1819  	var lipr cms.InvoicePayoutsReply
  1820  	err = json.Unmarshal(respBody, &lipr)
  1821  	if err != nil {
  1822  		return nil, fmt.Errorf("unmarshal InvoicePayouts: %v", err)
  1823  	}
  1824  
  1825  	if c.cfg.Verbose {
  1826  		err := prettyPrintJSON(lipr)
  1827  		if err != nil {
  1828  			return nil, err
  1829  		}
  1830  	}
  1831  	return &lipr, nil
  1832  }
  1833  
  1834  // CMSUserDetails returns the current cms user's information.
  1835  func (c *Client) CMSUserDetails(userID string) (*cms.UserDetailsReply, error) {
  1836  	route := "/user/" + userID
  1837  	statusCode, respBody, err := c.makeRequest(http.MethodGet, cms.APIRoute,
  1838  		route, nil)
  1839  	if err != nil {
  1840  		return nil, err
  1841  	}
  1842  
  1843  	if statusCode != http.StatusOK {
  1844  		return nil, wwwError(respBody, statusCode)
  1845  	}
  1846  
  1847  	var uir cms.UserDetailsReply
  1848  	err = json.Unmarshal(respBody, &uir)
  1849  	if err != nil {
  1850  		return nil, fmt.Errorf("unmarshal CMSUserDetailsReply: %v", err)
  1851  	}
  1852  
  1853  	if c.cfg.Verbose {
  1854  		err := prettyPrintJSON(uir)
  1855  		if err != nil {
  1856  			return nil, err
  1857  		}
  1858  	}
  1859  
  1860  	return &uir, nil
  1861  }
  1862  
  1863  // CodeStats returns the given cms user's code statistics.
  1864  func (c *Client) CodeStats(usc cms.UserCodeStats) (*cms.UserCodeStatsReply, error) {
  1865  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
  1866  		cms.APIRoute, cms.RouteUserCodeStats, usc)
  1867  	if err != nil {
  1868  		return nil, err
  1869  	}
  1870  
  1871  	if statusCode != http.StatusOK {
  1872  		return nil, wwwError(respBody, statusCode)
  1873  	}
  1874  
  1875  	var csr cms.UserCodeStatsReply
  1876  	err = json.Unmarshal(respBody, &csr)
  1877  	if err != nil {
  1878  		return nil, fmt.Errorf("unmarshal UserCodeStatsReply: %v", err)
  1879  	}
  1880  
  1881  	if c.cfg.Verbose {
  1882  		err := prettyPrintJSON(csr)
  1883  		if err != nil {
  1884  			return nil, err
  1885  		}
  1886  	}
  1887  
  1888  	return &csr, nil
  1889  }
  1890  
  1891  // CMSEditUser edits the current user's information.
  1892  func (c *Client) CMSEditUser(uui cms.EditUser) (*cms.EditUserReply, error) {
  1893  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
  1894  		cms.APIRoute, www.RouteEditUser, uui)
  1895  	if err != nil {
  1896  		return nil, err
  1897  	}
  1898  
  1899  	if statusCode != http.StatusOK {
  1900  		return nil, wwwError(respBody, statusCode)
  1901  	}
  1902  
  1903  	var eur cms.EditUserReply
  1904  	err = json.Unmarshal(respBody, &eur)
  1905  	if err != nil {
  1906  		return nil, fmt.Errorf("unmarshal CMSEditUserReply: %v", err)
  1907  	}
  1908  
  1909  	if c.cfg.Verbose {
  1910  		err := prettyPrintJSON(eur)
  1911  		if err != nil {
  1912  			return nil, err
  1913  		}
  1914  	}
  1915  
  1916  	return &eur, nil
  1917  }
  1918  
  1919  // CMSManageUser updates the given user's information.
  1920  func (c *Client) CMSManageUser(uui cms.CMSManageUser) (*cms.CMSManageUserReply, error) {
  1921  	statusCode, respBody, err := c.makeRequest(http.MethodPost, cms.APIRoute,
  1922  		cms.RouteManageCMSUser, uui)
  1923  	if err != nil {
  1924  		return nil, err
  1925  	}
  1926  
  1927  	if statusCode != http.StatusOK {
  1928  		return nil, wwwError(respBody, statusCode)
  1929  	}
  1930  
  1931  	var eur cms.CMSManageUserReply
  1932  	err = json.Unmarshal(respBody, &eur)
  1933  	if err != nil {
  1934  		return nil, fmt.Errorf("unmarshal CMSManageUserReply: %v", err)
  1935  	}
  1936  
  1937  	if c.cfg.Verbose {
  1938  		err := prettyPrintJSON(eur)
  1939  		if err != nil {
  1940  			return nil, err
  1941  		}
  1942  	}
  1943  
  1944  	return &eur, nil
  1945  }
  1946  
  1947  // NewDCC creates a new dcc proposal.
  1948  func (c *Client) NewDCC(nd cms.NewDCC) (*cms.NewDCCReply, error) {
  1949  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
  1950  		cms.APIRoute, cms.RouteNewDCC, nd)
  1951  	if err != nil {
  1952  		return nil, err
  1953  	}
  1954  
  1955  	if statusCode != http.StatusOK {
  1956  		return nil, wwwError(respBody, statusCode)
  1957  	}
  1958  
  1959  	var ndr cms.NewDCCReply
  1960  	err = json.Unmarshal(respBody, &ndr)
  1961  	if err != nil {
  1962  		return nil, fmt.Errorf("unmarshal NewDCCReply: %v", err)
  1963  	}
  1964  
  1965  	if c.cfg.Verbose {
  1966  		err := prettyPrintJSON(ndr)
  1967  		if err != nil {
  1968  			return nil, err
  1969  		}
  1970  	}
  1971  
  1972  	return &ndr, nil
  1973  }
  1974  
  1975  // SupportOpposeDCC issues support for a given DCC proposal.
  1976  func (c *Client) SupportOpposeDCC(sd cms.SupportOpposeDCC) (*cms.SupportOpposeDCCReply, error) {
  1977  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
  1978  		cms.APIRoute, cms.RouteSupportOpposeDCC, sd)
  1979  	if err != nil {
  1980  		return nil, err
  1981  	}
  1982  
  1983  	if statusCode != http.StatusOK {
  1984  		return nil, wwwError(respBody, statusCode)
  1985  	}
  1986  
  1987  	var sdr cms.SupportOpposeDCCReply
  1988  	err = json.Unmarshal(respBody, &sdr)
  1989  	if err != nil {
  1990  		return nil, fmt.Errorf("unmarshal SupportOpposeDCCReply: %v", err)
  1991  	}
  1992  
  1993  	if c.cfg.Verbose {
  1994  		err := prettyPrintJSON(sdr)
  1995  		if err != nil {
  1996  			return nil, err
  1997  		}
  1998  	}
  1999  
  2000  	return &sdr, nil
  2001  }
  2002  
  2003  // NewDCCComment submits a new dcc comment for the logged in user.
  2004  func (c *Client) NewDCCComment(nc *www.NewComment) (*www.NewCommentReply, error) {
  2005  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
  2006  		cms.APIRoute, cms.RouteNewCommentDCC, nc)
  2007  	if err != nil {
  2008  		return nil, err
  2009  	}
  2010  
  2011  	if statusCode != http.StatusOK {
  2012  		return nil, wwwError(respBody, statusCode)
  2013  	}
  2014  
  2015  	var ncr www.NewCommentReply
  2016  	err = json.Unmarshal(respBody, &ncr)
  2017  	if err != nil {
  2018  		return nil, fmt.Errorf("unmarshal NewDCCCommentReply: %v", err)
  2019  	}
  2020  
  2021  	if c.cfg.Verbose {
  2022  		err := prettyPrintJSON(ncr)
  2023  		if err != nil {
  2024  			return nil, err
  2025  		}
  2026  	}
  2027  
  2028  	return &ncr, nil
  2029  }
  2030  
  2031  // DCCComments retrieves the comments for the specified proposal.
  2032  func (c *Client) DCCComments(token string) (*www.GetCommentsReply, error) {
  2033  	route := "/dcc/" + token + "/comments"
  2034  	statusCode, respBody, err := c.makeRequest(http.MethodGet, cms.APIRoute,
  2035  		route, nil)
  2036  	if err != nil {
  2037  		return nil, err
  2038  	}
  2039  
  2040  	if statusCode != http.StatusOK {
  2041  		return nil, wwwError(respBody, statusCode)
  2042  	}
  2043  
  2044  	var gcr www.GetCommentsReply
  2045  	err = json.Unmarshal(respBody, &gcr)
  2046  	if err != nil {
  2047  		return nil, fmt.Errorf("unmarshal DCCCommentsReply: %v", err)
  2048  	}
  2049  
  2050  	if c.cfg.Verbose {
  2051  		err := prettyPrintJSON(gcr)
  2052  		if err != nil {
  2053  			return nil, err
  2054  		}
  2055  	}
  2056  
  2057  	return &gcr, nil
  2058  }
  2059  
  2060  // DCCDetails retrieves the specified dcc.
  2061  func (c *Client) DCCDetails(token string) (*cms.DCCDetailsReply, error) {
  2062  	route := "/dcc/" + token
  2063  	statusCode, respBody, err := c.makeRequest(http.MethodGet, cms.APIRoute,
  2064  		route, nil)
  2065  	if err != nil {
  2066  		return nil, err
  2067  	}
  2068  
  2069  	if statusCode != http.StatusOK {
  2070  		return nil, wwwError(respBody, statusCode)
  2071  	}
  2072  
  2073  	var ddr cms.DCCDetailsReply
  2074  	err = json.Unmarshal(respBody, &ddr)
  2075  	if err != nil {
  2076  		return nil, fmt.Errorf("unmarshal DCCDetailsReply: %v", err)
  2077  	}
  2078  
  2079  	if c.cfg.Verbose {
  2080  		err := prettyPrintJSON(ddr)
  2081  		if err != nil {
  2082  			return nil, err
  2083  		}
  2084  	}
  2085  
  2086  	return &ddr, nil
  2087  }
  2088  
  2089  // GetDCCs retrieves invoices base on possible field set in the request
  2090  // month/year and/or status
  2091  func (c *Client) GetDCCs(gd *cms.GetDCCs) (*cms.GetDCCsReply, error) {
  2092  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
  2093  		cms.APIRoute, cms.RouteGetDCCs, gd)
  2094  	if err != nil {
  2095  		return nil, err
  2096  	}
  2097  
  2098  	if statusCode != http.StatusOK {
  2099  		return nil, wwwError(respBody, statusCode)
  2100  	}
  2101  
  2102  	var gdr cms.GetDCCsReply
  2103  	err = json.Unmarshal(respBody, &gdr)
  2104  	if err != nil {
  2105  		return nil, fmt.Errorf("unmarshal GetDCCsReply: %v", err)
  2106  	}
  2107  
  2108  	if c.cfg.Verbose {
  2109  		err := prettyPrintJSON(gdr)
  2110  		if err != nil {
  2111  			return nil, err
  2112  		}
  2113  	}
  2114  
  2115  	return &gdr, nil
  2116  }
  2117  
  2118  // SetDCCStatus issues an status update for a given DCC proposal.
  2119  func (c *Client) SetDCCStatus(sd *cms.SetDCCStatus) (*cms.SetDCCStatusReply, error) {
  2120  	route := "/dcc/" + sd.Token + "/status"
  2121  	statusCode, respBody, err := c.makeRequest(http.MethodPost, cms.APIRoute,
  2122  		route, sd)
  2123  	if err != nil {
  2124  		return nil, err
  2125  	}
  2126  
  2127  	if statusCode != http.StatusOK {
  2128  		return nil, wwwError(respBody, statusCode)
  2129  	}
  2130  
  2131  	var sdr cms.SetDCCStatusReply
  2132  	err = json.Unmarshal(respBody, &sdr)
  2133  	if err != nil {
  2134  		return nil, fmt.Errorf("unmarshal SetDCCStatusReply: %v", err)
  2135  	}
  2136  
  2137  	if c.cfg.Verbose {
  2138  		err := prettyPrintJSON(sdr)
  2139  		if err != nil {
  2140  			return nil, err
  2141  		}
  2142  	}
  2143  
  2144  	return &sdr, nil
  2145  }
  2146  
  2147  // UserSubContractors retrieves the subcontractors that are linked to the requesting user
  2148  func (c *Client) UserSubContractors(usc *cms.UserSubContractors) (*cms.UserSubContractorsReply, error) {
  2149  	statusCode, respBody, err := c.makeRequest(http.MethodGet,
  2150  		cms.APIRoute, cms.RouteUserSubContractors, usc)
  2151  	if err != nil {
  2152  		return nil, err
  2153  	}
  2154  
  2155  	if statusCode != http.StatusOK {
  2156  		return nil, wwwError(respBody, statusCode)
  2157  	}
  2158  
  2159  	var uscr cms.UserSubContractorsReply
  2160  	err = json.Unmarshal(respBody, &uscr)
  2161  	if err != nil {
  2162  		return nil, fmt.Errorf("unmarshal UserSubContractorsReply: %v", err)
  2163  	}
  2164  
  2165  	if c.cfg.Verbose {
  2166  		err := prettyPrintJSON(uscr)
  2167  		if err != nil {
  2168  			return nil, err
  2169  		}
  2170  	}
  2171  	return &uscr, nil
  2172  }
  2173  
  2174  // ProposalOwner retrieves the subcontractors that are linked to the requesting user
  2175  func (c *Client) ProposalOwner(po *cms.ProposalOwner) (*cms.ProposalOwnerReply, error) {
  2176  	statusCode, respBody, err := c.makeRequest(http.MethodGet,
  2177  		cms.APIRoute, cms.RouteProposalOwner, po)
  2178  	if err != nil {
  2179  		return nil, err
  2180  	}
  2181  
  2182  	if statusCode != http.StatusOK {
  2183  		return nil, wwwError(respBody, statusCode)
  2184  	}
  2185  
  2186  	var por cms.ProposalOwnerReply
  2187  	err = json.Unmarshal(respBody, &por)
  2188  	if err != nil {
  2189  		return nil, fmt.Errorf("unmarshal ProposalOwnerReply: %v", err)
  2190  	}
  2191  
  2192  	if c.cfg.Verbose {
  2193  		err := prettyPrintJSON(por)
  2194  		if err != nil {
  2195  			return nil, err
  2196  		}
  2197  	}
  2198  
  2199  	return &por, nil
  2200  }
  2201  
  2202  // CastVoteDCC issues a signed vote for a given DCC proposal. approval
  2203  func (c *Client) CastVoteDCC(cv cms.CastVote) (*cms.CastVoteReply, error) {
  2204  	statusCode, respBody, err := c.makeRequest("POST", cms.APIRoute,
  2205  		cms.RouteCastVoteDCC, cv)
  2206  	if err != nil {
  2207  		return nil, err
  2208  	}
  2209  
  2210  	if statusCode != http.StatusOK {
  2211  		return nil, wwwError(respBody, statusCode)
  2212  	}
  2213  
  2214  	var cvr cms.CastVoteReply
  2215  	err = json.Unmarshal(respBody, &cvr)
  2216  	if err != nil {
  2217  		return nil, fmt.Errorf("unmarshal VoteDCCReply: %v", err)
  2218  	}
  2219  
  2220  	if c.cfg.Verbose {
  2221  		err := prettyPrintJSON(cvr)
  2222  		if err != nil {
  2223  			return nil, err
  2224  		}
  2225  	}
  2226  
  2227  	return &cvr, nil
  2228  }
  2229  
  2230  // VoteDetailsDCC returns all the needed information about a given vote for a
  2231  // DCC proposal.
  2232  func (c *Client) VoteDetailsDCC(cv cms.VoteDetails) (*cms.VoteDetailsReply, error) {
  2233  	statusCode, respBody, err := c.makeRequest("POST", cms.APIRoute,
  2234  		cms.RouteVoteDetailsDCC, cv)
  2235  	if err != nil {
  2236  		return nil, err
  2237  	}
  2238  
  2239  	if statusCode != http.StatusOK {
  2240  		return nil, wwwError(respBody, statusCode)
  2241  	}
  2242  
  2243  	var vdr cms.VoteDetailsReply
  2244  	err = json.Unmarshal(respBody, &vdr)
  2245  	if err != nil {
  2246  		return nil, fmt.Errorf("unmarshal VoteDCCReply: %v", err)
  2247  	}
  2248  
  2249  	if c.cfg.Verbose {
  2250  		err := prettyPrintJSON(vdr)
  2251  		if err != nil {
  2252  			return nil, err
  2253  		}
  2254  	}
  2255  
  2256  	return &vdr, nil
  2257  }
  2258  
  2259  // StartVoteDCC sends the provided StartVoteDCC to the politeiawww backend.
  2260  func (c *Client) StartVoteDCC(sv cms.StartVote) (*cms.StartVoteReply, error) {
  2261  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
  2262  		cms.APIRoute, cms.RouteStartVoteDCC, sv)
  2263  	if err != nil {
  2264  		return nil, err
  2265  	}
  2266  
  2267  	if statusCode != http.StatusOK {
  2268  		return nil, wwwError(respBody, statusCode)
  2269  	}
  2270  
  2271  	var svr cms.StartVoteReply
  2272  	err = json.Unmarshal(respBody, &svr)
  2273  	if err != nil {
  2274  		return nil, fmt.Errorf("unmarshal StartVoteReply: %v", err)
  2275  	}
  2276  
  2277  	if c.cfg.Verbose {
  2278  		svr.UserWeights = []string{"removed by piwww for readability"}
  2279  		err := prettyPrintJSON(svr)
  2280  		if err != nil {
  2281  			return nil, err
  2282  		}
  2283  	}
  2284  
  2285  	return &svr, nil
  2286  }
  2287  
  2288  // SetTOTP sets the logged in user's TOTP Key.
  2289  func (c *Client) SetTOTP(st *www.SetTOTP) (*www.SetTOTPReply, error) {
  2290  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
  2291  		cms.APIRoute, www.RouteSetTOTP, st)
  2292  	if err != nil {
  2293  		return nil, err
  2294  	}
  2295  
  2296  	if statusCode != http.StatusOK {
  2297  		return nil, wwwError(respBody, statusCode)
  2298  	}
  2299  
  2300  	var str www.SetTOTPReply
  2301  	err = json.Unmarshal(respBody, &str)
  2302  	if err != nil {
  2303  		return nil, fmt.Errorf("unmarshal SetTOTPReply: %v", err)
  2304  	}
  2305  
  2306  	if c.cfg.Verbose {
  2307  		err := prettyPrintJSON(str)
  2308  		if err != nil {
  2309  			return nil, err
  2310  		}
  2311  	}
  2312  
  2313  	return &str, nil
  2314  }
  2315  
  2316  // VerifyTOTP comfirms the logged in user's TOTP Key.
  2317  func (c *Client) VerifyTOTP(vt *www.VerifyTOTP) (*www.VerifyTOTPReply, error) {
  2318  	statusCode, respBody, err := c.makeRequest(http.MethodPost,
  2319  		cms.APIRoute, www.RouteVerifyTOTP, vt)
  2320  	if err != nil {
  2321  		return nil, err
  2322  	}
  2323  
  2324  	if statusCode != http.StatusOK {
  2325  		return nil, wwwError(respBody, statusCode)
  2326  	}
  2327  
  2328  	var vtr www.VerifyTOTPReply
  2329  	err = json.Unmarshal(respBody, &vtr)
  2330  	if err != nil {
  2331  		return nil, fmt.Errorf("unmarshal VerifyTOTPReply: %v", err)
  2332  	}
  2333  
  2334  	if c.cfg.Verbose {
  2335  		err := prettyPrintJSON(vtr)
  2336  		if err != nil {
  2337  			return nil, err
  2338  		}
  2339  	}
  2340  
  2341  	return &vtr, nil
  2342  }
  2343  
  2344  // NewClient returns a new politeiawww client.
  2345  func NewClient(cfg *Config) (*Client, error) {
  2346  	// Setup http client
  2347  	httpClient, err := util.NewHTTPClient(cfg.SkipVerify, cfg.HTTPSCert)
  2348  	if err != nil {
  2349  		return nil, err
  2350  	}
  2351  
  2352  	// Setup cookies
  2353  	jar, err := cookiejar.New(&cookiejar.Options{
  2354  		PublicSuffixList: publicsuffix.List,
  2355  	})
  2356  	if err != nil {
  2357  		return nil, err
  2358  	}
  2359  	u, err := url.Parse(cfg.Host)
  2360  	if err != nil {
  2361  		return nil, err
  2362  	}
  2363  	jar.SetCookies(u, cfg.Cookies)
  2364  	httpClient.Jar = jar
  2365  
  2366  	return &Client{
  2367  		http: httpClient,
  2368  		cfg:  cfg,
  2369  	}, nil
  2370  }