github.com/haagen/force@v0.19.6-0.20140911230915-22addd930b34/force.go (about)

     1  package main
     2  
     3  import (
     4  	"bytes"
     5  	"crypto/tls"
     6  	"crypto/x509"
     7  	"encoding/json"
     8  	"encoding/pem"
     9  	"encoding/xml"
    10  	"errors"
    11  	"fmt"
    12  	"io"
    13  	"io/ioutil"
    14  	"net"
    15  	"net/http"
    16  	"net/url"
    17  	"runtime"
    18  	"strings"
    19  )
    20  
    21  const (
    22  	ProductionClientId = "3MVG9A2kN3Bn17huXZp1OQhPe8y4_ozAQZZCKxsWbef9GjSnHGOunHSwhnY1BWz_5vHkTL9BeLMriIX5EUKaw"
    23  	PrereleaseClientId = "3MVG9lKcPoNINVBIRgC7lsz5tIhlg0mtoEqkA9ZjDAwEMbBy43gsnfkzzdTdhFLeNnWS8M4bnRnVv1Qj0k9MD"
    24  	Mobile1ClientId    = "3MVG9Iu66FKeHhIPqCB9VWfYPxjfcb5Ube.v5L81BLhnJtDYVP2nkA.mDPwfm5FTLbvL6aMftfi8w0rL7Dv7f"
    25  	RedirectUri        = "https://force-cli.herokuapp.com/auth/callback"
    26  )
    27  
    28  var CustomEndpoint = ``
    29  
    30  const (
    31  	EndpointProduction = iota
    32  	EndpointTest       = iota
    33  	EndpointPrerelease = iota
    34  	EndpointMobile1    = iota
    35  	EndpointCustom     = iota
    36  )
    37  
    38  const (
    39  	apiVersion       = "v31.0"
    40  	apiVersionNumber = "31.0"
    41  )
    42  
    43  var RootCertificates = `
    44  -----BEGIN CERTIFICATE-----
    45  MIICPDCCAaUCEHC65B0Q2Sk0tjjKewPMur8wDQYJKoZIhvcNAQECBQAwXzELMAkG
    46  A1UEBhMCVVMxFzAVBgNVBAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFz
    47  cyAzIFB1YmxpYyBQcmltYXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MB4XDTk2
    48  MDEyOTAwMDAwMFoXDTI4MDgwMTIzNTk1OVowXzELMAkGA1UEBhMCVVMxFzAVBgNV
    49  BAoTDlZlcmlTaWduLCBJbmMuMTcwNQYDVQQLEy5DbGFzcyAzIFB1YmxpYyBQcmlt
    50  YXJ5IENlcnRpZmljYXRpb24gQXV0aG9yaXR5MIGfMA0GCSqGSIb3DQEBAQUAA4GN
    51  ADCBiQKBgQDJXFme8huKARS0EN8EQNvjV69qRUCPhAwL0TPZ2RHP7gJYHyX3KqhE
    52  BarsAx94f56TuZoAqiN91qyFomNFx3InzPRMxnVx0jnvT0Lwdd8KkMaOIG+YD/is
    53  I19wKTakyYbnsZogy1Olhec9vn2a/iRFM9x2Fe0PonFkTGUugWhFpwIDAQABMA0G
    54  CSqGSIb3DQEBAgUAA4GBALtMEivPLCYATxQT3ab7/AoRhIzzKBxnki98tsX63/Do
    55  lbwdj2wsqFHMc9ikwFPwTtYmwHYBV4GSXiHx0bH/59AhWM1pF+NEHJwZRDmJXNyc
    56  AA9WjQKZ7aKQRUzkuxCkPfAyAw7xzvjoyVGM5mKf5p/AfbdynMk2OmufTqj/ZA1k
    57  -----END CERTIFICATE-----
    58  -----BEGIN CERTIFICATE-----
    59  MIIDxTCCAq2gAwIBAgIQAqxcJmoLQJuPC3nyrkYldzANBgkqhkiG9w0BAQUFADBs
    60  MQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3
    61  d3cuZGlnaWNlcnQuY29tMSswKQYDVQQDEyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5j
    62  ZSBFViBSb290IENBMB4XDTA2MTExMDAwMDAwMFoXDTMxMTExMDAwMDAwMFowbDEL
    63  MAkGA1UEBhMCVVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3
    64  LmRpZ2ljZXJ0LmNvbTErMCkGA1UEAxMiRGlnaUNlcnQgSGlnaCBBc3N1cmFuY2Ug
    65  RVYgUm9vdCBDQTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMbM5XPm
    66  +9S75S0tMqbf5YE/yc0lSbZxKsPVlDRnogocsF9ppkCxxLeyj9CYpKlBWTrT3JTW
    67  PNt0OKRKzE0lgvdKpVMSOO7zSW1xkX5jtqumX8OkhPhPYlG++MXs2ziS4wblCJEM
    68  xChBVfvLWokVfnHoNb9Ncgk9vjo4UFt3MRuNs8ckRZqnrG0AFFoEt7oT61EKmEFB
    69  Ik5lYYeBQVCmeVyJ3hlKV9Uu5l0cUyx+mM0aBhakaHPQNAQTXKFx01p8VdteZOE3
    70  hzBWBOURtCmAEvF5OYiiAhF8J2a3iLd48soKqDirCmTCv2ZdlYTBoSUeh10aUAsg
    71  EsxBu24LUTi4S8sCAwEAAaNjMGEwDgYDVR0PAQH/BAQDAgGGMA8GA1UdEwEB/wQF
    72  MAMBAf8wHQYDVR0OBBYEFLE+w2kD+L9HAdSYJhoIAu9jZCvDMB8GA1UdIwQYMBaA
    73  FLE+w2kD+L9HAdSYJhoIAu9jZCvDMA0GCSqGSIb3DQEBBQUAA4IBAQAcGgaX3Nec
    74  nzyIZgYIVyHbIUf4KmeqvxgydkAQV8GK83rZEWWONfqe/EW1ntlMMUu4kehDLI6z
    75  eM7b41N5cdblIZQB2lWHmiRk9opmzN6cN82oNLFpmyPInngiK3BD41VHMWEZ71jF
    76  hS9OMPagMRYjyOfiZRYzy78aG6A9+MpeizGLYAiJLQwGXFK3xPkKmNEVX58Svnw2
    77  Yzi9RKR/5CYrCsSXaQ3pjOLAEFe4yHYSkVXySGnYvCoCWw9E1CAx2/S6cCZdkGCe
    78  vEsXCS+0yx5DaMkHJ8HSXPfqIbloEpw8nL+e/IBcm2PN7EeqJSdnoDfzAIJ9VNep
    79  +OkuE6N36B9K
    80  -----END CERTIFICATE-----
    81  -----BEGIN CERTIFICATE-----
    82  MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ
    83  RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
    84  VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX
    85  DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y
    86  ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy
    87  VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr
    88  mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr
    89  IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK
    90  mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu
    91  XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy
    92  dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye
    93  jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1
    94  BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3
    95  DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92
    96  9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx
    97  jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0
    98  Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz
    99  ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS
   100  R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
   101  -----END CERTIFICATE-----
   102  -----BEGIN CERTIFICATE-----
   103  MIIDdzCCAl+gAwIBAgIEAgAAuTANBgkqhkiG9w0BAQUFADBaMQswCQYDVQQGEwJJ
   104  RTESMBAGA1UEChMJQmFsdGltb3JlMRMwEQYDVQQLEwpDeWJlclRydXN0MSIwIAYD
   105  VQQDExlCYWx0aW1vcmUgQ3liZXJUcnVzdCBSb290MB4XDTAwMDUxMjE4NDYwMFoX
   106  DTI1MDUxMjIzNTkwMFowWjELMAkGA1UEBhMCSUUxEjAQBgNVBAoTCUJhbHRpbW9y
   107  ZTETMBEGA1UECxMKQ3liZXJUcnVzdDEiMCAGA1UEAxMZQmFsdGltb3JlIEN5YmVy
   108  VHJ1c3QgUm9vdDCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKMEuyKr
   109  mD1X6CZymrV51Cni4eiVgLGw41uOKymaZN+hXe2wCQVt2yguzmKiYv60iNoS6zjr
   110  IZ3AQSsBUnuId9Mcj8e6uYi1agnnc+gRQKfRzMpijS3ljwumUNKoUMMo6vWrJYeK
   111  mpYcqWe4PwzV9/lSEy/CG9VwcPCPwBLKBsua4dnKM3p31vjsufFoREJIE9LAwqSu
   112  XmD+tqYF/LTdB1kC1FkYmGP1pWPgkAx9XbIGevOF6uvUA65ehD5f/xXtabz5OTZy
   113  dc93Uk3zyZAsuT3lySNTPx8kmCFcB5kpvcY67Oduhjprl3RjM71oGDHweI12v/ye
   114  jl0qhqdNkNwnGjkCAwEAAaNFMEMwHQYDVR0OBBYEFOWdWTCCR1jMrPoIVDaGezq1
   115  BE3wMBIGA1UdEwEB/wQIMAYBAf8CAQMwDgYDVR0PAQH/BAQDAgEGMA0GCSqGSIb3
   116  DQEBBQUAA4IBAQCFDF2O5G9RaEIFoN27TyclhAO992T9Ldcw46QQF+vaKSm2eT92
   117  9hkTI7gQCvlYpNRhcL0EYWoSihfVCr3FvDB81ukMJY2GQE/szKN+OMY3EU/t3Wgx
   118  jkzSswF07r51XgdIGn9w/xZchMB5hbgF/X++ZRGjD8ACtPhSNzkE1akxehi/oCr0
   119  Epn3o0WC4zxe9Z2etciefC7IpJ5OCBRLbf1wbWsaY71k5h+3zvDyny67G7fyUIhz
   120  ksLi4xaNmjICq44Y3ekQEe5+NauQrz4wlHrQMz2nZQ/1/I6eYs9HRCwBXbsdtTLS
   121  R9I4LtD+gdwyah617jzV/OeBHRnDJELqYzmp
   122  -----END CERTIFICATE-----
   123  -----BEGIN CERTIFICATE-----
   124  MIIEeDCCA2CgAwIBAgIOAQAAAAABNwQKT8CN490wDQYJKoZIhvcNAQEFBQAwUDEX
   125  MBUGA1UEChMOQ3liZXJ0cnVzdCBJbmMxNTAzBgNVBAMTLEN5YmVydHJ1c3QgU3Vy
   126  ZVNlcnZlciBTdGFuZGFyZCBWYWxpZGF0aW9uIENBMB4XDTEyMDQzMDE2MzI0NloX
   127  DTE1MDQzMDE2MzI0NlowgbExCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpDYWxpZm9y
   128  bmlhMRYwFAYDVQQHEw1TYW4gRnJhbmNpc2NvMRswGQYDVQQKExJTYWxlc2ZvcmNl
   129  LmNvbSBJbmMxFTATBgNVBAsTDEFwcGxpY2F0aW9uczEhMB8GCSqGSIb3DQEJARYS
   130  bm9jQHNhbGVzZm9yY2UuY29tMR4wHAYDVQQDDBUqLnNvbWEuc2FsZXNmb3JjZS5j
   131  b20wggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDHo8bILux1ZYwD1JTW
   132  PBE6LTV0hAdILIv06++N0RkhYU0ry69/yAFKWM5SUqt19dk9H3x43uC50tFRUT/l
   133  UoP/ztjT8Z47UczjVXSPrRCa/HloiA9zobLNFrsES/atCLHjzoxBLth477iZNnFs
   134  sINW8Kz6+v+7G83zzrMs6J4eYzZauNlhvCHBjwotPqtbJEp6MESoEO0XcNJkVLXA
   135  2sysfOpZH89P8j+1AMByc/32aauAZqwfmTD1iyGHyguieFdySWMDYL3r3j+uhey8
   136  XjxMO5AYRRh6EB4UQ5IlsjyzoAeJg5+q7+dJRhZ3KVr9KJ74UkRLab4NiUFRbXjj
   137  TnqjAgMBAAGjge0wgeowHwYDVR0jBBgwFoAUzTqWn65uD0BcHEj4Sy24cQHridow
   138  OQYDVR0fBDIwMDAuoCygKoYoaHR0cDovL2NybC5vbW5pcm9vdC5jb20vU3VyZVNl
   139  cnZlckcyLmNybDAdBgNVHQ4EFgQUVKhO+ZroJ0ZNcV80wap72/eTqGswCQYDVR0T
   140  BAIwADAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwEGCCsGAQUF
   141  BwMCMBEGCWCGSAGG+EIBAQQEAwIGwDAgBgNVHREEGTAXghUqLnNvbWEuc2FsZXNm
   142  b3JjZS5jb20wDQYJKoZIhvcNAQEFBQADggEBACiMRXtltlPjDdzuTG6B8F6c0AOE
   143  nJl5T4Lz4BMc5jvyin3zR1uPrZC7H/VEc6MOzXQK+n1i9xfNGURjTtfpCOdbcmZ9
   144  MkkRbu8EJyoO2FM84BdVtCOs5nomE/Py9xqX4mdy38yhjnJywvFa+M4rGDNcVR4W
   145  ZOV5H9LlMuEjuVuWYRLSRwu6Uk+QVN/tL9ImiWM1p4cziuizWXtjPqLyaQmOvykY
   146  4ihtSnZuel7PqGhBMoFHbuw11CB3S3ap2hzfreeJcYT/019Y5p8DPuFh6BJ3Q85J
   147  oo54Un5pgx/wX8L1UaMLMLUSv9d+nuKKLYYg+MW+1+LNNkLP704/Y/GWPvE=
   148  -----END CERTIFICATE-----`
   149  
   150  type Force struct {
   151  	Credentials ForceCredentials
   152  	Metadata    *ForceMetadata
   153  	Partner     *ForcePartner
   154  }
   155  
   156  type ForceCredentials struct {
   157  	AccessToken   string
   158  	Id            string
   159  	InstanceUrl   string
   160  	IssuedAt      string
   161  	Scope         string
   162  	IsCustomEP    bool
   163  	Namespace     string
   164  	ForceEndpoint ForceEndpoint
   165  }
   166  
   167  type LoginFault struct {
   168  	ExceptionCode    string `xml:"exceptionCode"`
   169  	ExceptionMessage string `xml:"exceptionMessage"`
   170  }
   171  
   172  type SoapFault struct {
   173  	FaultCode   string     `xml:"Body>Fault>faultcode"`
   174  	FaultString string     `xml:"Body>Fault>faultstring"`
   175  	Detail      LoginFault `xml:"Body>Fault>detail>LoginFault"`
   176  }
   177  
   178  type GenericForceError struct {
   179  	Error_Description string
   180  	Error             string
   181  }
   182  
   183  type ForceError struct {
   184  	Message   string
   185  	ErrorCode string
   186  }
   187  
   188  type ForceEndpoint int
   189  
   190  type ForceRecord map[string]interface{}
   191  
   192  type ForceSobject map[string]interface{}
   193  
   194  type ForceCreateRecordResult struct {
   195  	Errors  []string
   196  	Id      string
   197  	Success bool
   198  }
   199  
   200  type ForcePasswordStatusResult struct {
   201  	IsExpired bool
   202  }
   203  
   204  type ForcePasswordResetResult struct {
   205  	NewPassword string
   206  }
   207  
   208  type ForceQueryResult struct {
   209  	Done      bool
   210  	Records   []ForceRecord
   211  	TotalSize int
   212  }
   213  
   214  type ForceSobjectsResult struct {
   215  	Encoding     string
   216  	MaxBatchSize int
   217  	Sobjects     []ForceSobject
   218  }
   219  
   220  type Result struct {
   221  	Id      string
   222  	Success bool
   223  	Created bool
   224  	Message string
   225  }
   226  
   227  type BatchResult struct {
   228  	Results []Result
   229  }
   230  
   231  type BatchInfo struct {
   232  	Id                     string `xml:"id"`
   233  	JobId                  string `xml:"jobId"`
   234  	State                  string `xml:"state"`
   235  	CreatedDate            string `xml:"createdDate"`
   236  	SystemModstamp         string `xml:"systemModstamp"`
   237  	NumberRecordsProcessed int    `xml:"numberRecordsProcessed"`
   238  }
   239  
   240  type JobInfo struct {
   241  	Id                      string `xml:"id"`
   242  	Operation               string `xml:"operation"`
   243  	Object                  string `xml:"object"`
   244  	CreatedById             string `xml:"createdById"`
   245  	CreatedDate             string `xml:"createdDate"`
   246  	SystemModStamp          string `xml:"systemModstamp"`
   247  	State                   string `xml:"state"`
   248  	ContentType             string `xml:"contentType"`
   249  	ConcurrencyMode         string `xml:"concurrencyMode"`
   250  	NumberBatchesQueued     int    `xml:"numberBatchesQueued"`
   251  	NumberBatchesInProgress int    `xml:"numberBatchesInProgress"`
   252  	NumberBatchesCompleted  int    `xml:"numberBatchesCompleted"`
   253  	NumberBatchesFailed     int    `xml:"numberBatchesFailed"`
   254  	NumberBatchesTotal      int    `xml:"numberBatchesTotal"`
   255  	NumberRecordsProcessed  int    `xml:"numberRecordsProcessed"`
   256  	NumberRetries           int    `xml:"numberRetries"`
   257  	ApiVersion              string `xml:"apiVersion"`
   258  	NumberRecordsFailed     int    `xml:"numberRecordsFailed"`
   259  	TotalProcessingTime     int    `xml:"totalProcessingTime"`
   260  	ApiActiveProcessingTime int    `xml:"apiActiveProcessingTime"`
   261  	ApexProcessingTime      int    `xml:"apexProcessingTime"`
   262  }
   263  
   264  type AuraDefinitionBundleResult struct {
   265  	Done           bool
   266  	Records        []ForceRecord
   267  	TotalSize      int
   268  	QueryLocator   string
   269  	Size           int
   270  	EntityTypeName string
   271  }
   272  
   273  type AuraDefinitionBundle struct {
   274  	Id               string
   275  	IsDeleted        bool
   276  	DeveloperName    string
   277  	Language         string
   278  	MasterLabel      string
   279  	NamespacePrefix  string
   280  	CreatedDate      string
   281  	CreatedById      string
   282  	LastModifiedDate string
   283  	LastModifiedById string
   284  	SystemModstamp   string
   285  	ApiVersion       int
   286  	Description      string
   287  }
   288  
   289  type AuraDefinition struct {
   290  	Id                     string
   291  	IsDeleted              bool
   292  	CreatedDate            string
   293  	CreatedById            string
   294  	LastModifiedDate       string
   295  	LastModifiedById       string
   296  	SystemModstamp         string
   297  	AuraDefinitionBundleId string
   298  	DefType                string
   299  	Format                 string
   300  	Source                 string
   301  }
   302  
   303  type ComponentFile struct {
   304  	FileName    string
   305  	ComponentId string
   306  }
   307  
   308  type BundleManifest struct {
   309  	Name  string
   310  	Id    string
   311  	Files []ComponentFile
   312  }
   313  
   314  func NewForce(creds ForceCredentials) (force *Force) {
   315  	force = new(Force)
   316  	force.Credentials = creds
   317  	force.Metadata = NewForceMetadata(force)
   318  	force.Partner = NewForcePartner(force)
   319  	return
   320  }
   321  
   322  func ForceSoapLogin(endpoint ForceEndpoint, username string, password string) (creds ForceCredentials, err error) {
   323  	var surl string
   324  	version := strings.Split(apiVersion, "v")[1]
   325  	switch endpoint {
   326  	case EndpointProduction:
   327  		surl = fmt.Sprintf("https://login.salesforce.com/services/Soap/u/%s", version)
   328  	case EndpointTest:
   329  		surl = fmt.Sprintf("https://test.salesforce.com/services/Soap/u/%s", version)
   330  	case EndpointPrerelease:
   331  		surl = fmt.Sprintf("https://prerelna1.pre.salesforce.com/services/Soap/u/%s", version)
   332  	case EndpointCustom:
   333  		surl = fmt.Sprintf("%s/services/Soap/u/%s", CustomEndpoint, version)
   334  	default:
   335  		ErrorAndExit("no such endpoint type")
   336  	}
   337  
   338  	soap := NewSoap(surl, "", "")
   339  	response, err := soap.ExecuteLogin(username, password)
   340  	var result struct {
   341  		SessionId    string `xml:"Body>loginResponse>result>sessionId"`
   342  		Id           string `xml:"Body>loginResponse>result>userId"`
   343  		Instance_url string `xml:"Body>loginResponse>result>serverUrl"`
   344  	}
   345  	var fault SoapFault
   346  	if err = xml.Unmarshal(response, &fault); fault.Detail.ExceptionMessage != "" {
   347  		ErrorAndExit(fault.Detail.ExceptionCode + ": " + fault.Detail.ExceptionMessage)
   348  	}
   349  	if err = xml.Unmarshal(response, &result); err != nil {
   350  		return
   351  	}
   352  	orgid := strings.Split(result.SessionId, "!")[0]
   353  	u, err := url.Parse(result.Instance_url)
   354  	if err != nil {
   355  		return
   356  	}
   357  	instanceUrl := u.Scheme + "://" + u.Host
   358  	identity := u.Scheme + "://" + u.Host + "/id/" + orgid + "/" + result.Id
   359  	creds = ForceCredentials{result.SessionId, identity, instanceUrl, "", "", endpoint == EndpointCustom, "", endpoint}
   360  
   361  	return
   362  }
   363  
   364  func ForceLogin(endpoint ForceEndpoint) (creds ForceCredentials, err error) {
   365  	ch := make(chan ForceCredentials)
   366  	port, err := startLocalHttpServer(ch)
   367  	var url string
   368  	switch endpoint {
   369  	case EndpointProduction:
   370  		url = fmt.Sprintf("https://login.salesforce.com/services/oauth2/authorize?response_type=token&client_id=%s&redirect_uri=%s&state=%d&prompt=login", ProductionClientId, RedirectUri, port)
   371  	case EndpointTest:
   372  		url = fmt.Sprintf("https://test.salesforce.com/services/oauth2/authorize?response_type=token&client_id=%s&redirect_uri=%s&state=%d&prompt=login", ProductionClientId, RedirectUri, port)
   373  	case EndpointPrerelease:
   374  		url = fmt.Sprintf("https://prerellogin.pre.salesforce.com/services/oauth2/authorize?response_type=token&client_id=%s&redirect_uri=%s&state=%d&prompt=login", PrereleaseClientId, RedirectUri, port)
   375  	case EndpointMobile1:
   376  		url = fmt.Sprintf("https://EndpointMobile1.t.salesforce.com/services/oauth2/authorize?response_type=token&client_id=%s&redirect_uri=%s&state=%d&prompt=login", Mobile1ClientId, RedirectUri, port)
   377  	default:
   378  		ErrorAndExit("no such endpoint type")
   379  	}
   380  
   381  	err = Open(url)
   382  	creds = <-ch
   383  	creds.ForceEndpoint = endpoint
   384  	return
   385  }
   386  
   387  func (f *Force) GetCodeCoverage(classId string, className string) (err error) {
   388  	url := fmt.Sprintf("%s/services/data/%s/query/?q=Select+Id+From+ApexClass+Where+Name+=+'%s'", f.Credentials.InstanceUrl, apiVersion, className)
   389  
   390  	body, err := f.httpGet(url)
   391  	if err != nil {
   392  		return
   393  	}
   394  	var result ForceQueryResult
   395  	json.Unmarshal(body, &result)
   396  
   397  	classId = result.Records[0]["Id"].(string)
   398  	url = fmt.Sprintf("%s/services/data/%s/tooling/query/?q=Select+Coverage,+NumLinesCovered,+NumLinesUncovered,+ApexTestClassId,+ApexClassorTriggerId+From+ApexCodeCoverage+Where+ApexClassorTriggerId='%s'", f.Credentials.InstanceUrl, apiVersion, classId)
   399  
   400  	body, err = f.httpGet(url)
   401  	if err != nil {
   402  		return
   403  	}
   404  
   405  	//var result ForceSobjectsResult
   406  	json.Unmarshal(body, &result)
   407  	fmt.Printf("\n%d lines covered\n%d lines not covered\n", int(result.Records[0]["NumLinesCovered"].(float64)), int(result.Records[0]["NumLinesUncovered"].(float64)))
   408  	return
   409  }
   410  
   411  func (f *Force) GetAuraBundles() (bundles AuraDefinitionBundleResult, definitions AuraDefinitionBundleResult, err error) {
   412  	bundles, err = f.GetAuraBundlesList()
   413  	definitions, err = f.GetAuraBundleDefinitions()
   414  	return
   415  }
   416  
   417  func (f *Force) GetAuraBundleDefinitions() (definitions AuraDefinitionBundleResult, err error) {
   418  	aurl := fmt.Sprintf("%s/services/data/%s/tooling/query?q=%s", f.Credentials.InstanceUrl, apiVersion,
   419  		url.QueryEscape("SELECT Id, Source, AuraDefinitionBundleId, DefType, Format FROM AuraDefinition"))
   420  
   421  	body, err := f.httpGet(aurl)
   422  	if err != nil {
   423  		return
   424  	}
   425  	json.Unmarshal(body, &definitions)
   426  
   427  	return
   428  }
   429  
   430  func (f *Force) GetAuraBundlesList() (bundles AuraDefinitionBundleResult, err error) {
   431  	aurl := fmt.Sprintf("%s/services/data/%s/tooling/query?q=%s", f.Credentials.InstanceUrl, apiVersion,
   432  		url.QueryEscape("SELECT Id, DeveloperName, NamespacePrefix, ApiVersion, Description FROM AuraDefinitionBundle"))
   433  	body, err := f.httpGet(aurl)
   434  	if err != nil {
   435  		return
   436  	}
   437  	json.Unmarshal(body, &bundles)
   438  
   439  	return
   440  }
   441  
   442  func (f *Force) GetAuraBundle(bundleName string) (bundles AuraDefinitionBundleResult, definitions AuraDefinitionBundleResult, err error) {
   443  	bundles, err = f.GetAuraBundleByName(bundleName)
   444  	if len(bundles.Records) == 0 {
   445  		ErrorAndExit(fmt.Sprintf("There is no Aura bundle named %q", bundleName))
   446  	}
   447  	bundle := bundles.Records[0]
   448  	definitions, err = f.GetAuraBundleDefinition(bundle["Id"].(string))
   449  	return
   450  }
   451  
   452  func (f *Force) GetAuraBundleByName(bundleName string) (bundles AuraDefinitionBundleResult, err error) {
   453  	criteria := fmt.Sprintf(" Where DeveloperName = '%s'", bundleName)
   454  
   455  	aurl := fmt.Sprintf("%s/services/data/%s/tooling/query?q=%s", f.Credentials.InstanceUrl, apiVersion,
   456  		url.QueryEscape(fmt.Sprintf("SELECT Id, DeveloperName, NamespacePrefix, ApiVersion, Description FROM AuraDefinitionBundle%s", criteria)))
   457  
   458  	body, err := f.httpGet(aurl)
   459  	if err != nil {
   460  		return
   461  	}
   462  	json.Unmarshal(body, &bundles)
   463  
   464  	return
   465  }
   466  
   467  func (f *Force) GetAuraBundleDefinition(id string) (definitions AuraDefinitionBundleResult, err error) {
   468  	aurl := fmt.Sprintf("%s/services/data/%s/tooling/query?q=%s", f.Credentials.InstanceUrl, apiVersion,
   469  		url.QueryEscape("SELECT Id, Source, AuraDefinitionBundleId, DefType, Format FROM AuraDefinition"))
   470  
   471  	body, err := f.httpGet(aurl)
   472  	if err != nil {
   473  		return
   474  	}
   475  	json.Unmarshal(body, &definitions)
   476  
   477  	return
   478  }
   479  
   480  func (f *Force) CreateAuraBundle(bundleName string) (result ForceCreateRecordResult, err error) {
   481  	aurl := fmt.Sprintf("%s/services/data/%s/tooling/sobjects/AuraDefinitionBundle", f.Credentials.InstanceUrl, apiVersion)
   482  	attrs := make(map[string]string)
   483  	attrs["DeveloperName"] = bundleName
   484  	attrs["Description"] = "An Aura Bundle"
   485  	attrs["MasterLabel"] = bundleName
   486  	attrs["ApiVersion"] = apiVersionNumber
   487  	body, err := f.httpPost(aurl, attrs)
   488  	if err != nil {
   489  		return
   490  	}
   491  	json.Unmarshal(body, &result)
   492  
   493  	return
   494  }
   495  
   496  func (f *Force) CreateAuraComponent(attrs map[string]string) (result ForceCreateRecordResult, err error) {
   497  	aurl := fmt.Sprintf("%s/services/data/%s/tooling/sobjects/AuraDefinition", f.Credentials.InstanceUrl, apiVersion)
   498  	body, err := f.httpPost(aurl, attrs)
   499  	if err != nil {
   500  		fmt.Println("The error is: ", err.Error())
   501  		return
   502  	}
   503  	json.Unmarshal(body, &result)
   504  
   505  	return
   506  }
   507  
   508  func (f *Force) ListSobjects() (sobjects []ForceSobject, err error) {
   509  	url := fmt.Sprintf("%s/services/data/%s/sobjects", f.Credentials.InstanceUrl, apiVersion)
   510  	body, err := f.httpGet(url)
   511  	if err != nil {
   512  		return
   513  	}
   514  	var result ForceSobjectsResult
   515  	json.Unmarshal(body, &result)
   516  	sobjects = result.Sobjects
   517  	return
   518  }
   519  
   520  func (f *Force) GetSobject(name string) (sobject ForceSobject, err error) {
   521  	url := fmt.Sprintf("%s/services/data/%s/sobjects/%s/describe", f.Credentials.InstanceUrl, apiVersion, name)
   522  	body, err := f.httpGet(url)
   523  	if err != nil {
   524  		return
   525  	}
   526  	json.Unmarshal(body, &sobject)
   527  	return
   528  }
   529  
   530  func (f *Force) Query(query string) (result ForceQueryResult, err error) {
   531  	url := fmt.Sprintf("%s/services/data/%s/query?q=%s", f.Credentials.InstanceUrl, apiVersion, url.QueryEscape(query))
   532  	body, err := f.httpGet(url)
   533  	if err != nil {
   534  		return
   535  	}
   536  	json.Unmarshal(body, &result)
   537  	return
   538  }
   539  
   540  func (f *Force) Get(url string) (object ForceRecord, err error) {
   541  	body, err := f.httpGet(url)
   542  	if err != nil {
   543  		return
   544  	}
   545  	err = json.Unmarshal(body, &object)
   546  	return
   547  }
   548  
   549  func (f *Force) GetPasswordStatus(id string) (result ForcePasswordStatusResult, err error) {
   550  	url := fmt.Sprintf("%s/services/data/%s/sobjects/User/%s/password", f.Credentials.InstanceUrl, apiVersion, id)
   551  	body, err := f.httpGet(url)
   552  	if err != nil {
   553  		return
   554  	}
   555  	err = json.Unmarshal(body, &result)
   556  	return
   557  }
   558  
   559  func (f *Force) ResetPassword(id string) (result ForcePasswordResetResult, err error) {
   560  	url := fmt.Sprintf("%s/services/data/%s/sobjects/User/%s/password", f.Credentials.InstanceUrl, apiVersion, id)
   561  	body, err := f.httpDelete(url)
   562  	if err != nil {
   563  		return
   564  	}
   565  	err = json.Unmarshal(body, &result)
   566  	return
   567  }
   568  
   569  func (f *Force) ChangePassword(id string, attrs map[string]string) (result string, err error) {
   570  	url := fmt.Sprintf("%s/services/data/%s/sobjects/User/%s/password", f.Credentials.InstanceUrl, apiVersion, id)
   571  	_, err = f.httpPost(url, attrs)
   572  	return
   573  }
   574  
   575  func (f *Force) GetRecord(sobject, id string) (object ForceRecord, err error) {
   576  	url := fmt.Sprintf("%s/services/data/%s/sobjects/%s/%s", f.Credentials.InstanceUrl, apiVersion, sobject, id)
   577  	body, err := f.httpGet(url)
   578  	if err != nil {
   579  		return
   580  	}
   581  	err = json.Unmarshal(body, &object)
   582  	return
   583  }
   584  
   585  func (f *Force) CreateRecord(sobject string, attrs map[string]string) (id string, err error) {
   586  	url := fmt.Sprintf("%s/services/data/%s/sobjects/%s", f.Credentials.InstanceUrl, apiVersion, sobject)
   587  	body, err := f.httpPost(url, attrs)
   588  	var result ForceCreateRecordResult
   589  	json.Unmarshal(body, &result)
   590  	id = result.Id
   591  	return
   592  }
   593  
   594  func (f *Force) CreateBulkJob(xmlbody string) (result JobInfo, err error) {
   595  	url := fmt.Sprintf("%s/services/async/%s/job", f.Credentials.InstanceUrl, apiVersionNumber)
   596  	body, err := f.httpPostXML(url, xmlbody)
   597  	xml.Unmarshal(body, &result)
   598  	if len(result.Id) == 0 {
   599  		var fault LoginFault
   600  		xml.Unmarshal(body, &fault)
   601  		err = errors.New(fmt.Sprintf("%s: %s", fault.ExceptionCode, fault.ExceptionMessage))
   602  	}
   603  	return
   604  }
   605  
   606  func (f *Force) CloseBulkJob(jobId string, xmlbody string) (result JobInfo, err error) {
   607  	url := fmt.Sprintf("%s/services/async/%s/job/%s", f.Credentials.InstanceUrl, apiVersionNumber, jobId)
   608  	body, err := f.httpPostXML(url, xmlbody)
   609  	xml.Unmarshal(body, &result)
   610  	if len(result.Id) == 0 {
   611  		var fault LoginFault
   612  		xml.Unmarshal(body, &fault)
   613  		err = errors.New(fmt.Sprintf("%s: %s", fault.ExceptionCode, fault.ExceptionMessage))
   614  	}
   615  	return
   616  }
   617  
   618  func (f *Force) GetBulkJobs() (result []JobInfo, err error) {
   619  	url := fmt.Sprintf("%s/services/async/%s/jobs", f.Credentials.InstanceUrl, apiVersionNumber)
   620  	body, err := f.httpGetBulk(url)
   621  	xml.Unmarshal(body, &result)
   622  	if len(result[0].Id) == 0 {
   623  		var fault LoginFault
   624  		xml.Unmarshal(body, &fault)
   625  		err = errors.New(fmt.Sprintf("%s: %s", fault.ExceptionCode, fault.ExceptionMessage))
   626  	}
   627  	return
   628  }
   629  
   630  func (f *Force) BulkQuery(soql string, jobId string, contettype string) (result BatchInfo, err error) {
   631  	url := fmt.Sprintf("%s/services/async/%s/job/%s/batch", f.Credentials.InstanceUrl, apiVersionNumber, jobId)
   632  	var body []byte
   633  
   634  	if contettype == "CSV" {
   635  		body, err = f.httpPostCSV(url, soql)
   636  		xml.Unmarshal(body, &result)
   637  	} else {
   638  		body, err = f.httpPostXML(url, soql)
   639  		xml.Unmarshal(body, &result)
   640  	}
   641  	if len(result.Id) == 0 {
   642  		var fault LoginFault
   643  		xml.Unmarshal(body, &fault)
   644  		err = errors.New(fmt.Sprintf("%s: %s", fault.ExceptionCode, fault.ExceptionMessage))
   645  	}
   646  	return
   647  }
   648  
   649  func (f *Force) AddBatchToJob(xmlbody string, jobId string) (result BatchInfo, err error) {
   650  	url := fmt.Sprintf("%s/services/async/%s/job/%s/batch", f.Credentials.InstanceUrl, apiVersionNumber, jobId)
   651  	body, err := f.httpPostCSV(url, xmlbody)
   652  	xml.Unmarshal(body, &result)
   653  	if len(result.Id) == 0 {
   654  		var fault LoginFault
   655  		xml.Unmarshal(body, &fault)
   656  		err = errors.New(fmt.Sprintf("%s: %s", fault.ExceptionCode, fault.ExceptionMessage))
   657  	}
   658  	return
   659  }
   660  
   661  func (f *Force) GetBatchInfo(jobId string, batchId string) (result BatchInfo, err error) {
   662  	url := fmt.Sprintf("%s/services/async/%s/job/%s/batch/%s", f.Credentials.InstanceUrl, apiVersionNumber, jobId, batchId)
   663  	body, err := f.httpGetBulk(url)
   664  	xml.Unmarshal(body, &result)
   665  	if len(result.Id) == 0 {
   666  		var fault LoginFault
   667  		xml.Unmarshal(body, &fault)
   668  		err = errors.New(fmt.Sprintf("%s: %s", fault.ExceptionCode, fault.ExceptionMessage))
   669  	}
   670  	return
   671  }
   672  
   673  func (f *Force) GetBatches(jobId string) (result []BatchInfo, err error) {
   674  	url := fmt.Sprintf("%s/services/async/%s/job/%s/batch", f.Credentials.InstanceUrl, apiVersionNumber, jobId)
   675  	body, err := f.httpGetBulk(url)
   676  
   677  	var batchInfoList struct {
   678  		BatchInfos []BatchInfo `xml:"batchInfo"`
   679  	}
   680  
   681  	xml.Unmarshal(body, &batchInfoList)
   682  	result = batchInfoList.BatchInfos
   683  	if len(result) == 0 {
   684  		var fault LoginFault
   685  		xml.Unmarshal(body, &fault)
   686  		err = errors.New(fmt.Sprintf("%s: %s", fault.ExceptionCode, fault.ExceptionMessage))
   687  	}
   688  	return
   689  }
   690  
   691  func (f *Force) GetJobInfo(jobId string) (result JobInfo, err error) {
   692  	url := fmt.Sprintf("%s/services/async/%s/job/%s", f.Credentials.InstanceUrl, apiVersionNumber, jobId)
   693  	body, err := f.httpGetBulk(url)
   694  	xml.Unmarshal(body, &result)
   695  	if len(result.Id) == 0 {
   696  		var fault LoginFault
   697  		xml.Unmarshal(body, &fault)
   698  		err = errors.New(fmt.Sprintf("%s: %s", fault.ExceptionCode, fault.ExceptionMessage))
   699  	}
   700  	return
   701  }
   702  
   703  func (f *Force) RetrieveBulkQuery(jobId string, batchId string) (result []byte, err error) {
   704  	url := fmt.Sprintf("%s/services/async/%s/job/%s/batch/%s/result", f.Credentials.InstanceUrl, apiVersionNumber, jobId, batchId)
   705  	result, err = f.httpGetBulk(url)
   706  	return
   707  }
   708  
   709  func (f *Force) RetrieveBulkQueryResults(jobId string, batchId string, resultId string) (result []byte, err error) {
   710  	url := fmt.Sprintf("%s/services/async/%s/job/%s/batch/%s/result/%s", f.Credentials.InstanceUrl, apiVersionNumber, jobId, batchId, resultId)
   711  	result, err = f.httpGetBulk(url)
   712  	return
   713  }
   714  
   715  func (f *Force) RetrieveBulkBatchResults(jobId string, batchId string) (results BatchResult, err error) {
   716  	url := fmt.Sprintf("%s/services/async/%s/job/%s/batch/%s/result", f.Credentials.InstanceUrl, apiVersionNumber, jobId, batchId)
   717  	result, err := f.httpGetBulk(url)
   718  	if len(result) == 0 {
   719  		var fault LoginFault
   720  		xml.Unmarshal(result, &fault)
   721  		err = errors.New(fmt.Sprintf("%s: %s", fault.ExceptionCode, fault.ExceptionMessage))
   722  	}
   723  	//	sreader = Reader.NewReader(result);
   724  	return
   725  }
   726  
   727  func (f *Force) UpdateAuraComponent(source map[string]string, id string) (err error) {
   728  	url := fmt.Sprintf("%s/services/data/%s/tooling/sobjects/AuraDefinition/%s", f.Credentials.InstanceUrl, apiVersion, id)
   729  	_, err = f.httpPatch(url, source)
   730  	return
   731  }
   732  
   733  func (f *Force) DeleteToolingRecord(objecttype string, id string) (err error) {
   734  	url := fmt.Sprintf("%s/services/data/%s/tooling/sobjects/%s/%s", f.Credentials.InstanceUrl, apiVersion, objecttype, id)
   735  	_, err = f.httpDelete(url)
   736  	return
   737  }
   738  
   739  func (f *Force) UpdateRecord(sobject string, id string, attrs map[string]string) (err error) {
   740  	url := fmt.Sprintf("%s/services/data/%s/sobjects/%s/%s", f.Credentials.InstanceUrl, apiVersion, sobject, id)
   741  	_, err = f.httpPatch(url, attrs)
   742  	return
   743  }
   744  
   745  func (f *Force) DeleteRecord(sobject string, id string) (err error) {
   746  	url := fmt.Sprintf("%s/services/data/%s/sobjects/%s/%s", f.Credentials.InstanceUrl, apiVersion, sobject, id)
   747  	_, err = f.httpDelete(url)
   748  	return
   749  }
   750  
   751  func (f *Force) Whoami() (me ForceRecord, err error) {
   752  	parts := strings.Split(f.Credentials.Id, "/")
   753  	me, err = f.GetRecord("User", parts[len(parts)-1])
   754  	return
   755  }
   756  
   757  func (f *Force) httpGet(url string) (body []byte, err error) {
   758  	body, err = f.httpGetRequest(url, "Authorization", fmt.Sprintf("Bearer %s", f.Credentials.AccessToken))
   759  	return
   760  }
   761  
   762  func (f *Force) httpGetBulk(url string) (body []byte, err error) {
   763  	body, err = f.httpGetRequest(url, "X-SFDC-Session", fmt.Sprintf("Bearer %s", f.Credentials.AccessToken))
   764  	return
   765  }
   766  
   767  func (f *Force) httpGetRequest(url string, headerName string, headerValue string) (body []byte, err error) {
   768  	req, err := httpRequest("GET", url, nil)
   769  	if err != nil {
   770  		return
   771  	}
   772  	req.Header.Add(headerName, headerValue)
   773  	res, err := httpClient().Do(req)
   774  	if err != nil {
   775  		return
   776  	}
   777  	defer res.Body.Close()
   778  	if res.StatusCode == 401 {
   779  		err = errors.New("authorization expired, please run `force login`")
   780  		return
   781  	}
   782  	if res.StatusCode == 403 {
   783  		err = errors.New("Forbidden; Your authorization may have expired, or you do not have access. Please run `force login` and try again")
   784  		return
   785  	}
   786  	body, err = ioutil.ReadAll(res.Body)
   787  	if res.StatusCode/100 != 2 {
   788  		var messages []ForceError
   789  		json.Unmarshal(body, &messages)
   790  		if len(messages) > 0 {
   791  			err = errors.New(messages[0].Message)
   792  		} else {
   793  			err = errors.New(string(body))
   794  		}
   795  		return
   796  	}
   797  	return
   798  }
   799  
   800  func (f *Force) httpPostCSV(url string, data string) (body []byte, err error) {
   801  	body, err = f.httpPostWithContentType(url, data, "text/csv")
   802  	return
   803  }
   804  
   805  func (f *Force) httpPostXML(url string, data string) (body []byte, err error) {
   806  	body, err = f.httpPostWithContentType(url, data, "application/xml")
   807  	return
   808  }
   809  
   810  func (f *Force) httpPostWithContentType(url string, data string, contenttype string) (body []byte, err error) {
   811  	rbody := data
   812  	req, err := httpRequest("POST", url, bytes.NewReader([]byte(rbody)))
   813  	if err != nil {
   814  		return
   815  	}
   816  
   817  	req.Header.Add("X-SFDC-Session", f.Credentials.AccessToken)
   818  	req.Header.Add("Content-Type", contenttype)
   819  	res, err := httpClient().Do(req)
   820  	if err != nil {
   821  		return
   822  	}
   823  	defer res.Body.Close()
   824  	if res.StatusCode == 401 {
   825  		err = errors.New("authorization expired, please run `force login`")
   826  		return
   827  	}
   828  	body, err = ioutil.ReadAll(res.Body)
   829  
   830  	if res.StatusCode/100 != 2 {
   831  		var messages []ForceError
   832  		json.Unmarshal(body, &messages)
   833  		if messages != nil {
   834  			err = errors.New(messages[0].Message)
   835  		}
   836  		return
   837  	}
   838  	return
   839  }
   840  
   841  /*func (f *Force) httpGet(url string) (body []byte, err error) {
   842  	req, err := httpRequest("GET", url, nil)
   843  	if err != nil {
   844  		return
   845  	}
   846  	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", f.Credentials.AccessToken))
   847  	res, err := httpClient().Do(req)
   848  	if err != nil {
   849  		return
   850  	}
   851  	defer res.Body.Close()
   852  	if res.StatusCode == 401 {
   853  		err = errors.New("authorization expired, please run `force login`")
   854  		return
   855  	}
   856  	if res.StatusCode == 403 {
   857  		err = errors.New("Forbidden; Your authorization may have expired, or you do not have access. Please run `force login` and try again")
   858  		return
   859  	}
   860  	body, err = ioutil.ReadAll(res.Body)
   861  	if res.StatusCode/100 != 2 {
   862  		var messages []ForceError
   863  		json.Unmarshal(body, &messages)
   864  		if len(messages) > 0 {
   865  			err = errors.New(messages[0].Message)
   866  		} else {
   867  			err = errors.New(string(body))
   868  		}
   869  		return
   870  	}
   871  	return
   872  }*/
   873  
   874  func (f *Force) httpPost(url string, attrs map[string]string) (body []byte, err error) {
   875  	rbody, _ := json.Marshal(attrs)
   876  	req, err := httpRequest("POST", url, bytes.NewReader(rbody))
   877  	if err != nil {
   878  		return
   879  	}
   880  	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", f.Credentials.AccessToken))
   881  	req.Header.Add("Content-Type", "application/json")
   882  	res, err := httpClient().Do(req)
   883  	if err != nil {
   884  		return
   885  	}
   886  	defer res.Body.Close()
   887  	if res.StatusCode == 401 {
   888  		err = errors.New("authorization expired, please run `force login`")
   889  		return
   890  	}
   891  	body, err = ioutil.ReadAll(res.Body)
   892  	if res.StatusCode/100 != 2 {
   893  		var messages []ForceError
   894  		json.Unmarshal(body, &messages)
   895  		err = errors.New(messages[0].Message)
   896  		return
   897  	}
   898  	return
   899  }
   900  
   901  func (f *Force) httpPatch(url string, attrs map[string]string) (body []byte, err error) {
   902  	rbody, _ := json.Marshal(attrs)
   903  	req, err := httpRequest("PATCH", url, bytes.NewReader(rbody))
   904  	if err != nil {
   905  		return
   906  	}
   907  	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", f.Credentials.AccessToken))
   908  	req.Header.Add("Content-Type", "application/json")
   909  	res, err := httpClient().Do(req)
   910  	if err != nil {
   911  		return
   912  	}
   913  	defer res.Body.Close()
   914  	if res.StatusCode == 401 {
   915  		err = errors.New("Authorization expired, please run `force login`")
   916  		return
   917  	}
   918  	body, err = ioutil.ReadAll(res.Body)
   919  	if res.StatusCode/100 != 2 {
   920  		var messages []ForceError
   921  		json.Unmarshal(body, &messages)
   922  		err = errors.New(messages[0].Message)
   923  		return
   924  	}
   925  	return
   926  }
   927  
   928  func (f *Force) httpDelete(url string) (body []byte, err error) {
   929  	req, err := httpRequest("DELETE", url, nil)
   930  	if err != nil {
   931  		return
   932  	}
   933  	req.Header.Add("Authorization", fmt.Sprintf("Bearer %s", f.Credentials.AccessToken))
   934  	res, err := httpClient().Do(req)
   935  	if err != nil {
   936  		return
   937  	}
   938  	defer res.Body.Close()
   939  	if res.StatusCode == 401 {
   940  		err = errors.New("authorization expired, please run `force login`")
   941  		return
   942  	}
   943  	body, err = ioutil.ReadAll(res.Body)
   944  	if res.StatusCode/100 != 2 {
   945  		var messages []ForceError
   946  		json.Unmarshal(body, &messages)
   947  		err = errors.New(messages[0].Message)
   948  		return
   949  	}
   950  	return
   951  }
   952  
   953  func httpClient() (client *http.Client) {
   954  	if CustomEndpoint == "" {
   955  		chain := rootCertificate()
   956  		config := tls.Config{InsecureSkipVerify: true}
   957  		config.RootCAs = x509.NewCertPool()
   958  		for _, cert := range chain.Certificate {
   959  			x509Cert, err := x509.ParseCertificate(cert)
   960  			if err != nil {
   961  				panic(err)
   962  			}
   963  			config.RootCAs.AddCert(x509Cert)
   964  		}
   965  		config.BuildNameToCertificate()
   966  		tr := http.Transport{TLSClientConfig: &config}
   967  		client = &http.Client{Transport: &tr}
   968  	} else {
   969  		client = &http.Client{}
   970  	}
   971  	return
   972  }
   973  
   974  func httpRequest(method, url string, body io.Reader) (request *http.Request, err error) {
   975  	request, err = http.NewRequest(method, url, body)
   976  	if err != nil {
   977  		return
   978  	}
   979  	request.Header.Add("User-Agent", fmt.Sprintf("force/%s (%s-%s)", Version, runtime.GOOS, runtime.GOARCH))
   980  	return
   981  }
   982  
   983  func rootCertificate() (cert tls.Certificate) {
   984  	certPEMBlock := []byte(RootCertificates)
   985  	var certDERBlock *pem.Block
   986  	for {
   987  		certDERBlock, certPEMBlock = pem.Decode(certPEMBlock)
   988  		if certDERBlock == nil {
   989  			break
   990  		}
   991  		if certDERBlock.Type == "CERTIFICATE" {
   992  			cert.Certificate = append(cert.Certificate, certDERBlock.Bytes)
   993  		}
   994  	}
   995  	return
   996  }
   997  
   998  func startLocalHttpServer(ch chan ForceCredentials) (port int, err error) {
   999  	listener, err := net.Listen("tcp", ":0")
  1000  	if err != nil {
  1001  		return
  1002  	}
  1003  	port = listener.Addr().(*net.TCPAddr).Port
  1004  	h := http.NewServeMux()
  1005  	h.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
  1006  		w.Header().Set("Access-Control-Allow-Origin", "https://force-cli.herokuapp.com")
  1007  		if r.Method == "OPTIONS" {
  1008  			w.Header().Set("Access-Control-Allow-Headers", "X-Requested-With")
  1009  		} else {
  1010  			query := r.URL.Query()
  1011  			var creds ForceCredentials
  1012  			creds.AccessToken = query.Get("access_token")
  1013  			creds.Id = query.Get("id")
  1014  			creds.InstanceUrl = query.Get("instance_url")
  1015  			creds.IssuedAt = query.Get("issued_at")
  1016  			creds.Scope = query.Get("scope")
  1017  			ch <- creds
  1018  			if _, ok := r.Header["X-Requested-With"]; ok == false {
  1019  				http.Redirect(w, r, "https://force-cli.herokuapp.com/auth/complete", http.StatusSeeOther)
  1020  			}
  1021  			listener.Close()
  1022  		}
  1023  	})
  1024  	go http.Serve(listener, h)
  1025  	return
  1026  }