// sends a packet to the sentry server with a given timestamp func (client Client) send(packet []byte, timestamp time.Time) (err error) { apiURL := *client.URL apiURL.Path = path.Join(apiURL.Path, "/api/"+client.Project+"/store") apiURL.Path += "/" location := apiURL.String() buf := bytes.NewBuffer(packet) req, err := http.NewRequest("POST", location, buf) if err != nil { return err } authHeader := fmt.Sprintf(xSentryAuthTemplate, timestamp.Unix(), client.PublicKey) req.Header.Add("X-Sentry-Auth", authHeader) req.Header.Add("Content-Type", "application/octet-stream") req.Header.Add("Connection", "close") req.Header.Add("Accept-Encoding", "identity") resp, err := client.httpClient.Do(req) if err != nil { return err } defer resp.Body.Close() switch resp.StatusCode { case 200: return nil default: return errors.New(resp.Status) } }
func mockLoginTokenWithoutID(expires time.Time, auth_key []byte) (string, error) { token := jwt.New(jwt.SigningMethodHS256) // Set some claims token.Claims["exp"] = expires.Unix() // Sign and get the complete encoded token as a string return token.SignedString(auth_key) }
func timeToWindowsFileTime(t time.Time) []byte { var ll int64 ll = (int64(t.Unix()) * int64(10000000)) + int64(116444736000000000) buffer := bytes.NewBuffer(make([]byte, 0, 8)) binary.Write(buffer, binary.LittleEndian, ll) return buffer.Bytes() }
// RangeTimeFormat checks if value with given format is in given range inclusively, // and returns default value if it's not. func (k *Key) RangeTimeFormat(format string, defaultVal, min, max time.Time) time.Time { val := k.MustTimeFormat(format) if val.Unix() < min.Unix() || val.Unix() > max.Unix() { return defaultVal } return val }
// SignedURLWithMethod returns a signed URL that allows anyone holding the URL // to either retrieve the object at path or make a HEAD request against it. The signature is valid until expires. func (b *Bucket) SignedURLWithMethod(method, path string, expires time.Time, params url.Values, headers http.Header) string { var uv = url.Values{} if params != nil { uv = params } if b.S3.Signature == aws.V2Signature { uv.Set("Expires", strconv.FormatInt(expires.Unix(), 10)) } else { uv.Set("X-Amz-Expires", strconv.FormatInt(expires.Unix()-time.Now().Unix(), 10)) } req := &request{ method: method, bucket: b.Name, path: path, params: uv, headers: headers, } err := b.S3.prepare(req) if err != nil { panic(err) } u, err := req.url() if err != nil { panic(err) } if b.S3.Auth.Token() != "" && b.S3.Signature == aws.V2Signature { return u.String() + "&x-amz-security-token=" + url.QueryEscape(req.headers["X-Amz-Security-Token"][0]) } else { return u.String() } }
func goroutineWork(timestamps *[]int64, mutex *sync.Mutex, i int64, arrayCursor *int, url, token string) error { getParam := "?page=" if strings.Contains(url, "?") { getParam = "&page=" } pageUrl := url + getParam + strconv.Itoa(int(i)) stargazers, _, err := getStargazers(pageUrl, token) if err != nil { return err } for _, star := range stargazers { var t time.Time t, err = time.Parse(time.RFC3339, star.Timestamp) if err != nil { return fmt.Errorf("An error occured while parsing the timestamp: %v", err) } timestamp := t.Unix() mutex.Lock() (*timestamps)[*arrayCursor] = timestamp (*arrayCursor) = (*arrayCursor) + 1 mutex.Unlock() } return nil }
//----------------------------------------------- RAID->PROTECTED // A->B->A->A func Protect(id int32, until time.Time) bool { _lock_raids.Lock() player := _raids[id] _lock_raids.Unlock() if player != nil { player.LCK.Lock() state := player.State if state&RAID != 0 { player.State = int32(OFFLINE | PROTECTED) player.ProtectTime = until.Unix() player.LCK.Unlock() _lock_raids.Lock() delete(_raids, id) // remove from raids _lock_raids.Unlock() _lock_protects.Lock() _protects[id] = player // add to protects _lock_protects.Unlock() return true } player.LCK.Unlock() } return false }
// create creates a new log file and returns the file and its filename, which // contains severity ("INFO", "FATAL", etc.) and t. If the file is created // successfully, create also attempts to update the symlink for that tag, ignoring // errors. func create( severity Severity, t time.Time, lastRotation int64, ) (f *os.File, updatedRotation int64, filename string, err error) { dir, err := logDir.get() if err != nil { return nil, lastRotation, "", err } // Ensure that the timestamp of the new file name is greater than // the timestamp of the previous generated file name. unix := t.Unix() if unix <= lastRotation { unix = lastRotation + 1 } updatedRotation = unix t = time.Unix(unix, 0) // Generate the file name. name, link := logName(severity, t) fname := filepath.Join(dir, name) // Open the file os.O_APPEND|os.O_CREATE rather than use os.Create. // Append is almost always more efficient than O_RDRW on most modern file systems. f, err = os.OpenFile(fname, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0664) if err == nil { symlink := filepath.Join(dir, link) _ = os.Remove(symlink) // ignore err err = os.Symlink(fname, symlink) } return f, updatedRotation, fname, errors.Wrapf(err, "log: cannot create log") }
// VerifyXsrfToken returns true if token is valid or false otherwise. // action identifies the web page; now is the current time. // If no userId is set, VerifyXsrfToken returns false. func (s UserIdSession) VerifyXsrfToken( tokenToBeVerified, action string, now time.Time) bool { idx := strings.IndexByte(tokenToBeVerified, ':') if idx == -1 { return false } expireUnix, err := strconv.ParseInt(tokenToBeVerified[:idx], 10, 64) if err != nil { return false } if now.Unix() >= expireUnix { return false } userId, ok := s.UserId() if !ok { return false } secret, ok := s.xsrfSecret() if !ok { return false } expectedChecksum := tokenToBeVerified[idx+1:] mac := hmac.New(sha256.New, secret) message := fmt.Sprintf("%d_%d_%s", expireUnix, userId, action) mac.Write(([]byte)(message)) checksum := strings.TrimRight( base32.StdEncoding.EncodeToString(mac.Sum(nil)), "=") return hmac.Equal(([]byte)(expectedChecksum), ([]byte)(checksum)) }
// UploadSignedURL returns a signed URL that allows anyone holding the URL // to upload the object at path. The signature is valid until expires. // contenttype is a string like image/png // name is the resource name in OSS terminology like images/ali.png [obviously excluding the bucket name itself] func (b *Bucket) UploadSignedURL(name, method, contentType string, expires time.Time) string { //TODO TESTING expireDate := expires.Unix() if method != "POST" { method = "PUT" } tokenData := "" stringToSign := method + "\n\n" + contentType + "\n" + strconv.FormatInt(expireDate, 10) + "\n" + tokenData + "/" + path.Join(b.Name, name) secretKey := b.AccessKeySecret accessId := b.AccessKeyId mac := hmac.New(sha1.New, []byte(secretKey)) mac.Write([]byte(stringToSign)) macsum := mac.Sum(nil) signature := base64.StdEncoding.EncodeToString([]byte(macsum)) signature = strings.TrimSpace(signature) signedurl, err := url.Parse("https://" + b.Name + ".client.amazonaws.com/") if err != nil { log.Println("ERROR sining url for OSS upload", err) return "" } signedurl.Path = name params := url.Values{} params.Add("OSSAccessKeyId", accessId) params.Add("Expires", strconv.FormatInt(expireDate, 10)) params.Add("Signature", signature) signedurl.RawQuery = params.Encode() return signedurl.String() }
func (x *ActiveApps) ActiveSince(y time.Time) []string { t := y.Unix() x.Lock() a := byTimeMaxHeapSnapshot{} a.Heap = x.j.Copy() a.Init() x.Unlock() // Collect active applications b := make([]string, 0) for a.Len() > 0 { z := heap.Pop(&a).(*activeAppsEntry) if z.t < t { break } // Add active application b = append(b, z.ApplicationId) } return b }
// SignedURLWithMethod returns a signed URL that allows anyone holding the URL // to either retrieve the object at path or make a HEAD request against it. The signature is valid until expires. func (b *Bucket) SignedURLWithMethod(method, path string, expires time.Time, params url.Values, headers http.Header) string { var uv = url.Values{} if params != nil { uv = params } uv.Set("Expires", strconv.FormatInt(expires.Unix(), 10)) uv.Set("OSSAccessKeyId", b.AccessKeyId) req := &request{ method: method, bucket: b.Name, path: path, params: uv, headers: headers, } err := b.Client.prepare(req) if err != nil { panic(err) } u, err := req.url() if err != nil { panic(err) } return u.String() }
func ListIssueComments(ctx *context.APIContext) { var since time.Time if len(ctx.Query("since")) > 0 { since, _ = time.Parse(time.RFC3339, ctx.Query("since")) } // comments,err:=models.GetCommentsByIssueIDSince(, since) issue, err := models.GetRawIssueByIndex(ctx.Repo.Repository.ID, ctx.ParamsInt64(":index")) if err != nil { ctx.Error(500, "GetRawIssueByIndex", err) return } comments, err := models.GetCommentsByIssueIDSince(issue.ID, since.Unix()) if err != nil { ctx.Error(500, "GetCommentsByIssueIDSince", err) return } apiComments := make([]*api.Comment, len(comments)) for i := range comments { apiComments[i] = comments[i].APIFormat() } ctx.JSON(200, &apiComments) }
// GetModifyDate returns last modify date of given mirror repository. func (mirror *Mirror) GetModifyDate() (time.Time, error) { dirs := []string{ "refs/heads", "refs/tags", } var ( modDate time.Time err error fileinfo os.FileInfo ) for _, dir := range dirs { fileinfo, err = os.Stat(mirror.Dir + "/" + dir) if err != nil { if os.IsNotExist(err) { continue } break } newModDate := fileinfo.ModTime() if newModDate.Unix() > modDate.Unix() { modDate = newModDate } } return modDate, err }
// processTimeRange calls gs.GetLatestGSDirs to get a list of func (xformer *pdfXformer) processTimeRange(start time.Time, end time.Time) { glog.Infof("Processing time range: (%s, %s)", start.Truncate(time.Second), end.Truncate(time.Second)) for _, dir := range gs.GetLatestGSDirs(start.Unix(), end.Unix(), *storageJsonDirectory) { glog.Infof("> Reading gs://%s/%s\n", *storageBucket, dir) requestedObjects := xformer.client.storageService.Objects.List(*storageBucket).Prefix(dir).Fields( "nextPageToken", "items/updated", "items/md5Hash", "items/mediaLink", "items/name", "items/metadata") for requestedObjects != nil { responseObjects, err := requestedObjects.Do() if err != nil { glog.Errorf("request %#v failed: %s", requestedObjects, err) } else { for _, jsonObject := range responseObjects.Items { xformer.counter++ glog.Infof("> > Processing object: gs://%s/%s {%d}", *storageBucket, jsonObject.Name, xformer.counter) xformer.processJsonFile(jsonObject) } } if len(responseObjects.NextPageToken) > 0 { requestedObjects.PageToken(responseObjects.NextPageToken) } else { requestedObjects = nil } } } glog.Infof("finished time range.") }
func (f *RrdRawFile) StoreLastUpdate(lastUpdate time.Time) error { writer := f.dataFile.Writer(f.baseHeaderSize) if err := writer.WriteUnival(unival(lastUpdate.Unix())); err != nil { return errors.Wrap(err, 0) } return writer.WriteUnival(unival(lastUpdate.Nanosecond() / 1000)) }
func (x *Index) PathLookup(signer, base blob.Ref, suffix string, at time.Time) (*camtypes.Path, error) { paths, err := x.PathsLookup(signer, base, suffix) if err != nil { return nil, err } var ( newest = int64(0) atSeconds = int64(0) best *camtypes.Path ) if !at.IsZero() { atSeconds = at.Unix() } for _, path := range paths { t := path.ClaimDate secs := t.Unix() if atSeconds != 0 && secs > atSeconds { // Too new continue } if newest > secs { // Too old continue } // Just right newest, best = secs, path } if best == nil { return nil, os.ErrNotExist } return best, nil }
func getTimeSeries(w http.ResponseWriter, r *http.Request) { type count struct { Ts int64 `json:"time"` Count int64 `json:"count"` } var ts time.Time //var timeSeries []map[string]int64 var timeSeries []count var c int64 const layout = "Jan 2, 2006 at 3:04pm (MST)" params := r.URL.Query() stream := params.Get(":stream") key := params.Get("key") val := params.Get("value") fmt.Println(stream + " " + key + " " + val) iter := *session.Query(`SELECT event_time, count FROM dist_over_time WHERE stream=? AND attr_name=? AND attr_value=?`, stream, key, val).Iter() for iter.Scan(&ts, &c) { //timeElem := make(map[string]int64) timeElem := count{ts.Unix() * 1000, c} //timeElem[ts.Format(layout)] = c timeSeries = append(timeSeries, timeElem) } b, err := json.Marshal(timeSeries) if err != nil { http.Error(w, err.Error(), http.StatusInternalServerError) return } w.Header().Set("Access-Control-Allow-Origin", "*") w.Write(b) }
// int returns the ban score, the sum of the persistent and decaying scores at a // given point in time. // // This function is not safe for concurrent access. It is intended to be used // internally and during testing. func (s *dynamicBanScore) int(t time.Time) uint32 { dt := t.Unix() - s.lastUnix if s.transient < 1 || dt < 0 || Lifetime < dt { return s.persistent } return s.persistent + uint32(s.transient*decayFactor(dt)) }
func (a *HotActor) updateQueryCache(t time.Time) error { defer atomic.CompareAndSwapInt32(&a.queryCacheUpdated, 1, 0) if !atomic.CompareAndSwapInt32(&a.queryCacheUpdated, 0, 1) { return nil } if (t.Unix() - a.lastQueryCacheUpdate.Unix()) < GOAT_UPDATE_SPAN_SEC { return nil } qcsize := len(a.queryDurations) a.wg = utils.NewWaitGroup(2 * qcsize) //hot and flame var from time.Time for i := 0; i < qcsize; i++ { if a.queryDurations[i] > 0 { from = t.Add(-1 * a.queryDurations[i]) } else { from = time.Time{} } go a.updateQueryCacheTask(from, t, proto.TopicListRequest_Hot, &a.hotQueryCache[i], a.wg) go a.updateQueryCacheTask(from, t, proto.TopicListRequest_Flame, &a.flameQueryCache[i], a.wg) } if err := a.wg.Wait(); err != nil { log.Printf("update fails with %v", err) a.available = false return err } else { log.Printf("update success") a.available = true a.lastQueryCacheUpdate = t } return nil }
// Taken from http://stackoverflow.com/a/26579165/196964 and // https://cloud.google.com/storage/docs/access-control#Signed-URLs func generateSignedURLs(c context.Context, host, resource string, expiry time.Time, httpVerb, contentMD5, contentType string) (string, error) { sa, err := appengine.ServiceAccount(c) if err != nil { return "", err } expiryStr := strconv.FormatInt(expiry.Unix(), 10) // The optional components should be the empty string. // https://cloud.google.com/storage/docs/access-control#Construct-the-String components := []string{ httpVerb, // PUT, GET, DELETE (but not POST) contentMD5, // Optional. The MD5 digest value in base64. Client must provide same value if present. contentType, // Optional. Client must provide same value if present. expiryStr, // Unix timestamp resource, // /bucket/objectname } unsigned := strings.Join(components, "\n") _, b, err := appengine.SignBytes(c, []byte(unsigned)) if err != nil { return "", err } sig := base64.StdEncoding.EncodeToString(b) p := url.Values{ "GoogleAccessId": {sa}, "Expires": {expiryStr}, "Signature": {sig}, } return fmt.Sprintf("%s%s?%s", host, resource, p.Encode()), err }
func (x *TopApps) Mark(ApplicationId string, z time.Time) { t := z.Unix() x.Lock() defer x.Unlock() y := x.m[ApplicationId] if y != nil { z1 := heap.Remove(&x.t, y.ti).(*topAppsEntry) if z1 != y { panic("z1 != y") } z2 := heap.Remove(&x.n, y.ni).(*topAppsEntry) if z2 != y { panic("z2 != y") } } else { // New entry y = &topAppsEntry{ApplicationId: ApplicationId} x.m[ApplicationId] = y } y.Mark(t) heap.Push(&x.t, y) heap.Push(&x.n, y) }
func (l *List) Set(urn string, finished, last time.Time, progress uint64) *Playable { playable := &Playable{Urn: urn} if finished.Unix() != 0 { playable.FinishedAt = finished } if last.Unix() != 0 { playable.LastPlayedAt = last } if progress != 0 { playable.Progress = progress } l.Lock() defer l.Unlock() for e := l.Front(); e != nil; e = e.Next() { p := e.Value.(*Playable) if p.Urn == urn { p.FinishedAt = playable.FinishedAt p.LastPlayedAt = playable.LastPlayedAt p.Progress = playable.Progress return p } } l.PushBack(playable) return playable }
// ExportBeamStats gets a URL to download a CSV file which contains all stats of Beam for the operator for a specified period func (ac *APIClient) ExportBeamStats(from, to time.Time, period StatsPeriod) (*url.URL, error) { params := &apiParams{ method: "POST", path: fmt.Sprintf("/v1/stats/beam/operators/%s/export", ac.OperatorID), contentType: "application/json", body: (&exportBeamStatsRequest{ From: from.Unix(), To: to.Unix(), Period: period.String(), }).JSON(), } resp, err := ac.callAPI(params) if err != nil { return nil, err } defer resp.Body.Close() respBody := parseExportBeamStatsResponse(resp) url, err := url.Parse(respBody.URL) if err != nil { return nil, err } return url, nil }
// Events is an endpoint that returns the latest event log entries, with the following // optional URL parameters: // // type=STRING returns events with this type (e.g. "create_table") // targetID=INT returns events for that have this targetID func (s *adminServer) Events( ctx context.Context, req *serverpb.EventsRequest, ) (*serverpb.EventsResponse, error) { args := sql.SessionArgs{User: s.getUser(req)} session := s.NewSessionForRPC(ctx, args) defer session.Finish(s.server.sqlExecutor) // Execute the query. q := makeSQLQuery() q.Append("SELECT timestamp, eventType, targetID, reportingID, info, uniqueID ") q.Append("FROM system.eventlog ") q.Append("WHERE true ") // This simplifies the WHERE clause logic below. if len(req.Type) > 0 { q.Append("AND eventType = $ ", parser.NewDString(req.Type)) } if req.TargetId > 0 { q.Append("AND targetID = $ ", parser.NewDInt(parser.DInt(req.TargetId))) } q.Append("ORDER BY timestamp DESC ") q.Append("LIMIT $", parser.NewDInt(parser.DInt(apiEventLimit))) if len(q.Errors()) > 0 { return nil, s.serverErrors(q.Errors()) } r := s.server.sqlExecutor.ExecuteStatements(session, q.String(), q.QueryArguments()) defer r.Close() if err := s.checkQueryResults(r.ResultList, 1); err != nil { return nil, s.serverError(err) } // Marshal response. var resp serverpb.EventsResponse scanner := makeResultScanner(r.ResultList[0].Columns) for i, nRows := 0, r.ResultList[0].Rows.Len(); i < nRows; i++ { row := r.ResultList[0].Rows.At(i) var event serverpb.EventsResponse_Event var ts time.Time if err := scanner.ScanIndex(row, 0, &ts); err != nil { return nil, err } event.Timestamp = serverpb.EventsResponse_Event_Timestamp{Sec: ts.Unix(), Nsec: uint32(ts.Nanosecond())} if err := scanner.ScanIndex(row, 1, &event.EventType); err != nil { return nil, err } if err := scanner.ScanIndex(row, 2, &event.TargetID); err != nil { return nil, err } if err := scanner.ScanIndex(row, 3, &event.ReportingID); err != nil { return nil, err } if err := scanner.ScanIndex(row, 4, &event.Info); err != nil { return nil, err } if err := scanner.ScanIndex(row, 5, &event.UniqueID); err != nil { return nil, err } resp.Events = append(resp.Events, event) } return &resp, nil }
func (this *WatchActord) dueJobsWithin(topic string, timeSpan int64, now time.Time) (backlog int64, archive int64) { jobTable := jm.JobTable(topic) appid := manager.Default.TopicAppid(topic) aid := jm.App_id(appid) sql := fmt.Sprintf("SELECT count(job_id) FROM %s WHERE due_time<=?", jobTable) rows, err := this.mc.Query(jm.AppPool, jobTable, aid, sql, now.Unix()+timeSpan) if err != nil { log.Error("%s: %s", this.ident(), err) return } var n int for rows.Next() { rows.Scan(&n) } rows.Close() backlog += int64(n) archiveTable := jm.HistoryTable(topic) sql = fmt.Sprintf("SELECT count(job_id) FROM %s WHERE due_time>=?", archiveTable) rows, err = this.mc.Query(jm.AppPool, archiveTable, aid, sql, now.Unix()-timeSpan) if err != nil { log.Error("%s: %s", this.ident(), err) return } for rows.Next() { rows.Scan(&n) } rows.Close() archive += int64(n) return }
// NewMongoTimestamp returns a bson.MongoTimestamp repesentation for // the time.Time given. Note that these timestamps are not the same // the usual MongoDB time fields. These are an internal format used // only in a few places such as the replication oplog. // // See: http://docs.mongodb.org/manual/reference/bson-types/#timestamps func NewMongoTimestamp(t time.Time) bson.MongoTimestamp { unixTime := t.Unix() if unixTime < 0 { unixTime = 0 } return bson.MongoTimestamp(unixTime << 32) }
// IsFinalizedTransaction determines whether or not a transaction is finalized. func IsFinalizedTransaction(tx *btcutil.Tx, blockHeight int32, blockTime time.Time) bool { msgTx := tx.MsgTx() // Lock time of zero means the transaction is finalized. lockTime := msgTx.LockTime if lockTime == 0 { return true } // The lock time field of a transaction is either a block height at // which the transaction is finalized or a timestamp depending on if the // value is before the txscript.LockTimeThreshold. When it is under the // threshold it is a block height. blockTimeOrHeight := int64(0) if lockTime < txscript.LockTimeThreshold { blockTimeOrHeight = int64(blockHeight) } else { blockTimeOrHeight = blockTime.Unix() } if int64(lockTime) < blockTimeOrHeight { return true } // At this point, the transaction's lock time hasn't occurred yet, but // the transaction might still be finalized if the sequence number // for all transaction inputs is maxed out. for _, txIn := range msgTx.TxIn { if txIn.Sequence != math.MaxUint32 { return false } } return true }
// UploadSignedURL returns a signed URL that allows anyone holding the URL // to upload the object at path. The signature is valid until expires. // contenttype is a string like image/png // path is the resource name in s3 terminalogy like images/ali.png [obviously exclusing the bucket name itself] func (b *Bucket) UploadSignedURL(path, method, content_type string, expires time.Time) string { expire_date := expires.Unix() if method != "POST" { method = "PUT" } stringToSign := method + "\n\n" + content_type + "\n" + strconv.FormatInt(expire_date, 10) + "\n/" + b.Name + "/" + path fmt.Println("String to sign:\n", stringToSign) a := b.S3.Auth secretKey := a.SecretKey accessId := a.AccessKey mac := hmac.New(sha1.New, []byte(secretKey)) mac.Write([]byte(stringToSign)) macsum := mac.Sum(nil) signature := base64.StdEncoding.EncodeToString([]byte(macsum)) signature = strings.TrimSpace(signature) signedurl, err := url.Parse("https://" + b.Name + ".s3.amazonaws.com/") if err != nil { log.Println("ERROR sining url for S3 upload", err) return "" } signedurl.Path += path params := url.Values{} params.Add("AWSAccessKeyId", accessId) params.Add("Expires", strconv.FormatInt(expire_date, 10)) params.Add("Signature", signature) if a.Token() != "" { params.Add("token", a.Token()) } signedurl.RawQuery = params.Encode() return signedurl.String() }
// GetRandomSTHPollination returns a random selection of "fresh" (i.e. at most 14 days old) STHs from the pool. func (s *Storage) GetRandomSTHPollination(newerThan time.Time, limit int) (*STHPollination, error) { // Occasionally this fails to select the pollen which was added by the // AddSTHPollination request which went on trigger this query, even though // the transaction committed successfully. Attempting this query under a // transaction doesn't fix it. /sadface // Still, that shouldn't really matter too much in practice. r, err := s.selectRandomRecentPollination.Query(newerThan.Unix()*1000, limit) if err != nil { return nil, err } var pollination STHPollination for r.Next() { var entry ct.SignedTreeHead var rootB64, sigB64, idB64 string if err := r.Scan(&entry.Version, &entry.TreeSize, &entry.Timestamp, &rootB64, &sigB64, &idB64); err != nil { return nil, err } if err := entry.SHA256RootHash.FromBase64String(rootB64); err != nil { return nil, err } if err := entry.TreeHeadSignature.FromBase64String(sigB64); err != nil { return nil, err } if err := entry.LogID.FromBase64String(idB64); err != nil { return nil, err } pollination.STHs = append(pollination.STHs, entry) } // If there are no entries to return, wedge an empty array in there so that the json encoder returns something valid. if pollination.STHs == nil { pollination.STHs = make([]ct.SignedTreeHead, 0) } return &pollination, nil }