<commit_before>package slack

import (
	"context"
	"encoding/json"
	"net/url"
	"strings"
)

const (
	DEFAULT_MESSAGE_USERNAME         = ""
	DEFAULT_MESSAGE_REPLY_BROADCAST  = false
	DEFAULT_MESSAGE_ASUSER           = false
	DEFAULT_MESSAGE_PARSE            = ""
	DEFAULT_MESSAGE_THREAD_TIMESTAMP = ""
	DEFAULT_MESSAGE_LINK_NAMES       = 0
	DEFAULT_MESSAGE_UNFURL_LINKS     = false
	DEFAULT_MESSAGE_UNFURL_MEDIA     = true
	DEFAULT_MESSAGE_ICON_URL         = ""
	DEFAULT_MESSAGE_ICON_EMOJI       = ""
	DEFAULT_MESSAGE_MARKDOWN         = true
	DEFAULT_MESSAGE_ESCAPE_TEXT      = true
)

type chatResponseFull struct {
	Channel   string `json:"channel"`
	Timestamp string `json:"ts"`
	Text      string `json:"text"`
	SlackResponse
}

// PostMessageParameters contains all the parameters necessary (including the optional ones) for a PostMessage() request
type PostMessageParameters struct {
	Username        string       `json:"user_name"`
	AsUser          bool         `json:"as_user"`
	Parse           string       `json:"parse"`
	ThreadTimestamp string       `json:"thread_ts"`
	ReplyBroadcast  bool         `json:"reply_broadcast"`
	LinkNames       int          `json:"link_names"`
	Attachments     []Attachment `json:"attachments"`
	UnfurlLinks     bool         `json:"unfurl_links"`
	UnfurlMedia     bool         `json:"unfurl_media"`
	IconURL         string       `json:"icon_url"`
	IconEmoji       string       `json:"icon_emoji"`
	Markdown        bool         `json:"mrkdwn,omitempty"`
	EscapeText      bool         `json:"escape_text"`

	// chat.postEphemeral support
	Channel string `json:"channel"`
	User    string `json:"user"`
}

// NewPostMessageParameters provides an instance of PostMessageParameters with all the sane default values set
func NewPostMessageParameters() PostMessageParameters {
	return PostMessageParameters{
		Username:        DEFAULT_MESSAGE_USERNAME,
		User:            DEFAULT_MESSAGE_USERNAME,
		AsUser:          DEFAULT_MESSAGE_ASUSER,
		Parse:           DEFAULT_MESSAGE_PARSE,
		ThreadTimestamp: DEFAULT_MESSAGE_THREAD_TIMESTAMP,
		LinkNames:       DEFAULT_MESSAGE_LINK_NAMES,
		Attachments:     nil,
		UnfurlLinks:     DEFAULT_MESSAGE_UNFURL_LINKS,
		UnfurlMedia:     DEFAULT_MESSAGE_UNFURL_MEDIA,
		IconURL:         DEFAULT_MESSAGE_ICON_URL,
		IconEmoji:       DEFAULT_MESSAGE_ICON_EMOJI,
		Markdown:        DEFAULT_MESSAGE_MARKDOWN,
		EscapeText:      DEFAULT_MESSAGE_ESCAPE_TEXT,
	}
}

// DeleteMessage deletes a message in a channel
func (api *Client) DeleteMessage(channel, messageTimestamp string) (string, string, error) {
	respChannel, respTimestamp, _, err := api.SendMessageContext(context.Background(), channel, MsgOptionDelete(messageTimestamp))
	return respChannel, respTimestamp, err
}

// DeleteMessageContext deletes a message in a channel with a custom context
func (api *Client) DeleteMessageContext(ctx context.Context, channel, messageTimestamp string) (string, string, error) {
	respChannel, respTimestamp, _, err := api.SendMessageContext(ctx, channel, MsgOptionDelete(messageTimestamp))
	return respChannel, respTimestamp, err
}

// PostMessage sends a message to a channel.
// Message is escaped by default according to https://api.slack.com/docs/formatting
// Use http://davestevens.github.io/slack-message-builder/ to help crafting your message.
func (api *Client) PostMessage(channel, text string, params PostMessageParameters) (string, string, error) {
	respChannel, respTimestamp, _, err := api.SendMessageContext(
		context.Background(),
		channel,
		MsgOptionText(text, params.EscapeText),
		MsgOptionAttachments(params.Attachments...),
		MsgOptionPostMessageParameters(params),
	)
	return respChannel, respTimestamp, err
}

// PostMessageContext sends a message to a channel with a custom context
// For more details, see PostMessage documentation
func (api *Client) PostMessageContext(ctx context.Context, channel, text string, params PostMessageParameters) (string, string, error) {
	respChannel, respTimestamp, _, err := api.SendMessageContext(
		ctx,
		channel,
		MsgOptionText(text, params.EscapeText),
		MsgOptionAttachments(params.Attachments...),
		MsgOptionPostMessageParameters(params),
	)
	return respChannel, respTimestamp, err
}

// PostEphemeral sends an ephemeral message to a user in a channel.
// Message is escaped by default according to https://api.slack.com/docs/formatting
// Use http://davestevens.github.io/slack-message-builder/ to help crafting your message.
func (api *Client) PostEphemeral(channelID, userID string, options ...MsgOption) (string, error) {
	return api.PostEphemeralContext(
		context.Background(),
		channelID,
		userID,
		options...,
	)
}

// PostEphemeralContext sends an ephemeal message to a user in a channel with a custom context
// For more details, see PostEphemeral documentation
func (api *Client) PostEphemeralContext(ctx context.Context, channelID, userID string, options ...MsgOption) (timestamp string, err error) {
	_, timestamp, _, err = api.SendMessageContext(ctx, channelID, append(options, MsgOptionPostEphemeral2(userID))...)
	return timestamp, err
}

// UpdateMessage updates a message in a channel
func (api *Client) UpdateMessage(channelID, timestamp, text string) (string, string, string, error) {
	return api.UpdateMessageContext(context.Background(), channelID, timestamp, text)
}

// UpdateMessageContext updates a message in a channel
func (api *Client) UpdateMessageContext(ctx context.Context, channelID, timestamp, text string) (string, string, string, error) {
	return api.SendMessageContext(ctx, channelID, MsgOptionUpdate(timestamp), MsgOptionText(text, true))
}

// SendMessage more flexible method for configuring messages.
func (api *Client) SendMessage(channel string, options ...MsgOption) (string, string, string, error) {
	return api.SendMessageContext(context.Background(), channel, options...)
}

// SendMessageContext more flexible method for configuring messages with a custom context.
func (api *Client) SendMessageContext(ctx context.Context, channelID string, options ...MsgOption) (channel string, timestamp string, text string, err error) {
	var (
		config   sendConfig
		response chatResponseFull
	)

	if config, err = applyMsgOptions(api.token, channelID, options...); err != nil {
		return "", "", "", err
	}

	if err = post(ctx, api.httpclient, string(config.mode), config.values, &response, api.debug); err != nil {
		return "", "", "", err
	}

	return response.Channel, response.Timestamp, response.Text, response.Err()
}

// ApplyMsgOptions utility function for debugging/testing chat requests.
func ApplyMsgOptions(token, channel string, options ...MsgOption) (string, url.Values, error) {
	config, err := applyMsgOptions(token, channel, options...)
	return string(config.mode), config.values, err
}

func applyMsgOptions(token, channel string, options ...MsgOption) (sendConfig, error) {
	config := sendConfig{
		mode: chatPostMessage,
		values: url.Values{
			"token":   {token},
			"channel": {channel},
		},
	}

	for _, opt := range options {
		if err := opt(&config); err != nil {
			return config, err
		}
	}

	return config, nil
}

func escapeMessage(message string) string {
	replacer := strings.NewReplacer("&", "&", "<", "<", ">", ">")
	return replacer.Replace(message)
}

type sendMode string

const (
	chatUpdate        sendMode = "chat.update"
	chatPostMessage   sendMode = "chat.postMessage"
	chatDelete        sendMode = "chat.delete"
	chatPostEphemeral sendMode = "chat.postEphemeral"
)

type sendConfig struct {
	mode   sendMode
	values url.Values
}

// MsgOption option provided when sending a message.
type MsgOption func(*sendConfig) error

// MsgOptionPost posts a messages, this is the default.
func MsgOptionPost() MsgOption {
	return func(config *sendConfig) error {
		config.mode = chatPostMessage
		config.values.Del("ts")
		return nil
	}
}

// MsgOptionPostEphemeral - DEPRECATED: use MsgOptionPostEphemeral2
// posts an ephemeral message.
func MsgOptionPostEphemeral() MsgOption {
	return func(config *sendConfig) error {
		config.mode = chatPostEphemeral
		config.values.Del("ts")
		return nil
	}
}

// MsgOptionPostEphemeral2 - posts an ephemeral message to the provided user.
func MsgOptionPostEphemeral2(userID string) MsgOption {
	return func(config *sendConfig) error {
		config.mode = chatPostEphemeral
		MsgOptionUser(userID)(config)
		config.values.Del("ts")

		return nil
	}
}

// MsgOptionUpdate updates a message based on the timestamp.
func MsgOptionUpdate(timestamp string) MsgOption {
	return func(config *sendConfig) error {
		config.mode = chatUpdate
		config.values.Add("ts", timestamp)
		return nil
	}
}

// MsgOptionDelete deletes a message based on the timestamp.
func MsgOptionDelete(timestamp string) MsgOption {
	return func(config *sendConfig) error {
		config.mode = chatDelete
		config.values.Add("ts", timestamp)
		return nil
	}
}

// MsgOptionAsUser whether or not to send the message as the user.
func MsgOptionAsUser(b bool) MsgOption {
	return func(config *sendConfig) error {
		if b != DEFAULT_MESSAGE_ASUSER {
			config.values.Set("as_user", "true")
		}
		return nil
	}
}

// MsgOptionUser set the user for the message.
func MsgOptionUser(userID string) MsgOption {
	return func(config *sendConfig) error {
		config.values.Set("user", userID)
		return nil
	}
}

// MsgOptionText provide the text for the message, optionally escape the provided
// text.
func MsgOptionText(text string, escape bool) MsgOption {
	return func(config *sendConfig) error {
		if escape {
			text = escapeMessage(text)
		}
		config.values.Add("text", text)
		return nil
	}
}

// MsgOptionAttachments provide attachments for the message.
func MsgOptionAttachments(attachments ...Attachment) MsgOption {
	return func(config *sendConfig) error {
		if attachments == nil {
			return nil
		}

		attachments, err := json.Marshal(attachments)
		if err == nil {
			config.values.Set("attachments", string(attachments))
		}
		return err
	}
}

// MsgOptionEnableLinkUnfurl enables link unfurling
func MsgOptionEnableLinkUnfurl() MsgOption {
	return func(config *sendConfig) error {
		config.values.Set("unfurl_links", "true")
		return nil
	}
}

// MsgOptionDisableLinkUnfurl disables link unfurling
func MsgOptionDisableLinkUnfurl() MsgOption {
	return func(config *sendConfig) error {
		config.values.Set("unfurl_links", "false")
		return nil
	}
}

// MsgOptionDisableMediaUnfurl disables media unfurling.
func MsgOptionDisableMediaUnfurl() MsgOption {
	return func(config *sendConfig) error {
		config.values.Set("unfurl_media", "false")
		return nil
	}
}

// MsgOptionDisableMarkdown disables markdown.
func MsgOptionDisableMarkdown() MsgOption {
	return func(config *sendConfig) error {
		config.values.Set("mrkdwn", "false")
		return nil
	}
}

// this function combines multiple options into a single option.
func MsgOptionCompose(options ...MsgOption) MsgOption {
	return func(c *sendConfig) error {
		for _, opt := range options { opt(c) }
		return nil
	}
}

func MsgOptionParse(b bool) MsgOption {
	return func(c *sendConfig) error {
		var v string
		if b { v = "1" } else { v = "0" }
		c.values.Set("parse", v)
		return nil
	}
}

// MsgOptionPostMessageParameters maintain backwards compatibility.
func MsgOptionPostMessageParameters(params PostMessageParameters) MsgOption {
	return func(config *sendConfig) error {
		if params.Username != DEFAULT_MESSAGE_USERNAME {
			config.values.Set("username", params.Username)
		}

		// chat.postEphemeral support
		if params.User != DEFAULT_MESSAGE_USERNAME {
			config.values.Set("user", params.User)
		}

		// never generates an error.
		MsgOptionAsUser(params.AsUser)(config)

		if params.Parse != DEFAULT_MESSAGE_PARSE {
			config.values.Set("parse", params.Parse)
		}
		if params.LinkNames != DEFAULT_MESSAGE_LINK_NAMES {
			config.values.Set("link_names", "1")
		}

		if params.UnfurlLinks != DEFAULT_MESSAGE_UNFURL_LINKS {
			config.values.Set("unfurl_links", "true")
		}

		// I want to send a message with explicit `as_user` `true` and `unfurl_links` `false` in request.
		// Because setting `as_user` to `true` will change the default value for `unfurl_links` to `true` on Slack API side.
		if params.AsUser != DEFAULT_MESSAGE_ASUSER && params.UnfurlLinks == DEFAULT_MESSAGE_UNFURL_LINKS {
			config.values.Set("unfurl_links", "false")
		}
		if params.UnfurlMedia != DEFAULT_MESSAGE_UNFURL_MEDIA {
			config.values.Set("unfurl_media", "false")
		}
		if params.IconURL != DEFAULT_MESSAGE_ICON_URL {
			config.values.Set("icon_url", params.IconURL)
		}
		if params.IconEmoji != DEFAULT_MESSAGE_ICON_EMOJI {
			config.values.Set("icon_emoji", params.IconEmoji)
		}
		if params.Markdown != DEFAULT_MESSAGE_MARKDOWN {
			config.values.Set("mrkdwn", "false")
		}

		if params.ThreadTimestamp != DEFAULT_MESSAGE_THREAD_TIMESTAMP {
			config.values.Set("thread_ts", params.ThreadTimestamp)
		}
		if params.ReplyBroadcast != DEFAULT_MESSAGE_REPLY_BROADCAST {
			config.values.Set("reply_broadcast", "true")
		}

		return nil
	}
}
<commit_msg>fix error handling.<commit_after>package slack

import (
	"context"
	"encoding/json"
	"net/url"
	"strings"
)

const (
	DEFAULT_MESSAGE_USERNAME         = ""
	DEFAULT_MESSAGE_REPLY_BROADCAST  = false
	DEFAULT_MESSAGE_ASUSER           = false
	DEFAULT_MESSAGE_PARSE            = ""
	DEFAULT_MESSAGE_THREAD_TIMESTAMP = ""
	DEFAULT_MESSAGE_LINK_NAMES       = 0
	DEFAULT_MESSAGE_UNFURL_LINKS     = false
	DEFAULT_MESSAGE_UNFURL_MEDIA     = true
	DEFAULT_MESSAGE_ICON_URL         = ""
	DEFAULT_MESSAGE_ICON_EMOJI       = ""
	DEFAULT_MESSAGE_MARKDOWN         = true
	DEFAULT_MESSAGE_ESCAPE_TEXT      = true
)

type chatResponseFull struct {
	Channel   string `json:"channel"`
	Timestamp string `json:"ts"`
	Text      string `json:"text"`
	SlackResponse
}

// PostMessageParameters contains all the parameters necessary (including the optional ones) for a PostMessage() request
type PostMessageParameters struct {
	Username        string       `json:"user_name"`
	AsUser          bool         `json:"as_user"`
	Parse           string       `json:"parse"`
	ThreadTimestamp string       `json:"thread_ts"`
	ReplyBroadcast  bool         `json:"reply_broadcast"`
	LinkNames       int          `json:"link_names"`
	Attachments     []Attachment `json:"attachments"`
	UnfurlLinks     bool         `json:"unfurl_links"`
	UnfurlMedia     bool         `json:"unfurl_media"`
	IconURL         string       `json:"icon_url"`
	IconEmoji       string       `json:"icon_emoji"`
	Markdown        bool         `json:"mrkdwn,omitempty"`
	EscapeText      bool         `json:"escape_text"`

	// chat.postEphemeral support
	Channel string `json:"channel"`
	User    string `json:"user"`
}

// NewPostMessageParameters provides an instance of PostMessageParameters with all the sane default values set
func NewPostMessageParameters() PostMessageParameters {
	return PostMessageParameters{
		Username:        DEFAULT_MESSAGE_USERNAME,
		User:            DEFAULT_MESSAGE_USERNAME,
		AsUser:          DEFAULT_MESSAGE_ASUSER,
		Parse:           DEFAULT_MESSAGE_PARSE,
		ThreadTimestamp: DEFAULT_MESSAGE_THREAD_TIMESTAMP,
		LinkNames:       DEFAULT_MESSAGE_LINK_NAMES,
		Attachments:     nil,
		UnfurlLinks:     DEFAULT_MESSAGE_UNFURL_LINKS,
		UnfurlMedia:     DEFAULT_MESSAGE_UNFURL_MEDIA,
		IconURL:         DEFAULT_MESSAGE_ICON_URL,
		IconEmoji:       DEFAULT_MESSAGE_ICON_EMOJI,
		Markdown:        DEFAULT_MESSAGE_MARKDOWN,
		EscapeText:      DEFAULT_MESSAGE_ESCAPE_TEXT,
	}
}

// DeleteMessage deletes a message in a channel
func (api *Client) DeleteMessage(channel, messageTimestamp string) (string, string, error) {
	respChannel, respTimestamp, _, err := api.SendMessageContext(context.Background(), channel, MsgOptionDelete(messageTimestamp))
	return respChannel, respTimestamp, err
}

// DeleteMessageContext deletes a message in a channel with a custom context
func (api *Client) DeleteMessageContext(ctx context.Context, channel, messageTimestamp string) (string, string, error) {
	respChannel, respTimestamp, _, err := api.SendMessageContext(ctx, channel, MsgOptionDelete(messageTimestamp))
	return respChannel, respTimestamp, err
}

// PostMessage sends a message to a channel.
// Message is escaped by default according to https://api.slack.com/docs/formatting
// Use http://davestevens.github.io/slack-message-builder/ to help crafting your message.
func (api *Client) PostMessage(channel, text string, params PostMessageParameters) (string, string, error) {
	respChannel, respTimestamp, _, err := api.SendMessageContext(
		context.Background(),
		channel,
		MsgOptionText(text, params.EscapeText),
		MsgOptionAttachments(params.Attachments...),
		MsgOptionPostMessageParameters(params),
	)
	return respChannel, respTimestamp, err
}

// PostMessageContext sends a message to a channel with a custom context
// For more details, see PostMessage documentation
func (api *Client) PostMessageContext(ctx context.Context, channel, text string, params PostMessageParameters) (string, string, error) {
	respChannel, respTimestamp, _, err := api.SendMessageContext(
		ctx,
		channel,
		MsgOptionText(text, params.EscapeText),
		MsgOptionAttachments(params.Attachments...),
		MsgOptionPostMessageParameters(params),
	)
	return respChannel, respTimestamp, err
}

// PostEphemeral sends an ephemeral message to a user in a channel.
// Message is escaped by default according to https://api.slack.com/docs/formatting
// Use http://davestevens.github.io/slack-message-builder/ to help crafting your message.
func (api *Client) PostEphemeral(channelID, userID string, options ...MsgOption) (string, error) {
	return api.PostEphemeralContext(
		context.Background(),
		channelID,
		userID,
		options...,
	)
}

// PostEphemeralContext sends an ephemeal message to a user in a channel with a custom context
// For more details, see PostEphemeral documentation
func (api *Client) PostEphemeralContext(ctx context.Context, channelID, userID string, options ...MsgOption) (timestamp string, err error) {
	_, timestamp, _, err = api.SendMessageContext(ctx, channelID, append(options, MsgOptionPostEphemeral2(userID))...)
	return timestamp, err
}

// UpdateMessage updates a message in a channel
func (api *Client) UpdateMessage(channelID, timestamp, text string) (string, string, string, error) {
	return api.UpdateMessageContext(context.Background(), channelID, timestamp, text)
}

// UpdateMessageContext updates a message in a channel
func (api *Client) UpdateMessageContext(ctx context.Context, channelID, timestamp, text string) (string, string, string, error) {
	return api.SendMessageContext(ctx, channelID, MsgOptionUpdate(timestamp), MsgOptionText(text, true))
}

// SendMessage more flexible method for configuring messages.
func (api *Client) SendMessage(channel string, options ...MsgOption) (string, string, string, error) {
	return api.SendMessageContext(context.Background(), channel, options...)
}

// SendMessageContext more flexible method for configuring messages with a custom context.
func (api *Client) SendMessageContext(ctx context.Context, channelID string, options ...MsgOption) (channel string, timestamp string, text string, err error) {
	var (
		config   sendConfig
		response chatResponseFull
	)

	if config, err = applyMsgOptions(api.token, channelID, options...); err != nil {
		return "", "", "", err
	}

	if err = post(ctx, api.httpclient, string(config.mode), config.values, &response, api.debug); err != nil {
		return "", "", "", err
	}

	return response.Channel, response.Timestamp, response.Text, response.Err()
}

// ApplyMsgOptions utility function for debugging/testing chat requests.
func ApplyMsgOptions(token, channel string, options ...MsgOption) (string, url.Values, error) {
	config, err := applyMsgOptions(token, channel, options...)
	return string(config.mode), config.values, err
}

func applyMsgOptions(token, channel string, options ...MsgOption) (sendConfig, error) {
	config := sendConfig{
		mode: chatPostMessage,
		values: url.Values{
			"token":   {token},
			"channel": {channel},
		},
	}

	for _, opt := range options {
		if err := opt(&config); err != nil {
			return config, err
		}
	}

	return config, nil
}

func escapeMessage(message string) string {
	replacer := strings.NewReplacer("&", "&", "<", "<", ">", ">")
	return replacer.Replace(message)
}

type sendMode string

const (
	chatUpdate        sendMode = "chat.update"
	chatPostMessage   sendMode = "chat.postMessage"
	chatDelete        sendMode = "chat.delete"
	chatPostEphemeral sendMode = "chat.postEphemeral"
)

type sendConfig struct {
	mode   sendMode
	values url.Values
}

// MsgOption option provided when sending a message.
type MsgOption func(*sendConfig) error

// MsgOptionPost posts a messages, this is the default.
func MsgOptionPost() MsgOption {
	return func(config *sendConfig) error {
		config.mode = chatPostMessage
		config.values.Del("ts")
		return nil
	}
}

// MsgOptionPostEphemeral - DEPRECATED: use MsgOptionPostEphemeral2
// posts an ephemeral message.
func MsgOptionPostEphemeral() MsgOption {
	return func(config *sendConfig) error {
		config.mode = chatPostEphemeral
		config.values.Del("ts")
		return nil
	}
}

// MsgOptionPostEphemeral2 - posts an ephemeral message to the provided user.
func MsgOptionPostEphemeral2(userID string) MsgOption {
	return func(config *sendConfig) error {
		config.mode = chatPostEphemeral
		MsgOptionUser(userID)(config)
		config.values.Del("ts")

		return nil
	}
}

// MsgOptionUpdate updates a message based on the timestamp.
func MsgOptionUpdate(timestamp string) MsgOption {
	return func(config *sendConfig) error {
		config.mode = chatUpdate
		config.values.Add("ts", timestamp)
		return nil
	}
}

// MsgOptionDelete deletes a message based on the timestamp.
func MsgOptionDelete(timestamp string) MsgOption {
	return func(config *sendConfig) error {
		config.mode = chatDelete
		config.values.Add("ts", timestamp)
		return nil
	}
}

// MsgOptionAsUser whether or not to send the message as the user.
func MsgOptionAsUser(b bool) MsgOption {
	return func(config *sendConfig) error {
		if b != DEFAULT_MESSAGE_ASUSER {
			config.values.Set("as_user", "true")
		}
		return nil
	}
}

// MsgOptionUser set the user for the message.
func MsgOptionUser(userID string) MsgOption {
	return func(config *sendConfig) error {
		config.values.Set("user", userID)
		return nil
	}
}

// MsgOptionText provide the text for the message, optionally escape the provided
// text.
func MsgOptionText(text string, escape bool) MsgOption {
	return func(config *sendConfig) error {
		if escape {
			text = escapeMessage(text)
		}
		config.values.Add("text", text)
		return nil
	}
}

// MsgOptionAttachments provide attachments for the message.
func MsgOptionAttachments(attachments ...Attachment) MsgOption {
	return func(config *sendConfig) error {
		if attachments == nil {
			return nil
		}

		attachments, err := json.Marshal(attachments)
		if err == nil {
			config.values.Set("attachments", string(attachments))
		}
		return err
	}
}

// MsgOptionEnableLinkUnfurl enables link unfurling
func MsgOptionEnableLinkUnfurl() MsgOption {
	return func(config *sendConfig) error {
		config.values.Set("unfurl_links", "true")
		return nil
	}
}

// MsgOptionDisableLinkUnfurl disables link unfurling
func MsgOptionDisableLinkUnfurl() MsgOption {
	return func(config *sendConfig) error {
		config.values.Set("unfurl_links", "false")
		return nil
	}
}

// MsgOptionDisableMediaUnfurl disables media unfurling.
func MsgOptionDisableMediaUnfurl() MsgOption {
	return func(config *sendConfig) error {
		config.values.Set("unfurl_media", "false")
		return nil
	}
}

// MsgOptionDisableMarkdown disables markdown.
func MsgOptionDisableMarkdown() MsgOption {
	return func(config *sendConfig) error {
		config.values.Set("mrkdwn", "false")
		return nil
	}
}

// this function combines multiple options into a single option.
func MsgOptionCompose(options ...MsgOption) MsgOption {
	return func(c *sendConfig) error {
		for _, opt := range options {
			if err := opt(c); err != nil {
				return err
			}
		}
		return nil
	}
}

func MsgOptionParse(b bool) MsgOption {
	return func(c *sendConfig) error {
		var v string
		if b { v = "1" } else { v = "0" }
		c.values.Set("parse", v)
		return nil
	}
}

// MsgOptionPostMessageParameters maintain backwards compatibility.
func MsgOptionPostMessageParameters(params PostMessageParameters) MsgOption {
	return func(config *sendConfig) error {
		if params.Username != DEFAULT_MESSAGE_USERNAME {
			config.values.Set("username", params.Username)
		}

		// chat.postEphemeral support
		if params.User != DEFAULT_MESSAGE_USERNAME {
			config.values.Set("user", params.User)
		}

		// never generates an error.
		MsgOptionAsUser(params.AsUser)(config)

		if params.Parse != DEFAULT_MESSAGE_PARSE {
			config.values.Set("parse", params.Parse)
		}
		if params.LinkNames != DEFAULT_MESSAGE_LINK_NAMES {
			config.values.Set("link_names", "1")
		}

		if params.UnfurlLinks != DEFAULT_MESSAGE_UNFURL_LINKS {
			config.values.Set("unfurl_links", "true")
		}

		// I want to send a message with explicit `as_user` `true` and `unfurl_links` `false` in request.
		// Because setting `as_user` to `true` will change the default value for `unfurl_links` to `true` on Slack API side.
		if params.AsUser != DEFAULT_MESSAGE_ASUSER && params.UnfurlLinks == DEFAULT_MESSAGE_UNFURL_LINKS {
			config.values.Set("unfurl_links", "false")
		}
		if params.UnfurlMedia != DEFAULT_MESSAGE_UNFURL_MEDIA {
			config.values.Set("unfurl_media", "false")
		}
		if params.IconURL != DEFAULT_MESSAGE_ICON_URL {
			config.values.Set("icon_url", params.IconURL)
		}
		if params.IconEmoji != DEFAULT_MESSAGE_ICON_EMOJI {
			config.values.Set("icon_emoji", params.IconEmoji)
		}
		if params.Markdown != DEFAULT_MESSAGE_MARKDOWN {
			config.values.Set("mrkdwn", "false")
		}

		if params.ThreadTimestamp != DEFAULT_MESSAGE_THREAD_TIMESTAMP {
			config.values.Set("thread_ts", params.ThreadTimestamp)
		}
		if params.ReplyBroadcast != DEFAULT_MESSAGE_REPLY_BROADCAST {
			config.values.Set("reply_broadcast", "true")
		}

		return nil
	}
}
<|endoftext|>


<commit_before>// Copyright 2009 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package pe implements access to PE (Microsoft Windows Portable Executable) files.
package pe

import (
	"debug/dwarf"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"os"
	"strconv"
)

// A File represents an open PE file.
type File struct {
	FileHeader
	OptionalHeader interface{} // of type *OptionalHeader32 or *OptionalHeader64
	Sections       []*Section
	Symbols        []*Symbol

	closer io.Closer
}

type SectionHeader struct {
	Name                 string
	VirtualSize          uint32
	VirtualAddress       uint32
	Size                 uint32
	Offset               uint32
	PointerToRelocations uint32
	PointerToLineNumbers uint32
	NumberOfRelocations  uint16
	NumberOfLineNumbers  uint16
	Characteristics      uint32
}

type Section struct {
	SectionHeader

	// Embed ReaderAt for ReadAt method.
	// Do not embed SectionReader directly
	// to avoid having Read and Seek.
	// If a client wants Read and Seek it must use
	// Open() to avoid fighting over the seek offset
	// with other clients.
	io.ReaderAt
	sr *io.SectionReader
}

type Symbol struct {
	Name          string
	Value         uint32
	SectionNumber int16
	Type          uint16
	StorageClass  uint8
}

type ImportDirectory struct {
	OriginalFirstThunk uint32
	TimeDateStamp      uint32
	ForwarderChain     uint32
	Name               uint32
	FirstThunk         uint32

	dll string
}

// Data reads and returns the contents of the PE section.
func (s *Section) Data() ([]byte, error) {
	dat := make([]byte, s.sr.Size())
	n, err := s.sr.ReadAt(dat, 0)
	if n == len(dat) {
		err = nil
	}
	return dat[0:n], err
}

// Open returns a new ReadSeeker reading the PE section.
func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }

type FormatError struct {
	off int64
	msg string
	val interface{}
}

func (e *FormatError) Error() string {
	msg := e.msg
	if e.val != nil {
		msg += fmt.Sprintf(" '%v'", e.val)
	}
	msg += fmt.Sprintf(" in record at byte %#x", e.off)
	return msg
}

// Open opens the named file using os.Open and prepares it for use as a PE binary.
func Open(name string) (*File, error) {
	f, err := os.Open(name)
	if err != nil {
		return nil, err
	}
	ff, err := NewFile(f)
	if err != nil {
		f.Close()
		return nil, err
	}
	ff.closer = f
	return ff, nil
}

// Close closes the File.
// If the File was created using NewFile directly instead of Open,
// Close has no effect.
func (f *File) Close() error {
	var err error
	if f.closer != nil {
		err = f.closer.Close()
		f.closer = nil
	}
	return err
}

var (
	sizeofOptionalHeader32 = uintptr(binary.Size(OptionalHeader32{}))
	sizeofOptionalHeader64 = uintptr(binary.Size(OptionalHeader64{}))
)

// NewFile creates a new File for accessing a PE binary in an underlying reader.
func NewFile(r io.ReaderAt) (*File, error) {
	f := new(File)
	sr := io.NewSectionReader(r, 0, 1<<63-1)

	var dosheader [96]byte
	if _, err := r.ReadAt(dosheader[0:], 0); err != nil {
		return nil, err
	}
	var base int64
	if dosheader[0] == 'M' && dosheader[1] == 'Z' {
		signoff := int64(binary.LittleEndian.Uint32(dosheader[0x3c:]))
		var sign [4]byte
		r.ReadAt(sign[:], signoff)
		if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) {
			return nil, errors.New("Invalid PE File Format.")
		}
		base = signoff + 4
	} else {
		base = int64(0)
	}
	sr.Seek(base, os.SEEK_SET)
	if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
		return nil, err
	}
	if f.FileHeader.Machine != IMAGE_FILE_MACHINE_UNKNOWN && f.FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 && f.FileHeader.Machine != IMAGE_FILE_MACHINE_I386 {
		return nil, errors.New("Invalid PE File Format.")
	}

	var ss []byte
	if f.FileHeader.NumberOfSymbols > 0 {
		// Get COFF string table, which is located at the end of the COFF symbol table.
		sr.Seek(int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols), os.SEEK_SET)
		var l uint32
		if err := binary.Read(sr, binary.LittleEndian, &l); err != nil {
			return nil, err
		}
		ss = make([]byte, l)
		if _, err := r.ReadAt(ss, int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols)); err != nil {
			return nil, err
		}

		// Process COFF symbol table.
		sr.Seek(int64(f.FileHeader.PointerToSymbolTable), os.SEEK_SET)
		aux := uint8(0)
		for i := 0; i < int(f.FileHeader.NumberOfSymbols); i++ {
			cs := new(COFFSymbol)
			if err := binary.Read(sr, binary.LittleEndian, cs); err != nil {
				return nil, err
			}
			if aux > 0 {
				aux--
				continue
			}
			var name string
			if cs.Name[0] == 0 && cs.Name[1] == 0 && cs.Name[2] == 0 && cs.Name[3] == 0 {
				si := int(binary.LittleEndian.Uint32(cs.Name[4:]))
				name, _ = getString(ss, si)
			} else {
				name = cstring(cs.Name[:])
			}
			aux = cs.NumberOfAuxSymbols
			s := &Symbol{
				Name:          name,
				Value:         cs.Value,
				SectionNumber: cs.SectionNumber,
				Type:          cs.Type,
				StorageClass:  cs.StorageClass,
			}
			f.Symbols = append(f.Symbols, s)
		}
	}

	// Read optional header.
	sr.Seek(base, os.SEEK_SET)
	if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
		return nil, err
	}
	var oh32 OptionalHeader32
	var oh64 OptionalHeader64
	switch uintptr(f.FileHeader.SizeOfOptionalHeader) {
	case sizeofOptionalHeader32:
		if err := binary.Read(sr, binary.LittleEndian, &oh32); err != nil {
			return nil, err
		}
		if oh32.Magic != 0x10b { // PE32
			return nil, fmt.Errorf("pe32 optional header has unexpected Magic of 0x%x", oh32.Magic)
		}
		f.OptionalHeader = &oh32
	case sizeofOptionalHeader64:
		if err := binary.Read(sr, binary.LittleEndian, &oh64); err != nil {
			return nil, err
		}
		if oh64.Magic != 0x20b { // PE32+
			return nil, fmt.Errorf("pe32+ optional header has unexpected Magic of 0x%x", oh64.Magic)
		}
		f.OptionalHeader = &oh64
	}

	// Process sections.
	f.Sections = make([]*Section, f.FileHeader.NumberOfSections)
	for i := 0; i < int(f.FileHeader.NumberOfSections); i++ {
		sh := new(SectionHeader32)
		if err := binary.Read(sr, binary.LittleEndian, sh); err != nil {
			return nil, err
		}
		var name string
		if sh.Name[0] == '\x2F' {
			si, _ := strconv.Atoi(cstring(sh.Name[1:]))
			name, _ = getString(ss, si)
		} else {
			name = cstring(sh.Name[0:])
		}
		s := new(Section)
		s.SectionHeader = SectionHeader{
			Name:                 name,
			VirtualSize:          sh.VirtualSize,
			VirtualAddress:       sh.VirtualAddress,
			Size:                 sh.SizeOfRawData,
			Offset:               sh.PointerToRawData,
			PointerToRelocations: sh.PointerToRelocations,
			PointerToLineNumbers: sh.PointerToLineNumbers,
			NumberOfRelocations:  sh.NumberOfRelocations,
			NumberOfLineNumbers:  sh.NumberOfLineNumbers,
			Characteristics:      sh.Characteristics,
		}
		s.sr = io.NewSectionReader(r, int64(s.SectionHeader.Offset), int64(s.SectionHeader.Size))
		s.ReaderAt = s.sr
		f.Sections[i] = s
	}
	return f, nil
}

func cstring(b []byte) string {
	var i int
	for i = 0; i < len(b) && b[i] != 0; i++ {
	}
	return string(b[0:i])
}

// getString extracts a string from symbol string table.
func getString(section []byte, start int) (string, bool) {
	if start < 0 || start >= len(section) {
		return "", false
	}

	for end := start; end < len(section); end++ {
		if section[end] == 0 {
			return string(section[start:end]), true
		}
	}
	return "", false
}

// Section returns the first section with the given name, or nil if no such
// section exists.
func (f *File) Section(name string) *Section {
	for _, s := range f.Sections {
		if s.Name == name {
			return s
		}
	}
	return nil
}

func (f *File) DWARF() (*dwarf.Data, error) {
	// There are many other DWARF sections, but these
	// are the required ones, and the debug/dwarf package
	// does not use the others, so don't bother loading them.
	var names = [...]string{"abbrev", "info", "str"}
	var dat [len(names)][]byte
	for i, name := range names {
		name = ".debug_" + name
		s := f.Section(name)
		if s == nil {
			continue
		}
		b, err := s.Data()
		if err != nil && uint32(len(b)) < s.Size {
			return nil, err
		}
		dat[i] = b
	}

	abbrev, info, str := dat[0], dat[1], dat[2]
	return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str)
}

// ImportedSymbols returns the names of all symbols
// referred to by the binary f that are expected to be
// satisfied by other libraries at dynamic load time.
// It does not return weak symbols.
func (f *File) ImportedSymbols() ([]string, error) {
	pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64
	ds := f.Section(".idata")
	if ds == nil {
		// not dynamic, so no libraries
		return nil, nil
	}
	d, err := ds.Data()
	if err != nil {
		return nil, err
	}
	var ida []ImportDirectory
	for len(d) > 0 {
		var dt ImportDirectory
		dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4])
		dt.Name = binary.LittleEndian.Uint32(d[12:16])
		dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20])
		d = d[20:]
		if dt.OriginalFirstThunk == 0 {
			break
		}
		ida = append(ida, dt)
	}
	names, _ := ds.Data()
	var all []string
	for _, dt := range ida {
		dt.dll, _ = getString(names, int(dt.Name-ds.VirtualAddress))
		d, _ = ds.Data()
		// seek to OriginalFirstThunk
		d = d[dt.OriginalFirstThunk-ds.VirtualAddress:]
		for len(d) > 0 {
			if pe64 { // 64bit
				va := binary.LittleEndian.Uint64(d[0:8])
				d = d[8:]
				if va == 0 {
					break
				}
				if va&0x8000000000000000 > 0 { // is Ordinal
					// TODO add dynimport ordinal support.
				} else {
					fn, _ := getString(names, int(uint32(va)-ds.VirtualAddress+2))
					all = append(all, fn+":"+dt.dll)
				}
			} else { // 32bit
				va := binary.LittleEndian.Uint32(d[0:4])
				d = d[4:]
				if va == 0 {
					break
				}
				if va&0x80000000 > 0 { // is Ordinal
					// TODO add dynimport ordinal support.
					//ord := va&0x0000FFFF
				} else {
					fn, _ := getString(names, int(va-ds.VirtualAddress+2))
					all = append(all, fn+":"+dt.dll)
				}
			}
		}
	}

	return all, nil
}

// ImportedLibraries returns the names of all libraries
// referred to by the binary f that are expected to be
// linked with the binary at dynamic link time.
func (f *File) ImportedLibraries() ([]string, error) {
	// TODO
	// cgo -dynimport don't use this for windows PE, so just return.
	return nil, nil
}
<commit_msg>debug/pe: use appropriate type for sizeofOptionalHeader32<commit_after>// Copyright 2009 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

// Package pe implements access to PE (Microsoft Windows Portable Executable) files.
package pe

import (
	"debug/dwarf"
	"encoding/binary"
	"errors"
	"fmt"
	"io"
	"os"
	"strconv"
)

// A File represents an open PE file.
type File struct {
	FileHeader
	OptionalHeader interface{} // of type *OptionalHeader32 or *OptionalHeader64
	Sections       []*Section
	Symbols        []*Symbol

	closer io.Closer
}

type SectionHeader struct {
	Name                 string
	VirtualSize          uint32
	VirtualAddress       uint32
	Size                 uint32
	Offset               uint32
	PointerToRelocations uint32
	PointerToLineNumbers uint32
	NumberOfRelocations  uint16
	NumberOfLineNumbers  uint16
	Characteristics      uint32
}

type Section struct {
	SectionHeader

	// Embed ReaderAt for ReadAt method.
	// Do not embed SectionReader directly
	// to avoid having Read and Seek.
	// If a client wants Read and Seek it must use
	// Open() to avoid fighting over the seek offset
	// with other clients.
	io.ReaderAt
	sr *io.SectionReader
}

type Symbol struct {
	Name          string
	Value         uint32
	SectionNumber int16
	Type          uint16
	StorageClass  uint8
}

type ImportDirectory struct {
	OriginalFirstThunk uint32
	TimeDateStamp      uint32
	ForwarderChain     uint32
	Name               uint32
	FirstThunk         uint32

	dll string
}

// Data reads and returns the contents of the PE section.
func (s *Section) Data() ([]byte, error) {
	dat := make([]byte, s.sr.Size())
	n, err := s.sr.ReadAt(dat, 0)
	if n == len(dat) {
		err = nil
	}
	return dat[0:n], err
}

// Open returns a new ReadSeeker reading the PE section.
func (s *Section) Open() io.ReadSeeker { return io.NewSectionReader(s.sr, 0, 1<<63-1) }

type FormatError struct {
	off int64
	msg string
	val interface{}
}

func (e *FormatError) Error() string {
	msg := e.msg
	if e.val != nil {
		msg += fmt.Sprintf(" '%v'", e.val)
	}
	msg += fmt.Sprintf(" in record at byte %#x", e.off)
	return msg
}

// Open opens the named file using os.Open and prepares it for use as a PE binary.
func Open(name string) (*File, error) {
	f, err := os.Open(name)
	if err != nil {
		return nil, err
	}
	ff, err := NewFile(f)
	if err != nil {
		f.Close()
		return nil, err
	}
	ff.closer = f
	return ff, nil
}

// Close closes the File.
// If the File was created using NewFile directly instead of Open,
// Close has no effect.
func (f *File) Close() error {
	var err error
	if f.closer != nil {
		err = f.closer.Close()
		f.closer = nil
	}
	return err
}

var (
	sizeofOptionalHeader32 = uint16(binary.Size(OptionalHeader32{}))
	sizeofOptionalHeader64 = uint16(binary.Size(OptionalHeader64{}))
)

// NewFile creates a new File for accessing a PE binary in an underlying reader.
func NewFile(r io.ReaderAt) (*File, error) {
	f := new(File)
	sr := io.NewSectionReader(r, 0, 1<<63-1)

	var dosheader [96]byte
	if _, err := r.ReadAt(dosheader[0:], 0); err != nil {
		return nil, err
	}
	var base int64
	if dosheader[0] == 'M' && dosheader[1] == 'Z' {
		signoff := int64(binary.LittleEndian.Uint32(dosheader[0x3c:]))
		var sign [4]byte
		r.ReadAt(sign[:], signoff)
		if !(sign[0] == 'P' && sign[1] == 'E' && sign[2] == 0 && sign[3] == 0) {
			return nil, errors.New("Invalid PE File Format.")
		}
		base = signoff + 4
	} else {
		base = int64(0)
	}
	sr.Seek(base, os.SEEK_SET)
	if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
		return nil, err
	}
	if f.FileHeader.Machine != IMAGE_FILE_MACHINE_UNKNOWN && f.FileHeader.Machine != IMAGE_FILE_MACHINE_AMD64 && f.FileHeader.Machine != IMAGE_FILE_MACHINE_I386 {
		return nil, errors.New("Invalid PE File Format.")
	}

	var ss []byte
	if f.FileHeader.NumberOfSymbols > 0 {
		// Get COFF string table, which is located at the end of the COFF symbol table.
		sr.Seek(int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols), os.SEEK_SET)
		var l uint32
		if err := binary.Read(sr, binary.LittleEndian, &l); err != nil {
			return nil, err
		}
		ss = make([]byte, l)
		if _, err := r.ReadAt(ss, int64(f.FileHeader.PointerToSymbolTable+COFFSymbolSize*f.FileHeader.NumberOfSymbols)); err != nil {
			return nil, err
		}

		// Process COFF symbol table.
		sr.Seek(int64(f.FileHeader.PointerToSymbolTable), os.SEEK_SET)
		aux := uint8(0)
		for i := 0; i < int(f.FileHeader.NumberOfSymbols); i++ {
			cs := new(COFFSymbol)
			if err := binary.Read(sr, binary.LittleEndian, cs); err != nil {
				return nil, err
			}
			if aux > 0 {
				aux--
				continue
			}
			var name string
			if cs.Name[0] == 0 && cs.Name[1] == 0 && cs.Name[2] == 0 && cs.Name[3] == 0 {
				si := int(binary.LittleEndian.Uint32(cs.Name[4:]))
				name, _ = getString(ss, si)
			} else {
				name = cstring(cs.Name[:])
			}
			aux = cs.NumberOfAuxSymbols
			s := &Symbol{
				Name:          name,
				Value:         cs.Value,
				SectionNumber: cs.SectionNumber,
				Type:          cs.Type,
				StorageClass:  cs.StorageClass,
			}
			f.Symbols = append(f.Symbols, s)
		}
	}

	// Read optional header.
	sr.Seek(base, os.SEEK_SET)
	if err := binary.Read(sr, binary.LittleEndian, &f.FileHeader); err != nil {
		return nil, err
	}
	var oh32 OptionalHeader32
	var oh64 OptionalHeader64
	switch f.FileHeader.SizeOfOptionalHeader {
	case sizeofOptionalHeader32:
		if err := binary.Read(sr, binary.LittleEndian, &oh32); err != nil {
			return nil, err
		}
		if oh32.Magic != 0x10b { // PE32
			return nil, fmt.Errorf("pe32 optional header has unexpected Magic of 0x%x", oh32.Magic)
		}
		f.OptionalHeader = &oh32
	case sizeofOptionalHeader64:
		if err := binary.Read(sr, binary.LittleEndian, &oh64); err != nil {
			return nil, err
		}
		if oh64.Magic != 0x20b { // PE32+
			return nil, fmt.Errorf("pe32+ optional header has unexpected Magic of 0x%x", oh64.Magic)
		}
		f.OptionalHeader = &oh64
	}

	// Process sections.
	f.Sections = make([]*Section, f.FileHeader.NumberOfSections)
	for i := 0; i < int(f.FileHeader.NumberOfSections); i++ {
		sh := new(SectionHeader32)
		if err := binary.Read(sr, binary.LittleEndian, sh); err != nil {
			return nil, err
		}
		var name string
		if sh.Name[0] == '\x2F' {
			si, _ := strconv.Atoi(cstring(sh.Name[1:]))
			name, _ = getString(ss, si)
		} else {
			name = cstring(sh.Name[0:])
		}
		s := new(Section)
		s.SectionHeader = SectionHeader{
			Name:                 name,
			VirtualSize:          sh.VirtualSize,
			VirtualAddress:       sh.VirtualAddress,
			Size:                 sh.SizeOfRawData,
			Offset:               sh.PointerToRawData,
			PointerToRelocations: sh.PointerToRelocations,
			PointerToLineNumbers: sh.PointerToLineNumbers,
			NumberOfRelocations:  sh.NumberOfRelocations,
			NumberOfLineNumbers:  sh.NumberOfLineNumbers,
			Characteristics:      sh.Characteristics,
		}
		s.sr = io.NewSectionReader(r, int64(s.SectionHeader.Offset), int64(s.SectionHeader.Size))
		s.ReaderAt = s.sr
		f.Sections[i] = s
	}
	return f, nil
}

func cstring(b []byte) string {
	var i int
	for i = 0; i < len(b) && b[i] != 0; i++ {
	}
	return string(b[0:i])
}

// getString extracts a string from symbol string table.
func getString(section []byte, start int) (string, bool) {
	if start < 0 || start >= len(section) {
		return "", false
	}

	for end := start; end < len(section); end++ {
		if section[end] == 0 {
			return string(section[start:end]), true
		}
	}
	return "", false
}

// Section returns the first section with the given name, or nil if no such
// section exists.
func (f *File) Section(name string) *Section {
	for _, s := range f.Sections {
		if s.Name == name {
			return s
		}
	}
	return nil
}

func (f *File) DWARF() (*dwarf.Data, error) {
	// There are many other DWARF sections, but these
	// are the required ones, and the debug/dwarf package
	// does not use the others, so don't bother loading them.
	var names = [...]string{"abbrev", "info", "str"}
	var dat [len(names)][]byte
	for i, name := range names {
		name = ".debug_" + name
		s := f.Section(name)
		if s == nil {
			continue
		}
		b, err := s.Data()
		if err != nil && uint32(len(b)) < s.Size {
			return nil, err
		}
		dat[i] = b
	}

	abbrev, info, str := dat[0], dat[1], dat[2]
	return dwarf.New(abbrev, nil, nil, info, nil, nil, nil, str)
}

// ImportedSymbols returns the names of all symbols
// referred to by the binary f that are expected to be
// satisfied by other libraries at dynamic load time.
// It does not return weak symbols.
func (f *File) ImportedSymbols() ([]string, error) {
	pe64 := f.Machine == IMAGE_FILE_MACHINE_AMD64
	ds := f.Section(".idata")
	if ds == nil {
		// not dynamic, so no libraries
		return nil, nil
	}
	d, err := ds.Data()
	if err != nil {
		return nil, err
	}
	var ida []ImportDirectory
	for len(d) > 0 {
		var dt ImportDirectory
		dt.OriginalFirstThunk = binary.LittleEndian.Uint32(d[0:4])
		dt.Name = binary.LittleEndian.Uint32(d[12:16])
		dt.FirstThunk = binary.LittleEndian.Uint32(d[16:20])
		d = d[20:]
		if dt.OriginalFirstThunk == 0 {
			break
		}
		ida = append(ida, dt)
	}
	names, _ := ds.Data()
	var all []string
	for _, dt := range ida {
		dt.dll, _ = getString(names, int(dt.Name-ds.VirtualAddress))
		d, _ = ds.Data()
		// seek to OriginalFirstThunk
		d = d[dt.OriginalFirstThunk-ds.VirtualAddress:]
		for len(d) > 0 {
			if pe64 { // 64bit
				va := binary.LittleEndian.Uint64(d[0:8])
				d = d[8:]
				if va == 0 {
					break
				}
				if va&0x8000000000000000 > 0 { // is Ordinal
					// TODO add dynimport ordinal support.
				} else {
					fn, _ := getString(names, int(uint32(va)-ds.VirtualAddress+2))
					all = append(all, fn+":"+dt.dll)
				}
			} else { // 32bit
				va := binary.LittleEndian.Uint32(d[0:4])
				d = d[4:]
				if va == 0 {
					break
				}
				if va&0x80000000 > 0 { // is Ordinal
					// TODO add dynimport ordinal support.
					//ord := va&0x0000FFFF
				} else {
					fn, _ := getString(names, int(va-ds.VirtualAddress+2))
					all = append(all, fn+":"+dt.dll)
				}
			}
		}
	}

	return all, nil
}

// ImportedLibraries returns the names of all libraries
// referred to by the binary f that are expected to be
// linked with the binary at dynamic link time.
func (f *File) ImportedLibraries() ([]string, error) {
	// TODO
	// cgo -dynimport don't use this for windows PE, so just return.
	return nil, nil
}
<|endoftext|>


<commit_before>package buffalo

import (
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
)

func TestCookies_Get(t *testing.T) {
	r := require.New(t)
	req := httptest.NewRequest("POST", "/", nil)
	req.Header.Set("Cookie", "name=Arthur Dent; answer=42")

	c := Cookies{req, nil}

	v, err := c.Get("name")
	r.NoError(err)
	r.Equal("Arthur Dent", v)

	v, err = c.Get("answer")
	r.NoError(err)
	r.Equal("42", v)

	_, err = c.Get("unknown")
	r.EqualError(err, http.ErrNoCookie.Error())
}

func TestCookies_Set(t *testing.T) {
	r := require.New(t)
	res := httptest.NewRecorder()

	c := Cookies{&http.Request{}, res}

	c.Set("name", "Rob Pike", time.Hour*24)

	h := res.Header().Get("Set-Cookie")
	r.Equal("name=Rob Pike; Max-Age=86400", h)
}

func TestCookies_SetWithExpirationTime(t *testing.T) {
	r := require.New(t)
	res := httptest.NewRecorder()

	c := Cookies{&http.Request{}, res}

	e := time.Date(2017, 7, 29, 19, 28, 45, 0, time.UTC)
	c.SetWithExpirationTime("name", "Rob Pike", e)

	h := res.Header().Get("Set-Cookie")
	r.Equal("name=Rob Pike; Expires=Sat, 29 Jul 2017 19:28:45 GMT", h)
}

func TestCookies_Delete(t *testing.T) {
	r := require.New(t)
	res := httptest.NewRecorder()

	c := Cookies{&http.Request{}, res}

	c.Delete("remove-me")

	h := res.Header().Get("Set-Cookie")
	r.Equal("remove-me=v; Expires=Thu, 01 Jan 1970 00:00:00 GMT", h)
}
<commit_msg>Revert changes on cookies_test<commit_after>package buffalo

import (
	"net/http"
	"net/http/httptest"
	"testing"
	"time"

	"github.com/stretchr/testify/require"
)

func TestCookies_Get(t *testing.T) {
	r := require.New(t)
	req := httptest.NewRequest("POST", "/", nil)
	req.Header.Set("Cookie", "name=Arthur Dent; answer=42")

	c := Cookies{req, nil}

	v, err := c.Get("name")
	r.NoError(err)
	r.Equal("Arthur Dent", v)

	v, err = c.Get("answer")
	r.NoError(err)
	r.Equal("42", v)

	_, err = c.Get("unknown")
	r.EqualError(err, http.ErrNoCookie.Error())
}

func TestCookies_Set(t *testing.T) {
	r := require.New(t)
	res := httptest.NewRecorder()

	c := Cookies{&http.Request{}, res}

	c.Set("name", "Rob Pike", time.Hour*24)

	h := res.Header().Get("Set-Cookie")
	r.Equal("name=\"Rob Pike\"; Max-Age=86400", h)
}

func TestCookies_SetWithExpirationTime(t *testing.T) {
	r := require.New(t)
	res := httptest.NewRecorder()

	c := Cookies{&http.Request{}, res}

	e := time.Date(2017, 7, 29, 19, 28, 45, 0, time.UTC)
	c.SetWithExpirationTime("name", "Rob Pike", e)

	h := res.Header().Get("Set-Cookie")
	r.Equal("name=\"Rob Pike\"; Expires=Sat, 29 Jul 2017 19:28:45 GMT", h)
}

func TestCookies_Delete(t *testing.T) {
	r := require.New(t)
	res := httptest.NewRecorder()

	c := Cookies{&http.Request{}, res}

	c.Delete("remove-me")

	h := res.Header().Get("Set-Cookie")
	r.Equal("remove-me=v; Expires=Thu, 01 Jan 1970 00:00:00 GMT", h)
}
<|endoftext|>


<commit_before>package main

import (
    "fmt"
    "net/http"
    "math/rand"
    "time"
    "github.com/c9s/gatsby"
    "strconv"
    // "database/sql"
)

func NewsReadAction(w http.ResponseWriter, r *http.Request) {

    var newsId int64
    var err error
    output := ApiResponseJson{}
    freg := r.URL.Path[18:]

    if freg == "" {
        output.Error(404, "No news id.")
        writeResponseJson(w, output, r.FormValue("callback"))
        w.(http.Flusher).Flush()
        return
    }

    if newsId, err = strconv.ParseInt(freg, 10, 64); err != nil {
        output.Error(502, err.Error())
        writeResponseJson(w, output, r.FormValue("callback"))
        w.(http.Flusher).Flush()
        return
    }

    header := w.Header()
    header.Set("Content-Type", "application/javascript; charset=utf-8")
    header.Set("X-Content-Type-Options", "nosniff")
    header.Set("Cache-Control", "no-cache, no-store, must-revalidate")
    header.Set("Pragma", "no-cache")
    header.Set("Expires", "Thu, 01 Dec 1994 16:00:00 GMT")

    news := gatsby.NewRecord(&News{}).(*News)
    res := news.Load(newsId)
    if res.Error != nil {
        output.Error(501, res.Error.Error())
    } else {
        if res.IsEmpty {
            output.Error(404, "empty result")
        }
    }

    if output.NoError() {
        data, err := GetNewsMeta(news.Id)
        if err == nil {
            (*data)["news"] = News{
                Id: news.Id,
                Title: news.Title,
                Body: news.Body,
                PublishTime: news.PublishTime,
                Url: news.Url,
                Guid: news.Guid,
                OgImage: news.OgImage,
                PicPath: UrlDomain + news.PicPath[2:],
                ThumbPath: UrlDomain + news.ThumbPath[2:],
                Referral: news.Referral,
                CreateTime: news.CreateTime,
                IsSupport: news.IsSupport,
                IsHeadline: news.IsHeadline,
            }
            output.Data = *data
        } else {
            output.Error(501, err.Error())
        }
    }
    writeResponseJson(w, output, r.FormValue("callback"))
    w.(http.Flusher).Flush()
}

func NewsReportAction(w http.ResponseWriter, r *http.Request) {
    header := w.Header()
    header.Set("Content-Type", "application/javascript; charset=utf-8")
    header.Set("X-Content-Type-Options", "nosniff")
    header.Set("Cache-Control", "no-cache, no-store, must-revalidate")
    header.Set("Pragma", "no-cache")
    header.Set("Expires", "Thu, 01 Dec 1994 16:00:00 GMT")

    // fmt.Printf("%#v\n", news)
    output := ApiResponseJson{}
    var params []interface{}
    pi := 0
    offset := r.FormValue("offset")
    length := r.FormValue("rows")
    qsearch := r.FormValue("q")
    if offset == "" {
        offset = "0"
    }
    if length == "" {
        length = "20"
    }
    sql := "RIGHT JOIN `comments` ON `comments`.`news_id` = `news`.`id` WHERE 1 AND delete_time IS NULL ORDER BY `comments`.`created_at` DESC LIMIT ?, ? "
    if qsearch != "" {
        sql = "RIGHT JOIN `comments` ON `comments`.`news_id` = `news`.`id` WHERE 1 AND delete_time IS NULL AND title LIKE ? ORDER BY `comments`.`created_at` DESC LIMIT ?, ? "
        params = make([]interface{},3)
        params[pi] = "%" + qsearch + "%"
        pi++
    } else {
        params = make([]interface{},2)
    }
    params[pi] = offset
    pi++
    params[pi] = length
    pi++
    rows, err := gatsby.QuerySelectWith(DbConnect, &News{}, sql, params...)
    if err == nil {
        news := News{}
        data, err := gatsby.CreateStructSliceFromRows(&news, rows)
        if err != nil {
            fmt.Println(err)
            output.Error(501, err.Error())
        } else {
            newsList := data.([]News)
            for k, _ := range newsList {
                newsList[k].Raw = ""
                newsList[k].Body = ""
            }
            output.Data = newsList
        }
    } else {
        output.Error(404, err.Error())
    }
    writeResponseJson(w, output, r.FormValue("callback"))

    w.(http.Flusher).Flush()
}

func NewsListAction(w http.ResponseWriter, r *http.Request) {
    header := w.Header()
    header.Set("Content-Type", "application/javascript; charset=utf-8")
    header.Set("X-Content-Type-Options", "nosniff")
    header.Set("Cache-Control", "no-cache, no-store, must-revalidate")
    header.Set("Pragma", "no-cache")
    header.Set("Expires", "Thu, 01 Dec 1994 16:00:00 GMT")

    // fmt.Printf("%#v\n", news)
    output := ApiResponseJson{}
    var params []interface{}
    pi := 0
    offset := r.FormValue("offset")
    length := r.FormValue("rows")
    qsearch := r.FormValue("q")
    if offset == "" {
        offset = "0"
    }
    if length == "" {
        length = "20"
    }
    sql := "WHERE 1 AND delete_time IS NULL ORDER BY `create_time` LIMIT ?, ? "
    if qsearch != "" {
        sql = "WHERE 1 AND delete_time IS NULL AND title LIKE ? ORDER BY `create_time` DESC LIMIT ?, ? "
        params = make([]interface{},3)
        params[pi] = "%" + qsearch + "%"
        pi++
    } else {
        params = make([]interface{},2)
    }
    params[pi] = offset
    pi++
    params[pi] = length
    pi++
    rows, err := gatsby.QuerySelectWith(DbConnect, &News{}, sql, params...)
    if err == nil {
        news := News{}
        data, err := gatsby.CreateStructSliceFromRows(&news, rows)
        if err != nil {
            fmt.Println(err)
            output.Error(501, err.Error())
        } else {
            newsList := data.([]News)
            for k, _ := range newsList {
                newsList[k].Raw = ""
                newsList[k].Body = ""
            }
            output.Data = newsList
        }
    } else {
        output.Error(404, err.Error())
    }
    writeResponseJson(w, output, r.FormValue("callback"))

    w.(http.Flusher).Flush()
}

func NewsHotAction(w http.ResponseWriter, r *http.Request) {
    header := w.Header()
    header.Set("Content-Type", "application/javascript; charset=utf-8")
    header.Set("X-Content-Type-Options", "nosniff")
    header.Set("Cache-Control", "no-cache, no-store, must-revalidate")
    header.Set("Pragma", "no-cache")
    header.Set("Expires", "Thu, 01 Dec 1994 16:00:00 GMT")

    // fmt.Printf("%#v\n", news)
    output := ApiResponseJson{}
    randomSource := rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
    var params []interface{}
    pi := 0
    offset := fmt.Sprintf("%d", randomSource.Int31n(2000))
    qlength := 100
    // offset := r.FormValue("offset")
    length := r.FormValue("rows")
    qsearch := r.FormValue("q")
    // if offset == "" {
    //     offset = "0"
    // }
    if length == "" {
        length = "30"
    }
    sql := "WHERE 1 AND delete_time IS NULL ORDER BY guid LIMIT ?, ? "
    if qsearch != "" {
        sql = "WHERE 1 AND delete_time IS NULL AND title LIKE ? ORDER BY guid LIMIT ?, ? "
        params = make([]interface{},3)
        params[pi] = "%" + qsearch + "%"
        pi++
        offset = "0"
    } else {
        params = make([]interface{},2)
    }
    // params[pi] = randomSource.Int31n(29)
    // pi++
    params[pi] = offset
    pi++
    params[pi] = qlength
    pi++
    rows, err := gatsby.QuerySelectWith(DbConnect, &News{}, sql, params...)
    if err == nil {
        news := News{}
        data, err := gatsby.CreateStructSliceFromRows(&news, rows)
        if err != nil {
            fmt.Println(err)
            output.Error(501, err.Error())
        } else {
            newsList := data.([]News)
            l := len(newsList)
            for i := 1 ; i<l ; i++ {
                j := randomSource.Int63n(int64(i))
                newsList[i], newsList[j] = newsList[j], newsList[i]
            }
            var limit int
            if len(newsList) > 30 {
                limit = 30
            } else {
                limit = len(newsList)
            }
            nco := newsList[:limit]
            for k, _ := range nco {
                nco[k].Raw = ""
                nco[k].Body = ""
            }
            output.Data = nco
        }
    } else {
        output.Error(404, err.Error())
    }
    writeResponseJson(w, output, r.FormValue("callback"))

    w.(http.Flusher).Flush()
}

func NewsHotestsAction(w http.ResponseWriter, r *http.Request) {

    header := w.Header()
    header.Set("Content-Type", "application/javascript; charset=utf-8")
    header.Set("X-Content-Type-Options", "nosniff")
    header.Set("Cache-Control", "no-cache, no-store, must-revalidate")
    header.Set("Pragma", "no-cache")
    header.Set("Expires", "Thu, 01 Dec 1994 16:00:00 GMT")

    // fmt.Printf("%#v\n", news)
    output := ApiResponseJson{}
    randSource := rand.New(rand.NewSource( time.Now().UTC().UnixNano() ))
    var news_max int64
    err := DbConnect.QueryRow("SELECT MAX(id) FROM news").Scan(&news_max)
    switch {
    case err != nil:
        fmt.Println(err)
        output.Error(501, err.Error())
    default:
        params := make([]interface{}, 7)
        for i := 0; i< 6; i++ {
            params[i] = randSource.Int63n(news_max)
        }
        params[6] = time.Now().AddDate(0,0,-2).Unix()
        fmt.Println(params)
        rows, err := gatsby.QuerySelectWith(DbConnect, &News{}, "WHERE id IN (?,?,?,?,?,?) AND create_time > ? AND thumb_path <> '' AND delete_time IS NULL ", params...)
        if err == nil {
            news := News{}
            data, err := gatsby.CreateStructSliceFromRows(&news, rows)
            if err != nil {
                fmt.Println(err)
                output.Error(501, err.Error())
            } else {
                output.Data = data.([]News)
            }
        } else {
            output.Error(404, err.Error())
        }
    }
    writeResponseJson(w, output, r.FormValue("callback"))

    w.(http.Flusher).Flush()
}<commit_msg>Fix rand error.<commit_after>package main

import (
    "fmt"
    "net/http"
    "math/rand"
    "time"
    "github.com/c9s/gatsby"
    "strconv"
    // "database/sql"
)

func NewsReadAction(w http.ResponseWriter, r *http.Request) {

    var newsId int64
    var err error
    output := ApiResponseJson{}
    freg := r.URL.Path[18:]

    if freg == "" {
        output.Error(404, "No news id.")
        writeResponseJson(w, output, r.FormValue("callback"))
        w.(http.Flusher).Flush()
        return
    }

    if newsId, err = strconv.ParseInt(freg, 10, 64); err != nil {
        output.Error(502, err.Error())
        writeResponseJson(w, output, r.FormValue("callback"))
        w.(http.Flusher).Flush()
        return
    }

    header := w.Header()
    header.Set("Content-Type", "application/javascript; charset=utf-8")
    header.Set("X-Content-Type-Options", "nosniff")
    header.Set("Cache-Control", "no-cache, no-store, must-revalidate")
    header.Set("Pragma", "no-cache")
    header.Set("Expires", "Thu, 01 Dec 1994 16:00:00 GMT")

    news := gatsby.NewRecord(&News{}).(*News)
    res := news.Load(newsId)
    if res.Error != nil {
        output.Error(501, res.Error.Error())
    } else {
        if res.IsEmpty {
            output.Error(404, "empty result")
        }
    }

    if output.NoError() {
        data, err := GetNewsMeta(news.Id)
        if err == nil {
            (*data)["news"] = News{
                Id: news.Id,
                Title: news.Title,
                Body: news.Body,
                PublishTime: news.PublishTime,
                Url: news.Url,
                Guid: news.Guid,
                OgImage: news.OgImage,
                PicPath: UrlDomain + news.PicPath[2:],
                ThumbPath: UrlDomain + news.ThumbPath[2:],
                Referral: news.Referral,
                CreateTime: news.CreateTime,
                IsSupport: news.IsSupport,
                IsHeadline: news.IsHeadline,
            }
            output.Data = *data
        } else {
            output.Error(501, err.Error())
        }
    }
    writeResponseJson(w, output, r.FormValue("callback"))
    w.(http.Flusher).Flush()
}

func NewsReportAction(w http.ResponseWriter, r *http.Request) {
    header := w.Header()
    header.Set("Content-Type", "application/javascript; charset=utf-8")
    header.Set("X-Content-Type-Options", "nosniff")
    header.Set("Cache-Control", "no-cache, no-store, must-revalidate")
    header.Set("Pragma", "no-cache")
    header.Set("Expires", "Thu, 01 Dec 1994 16:00:00 GMT")

    // fmt.Printf("%#v\n", news)
    output := ApiResponseJson{}
    var params []interface{}
    pi := 0
    offset := r.FormValue("offset")
    length := r.FormValue("rows")
    qsearch := r.FormValue("q")
    if offset == "" {
        offset = "0"
    }
    if length == "" {
        length = "20"
    }
    sql := "RIGHT JOIN `comments` ON `comments`.`news_id` = `news`.`id` WHERE 1 AND `news`.`referral` = \"批踢踢實業坊\" AND delete_time IS NULL ORDER BY `comments`.`created_at` DESC LIMIT ?, ? "
    if qsearch != "" {
        sql = "RIGHT JOIN `comments` ON `comments`.`news_id` = `news`.`id` WHERE 1 AND `news`.`referral` = \"批踢踢實業坊\" AND delete_time IS NULL AND title LIKE ? ORDER BY `comments`.`created_at` DESC LIMIT ?, ? "
        params = make([]interface{},3)
        params[pi] = "%" + qsearch + "%"
        pi++
    } else {
        params = make([]interface{},2)
    }
    params[pi] = offset
    pi++
    params[pi] = length
    pi++
    rows, err := gatsby.QuerySelectWith(DbConnect, &News{}, sql, params...)
    if err == nil {
        news := News{}
        data, err := gatsby.CreateStructSliceFromRows(&news, rows)
        if err != nil {
            fmt.Println(err)
            output.Error(501, err.Error())
        } else {
            newsList := data.([]News)
            for k, _ := range newsList {
                newsList[k].Raw = ""
                newsList[k].Body = ""
            }
            output.Data = newsList
        }
    } else {
        output.Error(404, err.Error())
    }
    writeResponseJson(w, output, r.FormValue("callback"))

    w.(http.Flusher).Flush()
}

func NewsListAction(w http.ResponseWriter, r *http.Request) {
    header := w.Header()
    header.Set("Content-Type", "application/javascript; charset=utf-8")
    header.Set("X-Content-Type-Options", "nosniff")
    header.Set("Cache-Control", "no-cache, no-store, must-revalidate")
    header.Set("Pragma", "no-cache")
    header.Set("Expires", "Thu, 01 Dec 1994 16:00:00 GMT")

    // fmt.Printf("%#v\n", news)
    output := ApiResponseJson{}
    var params []interface{}
    pi := 0
    offset := r.FormValue("offset")
    length := r.FormValue("rows")
    qsearch := r.FormValue("q")
    if offset == "" {
        offset = "0"
    }
    if length == "" {
        length = "20"
    }
    sql := "WHERE 1 AND delete_time IS NULL ORDER BY `create_time` LIMIT ?, ? "
    if qsearch != "" {
        sql = "WHERE 1 AND delete_time IS NULL AND title LIKE ? ORDER BY `create_time` DESC LIMIT ?, ? "
        params = make([]interface{},3)
        params[pi] = "%" + qsearch + "%"
        pi++
    } else {
        params = make([]interface{},2)
    }
    params[pi] = offset
    pi++
    params[pi] = length
    pi++
    rows, err := gatsby.QuerySelectWith(DbConnect, &News{}, sql, params...)
    if err == nil {
        news := News{}
        data, err := gatsby.CreateStructSliceFromRows(&news, rows)
        if err != nil {
            fmt.Println(err)
            output.Error(501, err.Error())
        } else {
            newsList := data.([]News)
            for k, _ := range newsList {
                newsList[k].Raw = ""
                newsList[k].Body = ""
            }
            output.Data = newsList
        }
    } else {
        output.Error(404, err.Error())
    }
    writeResponseJson(w, output, r.FormValue("callback"))

    w.(http.Flusher).Flush()
}

func NewsHotAction(w http.ResponseWriter, r *http.Request) {
    header := w.Header()
    header.Set("Content-Type", "application/javascript; charset=utf-8")
    header.Set("X-Content-Type-Options", "nosniff")
    header.Set("Cache-Control", "no-cache, no-store, must-revalidate")
    header.Set("Pragma", "no-cache")
    header.Set("Expires", "Thu, 01 Dec 1994 16:00:00 GMT")

    // fmt.Printf("%#v\n", news)
    output := ApiResponseJson{}
    randomSource := rand.New(rand.NewSource(time.Now().UTC().UnixNano()))
    var params []interface{}
    pi := 0
    offset := fmt.Sprintf("%d", randomSource.Int31n(2000))
    qlength := 100
    // offset := r.FormValue("offset")
    length := r.FormValue("rows")
    qsearch := r.FormValue("q")
    // if offset == "" {
    //     offset = "0"
    // }
    if length == "" {
        length = "30"
    }
    sql := "WHERE 1 AND delete_time IS NULL ORDER BY guid LIMIT ?, ? "
    if qsearch != "" {
        sql = "WHERE 1 AND delete_time IS NULL AND title LIKE ? ORDER BY guid LIMIT ?, ? "
        params = make([]interface{},3)
        params[pi] = "%" + qsearch + "%"
        pi++
        offset = "0"
    } else {
        params = make([]interface{},2)
    }
    // params[pi] = randomSource.Int31n(29)
    // pi++
    params[pi] = offset
    pi++
    params[pi] = qlength
    pi++
    rows, err := gatsby.QuerySelectWith(DbConnect, &News{}, sql, params...)
    if err == nil {
        news := News{}
        data, err := gatsby.CreateStructSliceFromRows(&news, rows)
        if err != nil {
            fmt.Println(err)
            output.Error(501, err.Error())
        } else {
            newsList := data.([]News)
            l := len(newsList)
            for i := 1 ; i<l ; i++ {
                j := randomSource.Int63n(int64(i))
                newsList[i], newsList[j] = newsList[j], newsList[i]
            }
            var limit int
            if len(newsList) > 30 {
                limit = 30
            } else {
                limit = len(newsList)
            }
            nco := newsList[:limit]
            for k, _ := range nco {
                nco[k].Raw = ""
                nco[k].Body = ""
            }
            output.Data = nco
        }
    } else {
        output.Error(404, err.Error())
    }
    writeResponseJson(w, output, r.FormValue("callback"))

    w.(http.Flusher).Flush()
}

func NewsHotestsAction(w http.ResponseWriter, r *http.Request) {

    header := w.Header()
    header.Set("Content-Type", "application/javascript; charset=utf-8")
    header.Set("X-Content-Type-Options", "nosniff")
    header.Set("Cache-Control", "no-cache, no-store, must-revalidate")
    header.Set("Pragma", "no-cache")
    header.Set("Expires", "Thu, 01 Dec 1994 16:00:00 GMT")

    // fmt.Printf("%#v\n", news)
    output := ApiResponseJson{}
    randSource := rand.New(rand.NewSource( time.Now().UTC().UnixNano() ))
    var news_max int64
    err := DbConnect.QueryRow("SELECT MAX(id) FROM news").Scan(&news_max)
    switch {
    case err != nil:
        fmt.Println(err)
        output.Error(501, err.Error())
    default:
        params := make([]interface{}, 7)
        for i := 0; i< 6; i++ {
            params[i] = randSource.Int63n(news_max)
        }
        params[6] = time.Now().AddDate(0,0,-2).Unix()
        fmt.Println(params)
        rows, err := gatsby.QuerySelectWith(DbConnect, &News{}, "WHERE id IN (?,?,?,?,?,?) AND create_time > ? AND thumb_path <> '' AND delete_time IS NULL ", params...)
        if err == nil {
            news := News{}
            data, err := gatsby.CreateStructSliceFromRows(&news, rows)
            if err != nil {
                fmt.Println(err)
                output.Error(501, err.Error())
            } else {
                output.Data = data.([]News)
            }
        } else {
            output.Error(404, err.Error())
        }
    }
    writeResponseJson(w, output, r.FormValue("callback"))

    w.(http.Flusher).Flush()
}<|endoftext|>


<commit_before>package main

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/binary"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net"
	"time"
)

// Conn is a mobile client connection.
type Conn struct {
	conn              *tls.Conn
	headerSize        int
	maxMsgSize        int
	readWriteDeadline time.Duration
}

// NewConn creates a new server-side connection object.
// Default values for headerSize, maxMsgSize, and readWriteDeadline
// are 4 bytes, 4294967295 bytes (4GB), and 300 seconds, respectively.
func NewConn(conn *tls.Conn, headerSize, maxMsgSize, readWriteDeadline int) *Conn {
	if headerSize == 0 {
		headerSize = 4
	}
	if maxMsgSize == 0 {
		maxMsgSize = 4294967295
	}
	if readWriteDeadline == 0 {
		readWriteDeadline = 300
	}

	return &Conn{
		conn:              conn,
		headerSize:        headerSize,
		maxMsgSize:        maxMsgSize,
		readWriteDeadline: time.Second * time.Duration(readWriteDeadline),
	}
}

// Dial creates a new client side connection to a given network address with optional root CA and/or a client certificate (PEM encoded X.509 cert/key).
func Dial(addr string, rootCA []byte, clientCert []byte, clientCertKey []byte) (*Conn, error) {
	var roots *x509.CertPool
	var certs []tls.Certificate
	if rootCA != nil {
		roots = x509.NewCertPool()
		ok := roots.AppendCertsFromPEM(rootCA)
		if !ok {
			return nil, errors.New("failed to parse root certificate")
		}
	}
	if clientCert != nil {
		tlsCert, err := tls.X509KeyPair(clientCert, clientCertKey)
		if err != nil {
			return nil, fmt.Errorf("failed to parse the client certificate: %v", err)
		}
		certs = []tls.Certificate{tlsCert}
	}

	c, err := tls.Dial("tcp", addr, &tls.Config{RootCAs: roots, Certificates: certs})
	if err != nil {
		return nil, err
	}

	return NewConn(c, 0, 0, 0), nil
}

// ReadMsg waits for and reads the next incoming message from the TLS connection and deserializes it into the given message object.
func (c *Conn) ReadMsg(msg interface{}) (n int, err error) {
	n, data, err := c.Read()
	if err != nil {
		return
	}

	if err = json.Unmarshal(data, msg); err != nil {
		return
	}

	return
}

// Read waits for and reads the next incoming message from the TLS connection.
func (c *Conn) Read() (n int, msg []byte, err error) {
	if err = c.conn.SetReadDeadline(time.Now().Add(c.readWriteDeadline)); err != nil {
		return
	}

	// read the content length header
	h := make([]byte, c.headerSize)
	n, err = c.conn.Read(h)
	if err != nil {
		return
	}
	if n != c.headerSize {
		err = fmt.Errorf("expected to read header size %v bytes but instead read %v bytes", c.headerSize, n)
		return
	}

	// calculate the content length
	n = readHeaderBytes(h)

	// read the message content
	msg = make([]byte, n)
	total := 0
	for total != n {
		// todo: log here in case it gets stuck, or there is a dos attack, pumping up cpu usage!
		i, err := c.conn.Read(msg[total:])
		if err != nil {
			log.Fatalln("Error while reading incoming message:", err)
			break
		}
		total += i
	}
	if err != nil {
		log.Fatalln("Error while reading incoming message:", err)
	}

	return
}

// WriteMsg serializes and writes given message to the connection with appropriate header.
func (c *Conn) WriteMsg(msg *interface{}) (n int, err error) {
	data, err := json.Marshal(msg)
	if err != nil {
		return 0, fmt.Errorf("failed to serialize the given message: %v", err)
	}

	return c.Write(data)
}

// Write writes given message to the connection.
func (c *Conn) Write(msg []byte) (n int, err error) {
	l := len(msg)
	h := makeHeaderBytes(l, c.headerSize)

	// write the header
	n, err = c.conn.Write(h)
	if err != nil {
		return
	}
	if n != c.headerSize {
		err = fmt.Errorf("expected to write %v bytes but only wrote %v bytes", l, n)
	}

	// write the body
	n, err = c.conn.Write(msg)
	if err != nil {
		return
	}
	if n != l {
		err = fmt.Errorf("expected to write %v bytes but only wrote %v bytes", l, n)
	}

	return
}

// RemoteAddr returns the remote network address.
func (c *Conn) RemoteAddr() net.Addr {
	return c.conn.RemoteAddr()
}

// ConnectionState returns basic TLS details about the connection.
func (c *Conn) ConnectionState() tls.ConnectionState {
	return c.conn.ConnectionState()
}

// Close closes a connection.
func (c *Conn) Close() error {
	// todo: if session.err is nil, send a close req and wait ack then close? (or even wait for everything else to finish?)
	return c.conn.Close()
}

func makeHeaderBytes(h, size int) []byte {
	b := make([]byte, size)
	binary.LittleEndian.PutUint32(b, uint32(h))
	return b
}

func readHeaderBytes(h []byte) int {
	return int(binary.LittleEndian.Uint32(h))
}
<commit_msg>add note<commit_after>package main

import (
	"crypto/tls"
	"crypto/x509"
	"encoding/binary"
	"encoding/json"
	"errors"
	"fmt"
	"log"
	"net"
	"time"
)

// Conn is a mobile client connection.
type Conn struct {
	conn              *tls.Conn
	headerSize        int
	maxMsgSize        int
	readWriteDeadline time.Duration
}

// NewConn creates a new server-side connection object.
// Default values for headerSize, maxMsgSize, and readWriteDeadline
// are 4 bytes, 4294967295 bytes (4GB), and 300 seconds, respectively.
func NewConn(conn *tls.Conn, headerSize, maxMsgSize, readWriteDeadline int) *Conn {
	if headerSize == 0 {
		headerSize = 4
	}
	if maxMsgSize == 0 {
		maxMsgSize = 4294967295
	}
	if readWriteDeadline == 0 {
		readWriteDeadline = 300
	}

	return &Conn{
		conn:              conn,
		headerSize:        headerSize,
		maxMsgSize:        maxMsgSize,
		readWriteDeadline: time.Second * time.Duration(readWriteDeadline),
	}
}

// Dial creates a new client side connection to a given network address with optional root CA and/or a client certificate (PEM encoded X.509 cert/key).
func Dial(addr string, rootCA []byte, clientCert []byte, clientCertKey []byte) (*Conn, error) {
	var roots *x509.CertPool
	var certs []tls.Certificate
	if rootCA != nil {
		roots = x509.NewCertPool()
		ok := roots.AppendCertsFromPEM(rootCA)
		if !ok {
			return nil, errors.New("failed to parse root certificate")
		}
	}
	if clientCert != nil {
		tlsCert, err := tls.X509KeyPair(clientCert, clientCertKey)
		if err != nil {
			return nil, fmt.Errorf("failed to parse the client certificate: %v", err)
		}
		certs = []tls.Certificate{tlsCert}
	}

	c, err := tls.Dial("tcp", addr, &tls.Config{RootCAs: roots, Certificates: certs})
	if err != nil {
		return nil, err
	}

	return NewConn(c, 0, 0, 0), nil
}

// ReadMsg waits for and reads the next incoming message from the TLS connection and deserializes it into the given message object.
func (c *Conn) ReadMsg(msg interface{}) (n int, err error) {
	n, data, err := c.Read()
	if err != nil {
		return
	}

	if err = json.Unmarshal(data, msg); err != nil {
		return
	}

	return
}

// Read waits for and reads the next incoming message from the TLS connection.
func (c *Conn) Read() (n int, msg []byte, err error) {
	if err = c.conn.SetReadDeadline(time.Now().Add(c.readWriteDeadline)); err != nil {
		return
	}

	// read the content length header
	h := make([]byte, c.headerSize)
	n, err = c.conn.Read(h)
	if err != nil {
		return
	}
	if n != c.headerSize {
		err = fmt.Errorf("expected to read header size %v bytes but instead read %v bytes", c.headerSize, n)
		return
	}

	// calculate the content length
	n = readHeaderBytes(h)

	// read the message content
	msg = make([]byte, n)
	total := 0
	for total != n {
		// todo: log here in case it gets stuck, or there is a dos attack, pumping up cpu usage!
		i, err := c.conn.Read(msg[total:])
		if err != nil {
			log.Fatalln("Error while reading incoming message:", err)
			break
		}
		total += i
	}
	if err != nil {
		log.Fatalln("Error while reading incoming message:", err)
	}

	return
}

// WriteMsg serializes and writes given message to the connection with appropriate header.
func (c *Conn) WriteMsg(msg *interface{}) (n int, err error) {
	data, err := json.Marshal(msg)
	if err != nil {
		return 0, fmt.Errorf("failed to serialize the given message: %v", err)
	}

	return c.Write(data)
}

// Write writes given message to the connection.
func (c *Conn) Write(msg []byte) (n int, err error) {
	l := len(msg)
	h := makeHeaderBytes(l, c.headerSize)

	// write the header
	n, err = c.conn.Write(h)
	if err != nil {
		return
	}
	if n != c.headerSize {
		err = fmt.Errorf("expected to write %v bytes but only wrote %v bytes", l, n)
	}

	// write the body
	// todo: do we need a loop? bufio uses a loop but it might be due to buff length limitation
	n, err = c.conn.Write(msg)
	if err != nil {
		return
	}
	if n != l {
		err = fmt.Errorf("expected to write %v bytes but only wrote %v bytes", l, n)
	}

	return
}

// RemoteAddr returns the remote network address.
func (c *Conn) RemoteAddr() net.Addr {
	return c.conn.RemoteAddr()
}

// ConnectionState returns basic TLS details about the connection.
func (c *Conn) ConnectionState() tls.ConnectionState {
	return c.conn.ConnectionState()
}

// Close closes a connection.
func (c *Conn) Close() error {
	// todo: if session.err is nil, send a close req and wait ack then close? (or even wait for everything else to finish?)
	return c.conn.Close()
}

func makeHeaderBytes(h, size int) []byte {
	b := make([]byte, size)
	binary.LittleEndian.PutUint32(b, uint32(h))
	return b
}

func readHeaderBytes(h []byte) int {
	return int(binary.LittleEndian.Uint32(h))
}
<|endoftext|>


<commit_before>package golangNeo4jBoltDriver

import (
	"bytes"
	"database/sql/driver"
	"fmt"
	"net"
	"time"

	"net/url"
	"strings"

	"github.com/johnnadratowski/golang-neo4j-bolt-driver/encoding"
	"github.com/johnnadratowski/golang-neo4j-bolt-driver/structures/messages"
	"io"
)

// Conn represents a connection to Neo4J
//
// Conn objects, and any prepared statements/transactions within ARE NOT
// THREAD SAFE.  If you want to use multipe go routines with these objects,
// you should use a driver to create a new conn for each routine.
type Conn interface {
	Prepare(query string) (driver.Stmt, error)
	Close() error
	Begin() (driver.Tx, error)
	SetChunkSize(uint16)
	SetTimeout(time.Duration)
}

type boltConn struct {
	connStr       string
	url           *url.URL
	authToken     string
	conn          net.Conn
	serverVersion []byte
	timeout       time.Duration
	chunkSize     uint16
	closed 		bool
}

// newBoltConn Creates a new bolt connection
func newBoltConn(connStr string) (*boltConn, error) {
	url, err := url.Parse(connStr)
	if err != nil {
		return nil, err
	} else if strings.ToLower(url.Scheme) != "bolt" {
		return nil, fmt.Errorf("Unsupported connection string scheme: %s. Driver only supports 'bolt' scheme.", url.Scheme)
	}

	authToken := ""
	if url.User != nil {
		authToken = url.User.Username()
	}

	// TODO: TLS Support
	c := &boltConn{
		connStr:   connStr,
		url:       url,
		authToken: authToken,
		// TODO: Test best default
		// Default to 10 second timeout
		timeout: time.Second * time.Duration(10),
		// TODO: Test best default
		// Default to 4096 byte chunks. Same as a golang bufio reader.
		chunkSize:     4096,
		serverVersion: make([]byte, 4),
	}

	err = c.initialize()
	if err != nil {
		return nil, err
	}

	return c, nil
}

func (c *boltConn) initialize() error {
	var err error
	c.conn, err = net.DialTimeout("tcp", c.url.Host, c.timeout)
	if err != nil {
		Logger.Println("An error occurred connecting:", err)
		return err
	}

	numWritten, err := c.Write(magicPreamble)
	if numWritten != 4 {
		Logger.Printf("Couldn't write expected bytes for magic preamble. Written: %d. Expected: 4", numWritten)
		if err != nil {
			Logger.Println("An error occurred writing magic preamble:", err)
		}
		c.Close()
		return err
	}

	numWritten, err = c.Write(supportedVersions)
	if numWritten != 16 {
		Logger.Printf("Couldn't write expected bytes for magic preamble. Written: %d. Expected: 16", numWritten)
		if err != nil {
			Logger.Println("An error occurred writing supported versions:", err)
		}
		c.Close()
		return err
	}

	numRead, err := c.Read(c.serverVersion)
	if numRead != 4 {
		Logger.Printf("Could not read server version response. Read %d bytes. Expected 4 bytes. Output: %s", numRead, c.serverVersion)
		if err != nil {
			Logger.Println("An error occurred reading server version:", err)
		}
		c.Close()
		return err
	} else if bytes.Compare(c.serverVersion, noVersionSupported) == 0 {
		Logger.Println("No version supported from server")
		c.Close()
		return fmt.Errorf("NO VERSION SUPPORTED")
	}

	if err = encoding.NewEncoder(c, c.chunkSize).Encode(messages.NewInitMessage(ClientID, c.authToken)); err != nil {
		Logger.Println("An error occurred reading server version:", err)
		c.Close()
		return err
	}

	respInt, err := encoding.NewDecoder(c).Decode()
	if err != nil {
		c.Close()
		return err
	}

	switch resp := respInt.(type) {
	case messages.SuccessMessage:
		Logger.Printf("Successfully initiated Bolt connection:%+v", resp)
		return nil
	case messages.FailureMessage:
		Logger.Printf("Got a failure message when initializing connection :%+v", resp)
		c.Close()
		return fmt.Errorf("An error occurred initializing Neo4j Bolt Connection: %+v", resp.Metadata)
	default:
		Logger.Printf("Got an unrecognized message when initializing connection :%+v", resp)
		c.Close()
		return fmt.Errorf("Unrecognized response from the server: %#v", resp)
	}
}

// Read reads the data from the underlying connection
func (c *boltConn) Read(b []byte) (n int, err error) {
	if err := c.conn.SetReadDeadline(time.Now().Add(c.timeout)); err != nil {
		return 0, err
	}
	n, err = c.conn.Read(b)
	TraceLogger.Printf("Read %d bytes from stream:\n\n%s\n", n, SprintByteHex(b))
	if err != nil && err != io.EOF {
		Logger.Println("An error occurred reading from stream: ", err)
	}
	return n, err
}

// Write writes the data to the underlying connection
func (c *boltConn) Write(b []byte) (n int, err error) {
	if err := c.conn.SetWriteDeadline(time.Now().Add(c.timeout)); err != nil {
		return 0, err
	}
	n, err = c.conn.Write(b)
	TraceLogger.Printf("Wrote %d of %d bytes to stream:\n\n%s\n", len(b), n, SprintByteHex(b[:n]))
	if err != nil {
		Logger.Println("An error occurred writing to stream: ", err)
	}
	return n, err
}

// Close closes the connection
// Driver may allow for pooling in the future, keeping connections alive
func (c *boltConn) Close() error {
	c.closed = true
	// TODO: Connection Pooling?
	err := c.conn.Close()

	if err != nil {
		Logger.Print("An error occurred closing the connection", err)
		return err
	}
	return nil
}

// Prepare prepares a new statement for a query
func (c *boltConn) Prepare(query string) (driver.Stmt, error) {
	if c.closed {
		return nil, fmt.Errorf("Connection already closed")
	}
	return newStmt(query, c), nil
}

// Begin begins a new transaction with the Neo4J Database
func (c *boltConn) Begin() (driver.Tx, error) {
	if c.closed {
		return nil, fmt.Errorf("Connection already closed")
	}
	// TODO: Implement

	return nil, nil
}

// Sets the size of the chunks to write to the stream
func (c *boltConn) SetChunkSize(chunkSize uint16) {
	c.chunkSize = chunkSize
}

// Sets the timeout for reading and writing to the stream
func (c *boltConn) SetTimeout(timeout time.Duration) {
	c.timeout = timeout
}
<commit_msg>cleaned up some things before beginning work on driver<commit_after>package golangNeo4jBoltDriver

import (
	"bytes"
	"database/sql/driver"
	"fmt"
	"net"
	"time"

	"net/url"
	"strings"

	"github.com/johnnadratowski/golang-neo4j-bolt-driver/encoding"
	"github.com/johnnadratowski/golang-neo4j-bolt-driver/structures/messages"
	"io"
)

// Conn represents a connection to Neo4J
//
// Conn objects, and any prepared statements/transactions within ARE NOT
// THREAD SAFE.  If you want to use multipe go routines with these objects,
// you should use a driver to create a new conn for each routine.
type Conn interface {
	Prepare(query string) (driver.Stmt, error)
	Close() error
	Begin() (driver.Tx, error)
	SetChunkSize(uint16)
	SetTimeout(time.Duration)
}

type boltConn struct {
	connStr       string
	url           *url.URL
	authToken     string
	conn          net.Conn
	serverVersion []byte
	timeout       time.Duration
	chunkSize     uint16
	closed        bool
}

// newBoltConn Creates a new bolt connection
func newBoltConn(connStr string) (*boltConn, error) {
	url, err := url.Parse(connStr)
	if err != nil {
		return nil, err
	} else if strings.ToLower(url.Scheme) != "bolt" {
		return nil, fmt.Errorf("Unsupported connection string scheme: %s. Driver only supports 'bolt' scheme.", url.Scheme)
	}

	authToken := ""
	if url.User != nil {
		authToken = url.User.Username()
	}

	// TODO: TLS Support
	c := &boltConn{
		connStr:   connStr,
		url:       url,
		authToken: authToken,
		// TODO: Test best default
		// Default to 10 second timeout
		timeout: time.Second * time.Duration(10),
		// TODO: Test best default
		// Default to 4096 byte chunks. Same as a golang bufio reader.
		chunkSize:     4096,
		serverVersion: make([]byte, 4),
	}

	err = c.initialize()
	if err != nil {
		return nil, err
	}

	return c, nil
}

func (c *boltConn) initialize() error {
	var err error
	c.conn, err = net.DialTimeout("tcp", c.url.Host, c.timeout)
	if err != nil {
		Logger.Println("An error occurred connecting:", err)
		return err
	}

	numWritten, err := c.Write(magicPreamble)
	if numWritten != 4 {
		Logger.Printf("Couldn't write expected bytes for magic preamble. Written: %d. Expected: 4", numWritten)
		if err != nil {
			Logger.Println("An error occurred writing magic preamble:", err)
		}
		c.Close()
		return err
	}

	numWritten, err = c.Write(supportedVersions)
	if numWritten != 16 {
		Logger.Printf("Couldn't write expected bytes for magic preamble. Written: %d. Expected: 16", numWritten)
		if err != nil {
			Logger.Println("An error occurred writing supported versions:", err)
		}
		c.Close()
		return err
	}

	numRead, err := c.Read(c.serverVersion)
	if numRead != 4 {
		Logger.Printf("Could not read server version response. Read %d bytes. Expected 4 bytes. Output: %s", numRead, c.serverVersion)
		if err != nil {
			Logger.Println("An error occurred reading server version:", err)
		}
		c.Close()
		return err
	} else if bytes.Compare(c.serverVersion, noVersionSupported) == 0 {
		Logger.Println("No version supported from server")
		c.Close()
		return fmt.Errorf("NO VERSION SUPPORTED")
	}

	if err = encoding.NewEncoder(c, c.chunkSize).Encode(messages.NewInitMessage(ClientID, c.authToken)); err != nil {
		Logger.Println("An error occurred reading server version:", err)
		c.Close()
		return err
	}

	respInt, err := encoding.NewDecoder(c).Decode()
	if err != nil {
		c.Close()
		return err
	}

	switch resp := respInt.(type) {
	case messages.SuccessMessage:
		Logger.Printf("Successfully initiated Bolt connection:%+v", resp)
		return nil
	case messages.FailureMessage:
		Logger.Printf("Got a failure message when initializing connection :%+v", resp)
		c.Close()
		return fmt.Errorf("An error occurred initializing Neo4j Bolt Connection: %+v", resp.Metadata)
	default:
		Logger.Printf("Got an unrecognized message when initializing connection :%+v", resp)
		c.Close()
		return fmt.Errorf("Unrecognized response from the server: %#v", resp)
	}
}

// Read reads the data from the underlying connection
func (c *boltConn) Read(b []byte) (n int, err error) {
	if err := c.conn.SetReadDeadline(time.Now().Add(c.timeout)); err != nil {
		return 0, err
	}
	n, err = c.conn.Read(b)
	TraceLogger.Printf("Read %d bytes from stream:\n\n%s\n", n, SprintByteHex(b))
	if err != nil && err != io.EOF {
		Logger.Println("An error occurred reading from stream: ", err)
	}
	return n, err
}

// Write writes the data to the underlying connection
func (c *boltConn) Write(b []byte) (n int, err error) {
	if err := c.conn.SetWriteDeadline(time.Now().Add(c.timeout)); err != nil {
		return 0, err
	}
	n, err = c.conn.Write(b)
	TraceLogger.Printf("Wrote %d of %d bytes to stream:\n\n%s\n", len(b), n, SprintByteHex(b[:n]))
	if err != nil {
		Logger.Println("An error occurred writing to stream: ", err)
	}
	return n, err
}

// Close closes the connection
// Driver may allow for pooling in the future, keeping connections alive
func (c *boltConn) Close() error {
	c.closed = true
	// TODO: Connection Pooling?
	err := c.conn.Close()

	if err != nil {
		Logger.Print("An error occurred closing the connection", err)
		return err
	}
	return nil
}

// Prepare prepares a new statement for a query
func (c *boltConn) Prepare(query string) (driver.Stmt, error) {
	if c.closed {
		return nil, fmt.Errorf("Connection already closed")
	}
	return newStmt(query, c), nil
}

// Begin begins a new transaction with the Neo4J Database
func (c *boltConn) Begin() (driver.Tx, error) {
	if c.closed {
		return nil, fmt.Errorf("Connection already closed")
	}
	// TODO: Implement

	return nil, nil
}

// Sets the size of the chunks to write to the stream
func (c *boltConn) SetChunkSize(chunkSize uint16) {
	c.chunkSize = chunkSize
}

// Sets the timeout for reading and writing to the stream
func (c *boltConn) SetTimeout(timeout time.Duration) {
	c.timeout = timeout
}
<|endoftext|>