github.com/decred/politeia@v1.4.0/politeiawww/legacy/wwwcms.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 legacy
     6  
     7  import (
     8  	"bytes"
     9  	"encoding/json"
    10  	"errors"
    11  	"net/http"
    12  
    13  	cms "github.com/decred/politeia/politeiawww/api/cms/v1"
    14  	www "github.com/decred/politeia/politeiawww/api/www/v1"
    15  	"github.com/decred/politeia/politeiawww/legacy/sessions"
    16  	"github.com/decred/politeia/util"
    17  	"github.com/google/uuid"
    18  	"github.com/gorilla/mux"
    19  )
    20  
    21  // handleInviteNewUser handles the invitation of a new contractor by an
    22  // administrator for the Contractor Management System.
    23  func (p *Politeiawww) handleInviteNewUser(w http.ResponseWriter, r *http.Request) {
    24  	log.Tracef("handleInviteNewUser")
    25  
    26  	// Get the new user command.
    27  	var u cms.InviteNewUser
    28  	decoder := json.NewDecoder(r.Body)
    29  	if err := decoder.Decode(&u); err != nil {
    30  		RespondWithError(w, r, 0, "handleInviteNewUser: unmarshal", www.UserError{
    31  			ErrorCode: www.ErrorStatusInvalidInput,
    32  		})
    33  		return
    34  	}
    35  
    36  	reply, err := p.processInviteNewUser(u)
    37  	if err != nil {
    38  		RespondWithError(w, r, 0, "handleInviteNewUser: ProcessInviteNewUser %v", err)
    39  		return
    40  	}
    41  
    42  	// Reply with the verification token.
    43  	util.RespondWithJSON(w, http.StatusOK, reply)
    44  }
    45  
    46  // handleRegisterUser handles the completion of registration by invited users of
    47  // the Contractor Management System.
    48  func (p *Politeiawww) handleRegisterUser(w http.ResponseWriter, r *http.Request) {
    49  	log.Tracef("handleRegisterUser")
    50  
    51  	// Get the new user command.
    52  	var u cms.RegisterUser
    53  	decoder := json.NewDecoder(r.Body)
    54  	if err := decoder.Decode(&u); err != nil {
    55  		RespondWithError(w, r, 0, "handleRegisterUser: unmarshal", www.UserError{
    56  			ErrorCode: www.ErrorStatusInvalidInput,
    57  		})
    58  		return
    59  	}
    60  
    61  	reply, err := p.processRegisterUser(u)
    62  	if err != nil {
    63  		RespondWithError(w, r, 0, "handleRegisterUser: ProcessRegisterUser %v", err)
    64  		return
    65  	}
    66  
    67  	// Reply with the verification token.
    68  	util.RespondWithJSON(w, http.StatusOK, reply)
    69  }
    70  
    71  // handleCMSUsers handles fetching a list of cms users.
    72  func (p *Politeiawww) handleCMSUsers(w http.ResponseWriter, r *http.Request) {
    73  	log.Tracef("handleCMSUsers")
    74  
    75  	var cu cms.CMSUsers
    76  	err := util.ParseGetParams(r, &cu)
    77  	if err != nil {
    78  		RespondWithError(w, r, 0, "handleCMSUsers: ParseGetParams",
    79  			www.UserError{
    80  				ErrorCode: www.ErrorStatusInvalidInput,
    81  			})
    82  		return
    83  	}
    84  
    85  	cur, err := p.processCMSUsers(&cu)
    86  	if err != nil {
    87  		RespondWithError(w, r, 0,
    88  			"handleCMSUsers: processCMSUsers %v", err)
    89  		return
    90  	}
    91  
    92  	util.RespondWithJSON(w, http.StatusOK, cur)
    93  }
    94  
    95  // handleNewInvoice handles the incoming new invoice command.
    96  func (p *Politeiawww) handleNewInvoice(w http.ResponseWriter, r *http.Request) {
    97  	log.Tracef("handleNewInvoice")
    98  
    99  	// Get the new invoice command.
   100  	var ni cms.NewInvoice
   101  	decoder := json.NewDecoder(r.Body)
   102  	if err := decoder.Decode(&ni); err != nil {
   103  		RespondWithError(w, r, 0, "handleNewInvoice: unmarshal", www.UserError{
   104  			ErrorCode: www.ErrorStatusInvalidInput,
   105  		})
   106  		return
   107  	}
   108  
   109  	user, err := p.sessions.GetSessionUser(w, r)
   110  	if err != nil {
   111  		RespondWithError(w, r, 0,
   112  			"handleNewInvoice: getSessionUser %v", err)
   113  		return
   114  	}
   115  
   116  	reply, err := p.processNewInvoice(r.Context(), ni, user)
   117  	if err != nil {
   118  		RespondWithError(w, r, 0,
   119  			"handleNewInvoice: processNewInvoice %v", err)
   120  		return
   121  	}
   122  
   123  	// Reply with the challenge response and censorship token.
   124  	util.RespondWithJSON(w, http.StatusOK, reply)
   125  }
   126  
   127  // handleInvoiceDetails handles the incoming invoice details command. It fetches
   128  // the complete details for an existing invoice.
   129  func (p *Politeiawww) handleInvoiceDetails(w http.ResponseWriter, r *http.Request) {
   130  	log.Tracef("handleInvoiceDetails")
   131  
   132  	// Get the invoice details command
   133  	var pd cms.InvoiceDetails
   134  	// get version from query string parameters
   135  	err := util.ParseGetParams(r, &pd)
   136  	if err != nil {
   137  		RespondWithError(w, r, 0, "handleInvoiceDetails: ParseGetParams",
   138  			www.UserError{
   139  				ErrorCode: www.ErrorStatusInvalidInput,
   140  			})
   141  		return
   142  	}
   143  
   144  	// Get invoice token from path parameters
   145  	pathParams := mux.Vars(r)
   146  	pd.Token = pathParams["token"]
   147  
   148  	user, err := p.sessions.GetSessionUser(w, r)
   149  	if err != nil {
   150  		if !errors.Is(err, sessions.ErrSessionNotFound) {
   151  			RespondWithError(w, r, 0,
   152  				"handleInvoiceDetails: getSessionUser %v", err)
   153  			return
   154  		}
   155  	}
   156  	reply, err := p.processInvoiceDetails(pd, user)
   157  	if err != nil {
   158  		RespondWithError(w, r, 0,
   159  			"handleInvoiceDetails: processInvoiceDetails %v", err)
   160  		return
   161  	}
   162  
   163  	// Reply with the proposal details.
   164  	util.RespondWithJSON(w, http.StatusOK, reply)
   165  }
   166  
   167  // handleUserInvoices handles the request to get all of the invoices from the
   168  // currently logged in user.
   169  func (p *Politeiawww) handleUserInvoices(w http.ResponseWriter, r *http.Request) {
   170  	log.Tracef("handleUserInvoices")
   171  
   172  	user, err := p.sessions.GetSessionUser(w, r)
   173  	if err != nil {
   174  		RespondWithError(w, r, 0,
   175  			"handleUserInvoices: getSessionUser %v", err)
   176  		return
   177  	}
   178  
   179  	reply, err := p.processUserInvoices(user)
   180  	if err != nil {
   181  		RespondWithError(w, r, 0, "handleUserInvoices: processUserInvoices %v", err)
   182  		return
   183  	}
   184  
   185  	// Reply with the verification token.
   186  	util.RespondWithJSON(w, http.StatusOK, reply)
   187  }
   188  
   189  // handleAdminUserInvoices handles the request to get all of the invoices of a
   190  // user by an administrator for the Contractor Management System.
   191  func (p *Politeiawww) handleAdminUserInvoices(w http.ResponseWriter, r *http.Request) {
   192  	log.Tracef("handleAdminUserInvoices")
   193  
   194  	var aui cms.AdminUserInvoices
   195  	// get version from query string parameters
   196  	err := util.ParseGetParams(r, &aui)
   197  	if err != nil {
   198  		RespondWithError(w, r, 0, "handleAdminUserInvoices: ParseGetParams",
   199  			www.UserError{
   200  				ErrorCode: www.ErrorStatusInvalidInput,
   201  			})
   202  		return
   203  	}
   204  
   205  	_, err = uuid.Parse(aui.UserID)
   206  	if err != nil {
   207  		RespondWithError(w, r, 0, "handleAdminUserInvoices: ParseUint",
   208  			www.UserError{
   209  				ErrorCode: www.ErrorStatusInvalidInput,
   210  			})
   211  		return
   212  	}
   213  
   214  	reply, err := p.processAdminUserInvoices(aui)
   215  	if err != nil {
   216  		RespondWithError(w, r, 0, "handleAdminUserInvoices: processAdminUserInvoices %v", err)
   217  		return
   218  	}
   219  
   220  	util.RespondWithJSON(w, http.StatusOK, reply)
   221  }
   222  
   223  // handleSetInvoiceStatus handles the incoming set invoice status command.
   224  func (p *Politeiawww) handleSetInvoiceStatus(w http.ResponseWriter, r *http.Request) {
   225  	log.Tracef("handleSetInvoiceStatus")
   226  
   227  	// Get set invoice command
   228  	var sis cms.SetInvoiceStatus
   229  	decoder := json.NewDecoder(r.Body)
   230  	if err := decoder.Decode(&sis); err != nil {
   231  		RespondWithError(w, r, 0, "handleSetInvoiceStatus: unmarshal", www.UserError{
   232  			ErrorCode: www.ErrorStatusInvalidInput,
   233  		})
   234  		return
   235  	}
   236  
   237  	user, err := p.sessions.GetSessionUser(w, r)
   238  	if err != nil {
   239  		RespondWithError(w, r, 0,
   240  			"handleSetInvoiceStatus: getSessionUser %v", err)
   241  		return
   242  	}
   243  
   244  	reply, err := p.processSetInvoiceStatus(r.Context(), sis, user)
   245  	if err != nil {
   246  		RespondWithError(w, r, 0,
   247  			"handleSetInvoiceStatus: processSetInvoiceStatus %v", err)
   248  		return
   249  	}
   250  
   251  	// Reply with the challenge response and censorship token.
   252  	util.RespondWithJSON(w, http.StatusOK, reply)
   253  }
   254  
   255  // handleInvoices handles the request to get all of the  of a new contractor by an
   256  // administrator for the Contractor Management System.
   257  func (p *Politeiawww) handleInvoices(w http.ResponseWriter, r *http.Request) {
   258  	log.Tracef("handleInvoices")
   259  	var ai cms.Invoices
   260  	decoder := json.NewDecoder(r.Body)
   261  	if err := decoder.Decode(&ai); err != nil {
   262  		RespondWithError(w, r, 0, "handleInvoices: unmarshal", www.UserError{
   263  			ErrorCode: www.ErrorStatusInvalidInput,
   264  		})
   265  		return
   266  	}
   267  
   268  	user, err := p.sessions.GetSessionUser(w, r)
   269  	if err != nil {
   270  		RespondWithError(w, r, 0,
   271  			"handleInvoices: getSessionUser %v", err)
   272  		return
   273  	}
   274  
   275  	reply, err := p.processInvoices(ai, user)
   276  	if err != nil {
   277  		RespondWithError(w, r, 0, "handleInvoices: processInvoices %v", err)
   278  		return
   279  	}
   280  
   281  	// Reply with the verification token.
   282  	util.RespondWithJSON(w, http.StatusOK, reply)
   283  }
   284  
   285  // handleEditInvoice attempts to edit an invoice
   286  func (p *Politeiawww) handleEditInvoice(w http.ResponseWriter, r *http.Request) {
   287  	log.Tracef("handleEditInvoice")
   288  
   289  	// Get edit invoice command
   290  	var ei cms.EditInvoice
   291  	decoder := json.NewDecoder(r.Body)
   292  	if err := decoder.Decode(&ei); err != nil {
   293  		RespondWithError(w, r, 0, "handleEditInvoice: unmarshal",
   294  			www.UserError{
   295  				ErrorCode: www.ErrorStatusInvalidInput,
   296  			})
   297  		return
   298  	}
   299  
   300  	user, err := p.sessions.GetSessionUser(w, r)
   301  	if err != nil {
   302  		RespondWithError(w, r, 0,
   303  			"handleEditInvoice: getSessionUser %v", err)
   304  		return
   305  	}
   306  
   307  	log.Debugf("handleEditInvoice: %v", ei.Token)
   308  
   309  	epr, err := p.processEditInvoice(r.Context(), ei, user)
   310  	if err != nil {
   311  		RespondWithError(w, r, 0,
   312  			"handleEditInvoice: processEditInvoice %v", err)
   313  		return
   314  	}
   315  
   316  	util.RespondWithJSON(w, http.StatusOK, epr)
   317  }
   318  
   319  // handleGeneratePayouts handles the request to generate all of the payouts for any
   320  // currently approved invoice.
   321  func (p *Politeiawww) handleGeneratePayouts(w http.ResponseWriter, r *http.Request) {
   322  	log.Tracef("handleGeneratePayouts")
   323  
   324  	// Get generate payouts command
   325  	var gp cms.GeneratePayouts
   326  	decoder := json.NewDecoder(r.Body)
   327  	if err := decoder.Decode(&gp); err != nil {
   328  		RespondWithError(w, r, 0, "handleGeneratePayouts: unmarshal",
   329  			www.UserError{
   330  				ErrorCode: www.ErrorStatusInvalidInput,
   331  			})
   332  		return
   333  	}
   334  
   335  	user, err := p.sessions.GetSessionUser(w, r)
   336  	if err != nil {
   337  		RespondWithError(w, r, 0,
   338  			"handleGeneratePayouts: getSessionUser %v", err)
   339  		return
   340  	}
   341  
   342  	reply, err := p.processGeneratePayouts(gp, user)
   343  	if err != nil {
   344  		RespondWithError(w, r, 0, "handleGeneratePayouts: processGeneratePayouts %v", err)
   345  		return
   346  	}
   347  
   348  	// Reply with the generated payouts
   349  	util.RespondWithJSON(w, http.StatusOK, reply)
   350  }
   351  
   352  // handleNewCommentInvoice handles incomming comments for invoices.
   353  func (p *Politeiawww) handleNewCommentInvoice(w http.ResponseWriter, r *http.Request) {
   354  	log.Tracef("handleNewCommentInvoice")
   355  
   356  	var sc www.NewComment
   357  	decoder := json.NewDecoder(r.Body)
   358  	if err := decoder.Decode(&sc); err != nil {
   359  		RespondWithError(w, r, 0, "handleNewCommentInvoice: unmarshal",
   360  			www.UserError{
   361  				ErrorCode: www.ErrorStatusInvalidInput,
   362  			})
   363  		return
   364  	}
   365  
   366  	user, err := p.sessions.GetSessionUser(w, r)
   367  	if err != nil {
   368  		RespondWithError(w, r, 0,
   369  			"handleNewCommentInvoice: getSessionUser %v", err)
   370  		return
   371  	}
   372  
   373  	cr, err := p.processNewCommentInvoice(r.Context(), sc, user)
   374  	if err != nil {
   375  		RespondWithError(w, r, 0,
   376  			"handleNewCommentInvoice: processNewCommentInvoice: %v", err)
   377  		return
   378  	}
   379  
   380  	util.RespondWithJSON(w, http.StatusOK, cr)
   381  }
   382  
   383  // handleInvoiceComments handles batched invoice comments get.
   384  func (p *Politeiawww) handleInvoiceComments(w http.ResponseWriter, r *http.Request) {
   385  	log.Tracef("handleInvoiceComments")
   386  
   387  	pathParams := mux.Vars(r)
   388  	token := pathParams["token"]
   389  
   390  	user, err := p.sessions.GetSessionUser(w, r)
   391  	if err != nil {
   392  		if !errors.Is(err, sessions.ErrSessionNotFound) {
   393  			RespondWithError(w, r, 0,
   394  				"handleInvoiceComments: getSessionUser %v", err)
   395  			return
   396  		}
   397  	}
   398  	gcr, err := p.processInvoiceComments(r.Context(), token, user)
   399  	if err != nil {
   400  		RespondWithError(w, r, 0,
   401  			"handleInvoiceComments: processInvoiceComments %v", err)
   402  		return
   403  	}
   404  	util.RespondWithJSON(w, http.StatusOK, gcr)
   405  }
   406  
   407  // handleInvoiceExchangeRate handles incoming requests for monthly exchange rate
   408  func (p *Politeiawww) handleInvoiceExchangeRate(w http.ResponseWriter, r *http.Request) {
   409  	log.Tracef("handleInvoiceExchangeRate")
   410  
   411  	var ier cms.InvoiceExchangeRate
   412  	decoder := json.NewDecoder(r.Body)
   413  	if err := decoder.Decode(&ier); err != nil {
   414  		RespondWithError(w, r, 0, "handleInvoiceExchangeRate: unmarshal",
   415  			www.UserError{
   416  				ErrorCode: www.ErrorStatusInvalidInput,
   417  			})
   418  		return
   419  	}
   420  
   421  	ierr, err := p.processInvoiceExchangeRate(r.Context(), ier)
   422  	if err != nil {
   423  		RespondWithError(w, r, 0,
   424  			"handleInvoiceExchangeRate: processNewCommentInvoice: %v", err)
   425  		return
   426  	}
   427  
   428  	util.RespondWithJSON(w, http.StatusOK, ierr)
   429  }
   430  
   431  func (p *Politeiawww) handleCMSPolicy(w http.ResponseWriter, r *http.Request) {
   432  	// Get the policy command.
   433  	log.Tracef("handlePolicy")
   434  	reply := &cms.PolicyReply{
   435  		MinPasswordLength:             www.PolicyMinPasswordLength,
   436  		MinUsernameLength:             www.PolicyMinUsernameLength,
   437  		MaxUsernameLength:             www.PolicyMaxUsernameLength,
   438  		MaxImages:                     cms.PolicyMaxImages,
   439  		MaxImageSize:                  www.PolicyMaxImageSize,
   440  		MaxMDs:                        www.PolicyMaxMDs,
   441  		MaxMDSize:                     www.PolicyMaxMDSize,
   442  		ValidMIMETypes:                cms.PolicyValidMimeTypes,
   443  		MinLineItemColLength:          cms.PolicyMinLineItemColLength,
   444  		MaxLineItemColLength:          cms.PolicyMaxLineItemColLength,
   445  		MaxNameLength:                 cms.PolicyMaxNameLength,
   446  		MinNameLength:                 cms.PolicyMinNameLength,
   447  		MaxLocationLength:             cms.PolicyMaxLocationLength,
   448  		MinLocationLength:             cms.PolicyMinLocationLength,
   449  		MaxContactLength:              cms.PolicyMaxContactLength,
   450  		MinContactLength:              cms.PolicyMinContactLength,
   451  		InvoiceFieldSupportedChars:    cms.PolicyInvoiceFieldSupportedChars,
   452  		UsernameSupportedChars:        www.PolicyUsernameSupportedChars,
   453  		CMSNameLocationSupportedChars: cms.PolicyCMSNameLocationSupportedChars,
   454  		CMSContactSupportedChars:      cms.PolicyCMSContactSupportedChars,
   455  		CMSStatementSupportedChars:    cms.PolicySponsorStatementSupportedChars,
   456  		CMSSupportedDomains:           cms.PolicySupportedCMSDomains,
   457  		CMSSupportedLineItemTypes:     cms.PolicyCMSSupportedLineItemTypes,
   458  	}
   459  
   460  	util.RespondWithJSON(w, http.StatusOK, reply)
   461  }
   462  
   463  // handlePayInvoices handles the request to generate all of the payouts for any
   464  // currently approved invoice.
   465  func (p *Politeiawww) handlePayInvoices(w http.ResponseWriter, r *http.Request) {
   466  	log.Tracef("handlePayInvoices")
   467  
   468  	user, err := p.sessions.GetSessionUser(w, r)
   469  	if err != nil {
   470  		RespondWithError(w, r, 0,
   471  			"handlePayInvoices: getSessionUser %v", err)
   472  		return
   473  	}
   474  
   475  	reply, err := p.processPayInvoices(r.Context(), user)
   476  	if err != nil {
   477  		RespondWithError(w, r, 0, "handlePayInvoices: processPayInvoices %v",
   478  			err)
   479  		return
   480  	}
   481  
   482  	util.RespondWithJSON(w, http.StatusOK, reply)
   483  }
   484  
   485  // handleEditCMSUser handles the request to edit a given user's
   486  // additional user information.
   487  func (p *Politeiawww) handleEditCMSUser(w http.ResponseWriter, r *http.Request) {
   488  	log.Tracef("handleEditCMSUser")
   489  
   490  	var eu cms.EditUser
   491  	decoder := json.NewDecoder(r.Body)
   492  	if err := decoder.Decode(&eu); err != nil {
   493  		RespondWithError(w, r, 0, "handleEditCMSUser: unmarshal",
   494  			www.UserError{
   495  				ErrorCode: www.ErrorStatusInvalidInput,
   496  			})
   497  		return
   498  	}
   499  
   500  	user, err := p.sessions.GetSessionUser(w, r)
   501  	if err != nil {
   502  		RespondWithError(w, r, 0,
   503  			"handleEditCMSUser: getSessionUser %v", err)
   504  		return
   505  	}
   506  
   507  	reply, err := p.processEditCMSUser(eu, user)
   508  	if err != nil {
   509  		RespondWithError(w, r, 0, "handleEditCMSUser: "+
   510  			"processUpdateUserInformation %v", err)
   511  		return
   512  	}
   513  
   514  	util.RespondWithJSON(w, http.StatusOK, reply)
   515  }
   516  
   517  // handleManageCMSUser handles the request to edit a given user's
   518  // additional user information.
   519  func (p *Politeiawww) handleManageCMSUser(w http.ResponseWriter, r *http.Request) {
   520  	log.Tracef("handleManageCMSUser")
   521  
   522  	var mu cms.CMSManageUser
   523  	decoder := json.NewDecoder(r.Body)
   524  	if err := decoder.Decode(&mu); err != nil {
   525  		RespondWithError(w, r, 0, "handleManageCMSUser: unmarshal",
   526  			www.UserError{
   527  				ErrorCode: www.ErrorStatusInvalidInput,
   528  			})
   529  		return
   530  	}
   531  
   532  	reply, err := p.processManageCMSUser(mu)
   533  	if err != nil {
   534  		RespondWithError(w, r, 0, "handleManageCMSUser: "+
   535  			"processManageCMSUser %v", err)
   536  		return
   537  	}
   538  
   539  	util.RespondWithJSON(w, http.StatusOK, reply)
   540  }
   541  
   542  func (p *Politeiawww) handleCMSUserDetails(w http.ResponseWriter, r *http.Request) {
   543  	// Add the path param to the struct.
   544  	log.Tracef("handleCMSUserDetails")
   545  	pathParams := mux.Vars(r)
   546  	var ud cms.UserDetails
   547  	ud.UserID = pathParams["userid"]
   548  
   549  	userID, err := uuid.Parse(ud.UserID)
   550  	if err != nil {
   551  		RespondWithError(w, r, 0, "handleCMSUserDetails: ParseUint",
   552  			www.UserError{
   553  				ErrorCode: www.ErrorStatusInvalidInput,
   554  			})
   555  		return
   556  	}
   557  
   558  	user, err := p.sessions.GetSessionUser(w, r)
   559  	if err != nil {
   560  		RespondWithError(w, r, 0,
   561  			"handleCMSUserDetails: getSessionUser %v", err)
   562  		return
   563  	}
   564  
   565  	reply, err := p.processCMSUserDetails(&ud,
   566  		user != nil && user.ID == userID,
   567  		user != nil && user.Admin,
   568  	)
   569  
   570  	if err != nil {
   571  		RespondWithError(w, r, 0, "handleCMSUserDetails: "+
   572  			"processCMSUserDetails %v", err)
   573  		return
   574  	}
   575  
   576  	util.RespondWithJSON(w, http.StatusOK, reply)
   577  }
   578  
   579  // handleInvoicePayouts handles incoming requests for invoice payout information
   580  func (p *Politeiawww) handleInvoicePayouts(w http.ResponseWriter, r *http.Request) {
   581  	log.Tracef("handleInvoicePayouts")
   582  
   583  	var lip cms.InvoicePayouts
   584  	decoder := json.NewDecoder(r.Body)
   585  	if err := decoder.Decode(&lip); err != nil {
   586  		RespondWithError(w, r, 0, "handleInvoicePayouts: unmarshal",
   587  			www.UserError{
   588  				ErrorCode: www.ErrorStatusInvalidInput,
   589  			})
   590  		return
   591  	}
   592  
   593  	lipr, err := p.processInvoicePayouts(lip)
   594  	if err != nil {
   595  		RespondWithError(w, r, 0,
   596  			"handleInvoicePayouts: processInvoicePayouts: %v", err)
   597  		return
   598  	}
   599  
   600  	util.RespondWithJSON(w, http.StatusOK, lipr)
   601  }
   602  
   603  func (p *Politeiawww) handleNewDCC(w http.ResponseWriter, r *http.Request) {
   604  	log.Tracef("handleNewDCC")
   605  
   606  	var nd cms.NewDCC
   607  	decoder := json.NewDecoder(r.Body)
   608  	if err := decoder.Decode(&nd); err != nil {
   609  		RespondWithError(w, r, 0, "handleNewDCC: unmarshal",
   610  			www.UserError{
   611  				ErrorCode: www.ErrorStatusInvalidInput,
   612  			})
   613  		return
   614  	}
   615  	u, err := p.sessions.GetSessionUser(w, r)
   616  	if err != nil {
   617  		RespondWithError(w, r, 0,
   618  			"handleNewDCC: getSessionUser %v", err)
   619  		return
   620  	}
   621  
   622  	ndr, err := p.processNewDCC(r.Context(), nd, u)
   623  	if err != nil {
   624  		RespondWithError(w, r, 0,
   625  			"handleNewDCC: processNewDCC: %v", err)
   626  		return
   627  	}
   628  
   629  	util.RespondWithJSON(w, http.StatusOK, ndr)
   630  }
   631  
   632  func (p *Politeiawww) handleDCCDetails(w http.ResponseWriter, r *http.Request) {
   633  	log.Tracef("handleDCCDetails")
   634  
   635  	var gd cms.DCCDetails
   636  	// get version from query string parameters
   637  	err := util.ParseGetParams(r, &gd)
   638  	if err != nil {
   639  		RespondWithError(w, r, 0, "handleDCCDetails: ParseGetParams",
   640  			www.UserError{
   641  				ErrorCode: www.ErrorStatusInvalidInput,
   642  			})
   643  		return
   644  	}
   645  	// Get dcc token from path parameters
   646  	pathParams := mux.Vars(r)
   647  	gd.Token = pathParams["token"]
   648  
   649  	gdr, err := p.processDCCDetails(r.Context(), gd)
   650  	if err != nil {
   651  		RespondWithError(w, r, 0,
   652  			"handleDCCDetails: processDCCDetails: %v", err)
   653  		return
   654  	}
   655  
   656  	util.RespondWithJSON(w, http.StatusOK, gdr)
   657  }
   658  
   659  func (p *Politeiawww) handleGetDCCs(w http.ResponseWriter, r *http.Request) {
   660  	log.Tracef("handleGetDCCs")
   661  
   662  	var gds cms.GetDCCs
   663  	decoder := json.NewDecoder(r.Body)
   664  	if err := decoder.Decode(&gds); err != nil {
   665  		RespondWithError(w, r, 0, "handleGetDCCs: unmarshal",
   666  			www.UserError{
   667  				ErrorCode: www.ErrorStatusInvalidInput,
   668  			})
   669  		return
   670  	}
   671  	_, err := p.sessions.GetSessionUser(w, r)
   672  	if err != nil {
   673  		RespondWithError(w, r, 0,
   674  			"handleGetDCCs: getSessionUser %v", err)
   675  		return
   676  	}
   677  
   678  	gdsr, err := p.processGetDCCs(gds)
   679  	if err != nil {
   680  		RespondWithError(w, r, 0,
   681  			"handleGetDCCs: processGetDCCs: %v", err)
   682  		return
   683  	}
   684  
   685  	util.RespondWithJSON(w, http.StatusOK, gdsr)
   686  }
   687  
   688  func (p *Politeiawww) handleSupportOpposeDCC(w http.ResponseWriter, r *http.Request) {
   689  	log.Tracef("handleSupportOpposeDCC")
   690  
   691  	var sd cms.SupportOpposeDCC
   692  	decoder := json.NewDecoder(r.Body)
   693  	if err := decoder.Decode(&sd); err != nil {
   694  		RespondWithError(w, r, 0, "handleSupportOpposeDCC: unmarshal",
   695  			www.UserError{
   696  				ErrorCode: www.ErrorStatusInvalidInput,
   697  			})
   698  		return
   699  	}
   700  	u, err := p.sessions.GetSessionUser(w, r)
   701  	if err != nil {
   702  		RespondWithError(w, r, 0,
   703  			"handleSupportOpposeDCC: getSessionUser %v", err)
   704  		return
   705  	}
   706  
   707  	sdr, err := p.processSupportOpposeDCC(r.Context(), sd, u)
   708  	if err != nil {
   709  		RespondWithError(w, r, 0,
   710  			"handleSupportOpposeDCC: processSupportOpposeDCC: %v", err)
   711  		return
   712  	}
   713  
   714  	util.RespondWithJSON(w, http.StatusOK, sdr)
   715  }
   716  
   717  // handleNewCommentDCC handles incomming comments for DCC.
   718  func (p *Politeiawww) handleNewCommentDCC(w http.ResponseWriter, r *http.Request) {
   719  	log.Tracef("handleNewCommentDCC")
   720  
   721  	var sc www.NewComment
   722  	decoder := json.NewDecoder(r.Body)
   723  	if err := decoder.Decode(&sc); err != nil {
   724  		RespondWithError(w, r, 0, "handleNewCommentDCC: unmarshal",
   725  			www.UserError{
   726  				ErrorCode: www.ErrorStatusInvalidInput,
   727  			})
   728  		return
   729  	}
   730  
   731  	user, err := p.sessions.GetSessionUser(w, r)
   732  	if err != nil {
   733  		RespondWithError(w, r, 0,
   734  			"handleNewCommentDCC: getSessionUser %v", err)
   735  		return
   736  	}
   737  
   738  	cr, err := p.processNewCommentDCC(r.Context(), sc, user)
   739  	if err != nil {
   740  		RespondWithError(w, r, 0,
   741  			"handleNewCommentDCC: processNewCommentDCC: %v", err)
   742  		return
   743  	}
   744  
   745  	util.RespondWithJSON(w, http.StatusOK, cr)
   746  }
   747  
   748  // handleDCCComments handles batched comments get.
   749  func (p *Politeiawww) handleDCCComments(w http.ResponseWriter, r *http.Request) {
   750  	log.Tracef("handleDCCComments")
   751  
   752  	pathParams := mux.Vars(r)
   753  	token := pathParams["token"]
   754  
   755  	user, err := p.sessions.GetSessionUser(w, r)
   756  	if err != nil {
   757  		if !errors.Is(err, sessions.ErrSessionNotFound) {
   758  			RespondWithError(w, r, 0,
   759  				"handleDCCComments: getSessionUser %v", err)
   760  			return
   761  		}
   762  	}
   763  	gcr, err := p.processDCCComments(r.Context(), token, user)
   764  	if err != nil {
   765  		RespondWithError(w, r, 0,
   766  			"handleDCCComments: processDCCComments %v", err)
   767  		return
   768  	}
   769  	util.RespondWithJSON(w, http.StatusOK, gcr)
   770  }
   771  
   772  func (p *Politeiawww) handleSetDCCStatus(w http.ResponseWriter, r *http.Request) {
   773  	log.Tracef("handleSetDCCStatus")
   774  
   775  	var ad cms.SetDCCStatus
   776  	decoder := json.NewDecoder(r.Body)
   777  	if err := decoder.Decode(&ad); err != nil {
   778  		RespondWithError(w, r, 0, "handleSetDCCStatus: unmarshal",
   779  			www.UserError{
   780  				ErrorCode: www.ErrorStatusInvalidInput,
   781  			})
   782  		return
   783  	}
   784  	u, err := p.sessions.GetSessionUser(w, r)
   785  	if err != nil {
   786  		RespondWithError(w, r, 0,
   787  			"handleSetDCCStatus: getSessionUser %v", err)
   788  		return
   789  	}
   790  
   791  	adr, err := p.processSetDCCStatus(r.Context(), ad, u)
   792  	if err != nil {
   793  		RespondWithError(w, r, 0,
   794  			"handleSetDCCStatus: processSetDCCStatus: %v", err)
   795  		return
   796  	}
   797  
   798  	util.RespondWithJSON(w, http.StatusOK, adr)
   799  }
   800  
   801  func (p *Politeiawww) handleUserSubContractors(w http.ResponseWriter, r *http.Request) {
   802  	log.Tracef("handleUserSubContractors")
   803  
   804  	u, err := p.sessions.GetSessionUser(w, r)
   805  	if err != nil {
   806  		RespondWithError(w, r, 0,
   807  			"handleUserSubContractors: getSessionUser %v", err)
   808  		return
   809  	}
   810  
   811  	uscr, err := p.processUserSubContractors(u)
   812  	if err != nil {
   813  		RespondWithError(w, r, 0,
   814  			"handleUserSubContractors: processUserSubContractors: %v", err)
   815  		return
   816  	}
   817  
   818  	util.RespondWithJSON(w, http.StatusOK, uscr)
   819  }
   820  
   821  func (p *Politeiawww) handleProposalOwner(w http.ResponseWriter, r *http.Request) {
   822  	log.Tracef("handleProposalOwner")
   823  
   824  	var po cms.ProposalOwner
   825  	err := util.ParseGetParams(r, &po)
   826  	if err != nil {
   827  		RespondWithError(w, r, 0, "handleProposalOwner: ParseGetParams",
   828  			www.UserError{
   829  				ErrorCode: www.ErrorStatusInvalidInput,
   830  			})
   831  		return
   832  	}
   833  	por, err := p.processProposalOwner(po)
   834  	if err != nil {
   835  		RespondWithError(w, r, 0,
   836  			"handleProposalOwner: processProposalOwner: %v", err)
   837  		return
   838  	}
   839  
   840  	util.RespondWithJSON(w, http.StatusOK, por)
   841  }
   842  
   843  func (p *Politeiawww) handleProposalBilling(w http.ResponseWriter, r *http.Request) {
   844  	log.Tracef("handleProposalBilling")
   845  
   846  	var pb cms.ProposalBilling
   847  	decoder := json.NewDecoder(r.Body)
   848  	if err := decoder.Decode(&pb); err != nil {
   849  		RespondWithError(w, r, 0, "handleProposalBilling: unmarshal",
   850  			www.UserError{
   851  				ErrorCode: www.ErrorStatusInvalidInput,
   852  			})
   853  		return
   854  	}
   855  
   856  	u, err := p.sessions.GetSessionUser(w, r)
   857  	if err != nil {
   858  		RespondWithError(w, r, 0,
   859  			"handleProposalBilling: getSessionUser %v", err)
   860  		return
   861  	}
   862  
   863  	pbr, err := p.processProposalBilling(pb, u)
   864  	if err != nil {
   865  		RespondWithError(w, r, 0,
   866  			"handleProposalBilling: processProposalBilling: %v", err)
   867  		return
   868  	}
   869  
   870  	util.RespondWithJSON(w, http.StatusOK, pbr)
   871  }
   872  
   873  func (p *Politeiawww) handleCastVoteDCC(w http.ResponseWriter, r *http.Request) {
   874  	log.Tracef("handleCastVoteDCC")
   875  
   876  	var cv cms.CastVote
   877  	decoder := json.NewDecoder(r.Body)
   878  	if err := decoder.Decode(&cv); err != nil {
   879  		RespondWithError(w, r, 0, "handleCastVoteDCC: unmarshal", www.UserError{
   880  			ErrorCode: www.ErrorStatusInvalidInput,
   881  		})
   882  		return
   883  	}
   884  
   885  	u, err := p.sessions.GetSessionUser(w, r)
   886  	if err != nil {
   887  		RespondWithError(w, r, 0,
   888  			"handleCastVoteDCC: getSessionUser %v", err)
   889  		return
   890  	}
   891  
   892  	cvr, err := p.processCastVoteDCC(r.Context(), cv, u)
   893  	if err != nil {
   894  		RespondWithError(w, r, 0,
   895  			"handleCastVoteDCC: processCastVoteDCC: %v", err)
   896  		return
   897  	}
   898  
   899  	util.RespondWithJSON(w, http.StatusOK, cvr)
   900  }
   901  
   902  func (p *Politeiawww) handleVoteDetailsDCC(w http.ResponseWriter, r *http.Request) {
   903  	log.Tracef("handleVoteDetailsDCC")
   904  
   905  	var vd cms.VoteDetails
   906  	decoder := json.NewDecoder(r.Body)
   907  	if err := decoder.Decode(&vd); err != nil {
   908  		RespondWithError(w, r, 0, "handleVoteDetailsDCC: unmarshal", www.UserError{
   909  			ErrorCode: www.ErrorStatusInvalidInput,
   910  		})
   911  		return
   912  	}
   913  
   914  	vdr, err := p.processVoteDetailsDCC(r.Context(), vd.Token)
   915  	if err != nil {
   916  		RespondWithError(w, r, 0,
   917  			"handleVoteDetailsDCC: processVoteDetailsDCC: %v", err)
   918  		return
   919  	}
   920  
   921  	util.RespondWithJSON(w, http.StatusOK, vdr)
   922  }
   923  
   924  // handleActiveVoteDCC returns all active dccs that have an active vote.
   925  func (p *Politeiawww) handleActiveVoteDCC(w http.ResponseWriter, r *http.Request) {
   926  	log.Tracef("handleActiveVoteDCC")
   927  
   928  	avr, err := p.processActiveVoteDCC(r.Context())
   929  	if err != nil {
   930  		RespondWithError(w, r, 0,
   931  			"handleActiveVoteDCC: processActiveVoteDCC %v", err)
   932  		return
   933  	}
   934  
   935  	util.RespondWithJSON(w, http.StatusOK, avr)
   936  }
   937  
   938  // handleStartVoteDCC handles the dcc StartVote route.
   939  func (p *Politeiawww) handleStartVoteDCC(w http.ResponseWriter, r *http.Request) {
   940  	log.Tracef("handleStartVoteDCC")
   941  
   942  	var sv cms.StartVote
   943  	decoder := json.NewDecoder(r.Body)
   944  	if err := decoder.Decode(&sv); err != nil {
   945  		RespondWithError(w, r, 0, "handleStartVoteDCC: unmarshal",
   946  			www.UserError{
   947  				ErrorCode: www.ErrorStatusInvalidInput,
   948  			})
   949  		return
   950  	}
   951  	user, err := p.sessions.GetSessionUser(w, r)
   952  	if err != nil {
   953  		RespondWithError(w, r, 0,
   954  			"handleStartVoteDCC: getSessionUser %v", err)
   955  		return
   956  	}
   957  
   958  	// Sanity
   959  	if !user.Admin {
   960  		RespondWithError(w, r, 0,
   961  			"handleStartVoteDCC: admin %v", user.Admin)
   962  		return
   963  	}
   964  
   965  	svr, err := p.processStartVoteDCC(r.Context(), sv, user)
   966  	if err != nil {
   967  		RespondWithError(w, r, 0,
   968  			"handleStartVoteDCC: processStartVoteDCC %v", err)
   969  		return
   970  	}
   971  
   972  	util.RespondWithJSON(w, http.StatusOK, svr)
   973  }
   974  
   975  func (p *Politeiawww) handlePassThroughTokenInventory(w http.ResponseWriter, r *http.Request) {
   976  	log.Tracef("handlePassThroughTokenInventory")
   977  
   978  	data, err := p.makeProposalsRequest(http.MethodGet, www.RouteTokenInventory, nil)
   979  	if err != nil {
   980  		RespondWithError(w, r, 0,
   981  			"handlePassThroughTokenInventory: makeProposalsRequest: %v", err)
   982  		return
   983  	}
   984  	util.RespondRaw(w, http.StatusOK, data)
   985  }
   986  
   987  func (p *Politeiawww) handlePassThroughBatchProposals(w http.ResponseWriter, r *http.Request) {
   988  	log.Tracef("handlePassThroughBatchProposals")
   989  
   990  	var bp www.BatchProposals
   991  	decoder := json.NewDecoder(r.Body)
   992  	if err := decoder.Decode(&bp); err != nil {
   993  		RespondWithError(w, r, 0, "handlePassThroughBatchProposals: unmarshal",
   994  			www.UserError{
   995  				ErrorCode: www.ErrorStatusInvalidInput,
   996  			})
   997  		return
   998  	}
   999  
  1000  	data, err := p.makeProposalsRequest(http.MethodPost, www.RouteBatchProposals, bp)
  1001  	if err != nil {
  1002  		RespondWithError(w, r, 0,
  1003  			"handlePassThroughBatchProposals: makeProposalsRequest: %v", err)
  1004  		return
  1005  	}
  1006  	util.RespondRaw(w, http.StatusOK, data)
  1007  }
  1008  
  1009  func (p *Politeiawww) handleProposalBillingSummary(w http.ResponseWriter, r *http.Request) {
  1010  	log.Tracef("handleProposalBillingSummary")
  1011  
  1012  	var pbs cms.ProposalBillingSummary
  1013  	// get version from query string parameters
  1014  	err := util.ParseGetParams(r, &pbs)
  1015  	if err != nil {
  1016  		RespondWithError(w, r, 0, "handleProposalBillingSummary: ParseGetParams",
  1017  			www.UserError{
  1018  				ErrorCode: www.ErrorStatusInvalidInput,
  1019  			})
  1020  		return
  1021  	}
  1022  
  1023  	pbsr, err := p.processProposalBillingSummary(pbs)
  1024  	if err != nil {
  1025  		RespondWithError(w, r, 0,
  1026  			"handleProposalBillingSummary: processProposalBillingSummary %v", err)
  1027  		return
  1028  	}
  1029  
  1030  	util.RespondWithJSON(w, http.StatusOK, pbsr)
  1031  }
  1032  
  1033  func (p *Politeiawww) handleProposalBillingDetails(w http.ResponseWriter, r *http.Request) {
  1034  	log.Tracef("handleProposalBillingDetails")
  1035  
  1036  	var pbd cms.ProposalBillingDetails
  1037  	decoder := json.NewDecoder(r.Body)
  1038  	if err := decoder.Decode(&pbd); err != nil {
  1039  		RespondWithError(w, r, 0, "handleProposalBillingDetails: unmarshal",
  1040  			www.UserError{
  1041  				ErrorCode: www.ErrorStatusInvalidInput,
  1042  			})
  1043  		return
  1044  	}
  1045  
  1046  	svr, err := p.processProposalBillingDetails(pbd)
  1047  	if err != nil {
  1048  		RespondWithError(w, r, 0,
  1049  			"handleProposalBillingDetails: processProposalBillingDetails %v", err)
  1050  		return
  1051  	}
  1052  
  1053  	util.RespondWithJSON(w, http.StatusOK, svr)
  1054  }
  1055  
  1056  // makeProposalsRequest submits pass through requests to the proposals sites
  1057  // (testnet or mainnet).  It takes a http method type, proposals route and a
  1058  // request interface as arguments.  It returns the response body as byte array
  1059  // (which can then be decoded as though a response directly from proposals).
  1060  func (p *Politeiawww) makeProposalsRequest(method string, route string, v interface{}) ([]byte, error) {
  1061  	var (
  1062  		requestBody  []byte
  1063  		responseBody []byte
  1064  		err          error
  1065  	)
  1066  	if v != nil {
  1067  		requestBody, err = json.Marshal(v)
  1068  		if err != nil {
  1069  			return nil, err
  1070  		}
  1071  	}
  1072  
  1073  	client, err := util.NewHTTPClient(false, "")
  1074  	if err != nil {
  1075  		return nil, err
  1076  	}
  1077  
  1078  	dest := cms.ProposalsMainnet
  1079  	if p.cfg.TestNet {
  1080  		dest = cms.ProposalsTestnet
  1081  	}
  1082  
  1083  	route = dest + "/api/v1" + route
  1084  
  1085  	req, err := http.NewRequest(method, route,
  1086  		bytes.NewReader(requestBody))
  1087  	if err != nil {
  1088  		return nil, err
  1089  	}
  1090  
  1091  	r, err := client.Do(req)
  1092  	if err != nil {
  1093  		return nil, err
  1094  	}
  1095  	defer r.Body.Close()
  1096  
  1097  	if r.StatusCode != http.StatusOK {
  1098  		return nil, www.UserError{
  1099  			ErrorCode: www.ErrorStatusT(r.StatusCode),
  1100  		}
  1101  	}
  1102  
  1103  	responseBody = util.ConvertBodyToByteArray(r.Body, false)
  1104  	return responseBody, nil
  1105  }
  1106  
  1107  func (p *Politeiawww) handleUserCodeStats(w http.ResponseWriter, r *http.Request) {
  1108  	log.Tracef("handleUserCodeStats")
  1109  
  1110  	var ucs cms.UserCodeStats
  1111  	decoder := json.NewDecoder(r.Body)
  1112  	if err := decoder.Decode(&ucs); err != nil {
  1113  		RespondWithError(w, r, 0, "handleUserCodeStats: unmarshal",
  1114  			www.UserError{
  1115  				ErrorCode: www.ErrorStatusInvalidInput,
  1116  			})
  1117  		return
  1118  	}
  1119  
  1120  	u, err := p.sessions.GetSessionUser(w, r)
  1121  	if err != nil {
  1122  		RespondWithError(w, r, 0,
  1123  			"handleUserCodeStats: getSessionUser %v", err)
  1124  		return
  1125  	}
  1126  
  1127  	uscr, err := p.processUserCodeStats(ucs, u)
  1128  	if err != nil {
  1129  		RespondWithError(w, r, 0,
  1130  			"handleUserCodeStats: processUserCodeStats: %v", err)
  1131  		return
  1132  	}
  1133  
  1134  	util.RespondWithJSON(w, http.StatusOK, uscr)
  1135  }