<commit_before>package mysql

import (
  "strings"
  "os"
  _ "github.com/go-sql-driver/mysql"
  "database/sql"
  "log"
)

/* 起動前に変更 */
const User = "user"
const Pass = "pass"
const Addr = os.Getenv("$TEST_DB_PORT_3306_TCP_ADDR")
const Port = 3306

type MySQL struct {
  db *sql.DB
  dbName string
}

type ColmunDefinition struct {
  name string
  dataType string
  primaryKey bool
}

func NewColumnDefinition(n string, dt string, pk bool) *ColumnDefinition {
  columnDef := &Column{name: n, dataType: dt, primaryKey: pk}
  return columnDef
}

func (this *MySQL) ConnectServer() {
  var err error
  this.db, err = sql.Open("mysql", User + ":" + Pass + "@(" + Addr ":" + Port + ")/")
  if err != nil {
    log.Fatal(err)
  }
}

func (this *MySQL) CreateDB(dbName string) {
  CreateDBSetChar(dbName, nil)
}

func (this *MySQL) CreateDBSetChar(dbName string, charType string) {
  createDBSql := "CREATE DATABASE " + dbName
  if charSet != nil {
    createDBSql += " CHARACTER SET " + charType
  }
  var err error
  this.db, err = sql.Exec(createDBSql)
  if err != nil {
    log.Fatal(err)
  }
  UseDB(dbName)
}

func (this *MySQL) UseDB(dbName string) {
  this.db, err = sql.Exec("USE " + dbName)
  if err != nil {
    log.Fatal(err)
  } else {
    this.dBName = dbName
  }
}

func (this *MySQL) CreateTable(tableName string, columns []ColumnDefinition) {
  cSql = "("
  []pkeys string
  for _, column := range columns {
    cSql += column.name + " " + column.dataType
    if column.primaryKey {
      cSql += " NOT NULL"
      pkeys = append([]pkeys, column.name)
    }
    cSql += ","
  }
  cSql = Trim(cSql, ",")
  cSql += ") ENGINE=InnoDB"

  this.db, cterr = sql.Exec("CREATE TABLE " + tableName + cSql)
  if cterr != nil {
    log.Fatal(cterr)
  }

  if len(pkeys) != 0 {
    pSql := "ALTER TABLE " + tableName + " ADD PRIMARY KEY("
    for _, key := range pkeys {
      pSql += key ","
    }
    pSql = Trim(pSql, ",")
    pSql += ")"
    this.db, pkerr = sql.Exec(pSql)
  }
}

func (this *MySQL) Close() {
  var err error
  err = this.db.Close()
  if err != nil {
    log.Fatal(err)
  }
}

func (this *MySQL) Insert (tableName string, datas []string) {
  iSql := "INSERT INTO " + tableName + " "
  params := "values("
  for _, data := range datas {
    params += data + ","
  }
  params = Trim(params, ",")
  params += ")"
  iSql += params
  this.db, err := sql.Exec(iSql)
  if err != nil {
      log.Fatal("insert error: ", err)
  }
}

func (this *MySQL) Update(tableName string, setColumn string, setValue string, whereColumn string, whereValue string) {
  uSql := "UPDATE " + tableName + " SET " + setColumn + "=" + setValue + " WHERE " + whereColumn + "=" + whereValue
  this.db, err := sql.Exec(uSql)
  if err != nil {
    log.Fatal("update error: ", err)
  }
}

func (this *MySQL) Delete(tableName string, whereColumn string, whereValue string) {
  dSql := "DELETE FROM " + tableName + " " + whereColumn + "=" + whereValue
  this.db, err := sql.Exec(dSql)
  if err != nil {
    log.Fatal("delete error: ", err)
  }
}

func (this *MySQL) Query(qSql string) *sql.Rows {
  rows, err := this.db.Query(qSql)
  if err != nil {
    log.Fatal(err)
  }
  return rows;
}

func (this *MySQL) QueryRow(qSql string) *sql.Row {
  row := this.db.QueryRow(qSql)
  return row;
}

func (this *MySQL) FetchAll(qSql string) [][]interface{} {
  rows, err := this.db.Query(qSql)
  if err != nil {
    log.Fatal(err)
  }
  defer rows.Close()

  columns, _ := rows.Columns()
  count := len(columns)
  valuePtrs := make([]interface{}, count)

  ret := make([][]interface{}, 0)
  for rows.Next() {

    values := make([]interface{}, count)
    for i, _ := range columns {
      valuePtrs[i] = &values[i]
    }
    rows.Scan(valuePtrs...)

    for i, _ := range columns {
      var v interface{}
      val := values[i]
      b, ok := val.([]byte)
      if (ok) {
        v = string(b)
      } else {
        v = val
      }
      values[i] = v
    }
    ret = append(ret, values)
  }

  return ret;
}
<commit_msg>Update mysql.go<commit_after>package mysql

import (
  "os"
  _ "github.com/go-sql-driver/mysql"
  "database/sql"
  "log"
)

/* 起動前に変更 */
const User = "user"
const Pass = "pass"
const Port = 3306

type MySQL struct {
  db *sql.DB
  dbName string
}

type ColumnDefinition struct {
  name string
  dataType string
  primaryKey bool
}

func NewColumnDefinition(n string, dt string, pk bool) *ColumnDefinition {
  columnDef := &Column{name: n, dataType: dt, primaryKey: pk}
  return columnDef
}

func (this *MySQL) ConnectServer() {
  var err error
  this.db, err = sql.Open("mysql", User + ":" + Pass + "@(" + os.Getenv("$TEST_DB_PORT_3306_TCP_ADDR") + ":" + Port + ")/")
  if err != nil {
    log.Fatal(err)
  }
}

func (this *MySQL) UseDB(dbName string) {
  this.db, err = sql.Exec("USE " + dbName)
  if err != nil {
    log.Fatal(err)
  } else {
    this.dBName = dbName
  }
}

func (this *MySQL) CreateDBSetChar(dbName string, charType string) {
  createDBSql := "CREATE DATABASE " + dbName
  if charSet != nil {
    createDBSql += " CHARACTER SET " + charType
  }
  var err error
  this.db, err = sql.Exec(createDBSql)
  if err != nil {
    log.Fatal(err)
  }
  UseDB(dbName)
}

func (this *MySQL) CreateDB(dbName string) {
  CreateDBSetChar(dbName, nil)
}

func (this *MySQL) CreateTable(tableName string, columns []ColumnDefinition) {
  cSql = "("
  pkeys []string
  for _, column := range columns {
    cSql += column.name + " " + column.dataType
    if column.primaryKey {
      cSql += " NOT NULL"
      pkeys = append(pkeys, column.name)
    }
    cSql += ","
  }
  cSql = Trim(cSql, ",")
  cSql += ") ENGINE=InnoDB"

  this.db, cterr = sql.Exec("CREATE TABLE " + tableName + cSql)
  if cterr != nil {
    log.Fatal(cterr)
  }

  if len(pkeys) != 0 {
    pSql := "ALTER TABLE " + tableName + " ADD PRIMARY KEY("
    for _, key := range pkeys {
      pSql += key ","
    }
    pSql = Trim(pSql, ",")
    pSql += ")"
    this.db, pkerr = sql.Exec(pSql)
  }
}

func (this *MySQL) Close() {
  var err error
  err = this.db.Close()
  if err != nil {
    log.Fatal(err)
  }
}

func (this *MySQL) Insert (tableName string, datas []string) {
  iSql := "INSERT INTO " + tableName + " "
  params := "values("
  for _, data := range datas {
    params += data + ","
  }
  params = Trim(params, ",")
  params += ")"
  iSql += params
  this.db, err = sql.Exec(iSql)
  if err != nil {
      log.Fatal("insert error: ", err)
  }
}

func (this *MySQL) Update(tableName string, setColumn string, setValue string, whereColumn string, whereValue string) {
  uSql := "UPDATE " + tableName + " SET " + setColumn + "=" + setValue + " WHERE " + whereColumn + "=" + whereValue
  this.db, err = sql.Exec(uSql)
  if err != nil {
    log.Fatal("update error: ", err)
  }
}

func (this *MySQL) Delete(tableName string, whereColumn string, whereValue string) {
  dSql := "DELETE FROM " + tableName + " " + whereColumn + "=" + whereValue
  this.db, err = sql.Exec(dSql)
  if err != nil {
    log.Fatal("delete error: ", err)
  }
}

func (this *MySQL) Query(qSql string) *sql.Rows {
  rows, err := this.db.Query(qSql)
  if err != nil {
    log.Fatal(err)
  }
  return rows;
}

func (this *MySQL) QueryRow(qSql string) *sql.Row {
  row := this.db.QueryRow(qSql)
  return row;
}

func (this *MySQL) FetchAll(qSql string) [][]interface{} {
  rows, err := this.db.Query(qSql)
  if err != nil {
    log.Fatal(err)
  }
  defer rows.Close()

  columns, _ := rows.Columns()
  count := len(columns)
  valuePtrs := make([]interface{}, count)

  ret := make([][]interface{}, 0)
  for rows.Next() {

    values := make([]interface{}, count)
    for i, _ := range columns {
      valuePtrs[i] = &values[i]
    }
    rows.Scan(valuePtrs...)

    for i, _ := range columns {
      var v interface{}
      val := values[i]
      b, ok := val.([]byte)
      if (ok) {
        v = string(b)
      } else {
        v = val
      }
      values[i] = v
    }
    ret = append(ret, values)
  }

  return ret;
}
<|endoftext|>


<commit_before>package http

import (
	"github.com/gorilla/mux"
	"github.com/gorilla/pat"
	"net/http"
	"strings"
)

type Router struct {
	*pat.Router
	domain string
}

func (self *Router) Domain() string          { return self.domain }
func (self *Router) SetDomain(domain string) { self.domain = domain }

// Returns a new router with the given path
func NewRouter(domain string) *Router {
	return &Router{pat.New(), domain}
}

func (self *Router) Get(route string, h HandlerFunc) *mux.Route {
	return self.Add("GET", route, createHandler(h))
}

func (self *Router) Post(route string, h HandlerFunc) *mux.Route {
	return self.Add("POST", route, createHandler(h))
}

func (self *Router) Delete(route string, h HandlerFunc) *mux.Route {
	return self.Add("DELETE", route, createHandler(h))
}

func (self *Router) Put(route string, h HandlerFunc) *mux.Route {
	return self.Add("PUT", route, createHandler(h))
}

func (self *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	wr := createResponseWriter(w)
	if strings.ToLower(r.Method) == "options" {
		http.Redirect(wr, r, r.RequestURI, 200)
		return
	}
	self.Router.ServeHTTP(w, r)
}
<commit_msg>Added Hooks to check before handling the http<commit_after>package http

import (
	"github.com/gorilla/mux"
	"github.com/gorilla/pat"
	"net/http"
	"strings"
)

type Router struct {
	*pat.Router
	domain string
	hooks []HandlerFunc
}

func (self *Router) Domain() string          { return self.domain }
func (self *Router) SetDomain(domain string) { self.domain = domain }

// Returns a new router with the given path
func NewRouter(domain string) *Router {
	return &Router{pat.New(), domain, make([]HandlerFunc, 0)}
}

// Add a function to be executed before serving HTTP
func (self *Router) AddHooks(hooks ...HandlerFunc) {
	self.hooks = append(self.hooks, hooks...)
}

func (self *Router) runHooks(w ResponseWriter, r *Request) *Error {
	for _, hook := range self.hooks {
		if err := hook(w, r); err != nil {
			return err
		}
	}
	return nil
}
func (self *Router) Get(route string, h HandlerFunc) *mux.Route {
	return self.Add("GET", route, createHandler(h))
}

func (self *Router) Post(route string, h HandlerFunc) *mux.Route {
	return self.Add("POST", route, createHandler(h))
}

func (self *Router) Delete(route string, h HandlerFunc) *mux.Route {
	return self.Add("DELETE", route, createHandler(h))
}

func (self *Router) Put(route string, h HandlerFunc) *mux.Route {
	return self.Add("PUT", route, createHandler(h))
}

func (self *Router) ServeHTTP(w http.ResponseWriter, r *http.Request) {
	wr := createResponseWriter(w)
	rr := createRequest(r)
	if strings.ToLower(r.Method) == "options" {
		http.Redirect(wr, r, r.RequestURI, 200)
		return
	}
	if err := self.runHooks(wr, rr); err != nil {
		wr.WriteError(err)
		return
	}
	self.Router.ServeHTTP(w, r)
}
<|endoftext|>


<commit_before>package goxp

import (
	"fmt"
	"net/http"
	"reflect"
	"regxp"
	"strconv"
	"sync"
)

// Params is a map of name/value pairs for named routes. An instance of goxp.Params is available to be injected into any route handler.
type Params map[string]string

// Router is GoXp's de-facto interface. Supports HTTP verbs, stacked handlers, and dependency injection
// Idea is to use booster rather than injection
type Router interface {
	Routes

	// Group adds a group where related routes can be added.
	Group(string, func(Router), ...Handler)
	// Get adds a route for a HTTP GET request to the specified mathcing pattern.
	Get(string, ...Handler) Route
	// Patch adds a route for a HTTP PATCH request to the specified matching pattern.
	Patch(string, ...Handler) Route
	// Post adds a route for a HTTP POST request to the specified matching pattern.
	Post(string, ...Handler) Route
	// Put adds a route for a HTTP Put request to the specified matching pattern.
	Put(string, ...Handler) Route
	// Delete adds a route for a HTTP DELETE request to the specified matching pattern.
	Delete(string, ...Handler) Route
	// Options adds a route for HTTP OPTIONS request to the specified matching pattern.
	Options(string, ...Handler) Route
	// Head adds a route for HTTP HEAD request to the specified matching pattern.
	Head(string, ...Handler) Route
	// Any adds a route for a HTTP method request to the specified matching pattern.
	Any(string, ...Handler) Route
	// AddRoute adds a route for a given HTTP method request to the specified matching pattern.
	AddRoute(string, string, ...Handler) Route

	// NotFound sets the handlers that are called when a no route matches a request. Throws a basic 404 by default.
	NotFound(...Handler)

	// Handle is the entry point for routing. This is used as a goxp.Handler
	Handle(http.ResponseWriter, *http.Request, Context)
}

type router struct {
	routes     []*route
	notFounds  []Handler
	groups     []group
	routesLock sync.RWMutex
}

type group struct {
	pattern  string
	handlers []Handler
}

// NewRouter creates a new Router instance.
// If you aren't using ClassicGoXp, then you can add Routes as a
// service with:
//
//     m := goxp.New()
//     r := goxp.NewRouter()
//     m.MapTo(r, (*goxp.Routes)(nil))
//
// If you are using ClassicGoXp, then this is done for you.
func NewRouter() Router {
	return &router{notFounds: []Handler{http.NotFound}, groups: make([]group, 0)}
}

func (r *router) Group(pattern string, fn func(Router), h ...Handler) {
	r.groups = append(r.groups, group{pattern, h})
	fn(r)
	r.groups = r.groups[:len(r.groups)-1]
}

func (r *router) Get(pattern string, h ...Handler) Route {
	return r.addRoute("GET", pattern, h)
}

func (r *router) Patch(pattern string, h ...Handler) Route {
	return r.addRoute("PATCH", pattern, h)
}

func (r *router) Post(pattern string, h ...Handler) Router {
	return r.addRoute("POST", pattern, h)
}

func (r *router) Put(pattern string, h ...Handler) Router {
	return r.addRoute("PUT", pattern, h)
}

func (r *router) Delete(pattern string, h ...Handler) Route {
	return r.addRoute("DELETE", pattern, h)
}

func (r *router) Options(pattern string, h ...Handler) Route {
	return r.addRoute("OPTIONS", pattern, h)
}

func (r *router) Head(pattern string, h ...Handler) Route {
	return r.addRoute("HEAD", pattern, h)
}

func (r *router) Any(pattern string, h ...Handler) Route {
	return r.addRoute("*", pattern, h)
}

func (r *router) AddRoute(method, pattern string, h ...Handler) Route {
	return r.addRoute(method, pattern, h)
}

func (r *router) Handle(res http.ResponseWriter, req *http.Request, context Context) {
	bestMatch := NoMatch
	var bestVals map[string]string
	var vestRoute *route
	for _, route := range r.getRoutes() {
		match, vals := route.Match(req.Method, req.URL.Path)
		if match.BetterThan(bestMatch) {
			bestMatch = match
			bestVals = vals
			bestRoute = route
			if match == ExactMatch {
				break
			}
		}
	}
	if bestMatch != NoMatch {
		params := Params(bestVals)
		context.Map(params)
		bestRoute.Handle(context, res)
		return
	}

	// no routes exist, 404
	c := &routeContext{context, 0, r.notFounds}
	context.MapTo(c, (*Context)(nil))
	c.run()
}

func (r *router) NotFound(handler ...Handler) {
	r.notFounds = handler
}

func (r *router) addRoute(method string, pattern, pattern string, handlers []Handler) *route {
	if len(r.groups) > 0 {
		groupPattern := ""
		h := make([]Handler, 0)
		for _, g := range r.groups {
			groupPattern += g.pattern
			h = append(h, g.handlers...)
		}

		pattern = groupPattern + pattern
		h = append(h, handlers...)
		handlers = h
	}

	route := newRoute(method, pattern, handlers)
	route.Validate()
	r.appendRoute(route)
	return route
}

func (r *router) appendRoute(rt *route) {
	r.routeLock.Lock()
	defer r.routesLock.RUnlock()
	r.routes = append(r.routes, rt)
}

func (r *router) getRoutes() []*route {
	r.routesLock.RLock()
	defer r.routesLock.RUnlock()
	return r.routes[:]
}

func (r *router) findRoute(name string) *route {
	for _, route := range r.getRoutes() {
		if route.name == name {
			return route
		}
	}

	return nil
}
<commit_msg>router.go +type Route interface<commit_after>package goxp

import (
	"fmt"
	"net/http"
	"reflect"
	"regxp"
	"strconv"
	"sync"
)

// Params is a map of name/value pairs for named routes. An instance of goxp.Params is available to be injected into any route handler.
type Params map[string]string

// Router is GoXp's de-facto interface. Supports HTTP verbs, stacked handlers, and dependency injection
// Idea is to use booster rather than injection
type Router interface {
	Routes

	// Group adds a group where related routes can be added.
	Group(string, func(Router), ...Handler)
	// Get adds a route for a HTTP GET request to the specified mathcing pattern.
	Get(string, ...Handler) Route
	// Patch adds a route for a HTTP PATCH request to the specified matching pattern.
	Patch(string, ...Handler) Route
	// Post adds a route for a HTTP POST request to the specified matching pattern.
	Post(string, ...Handler) Route
	// Put adds a route for a HTTP Put request to the specified matching pattern.
	Put(string, ...Handler) Route
	// Delete adds a route for a HTTP DELETE request to the specified matching pattern.
	Delete(string, ...Handler) Route
	// Options adds a route for HTTP OPTIONS request to the specified matching pattern.
	Options(string, ...Handler) Route
	// Head adds a route for HTTP HEAD request to the specified matching pattern.
	Head(string, ...Handler) Route
	// Any adds a route for a HTTP method request to the specified matching pattern.
	Any(string, ...Handler) Route
	// AddRoute adds a route for a given HTTP method request to the specified matching pattern.
	AddRoute(string, string, ...Handler) Route

	// NotFound sets the handlers that are called when a no route matches a request. Throws a basic 404 by default.
	NotFound(...Handler)

	// Handle is the entry point for routing. This is used as a goxp.Handler
	Handle(http.ResponseWriter, *http.Request, Context)
}

type router struct {
	routes     []*route
	notFounds  []Handler
	groups     []group
	routesLock sync.RWMutex
}

type group struct {
	pattern  string
	handlers []Handler
}

// NewRouter creates a new Router instance.
// If you aren't using ClassicGoXp, then you can add Routes as a
// service with:
//
//     m := goxp.New()
//     r := goxp.NewRouter()
//     m.MapTo(r, (*goxp.Routes)(nil))
//
// If you are using ClassicGoXp, then this is done for you.
func NewRouter() Router {
	return &router{notFounds: []Handler{http.NotFound}, groups: make([]group, 0)}
}

func (r *router) Group(pattern string, fn func(Router), h ...Handler) {
	r.groups = append(r.groups, group{pattern, h})
	fn(r)
	r.groups = r.groups[:len(r.groups)-1]
}

func (r *router) Get(pattern string, h ...Handler) Route {
	return r.addRoute("GET", pattern, h)
}

func (r *router) Patch(pattern string, h ...Handler) Route {
	return r.addRoute("PATCH", pattern, h)
}

func (r *router) Post(pattern string, h ...Handler) Router {
	return r.addRoute("POST", pattern, h)
}

func (r *router) Put(pattern string, h ...Handler) Router {
	return r.addRoute("PUT", pattern, h)
}

func (r *router) Delete(pattern string, h ...Handler) Route {
	return r.addRoute("DELETE", pattern, h)
}

func (r *router) Options(pattern string, h ...Handler) Route {
	return r.addRoute("OPTIONS", pattern, h)
}

func (r *router) Head(pattern string, h ...Handler) Route {
	return r.addRoute("HEAD", pattern, h)
}

func (r *router) Any(pattern string, h ...Handler) Route {
	return r.addRoute("*", pattern, h)
}

func (r *router) AddRoute(method, pattern string, h ...Handler) Route {
	return r.addRoute(method, pattern, h)
}

func (r *router) Handle(res http.ResponseWriter, req *http.Request, context Context) {
	bestMatch := NoMatch
	var bestVals map[string]string
	var vestRoute *route
	for _, route := range r.getRoutes() {
		match, vals := route.Match(req.Method, req.URL.Path)
		if match.BetterThan(bestMatch) {
			bestMatch = match
			bestVals = vals
			bestRoute = route
			if match == ExactMatch {
				break
			}
		}
	}
	if bestMatch != NoMatch {
		params := Params(bestVals)
		context.Map(params)
		bestRoute.Handle(context, res)
		return
	}

	// no routes exist, 404
	c := &routeContext{context, 0, r.notFounds}
	context.MapTo(c, (*Context)(nil))
	c.run()
}

func (r *router) NotFound(handler ...Handler) {
	r.notFounds = handler
}

func (r *router) addRoute(method string, pattern, pattern string, handlers []Handler) *route {
	if len(r.groups) > 0 {
		groupPattern := ""
		h := make([]Handler, 0)
		for _, g := range r.groups {
			groupPattern += g.pattern
			h = append(h, g.handlers...)
		}

		pattern = groupPattern + pattern
		h = append(h, handlers...)
		handlers = h
	}

	route := newRoute(method, pattern, handlers)
	route.Validate()
	r.appendRoute(route)
	return route
}

func (r *router) appendRoute(rt *route) {
	r.routeLock.Lock()
	defer r.routesLock.RUnlock()
	r.routes = append(r.routes, rt)
}

func (r *router) getRoutes() []*route {
	r.routesLock.RLock()
	defer r.routesLock.RUnlock()
	return r.routes[:]
}

func (r *router) findRoute(name string) *route {
	for _, route := range r.getRoutes() {
		if route.name == name {
			return route
		}
	}

	return nil
}

// Route is an interface representing a Route in GoXp's routing layer.
type Route interface {
	// URLWith returns a rendering of the Routes's url with the given string params.
	Name(string)
	// GetName returns the name of the route.
	GetName() string
	// Pattern returns the pattern of the route.
	Pattern() string
	// Method returns the method of the route.
	Method() string
}

<|endoftext|>


<commit_before>/*

Package rpio provides GPIO access on the Raspberry PI without any need
for external c libraries (ex: WiringPI or BCM2835).

Supports simple operations such as:
- Pin mode/direction (input/output)
- Pin write (high/low)
- Pin read (high/low)
- Pull up/down/off

Example of use:

	rpio.Open()
	defer rpio.Close()

	pin := rpio.Pin(4)
	pin.Output()

	for {
		pin.Toggle()
		time.Sleep(time.Second)
	}

The library use the raw BCM2835 pinouts, not the ports as they are mapped
on the output pins for the raspberry pi

   Rev 1 Raspberry Pi
+------+------+--------+
| GPIO | Phys | Name   |
+------+------+--------+
|   0  |   3  | SDA    |
|   1  |   5  | SCL    |
|   4  |   7  | GPIO 7 |
|   7  |  26  | CE1    |
|   8  |  24  | CE0    |
|   9  |  21  | MISO   |
|  10  |  19  | MOSI   |
|  11  |  23  | SCLK   |
|  14  |   8  | TxD    |
|  15  |  10  | RxD    |
|  17  |  11  | GPIO 0 |
|  18  |  12  | GPIO 1 |
|  21  |  13  | GPIO 2 |
|  22  |  15  | GPIO 3 |
|  23  |  16  | GPIO 4 |
|  24  |  18  | GPIO 5 |
|  25  |  22  | GPIO 6 |
+------+------+--------+

See the spec for full details of the BCM2835 controller:
http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf

*/

package rpio

import (
	"os"
	"reflect"
	"sync"
	"syscall"
	"time"
	"unsafe"
)

type Direction uint8
type Pin uint8
type State uint8
type Pull uint8

// Memory offsets for gpio, see the spec for more details
const (
	bcm2835Base = 0x20000000
	gpioBase    = bcm2835Base + 0x200000
	memLength   = 4096

	pinMask uint32 = 7 // 0b111 - pinmode is 3 bits
)

// Pin direction, a pin can be set in Input or Output mode
const (
	Input Direction = iota
	Output
)

// State of pin, High / Low
const (
	Low State = iota
	High
)

// Pull Up / Down / Off
const (
	PullOff Pull = iota
	PullDown
	PullUp
)

// Arrays for 8 / 32 bit access to memory and a semaphore for write locking
var (
	memlock sync.Mutex
	mem     []uint32
	mem8    []uint8
)

// Set pin as Input
func (pin Pin) Input() {
	PinMode(pin, Input)
}

// Set pin as Output
func (pin Pin) Output() {
	PinMode(pin, Output)
}

// Set pin High
func (pin Pin) High() {
	WritePin(pin, High)
}

// Set pin Low
func (pin Pin) Low() {
	WritePin(pin, Low)
}

// Toggle pin state
func (pin Pin) Toggle() {
	TogglePin(pin)
}

// Set pin Direction
func (pin Pin) Mode(dir Direction) {
	PinMode(pin, dir)
}

// Set pin state (high/low)
func (pin Pin) Write(state State) {
	WritePin(pin, state)
}

// Read pin state (high/low)
func (pin Pin) Read() State {
	return ReadPin(pin)
}

// Set a given pull up/down mode
func (pin Pin) Pull(pull Pull) {
	PullMode(pin, pull)
}

// Pull up pin
func (pin Pin) PullUp() {
	PullMode(pin, PullUp)
}

// Pull down pin
func (pin Pin) PullDown() {
	PullMode(pin, PullDown)
}

// Disable pullup/down on pin
func (pin Pin) PullOff() {
	PullMode(pin, PullOff)
}

// PinMode sets the direction of a given pin (Input or Output)
func PinMode(pin Pin, direction Direction) {

	// Pin fsel register, 0 or 1 depending on bank
	fsel := uint8(pin) / 10
	shift := (uint8(pin) % 10) * 3

	memlock.Lock()

	if direction == Input {
		mem[fsel] = mem[fsel] &^ (pinMask << shift)
	} else {
		mem[fsel] = (mem[fsel] &^ (pinMask << shift)) | (1 << shift)
	}

	memlock.Unlock()
}

// WritePin sets a given pin High or Low
// by setting the clear or set registers respectively
func WritePin(pin Pin, state State) {

	p := uint8(pin)

	// Clear register, 10 / 11 depending on bank
	// Set register, 7 / 8 depending on bank
	clearReg := p/32 + 10
	setReg := p/32 + 7

	memlock.Lock()

	if state == Low {
		mem[clearReg] = 1 << (p & 31)
	} else {
		mem[setReg] = 1 << (p & 31)
	}

	memlock.Unlock()
}

// Read the state of a pin
func ReadPin(pin Pin) State {
	// Input level register offset (13 / 14 depending on bank)
	levelReg := uint8(pin)/32 + 13

	if (mem[levelReg] & (1 << uint8(pin))) != 0 {
		return High
	}

	return Low
}

// Toggle a pin state (high -> low -> high)
// TODO: probably possible to do this much faster without read
func TogglePin(pin Pin) {
	switch ReadPin(pin) {
	case Low:
		pin.High()
	case High:
		pin.Low()
	}
}

func PullMode(pin Pin, pull Pull) {
	// Pull up/down/off register has offset 38 / 39, pull is 37
	pullClkReg := uint8(pin)/32 + 38
	pullReg := 37
	shift := (uint8(pin) % 32)

	memlock.Lock()

	switch pull {
	case PullDown, PullUp:
		mem[pullReg] = mem[pullReg]&^3 | uint32(pull)
	case PullOff:
		mem[pullReg] = mem[pullReg] &^ 3
	}

	// Wait for value to clock in, this is ugly, sorry :(
	time.Sleep(time.Microsecond)

	mem[pullClkReg] = 1 << shift

	// Wait for value to clock in
	time.Sleep(time.Microsecond)

	mem[pullReg] = mem[pullReg] &^ 3
	mem[pullClkReg] = 0

	memlock.Unlock()

}

// Open and memory map GPIO memory range from /dev/mem .
// Some reflection magic is used to convert it to a unsafe []uint32 pointer
func Open() (err error) {
	var file *os.File

	// Open fd for rw mem access
	file, err = os.OpenFile(
		"/dev/mem",
		os.O_RDWR|os.O_SYNC,
		0)

	if err != nil {
		return
	}

	// FD can be closed after memory mapping
	defer file.Close()

	memlock.Lock()

	// Memory map GPIO registers to byte array
	mem8, err = syscall.Mmap(
		int(file.Fd()),
		gpioBase,
		memLength,
		syscall.PROT_READ|syscall.PROT_WRITE,
		syscall.MAP_SHARED)

	if err != nil {
		return
	}

	// Convert mapped byte memory to unsafe []uint32 pointer, adjust length as needed
	header := *(*reflect.SliceHeader)(unsafe.Pointer(&mem8))
	header.Len /= (32 / 8) // (32 bit = 4 bytes)
	header.Cap /= (32 / 8)

	mem = *(*[]uint32)(unsafe.Pointer(&header))

	memlock.Unlock()
	return nil
}

// Close unmaps GPIO memory
func Close() {
	memlock.Lock()
	syscall.Munmap(mem8)
	syscall.Munmap(mem)
	memlock.Unlock()
}
<commit_msg>Temporary revert "Unmap both arrays"<commit_after>/*

Package rpio provides GPIO access on the Raspberry PI without any need
for external c libraries (ex: WiringPI or BCM2835).

Supports simple operations such as:
- Pin mode/direction (input/output)
- Pin write (high/low)
- Pin read (high/low)
- Pull up/down/off

Example of use:

	rpio.Open()
	defer rpio.Close()

	pin := rpio.Pin(4)
	pin.Output()

	for {
		pin.Toggle()
		time.Sleep(time.Second)
	}

The library use the raw BCM2835 pinouts, not the ports as they are mapped
on the output pins for the raspberry pi

   Rev 1 Raspberry Pi
+------+------+--------+
| GPIO | Phys | Name   |
+------+------+--------+
|   0  |   3  | SDA    |
|   1  |   5  | SCL    |
|   4  |   7  | GPIO 7 |
|   7  |  26  | CE1    |
|   8  |  24  | CE0    |
|   9  |  21  | MISO   |
|  10  |  19  | MOSI   |
|  11  |  23  | SCLK   |
|  14  |   8  | TxD    |
|  15  |  10  | RxD    |
|  17  |  11  | GPIO 0 |
|  18  |  12  | GPIO 1 |
|  21  |  13  | GPIO 2 |
|  22  |  15  | GPIO 3 |
|  23  |  16  | GPIO 4 |
|  24  |  18  | GPIO 5 |
|  25  |  22  | GPIO 6 |
+------+------+--------+

See the spec for full details of the BCM2835 controller:
http://www.raspberrypi.org/wp-content/uploads/2012/02/BCM2835-ARM-Peripherals.pdf

*/

package rpio

import (
	"os"
	"reflect"
	"sync"
	"syscall"
	"time"
	"unsafe"
)

type Direction uint8
type Pin uint8
type State uint8
type Pull uint8

// Memory offsets for gpio, see the spec for more details
const (
	bcm2835Base = 0x20000000
	gpioBase    = bcm2835Base + 0x200000
	memLength   = 4096

	pinMask uint32 = 7 // 0b111 - pinmode is 3 bits
)

// Pin direction, a pin can be set in Input or Output mode
const (
	Input Direction = iota
	Output
)

// State of pin, High / Low
const (
	Low State = iota
	High
)

// Pull Up / Down / Off
const (
	PullOff Pull = iota
	PullDown
	PullUp
)

// Arrays for 8 / 32 bit access to memory and a semaphore for write locking
var (
	memlock sync.Mutex
	mem     []uint32
	mem8    []uint8
)

// Set pin as Input
func (pin Pin) Input() {
	PinMode(pin, Input)
}

// Set pin as Output
func (pin Pin) Output() {
	PinMode(pin, Output)
}

// Set pin High
func (pin Pin) High() {
	WritePin(pin, High)
}

// Set pin Low
func (pin Pin) Low() {
	WritePin(pin, Low)
}

// Toggle pin state
func (pin Pin) Toggle() {
	TogglePin(pin)
}

// Set pin Direction
func (pin Pin) Mode(dir Direction) {
	PinMode(pin, dir)
}

// Set pin state (high/low)
func (pin Pin) Write(state State) {
	WritePin(pin, state)
}

// Read pin state (high/low)
func (pin Pin) Read() State {
	return ReadPin(pin)
}

// Set a given pull up/down mode
func (pin Pin) Pull(pull Pull) {
	PullMode(pin, pull)
}

// Pull up pin
func (pin Pin) PullUp() {
	PullMode(pin, PullUp)
}

// Pull down pin
func (pin Pin) PullDown() {
	PullMode(pin, PullDown)
}

// Disable pullup/down on pin
func (pin Pin) PullOff() {
	PullMode(pin, PullOff)
}

// PinMode sets the direction of a given pin (Input or Output)
func PinMode(pin Pin, direction Direction) {

	// Pin fsel register, 0 or 1 depending on bank
	fsel := uint8(pin) / 10
	shift := (uint8(pin) % 10) * 3

	memlock.Lock()

	if direction == Input {
		mem[fsel] = mem[fsel] &^ (pinMask << shift)
	} else {
		mem[fsel] = (mem[fsel] &^ (pinMask << shift)) | (1 << shift)
	}

	memlock.Unlock()
}

// WritePin sets a given pin High or Low
// by setting the clear or set registers respectively
func WritePin(pin Pin, state State) {

	p := uint8(pin)

	// Clear register, 10 / 11 depending on bank
	// Set register, 7 / 8 depending on bank
	clearReg := p/32 + 10
	setReg := p/32 + 7

	memlock.Lock()

	if state == Low {
		mem[clearReg] = 1 << (p & 31)
	} else {
		mem[setReg] = 1 << (p & 31)
	}

	memlock.Unlock()
}

// Read the state of a pin
func ReadPin(pin Pin) State {
	// Input level register offset (13 / 14 depending on bank)
	levelReg := uint8(pin)/32 + 13

	if (mem[levelReg] & (1 << uint8(pin))) != 0 {
		return High
	}

	return Low
}

// Toggle a pin state (high -> low -> high)
// TODO: probably possible to do this much faster without read
func TogglePin(pin Pin) {
	switch ReadPin(pin) {
	case Low:
		pin.High()
	case High:
		pin.Low()
	}
}

func PullMode(pin Pin, pull Pull) {
	// Pull up/down/off register has offset 38 / 39, pull is 37
	pullClkReg := uint8(pin)/32 + 38
	pullReg := 37
	shift := (uint8(pin) % 32)

	memlock.Lock()

	switch pull {
	case PullDown, PullUp:
		mem[pullReg] = mem[pullReg]&^3 | uint32(pull)
	case PullOff:
		mem[pullReg] = mem[pullReg] &^ 3
	}

	// Wait for value to clock in, this is ugly, sorry :(
	time.Sleep(time.Microsecond)

	mem[pullClkReg] = 1 << shift

	// Wait for value to clock in
	time.Sleep(time.Microsecond)

	mem[pullReg] = mem[pullReg] &^ 3
	mem[pullClkReg] = 0

	memlock.Unlock()

}

// Open and memory map GPIO memory range from /dev/mem .
// Some reflection magic is used to convert it to a unsafe []uint32 pointer
func Open() (err error) {
	var file *os.File

	// Open fd for rw mem access
	file, err = os.OpenFile(
		"/dev/mem",
		os.O_RDWR|os.O_SYNC,
		0)

	if err != nil {
		return
	}

	// FD can be closed after memory mapping
	defer file.Close()

	memlock.Lock()

	// Memory map GPIO registers to byte array
	mem8, err = syscall.Mmap(
		int(file.Fd()),
		gpioBase,
		memLength,
		syscall.PROT_READ|syscall.PROT_WRITE,
		syscall.MAP_SHARED)

	if err != nil {
		return
	}

	// Convert mapped byte memory to unsafe []uint32 pointer, adjust length as needed
	header := *(*reflect.SliceHeader)(unsafe.Pointer(&mem8))
	header.Len /= (32 / 8) // (32 bit = 4 bytes)
	header.Cap /= (32 / 8)

	mem = *(*[]uint32)(unsafe.Pointer(&header))

	memlock.Unlock()
	return nil
}

// Close unmaps GPIO memory
func Close() {
	memlock.Lock()
	syscall.Munmap(mem8)
	memlock.Unlock()
}
<|endoftext|>


<commit_before>/*
 * RRDA (RRDA REST DNS API) 1.2.0
 * Copyright (c) 2012-2022, Frederic Cambus
 * https://www.statdns.com
 *
 * Created: 2012-03-11
 * Last Updated: 2022-01-14
 *
 * RRDA is released under the BSD 2-Clause license.
 * See LICENSE file for details.
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/go-chi/chi/v5"
	"github.com/miekg/dns"
	"golang.org/x/net/idna"
	"io"
	"net"
	"net/http"
	"net/http/fcgi"
	"os"
	"strings"
	"time"
)

var timeout_ms int

type Error struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

type Question struct {
	Name  string `json:"name"`
	Type  string `json:"type"`
	Class string `json:"class"`
}

type Section struct {
	Name     string `json:"name"`
	Type     string `json:"type"`
	Class    string `json:"class"`
	Ttl      uint32 `json:"ttl"`
	Rdlength uint16 `json:"rdlength"`
	Rdata    string `json:"rdata"`
}

type Message struct {
	Question   []*Question `json:"question"`
	Answer     []*Section  `json:"answer"`
	Authority  []*Section  `json:"authority,omitempty"`
	Additional []*Section  `json:"additional,omitempty"`
}

// Return rdata
func rdata(RR dns.RR) string {
	return strings.Replace(RR.String(), RR.Header().String(), "", -1)
}

// Return an HTTP Error along with a JSON-encoded error message
func error(w http.ResponseWriter, status int, code int, message string) {
	if output, err := json.Marshal(Error{Code: code, Message: message}); err == nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(status)
		fmt.Fprintln(w, string(output))
	}
}

// Generate JSON output
func jsonify(w http.ResponseWriter, r *http.Request, question []dns.Question, answer []dns.RR, authority []dns.RR, additional []dns.RR) {
	var answerArray, authorityArray, additionalArray []*Section

	callback := r.URL.Query().Get("callback")

	for _, answer := range answer {
		answerArray = append(answerArray, &Section{answer.Header().Name, dns.TypeToString[answer.Header().Rrtype], dns.ClassToString[answer.Header().Class], answer.Header().Ttl, answer.Header().Rdlength, rdata(answer)})
	}

	for _, authority := range authority {
		authorityArray = append(authorityArray, &Section{authority.Header().Name, dns.TypeToString[authority.Header().Rrtype], dns.ClassToString[authority.Header().Class], authority.Header().Ttl, authority.Header().Rdlength, rdata(authority)})
	}

	for _, additional := range additional {
		additionalArray = append(additionalArray, &Section{additional.Header().Name, dns.TypeToString[additional.Header().Rrtype], dns.ClassToString[additional.Header().Class], additional.Header().Ttl, additional.Header().Rdlength, rdata(additional)})
	}

	if json, err := json.MarshalIndent(Message{[]*Question{&Question{question[0].Name, dns.TypeToString[question[0].Qtype], dns.ClassToString[question[0].Qclass]}}, answerArray, authorityArray, additionalArray}, "", "    "); err == nil {
		if callback != "" {
			io.WriteString(w, callback+"("+string(json)+");")
		} else {
			io.WriteString(w, string(json))
		}
	}
}

// Perform DNS resolution
func resolve(w http.ResponseWriter, r *http.Request, server string, domain string, querytype uint16) {
	m := new(dns.Msg)
	m.SetQuestion(domain, querytype)
	m.MsgHdr.RecursionDesired = true

	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("Access-Control-Allow-Origin", "*")

	c := new(dns.Client)
	c.Dialer = &net.Dialer{
		Timeout: time.Duration(timeout_ms) * time.Millisecond,
	}

Redo:
	if in, _, err := c.Exchange(m, server); err == nil { // Second return value is RTT, not used for now
		if in.MsgHdr.Truncated {
			c.Net = "tcp"
			goto Redo
		}

		switch in.MsgHdr.Rcode {
		case dns.RcodeServerFailure:
			error(w, 500, 502, "The name server encountered an internal failure while processing this request (SERVFAIL)")
		case dns.RcodeNameError:
			error(w, 500, 503, "Some name that ought to exist, does not exist (NXDOMAIN)")
		case dns.RcodeRefused:
			error(w, 500, 505, "The name server refuses to perform the specified operation for policy or security reasons (REFUSED)")
		default:
			jsonify(w, r, in.Question, in.Answer, in.Ns, in.Extra)
		}
	} else {
		error(w, 500, 501, "DNS server could not be reached")
	}
}

// Handler for DNS queries
func query(w http.ResponseWriter, r *http.Request) {
	server := chi.URLParam(r, "server")
	domain := dns.Fqdn(chi.URLParam(r, "domain"))
	querytype := chi.URLParam(r, "querytype")

	if domain, err := idna.ToASCII(domain); err == nil { // Valid domain name (ASCII or IDN)
		if _, isDomain := dns.IsDomainName(domain); isDomain { // Well-formed domain name
			if querytype, ok := dns.StringToType[strings.ToUpper(querytype)]; ok { // Valid DNS query type
				resolve(w, r, server, domain, querytype)
			} else {
				error(w, 400, 404, "Invalid DNS query type")
			}
		} else {
			error(w, 400, 402, "Input string is not a well-formed domain name")
		}
	} else {
		error(w, 400, 401, "Input string could not be parsed")
	}
}

// Handler for reverse DNS queries
func ptr(w http.ResponseWriter, r *http.Request) {
	server := chi.URLParam(r, "server")
	ip := chi.URLParam(r, "ip")

	if arpa, err := dns.ReverseAddr(ip); err == nil { // Valid IP address (IPv4 or IPv6)
		resolve(w, r, server, arpa, dns.TypePTR)
	} else {
		error(w, 400, 403, "Input string is not a valid IP address")
	}
}

func main() {
	fastcgi := flag.Bool("fastcgi", false, "Enable FastCGI mode")
	host := flag.String("host", "127.0.0.1", "Set the server host")
	port := flag.String("port", "8080", "Set the server port")
	flag.IntVar(&timeout_ms, "timeout", 2000, "Set the query timeout in ms")
	version := flag.Bool("version", false, "Display version")

	mode := "HTTP"

	flag.Usage = func() {
		fmt.Println("\nUSAGE:")
		flag.PrintDefaults()
	}
	flag.Parse()

	if *version {
		fmt.Println("RRDA 1.2.0")
		os.Exit(0)
	}

	if *fastcgi {
		mode = "FastCGI"
	}

	address := *host + ":" + *port

	r := chi.NewRouter()
	r.Get("/{server}/x/{ip}", ptr)
	r.Get("/{server}/{domain}/{querytype}", query)

	if *fastcgi {
		listener, _ := net.Listen("tcp", address)

		if err := fcgi.Serve(listener, r); err != nil {
			fmt.Println("\nERROR:", err)
			os.Exit(1)
		}
	} else {
		if err := http.ListenAndServe(address, r); err != nil {
			fmt.Println("\nERROR:", err)
			os.Exit(1)
		}
	}

	fmt.Println("Listening on ("+mode+" mode):", address)
}
<commit_msg>Remove support for running in FastCGI mode.<commit_after>/*
 * RRDA (RRDA REST DNS API) 1.2.0
 * Copyright (c) 2012-2022, Frederic Cambus
 * https://www.statdns.com
 *
 * Created: 2012-03-11
 * Last Updated: 2022-01-14
 *
 * RRDA is released under the BSD 2-Clause license.
 * See LICENSE file for details.
 *
 * SPDX-License-Identifier: BSD-2-Clause
 */

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/go-chi/chi/v5"
	"github.com/miekg/dns"
	"golang.org/x/net/idna"
	"io"
	"net"
	"net/http"
	"os"
	"strings"
	"time"
)

var timeout_ms int

type Error struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

type Question struct {
	Name  string `json:"name"`
	Type  string `json:"type"`
	Class string `json:"class"`
}

type Section struct {
	Name     string `json:"name"`
	Type     string `json:"type"`
	Class    string `json:"class"`
	Ttl      uint32 `json:"ttl"`
	Rdlength uint16 `json:"rdlength"`
	Rdata    string `json:"rdata"`
}

type Message struct {
	Question   []*Question `json:"question"`
	Answer     []*Section  `json:"answer"`
	Authority  []*Section  `json:"authority,omitempty"`
	Additional []*Section  `json:"additional,omitempty"`
}

// Return rdata
func rdata(RR dns.RR) string {
	return strings.Replace(RR.String(), RR.Header().String(), "", -1)
}

// Return an HTTP Error along with a JSON-encoded error message
func error(w http.ResponseWriter, status int, code int, message string) {
	if output, err := json.Marshal(Error{Code: code, Message: message}); err == nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(status)
		fmt.Fprintln(w, string(output))
	}
}

// Generate JSON output
func jsonify(w http.ResponseWriter, r *http.Request, question []dns.Question, answer []dns.RR, authority []dns.RR, additional []dns.RR) {
	var answerArray, authorityArray, additionalArray []*Section

	callback := r.URL.Query().Get("callback")

	for _, answer := range answer {
		answerArray = append(answerArray, &Section{answer.Header().Name, dns.TypeToString[answer.Header().Rrtype], dns.ClassToString[answer.Header().Class], answer.Header().Ttl, answer.Header().Rdlength, rdata(answer)})
	}

	for _, authority := range authority {
		authorityArray = append(authorityArray, &Section{authority.Header().Name, dns.TypeToString[authority.Header().Rrtype], dns.ClassToString[authority.Header().Class], authority.Header().Ttl, authority.Header().Rdlength, rdata(authority)})
	}

	for _, additional := range additional {
		additionalArray = append(additionalArray, &Section{additional.Header().Name, dns.TypeToString[additional.Header().Rrtype], dns.ClassToString[additional.Header().Class], additional.Header().Ttl, additional.Header().Rdlength, rdata(additional)})
	}

	if json, err := json.MarshalIndent(Message{[]*Question{&Question{question[0].Name, dns.TypeToString[question[0].Qtype], dns.ClassToString[question[0].Qclass]}}, answerArray, authorityArray, additionalArray}, "", "    "); err == nil {
		if callback != "" {
			io.WriteString(w, callback+"("+string(json)+");")
		} else {
			io.WriteString(w, string(json))
		}
	}
}

// Perform DNS resolution
func resolve(w http.ResponseWriter, r *http.Request, server string, domain string, querytype uint16) {
	m := new(dns.Msg)
	m.SetQuestion(domain, querytype)
	m.MsgHdr.RecursionDesired = true

	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("Access-Control-Allow-Origin", "*")

	c := new(dns.Client)
	c.Dialer = &net.Dialer{
		Timeout: time.Duration(timeout_ms) * time.Millisecond,
	}

Redo:
	if in, _, err := c.Exchange(m, server); err == nil { // Second return value is RTT, not used for now
		if in.MsgHdr.Truncated {
			c.Net = "tcp"
			goto Redo
		}

		switch in.MsgHdr.Rcode {
		case dns.RcodeServerFailure:
			error(w, 500, 502, "The name server encountered an internal failure while processing this request (SERVFAIL)")
		case dns.RcodeNameError:
			error(w, 500, 503, "Some name that ought to exist, does not exist (NXDOMAIN)")
		case dns.RcodeRefused:
			error(w, 500, 505, "The name server refuses to perform the specified operation for policy or security reasons (REFUSED)")
		default:
			jsonify(w, r, in.Question, in.Answer, in.Ns, in.Extra)
		}
	} else {
		error(w, 500, 501, "DNS server could not be reached")
	}
}

// Handler for DNS queries
func query(w http.ResponseWriter, r *http.Request) {
	server := chi.URLParam(r, "server")
	domain := dns.Fqdn(chi.URLParam(r, "domain"))
	querytype := chi.URLParam(r, "querytype")

	if domain, err := idna.ToASCII(domain); err == nil { // Valid domain name (ASCII or IDN)
		if _, isDomain := dns.IsDomainName(domain); isDomain { // Well-formed domain name
			if querytype, ok := dns.StringToType[strings.ToUpper(querytype)]; ok { // Valid DNS query type
				resolve(w, r, server, domain, querytype)
			} else {
				error(w, 400, 404, "Invalid DNS query type")
			}
		} else {
			error(w, 400, 402, "Input string is not a well-formed domain name")
		}
	} else {
		error(w, 400, 401, "Input string could not be parsed")
	}
}

// Handler for reverse DNS queries
func ptr(w http.ResponseWriter, r *http.Request) {
	server := chi.URLParam(r, "server")
	ip := chi.URLParam(r, "ip")

	if arpa, err := dns.ReverseAddr(ip); err == nil { // Valid IP address (IPv4 or IPv6)
		resolve(w, r, server, arpa, dns.TypePTR)
	} else {
		error(w, 400, 403, "Input string is not a valid IP address")
	}
}

func main() {
	host := flag.String("host", "127.0.0.1", "Set the server host")
	port := flag.String("port", "8080", "Set the server port")
	flag.IntVar(&timeout_ms, "timeout", 2000, "Set the query timeout in ms")
	version := flag.Bool("version", false, "Display version")

	flag.Usage = func() {
		fmt.Println("\nUSAGE:")
		flag.PrintDefaults()
	}
	flag.Parse()

	if *version {
		fmt.Println("RRDA 1.2.0")
		os.Exit(0)
	}

	address := *host + ":" + *port

	r := chi.NewRouter()
	r.Get("/{server}/x/{ip}", ptr)
	r.Get("/{server}/{domain}/{querytype}", query)

	if err := http.ListenAndServe(address, r); err != nil {
		fmt.Println("\nERROR:", err)
		os.Exit(1)
	}

	fmt.Println("Listening on:", address)
}
<|endoftext|>


<commit_before>/*****************************************************************************/
/*                                                                           */
/* RRDA (RRDA REST DNS API) 1.02                                             */
/* Copyright (c) 2012-2016, Frederic Cambus                                  */
/* http://www.statdns.com                                                    */
/*                                                                           */
/* Created: 2012-03-11                                                       */
/* Last Updated: 2016-07-18                                                  */
/*                                                                           */
/* RRDA is released under the BSD 2-Clause license.                          */
/* See LICENSE file for details.                                             */
/*                                                                           */
/*****************************************************************************/

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/bmizerany/pat"
	"github.com/miekg/dns"
	"golang.org/x/net/idna"
	"io"
	"net"
	"net/http"
	"net/http/fcgi"
	"os"
	"strings"
)

type Error struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

type Question struct {
	Name  string `json:"name"`
	Type  string `json:"type"`
	Class string `json:"class"`
}

type Section struct {
	Name     string `json:"name"`
	Type     string `json:"type"`
	Class    string `json:"class"`
	Ttl      uint32 `json:"ttl"`
	Rdlength uint16 `json:"rdlength"`
	Rdata    string `json:"rdata"`
}

type Message struct {
	Question   []*Question `json:"question"`
	Answer     []*Section  `json:"answer"`
	Authority  []*Section  `json:"authority,omitempty"`
	Additional []*Section  `json:"additional,omitempty"`
}

// Return rdata
func rdata(RR dns.RR) string {
	return strings.Replace(RR.String(), RR.Header().String(), "", -1)
}

// Return an HTTP Error along with a JSON-encoded error message
func error(w http.ResponseWriter, status int, code int, message string) {
	if output, err := json.Marshal(Error{Code: code, Message: message}); err == nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(status)
		fmt.Fprintln(w, string(output))
	}
}

// Generate JSON output
func jsonify(w http.ResponseWriter, r *http.Request, question []dns.Question, answer []dns.RR, authority []dns.RR, additional []dns.RR) {
	var answerArray, authorityArray, additionalArray []*Section

	callback := r.URL.Query().Get("callback")

	for _, answer := range answer {
		answerArray = append(answerArray, &Section{answer.Header().Name, dns.TypeToString[answer.Header().Rrtype], dns.ClassToString[answer.Header().Class], answer.Header().Ttl, answer.Header().Rdlength, rdata(answer)})
	}

	for _, authority := range authority {
		authorityArray = append(authorityArray, &Section{authority.Header().Name, dns.TypeToString[authority.Header().Rrtype], dns.ClassToString[authority.Header().Class], authority.Header().Ttl, authority.Header().Rdlength, rdata(authority)})
	}

	for _, additional := range additional {
		additionalArray = append(additionalArray, &Section{additional.Header().Name, dns.TypeToString[additional.Header().Rrtype], dns.ClassToString[additional.Header().Class], additional.Header().Ttl, additional.Header().Rdlength, rdata(additional)})
	}

	if json, err := json.MarshalIndent(Message{[]*Question{&Question{question[0].Name, dns.TypeToString[question[0].Qtype], dns.ClassToString[question[0].Qclass]}}, answerArray, authorityArray, additionalArray}, "", "    "); err == nil {
		if callback != "" {
			io.WriteString(w, callback+"("+string(json)+");")
		} else {
			io.WriteString(w, string(json))
		}
	}
}

// Perform DNS resolution
func resolve(w http.ResponseWriter, r *http.Request, server string, domain string, querytype uint16) {
	m := new(dns.Msg)
	m.SetQuestion(domain, querytype)
	m.MsgHdr.RecursionDesired = true

	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("Access-Control-Allow-Origin", "*")

	c := new(dns.Client)

Redo:
	in, _, err := c.Exchange(m, server) // Second return value is RTT, not used for now

	if err == nil {
		switch in.MsgHdr.Rcode {
		case dns.RcodeServerFailure:
			error(w, 500, 502, "The name server encountered an internal failure while processing this request (SERVFAIL)")
		case dns.RcodeNameError:
			error(w, 500, 503, "Some name that ought to exist, does not exist (NXDOMAIN)")
		case dns.RcodeRefused:
			error(w, 500, 505, "The name server refuses to perform the specified operation for policy or security reasons (REFUSED)")
		default:
			jsonify(w, r, in.Question, in.Answer, in.Ns, in.Extra)
		}
	} else if err == dns.ErrTruncated {
		c.Net = "tcp"
		goto Redo
	} else {
		error(w, 500, 501, "DNS server could not be reached")
	}
}

// Handler for DNS queries
func query(w http.ResponseWriter, r *http.Request) {
	server := r.URL.Query().Get(":server")
	domain := dns.Fqdn(r.URL.Query().Get(":domain"))
	querytype := r.URL.Query().Get(":querytype")

	if domain, err := idna.ToASCII(domain); err == nil { // Valid domain name (ASCII or IDN)
		if _, isDomain := dns.IsDomainName(domain); isDomain { // Well-formed domain name
			if querytype, ok := dns.StringToType[strings.ToUpper(querytype)]; ok { // Valid DNS query type
				resolve(w, r, server, domain, querytype)
			} else {
				error(w, 400, 404, "Invalid DNS query type")
			}
		} else {
			error(w, 400, 402, "Input string is not a well-formed domain name")
		}
	} else {
		error(w, 400, 401, "Input string could not be parsed")
	}
}

// Handler for reverse DNS queries
func ptr(w http.ResponseWriter, r *http.Request) {
	server := r.URL.Query().Get(":server")
	ip := r.URL.Query().Get(":ip")

	if arpa, err := dns.ReverseAddr(ip); err == nil { // Valid IP address (IPv4 or IPv6)
		resolve(w, r, server, arpa, dns.TypePTR)
	} else {
		error(w, 400, 403, "Input string is not a valid IP address")
	}
}

func main() {
	header := "-------------------------------------------------------------------------------\n        RRDA (RRDA REST DNS API) 1.02 (c) by Frederic Cambus 2012-2016\n-------------------------------------------------------------------------------"

	fastcgi := flag.Bool("fastcgi", false, "Enable FastCGI mode")
	host := flag.String("host", "127.0.0.1", "Set the server host")
	port := flag.String("port", "8080", "Set the server port")

	flag.Usage = func() {
		fmt.Println(header)
		fmt.Println("\nUSAGE:")
		flag.PrintDefaults()
	}
	flag.Parse()

	fmt.Println(header)

	fmt.Println("\nListening on :", *host+":"+*port)

	m := pat.New()
	m.Get("/:server/x/:ip", http.HandlerFunc(ptr))
	m.Get("/:server/:domain/:querytype", http.HandlerFunc(query))

	if *fastcgi {
		listener, _ := net.Listen("tcp", *host+":"+*port)

		if err := fcgi.Serve(listener, m); err != nil {
			fmt.Println("\nERROR:", err)
			os.Exit(1)
		}
	} else {
		if err := http.ListenAndServe(*host+":"+*port, m); err != nil {
			fmt.Println("\nERROR:", err)
			os.Exit(1)
		}
	}
}
<commit_msg>Do not display header when starting<commit_after>/*****************************************************************************/
/*                                                                           */
/* RRDA (RRDA REST DNS API) 1.02                                             */
/* Copyright (c) 2012-2016, Frederic Cambus                                  */
/* http://www.statdns.com                                                    */
/*                                                                           */
/* Created: 2012-03-11                                                       */
/* Last Updated: 2016-07-18                                                  */
/*                                                                           */
/* RRDA is released under the BSD 2-Clause license.                          */
/* See LICENSE file for details.                                             */
/*                                                                           */
/*****************************************************************************/

package main

import (
	"encoding/json"
	"flag"
	"fmt"
	"github.com/bmizerany/pat"
	"github.com/miekg/dns"
	"golang.org/x/net/idna"
	"io"
	"net"
	"net/http"
	"net/http/fcgi"
	"os"
	"strings"
)

type Error struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

type Question struct {
	Name  string `json:"name"`
	Type  string `json:"type"`
	Class string `json:"class"`
}

type Section struct {
	Name     string `json:"name"`
	Type     string `json:"type"`
	Class    string `json:"class"`
	Ttl      uint32 `json:"ttl"`
	Rdlength uint16 `json:"rdlength"`
	Rdata    string `json:"rdata"`
}

type Message struct {
	Question   []*Question `json:"question"`
	Answer     []*Section  `json:"answer"`
	Authority  []*Section  `json:"authority,omitempty"`
	Additional []*Section  `json:"additional,omitempty"`
}

// Return rdata
func rdata(RR dns.RR) string {
	return strings.Replace(RR.String(), RR.Header().String(), "", -1)
}

// Return an HTTP Error along with a JSON-encoded error message
func error(w http.ResponseWriter, status int, code int, message string) {
	if output, err := json.Marshal(Error{Code: code, Message: message}); err == nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(status)
		fmt.Fprintln(w, string(output))
	}
}

// Generate JSON output
func jsonify(w http.ResponseWriter, r *http.Request, question []dns.Question, answer []dns.RR, authority []dns.RR, additional []dns.RR) {
	var answerArray, authorityArray, additionalArray []*Section

	callback := r.URL.Query().Get("callback")

	for _, answer := range answer {
		answerArray = append(answerArray, &Section{answer.Header().Name, dns.TypeToString[answer.Header().Rrtype], dns.ClassToString[answer.Header().Class], answer.Header().Ttl, answer.Header().Rdlength, rdata(answer)})
	}

	for _, authority := range authority {
		authorityArray = append(authorityArray, &Section{authority.Header().Name, dns.TypeToString[authority.Header().Rrtype], dns.ClassToString[authority.Header().Class], authority.Header().Ttl, authority.Header().Rdlength, rdata(authority)})
	}

	for _, additional := range additional {
		additionalArray = append(additionalArray, &Section{additional.Header().Name, dns.TypeToString[additional.Header().Rrtype], dns.ClassToString[additional.Header().Class], additional.Header().Ttl, additional.Header().Rdlength, rdata(additional)})
	}

	if json, err := json.MarshalIndent(Message{[]*Question{&Question{question[0].Name, dns.TypeToString[question[0].Qtype], dns.ClassToString[question[0].Qclass]}}, answerArray, authorityArray, additionalArray}, "", "    "); err == nil {
		if callback != "" {
			io.WriteString(w, callback+"("+string(json)+");")
		} else {
			io.WriteString(w, string(json))
		}
	}
}

// Perform DNS resolution
func resolve(w http.ResponseWriter, r *http.Request, server string, domain string, querytype uint16) {
	m := new(dns.Msg)
	m.SetQuestion(domain, querytype)
	m.MsgHdr.RecursionDesired = true

	w.Header().Set("Content-Type", "application/json")
	w.Header().Set("Access-Control-Allow-Origin", "*")

	c := new(dns.Client)

Redo:
	in, _, err := c.Exchange(m, server) // Second return value is RTT, not used for now

	if err == nil {
		switch in.MsgHdr.Rcode {
		case dns.RcodeServerFailure:
			error(w, 500, 502, "The name server encountered an internal failure while processing this request (SERVFAIL)")
		case dns.RcodeNameError:
			error(w, 500, 503, "Some name that ought to exist, does not exist (NXDOMAIN)")
		case dns.RcodeRefused:
			error(w, 500, 505, "The name server refuses to perform the specified operation for policy or security reasons (REFUSED)")
		default:
			jsonify(w, r, in.Question, in.Answer, in.Ns, in.Extra)
		}
	} else if err == dns.ErrTruncated {
		c.Net = "tcp"
		goto Redo
	} else {
		error(w, 500, 501, "DNS server could not be reached")
	}
}

// Handler for DNS queries
func query(w http.ResponseWriter, r *http.Request) {
	server := r.URL.Query().Get(":server")
	domain := dns.Fqdn(r.URL.Query().Get(":domain"))
	querytype := r.URL.Query().Get(":querytype")

	if domain, err := idna.ToASCII(domain); err == nil { // Valid domain name (ASCII or IDN)
		if _, isDomain := dns.IsDomainName(domain); isDomain { // Well-formed domain name
			if querytype, ok := dns.StringToType[strings.ToUpper(querytype)]; ok { // Valid DNS query type
				resolve(w, r, server, domain, querytype)
			} else {
				error(w, 400, 404, "Invalid DNS query type")
			}
		} else {
			error(w, 400, 402, "Input string is not a well-formed domain name")
		}
	} else {
		error(w, 400, 401, "Input string could not be parsed")
	}
}

// Handler for reverse DNS queries
func ptr(w http.ResponseWriter, r *http.Request) {
	server := r.URL.Query().Get(":server")
	ip := r.URL.Query().Get(":ip")

	if arpa, err := dns.ReverseAddr(ip); err == nil { // Valid IP address (IPv4 or IPv6)
		resolve(w, r, server, arpa, dns.TypePTR)
	} else {
		error(w, 400, 403, "Input string is not a valid IP address")
	}
}

func main() {
	fastcgi := flag.Bool("fastcgi", false, "Enable FastCGI mode")
	host := flag.String("host", "127.0.0.1", "Set the server host")
	port := flag.String("port", "8080", "Set the server port")

	flag.Usage = func() {
		fmt.Println("\nUSAGE:")
		flag.PrintDefaults()
	}
	flag.Parse()

	fmt.Println("\nListening on :", *host+":"+*port)

	m := pat.New()
	m.Get("/:server/x/:ip", http.HandlerFunc(ptr))
	m.Get("/:server/:domain/:querytype", http.HandlerFunc(query))

	if *fastcgi {
		listener, _ := net.Listen("tcp", *host+":"+*port)

		if err := fcgi.Serve(listener, m); err != nil {
			fmt.Println("\nERROR:", err)
			os.Exit(1)
		}
	} else {
		if err := http.ListenAndServe(*host+":"+*port, m); err != nil {
			fmt.Println("\nERROR:", err)
			os.Exit(1)
		}
	}
}
<|endoftext|>


<commit_before>/*****************************************************************************/
/*                                                                           */
/* RRDA (RRDA REST DNS API) 1.00 (c) by Frederic Cambus 2012-2014            */
/* http://www.statdns.com                                                    */
/*                                                                           */
/* Created: 2012/03/11                                                       */
/* Last Updated: 2014/03/26                                                  */
/*                                                                           */
/* RRDA is released under the BSD 3-Clause license.                          */
/* See LICENSE file for details.                                             */
/*                                                                           */
/*****************************************************************************/

package main

import (
	"code.google.com/p/go.net/idna"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/bmizerany/pat"
	"github.com/miekg/dns"
	"io"
	"net/http"
	"os"
	"strings"
)

type Error struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

type Question struct {
	Name  string `json:"name"`
	Type  string `json:"type"`
	Class string `json:"class"`
}

type Section struct {
	Name     string `json:"name"`
	Type     string `json:"type"`
	Class    string `json:"class"`
	Ttl      uint32 `json:"ttl"`
	Rdlength uint16 `json:"rdlength"`
	Rdata    string `json:"rdata"`
}

type Message struct {
	Question   []*Question `json:"question"`
	Answer     []*Section  `json:"answer"`
	Authority  []*Section  `json:"authority,omitempty"`
	Additional []*Section  `json:"additional,omitempty"`
}

// Return rdata
func rdata(RR dns.RR) string {
	return strings.Replace(RR.String(), RR.Header().String(), "", -1)
}

// Return an HTTP Error along with a JSON-encoded error message
func error(w http.ResponseWriter, status int, code int, message string) {
	if output, err := json.Marshal(Error{Code: code, Message: message}); err == nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(status)
		fmt.Fprintln(w, string(output))
	}
}

// Generate JSON output
func jsonify(w http.ResponseWriter, question []dns.Question, answer []dns.RR, authority []dns.RR, additional []dns.RR) {
	var answerArray, authorityArray, additionalArray []*Section

	for _, answer := range answer {
		answerArray = append(answerArray, &Section{answer.Header().Name, dns.TypeToString[answer.Header().Rrtype], dns.ClassToString[answer.Header().Class], answer.Header().Ttl, answer.Header().Rdlength, rdata(answer)})
	}

	for _, authority := range authority {
		authorityArray = append(authorityArray, &Section{authority.Header().Name, dns.TypeToString[authority.Header().Rrtype], dns.ClassToString[authority.Header().Class], authority.Header().Ttl, authority.Header().Rdlength, rdata(authority)})
	}

	for _, additional := range additional {
		additionalArray = append(additionalArray, &Section{additional.Header().Name, dns.TypeToString[additional.Header().Rrtype], dns.ClassToString[additional.Header().Class], additional.Header().Ttl, additional.Header().Rdlength, rdata(additional)})
	}

	if output, err := json.MarshalIndent(Message{[]*Question{&Question{question[0].Name, dns.TypeToString[question[0].Qtype], dns.ClassToString[question[0].Qclass]}}, answerArray, authorityArray, additionalArray}, "", "    "); err == nil {
		io.WriteString(w, string(output))
	}
}

// Perform DNS resolution
func resolve(w http.ResponseWriter, server string, domain string, querytype uint16) {
	m := new(dns.Msg)
	m.SetQuestion(domain, querytype)
	m.MsgHdr.RecursionDesired = true

	w.Header().Set("Content-Type", "application/json")

	c := new(dns.Client)

Redo:
	if in, _, err := c.Exchange(m, server); err == nil { // Second return value is RTT, not used for now
		if in.MsgHdr.Truncated {
			c.Net = "tcp"
			goto Redo
		}

		switch in.MsgHdr.Rcode {
		case dns.RcodeServerFailure:
			error(w, 500, 502, "The name server encountered an internal failure while processing this request (SERVFAIL)")
		case dns.RcodeNameError:
			error(w, 500, 503, "Some name that ought to exist, does not exist (NXDOMAIN)")
		case dns.RcodeRefused:
			error(w, 500, 505, "The name server refuses to perform the specified operation for policy or security reasons (REFUSED)")
		default:
			jsonify(w, in.Question, in.Answer, in.Ns, in.Extra)
		}
	} else {
		error(w, 500, 501, "DNS server could not be reached")
	}
}

// Handler for DNS queries
func query(w http.ResponseWriter, r *http.Request) {
	server := r.URL.Query().Get(":server")
	domain := dns.Fqdn(r.URL.Query().Get(":domain"))
	querytype := r.URL.Query().Get(":querytype")

	if domain, err := idna.ToASCII(domain); err == nil { // Valid domain name (ASCII or IDN)
		if _, isDomain := dns.IsDomainName(domain); isDomain { // Well-formed domain name
			if querytype, ok := dns.StringToType[strings.ToUpper(querytype)]; ok { // Valid DNS query type
				resolve(w, server, domain, querytype)
			} else {
				error(w, 400, 404, "Invalid DNS query type")
			}
		} else {
			error(w, 400, 402, "Input string is not a well-formed domain name")
		}
	} else {
		error(w, 400, 401, "Input string could not be parsed")
	}
}

// Handler for reverse DNS queries
func ptr(w http.ResponseWriter, r *http.Request) {
	server := r.URL.Query().Get(":server")
	ip := r.URL.Query().Get(":ip")

	if arpa, err := dns.ReverseAddr(ip); err == nil { // Valid IP address (IPv4 or IPv6)
		resolve(w, server, arpa, dns.TypePTR)
	} else {
		error(w, 400, 403, "Input string is not a valid IP address")
	}
}

func main() {
	header := "-------------------------------------------------------------------------------\n        RRDA (RRDA REST DNS API) 1.00 (c) by Frederic Cambus 2012-2014\n-------------------------------------------------------------------------------"

	host := flag.String("host", "127.0.0.1", "Set the server host")
	port := flag.String("port", "8080", "Set the server port")

	flag.Usage = func() {
		fmt.Println(header)
		fmt.Println("\nUSAGE :")
		flag.PrintDefaults()
	}
	flag.Parse()

	fmt.Println(header)

	fmt.Println("\nListening on :", *host + ":" + *port)

	m := pat.New()
	m.Get("/:server/x/:ip", http.HandlerFunc(ptr))
	m.Get("/:server/:domain/:querytype", http.HandlerFunc(query))

	if err := http.ListenAndServe(*host+":"+*port, m); err != nil {
		fmt.Println("\nERROR :", err)
		os.Exit(1)
	}
}
<commit_msg>Adding JSONP support<commit_after>/*****************************************************************************/
/*                                                                           */
/* RRDA (RRDA REST DNS API) 1.00 (c) by Frederic Cambus 2012-2014            */
/* http://www.statdns.com                                                    */
/*                                                                           */
/* Created: 2012/03/11                                                       */
/* Last Updated: 2014/03/26                                                  */
/*                                                                           */
/* RRDA is released under the BSD 3-Clause license.                          */
/* See LICENSE file for details.                                             */
/*                                                                           */
/*****************************************************************************/

package main

import (
	"code.google.com/p/go.net/idna"
	"encoding/json"
	"flag"
	"fmt"
	"github.com/bmizerany/pat"
	"github.com/miekg/dns"
	"io"
	"net/http"
	"os"
	"strings"
)

type Error struct {
	Code    int    `json:"code"`
	Message string `json:"message"`
}

type Question struct {
	Name  string `json:"name"`
	Type  string `json:"type"`
	Class string `json:"class"`
}

type Section struct {
	Name     string `json:"name"`
	Type     string `json:"type"`
	Class    string `json:"class"`
	Ttl      uint32 `json:"ttl"`
	Rdlength uint16 `json:"rdlength"`
	Rdata    string `json:"rdata"`
}

type Message struct {
	Question   []*Question `json:"question"`
	Answer     []*Section  `json:"answer"`
	Authority  []*Section  `json:"authority,omitempty"`
	Additional []*Section  `json:"additional,omitempty"`
}

// Return rdata
func rdata(RR dns.RR) string {
	return strings.Replace(RR.String(), RR.Header().String(), "", -1)
}

// Return an HTTP Error along with a JSON-encoded error message
func error(w http.ResponseWriter, status int, code int, message string) {
	if output, err := json.Marshal(Error{Code: code, Message: message}); err == nil {
		w.Header().Set("Content-Type", "application/json")
		w.WriteHeader(status)
		fmt.Fprintln(w, string(output))
	}
}

// Generate JSON output
func jsonify(w http.ResponseWriter, r *http.Request, question []dns.Question, answer []dns.RR, authority []dns.RR, additional []dns.RR) {
	var answerArray, authorityArray, additionalArray []*Section

	callback := r.URL.Query().Get("callback")

	for _, answer := range answer {
		answerArray = append(answerArray, &Section{answer.Header().Name, dns.TypeToString[answer.Header().Rrtype], dns.ClassToString[answer.Header().Class], answer.Header().Ttl, answer.Header().Rdlength, rdata(answer)})
	}

	for _, authority := range authority {
		authorityArray = append(authorityArray, &Section{authority.Header().Name, dns.TypeToString[authority.Header().Rrtype], dns.ClassToString[authority.Header().Class], authority.Header().Ttl, authority.Header().Rdlength, rdata(authority)})
	}

	for _, additional := range additional {
		additionalArray = append(additionalArray, &Section{additional.Header().Name, dns.TypeToString[additional.Header().Rrtype], dns.ClassToString[additional.Header().Class], additional.Header().Ttl, additional.Header().Rdlength, rdata(additional)})
	}

	if json, err := json.MarshalIndent(Message{[]*Question{&Question{question[0].Name, dns.TypeToString[question[0].Qtype], dns.ClassToString[question[0].Qclass]}}, answerArray, authorityArray, additionalArray}, "", "    "); err == nil {
		if (callback != "") {
			io.WriteString(w, callback + "(" + string(json) + ");")
		} else {
			io.WriteString(w, string(json))
		}
	}
}

// Perform DNS resolution
func resolve(w http.ResponseWriter, r *http.Request, server string, domain string, querytype uint16) {
	m := new(dns.Msg)
	m.SetQuestion(domain, querytype)
	m.MsgHdr.RecursionDesired = true

	w.Header().Set("Content-Type", "application/json")

	c := new(dns.Client)

Redo:
	if in, _, err := c.Exchange(m, server); err == nil { // Second return value is RTT, not used for now
		if in.MsgHdr.Truncated {
			c.Net = "tcp"
			goto Redo
		}

		switch in.MsgHdr.Rcode {
		case dns.RcodeServerFailure:
			error(w, 500, 502, "The name server encountered an internal failure while processing this request (SERVFAIL)")
		case dns.RcodeNameError:
			error(w, 500, 503, "Some name that ought to exist, does not exist (NXDOMAIN)")
		case dns.RcodeRefused:
			error(w, 500, 505, "The name server refuses to perform the specified operation for policy or security reasons (REFUSED)")
		default:
			jsonify(w, r, in.Question, in.Answer, in.Ns, in.Extra)
		}
	} else {
		error(w, 500, 501, "DNS server could not be reached")
	}
}

// Handler for DNS queries
func query(w http.ResponseWriter, r *http.Request) {
	server := r.URL.Query().Get(":server")
	domain := dns.Fqdn(r.URL.Query().Get(":domain"))
	querytype := r.URL.Query().Get(":querytype")

	if domain, err := idna.ToASCII(domain); err == nil { // Valid domain name (ASCII or IDN)
		if _, isDomain := dns.IsDomainName(domain); isDomain { // Well-formed domain name
			if querytype, ok := dns.StringToType[strings.ToUpper(querytype)]; ok { // Valid DNS query type
				resolve(w, r, server, domain, querytype)
			} else {
				error(w, 400, 404, "Invalid DNS query type")
			}
		} else {
			error(w, 400, 402, "Input string is not a well-formed domain name")
		}
	} else {
		error(w, 400, 401, "Input string could not be parsed")
	}
}

// Handler for reverse DNS queries
func ptr(w http.ResponseWriter, r *http.Request) {
	server := r.URL.Query().Get(":server")
	ip := r.URL.Query().Get(":ip")

	if arpa, err := dns.ReverseAddr(ip); err == nil { // Valid IP address (IPv4 or IPv6)
		resolve(w, r, server, arpa, dns.TypePTR)
	} else {
		error(w, 400, 403, "Input string is not a valid IP address")
	}
}

func main() {
	header := "-------------------------------------------------------------------------------\n        RRDA (RRDA REST DNS API) 1.00 (c) by Frederic Cambus 2012-2014\n-------------------------------------------------------------------------------"

	host := flag.String("host", "127.0.0.1", "Set the server host")
	port := flag.String("port", "8080", "Set the server port")

	flag.Usage = func() {
		fmt.Println(header)
		fmt.Println("\nUSAGE :")
		flag.PrintDefaults()
	}
	flag.Parse()

	fmt.Println(header)

	fmt.Println("\nListening on :", *host + ":" + *port)

	m := pat.New()
	m.Get("/:server/x/:ip", http.HandlerFunc(ptr))
	m.Get("/:server/:domain/:querytype", http.HandlerFunc(query))

	if err := http.ListenAndServe(*host+":"+*port, m); err != nil {
		fmt.Println("\nERROR :", err)
		os.Exit(1)
	}
}
<|endoftext|>


<commit_before>package main

import (
	"html/template"
	"io/ioutil"
	"net/http"
	"path"
	"regexp"
	"strconv"
	"strings"
	"time"

	"github.com/boltdb/bolt"
	"github.com/gin-gonic/gin"
	"github.com/microcosm-cc/bluemonday"
	"github.com/russross/blackfriday"
)

func newNote(c *gin.Context) {
	title := randomAlliterateCombo()
	c.Redirect(302, "/"+title)
}

func editNote(c *gin.Context) {
	title := c.Param("title")
	if title == "ws" {
		wshandler(c.Writer, c.Request)
	} else if title == "robots.txt" {
		c.Data(200, "text/plain", []byte(robotsTxt))
	} else if strings.ToLower(title) == "about" { //}&& strings.Contains(AllowedIPs, c.ClientIP()) != true {
		c.Redirect(302, "/about/view")
	} else {
		locked, _ := hasPassword(title)
		if locked {
			c.Redirect(302, "/"+title+"/view")
		} else {
			version := c.DefaultQuery("version", "-1")
			versionNum, _ := strconv.Atoi(version)
			currentText, versions, currentVersion, totalTime := getCurrentText(title, versionNum)
			numRows := len(strings.Split(currentText, "\n")) + 10
			if currentVersion {
				c.HTML(http.StatusOK, "index.tmpl", gin.H{
					"Title":       title,
					"WikiName":    RuntimeArgs.WikiName,
					"ExternalIP":  RuntimeArgs.ExternalIP,
					"CurrentText": currentText,
					"NumRows":     numRows,
					"Versions":    versions,
					"TotalTime":   totalTime,
				})
			} else {
				c.HTML(http.StatusOK, "index.tmpl", gin.H{
					"Title":       title,
					"WikiName":    RuntimeArgs.WikiName,
					"ExternalIP":  RuntimeArgs.ExternalIP,
					"CurrentText": currentText,
					"NumRows":     numRows,
					"Versions":    versions,
					"TotalTime":   totalTime,
					"NoEdit":      true,
				})
			}

		}
	}
}

func everythingElse(c *gin.Context) {
	option := c.Param("option")
	title := c.Param("title")
	if option == "/view" {
		version := c.DefaultQuery("version", "-1")
		versionNum, _ := strconv.Atoi(version)
		if strings.ToLower(title) == "about" {
			versionNum = -1
		}
		currentText, versions, _, totalTime := getCurrentText(title, versionNum)
		renderMarkdown(c, currentText, title, versions, "", totalTime)
	} else if title == "ls" && option == "/"+RuntimeArgs.AdminKey && len(RuntimeArgs.AdminKey) > 1 {
		renderMarkdown(c, listEverything(), "ls", nil, RuntimeArgs.AdminKey, time.Now().Sub(time.Now()))
	} else if option == "/list" {
		renderList(c, title)
	} else if title == "static" {
		serveStaticFile(c, option)
	} else {
		c.Redirect(302, "/"+title)
	}
}

func serveStaticFile(c *gin.Context, option string) {
	staticFile, err := ioutil.ReadFile(path.Join(RuntimeArgs.SourcePath, "static") + option)
	if err != nil {
		c.AbortWithStatus(404)
	} else {
		c.Data(200, contentType(option), []byte(staticFile))
	}
}

func renderMarkdown(c *gin.Context, currentText string, title string, versions []versionsInfo, AdminKey string, totalTime time.Duration) {
	r, _ := regexp.Compile("\\[\\[(.*?)\\]\\]")
	for _, s := range r.FindAllString(currentText, -1) {
		currentText = strings.Replace(currentText, s, "["+s[2:len(s)-2]+"](/"+s[2:len(s)-2]+"/view)", 1)
	}
	unsafe := blackfriday.MarkdownCommon([]byte(currentText))
	pClean := bluemonday.UGCPolicy()
	pClean.AllowElements("img")
	pClean.AllowAttrs("alt").OnElements("img")
	pClean.AllowAttrs("src").OnElements("img")
	pClean.AllowAttrs("class").OnElements("a")
	pClean.AllowAttrs("href").OnElements("a")
	pClean.AllowAttrs("id").OnElements("a")
	pClean.AllowDataURIImages()
	html := pClean.SanitizeBytes(unsafe)
	html2 := string(html)
	r, _ = regexp.Compile("\\$\\$(.*?)\\$\\$")
	for _, s := range r.FindAllString(html2, -1) {
		html2 = strings.Replace(html2, s, "", 1)
	}
	r, _ = regexp.Compile("\\$(.*?)\\$")
	for _, s := range r.FindAllString(html2, -1) {
		html2 = strings.Replace(html2, s, "", 1)
	}

	html2 = strings.Replace(html2, "&#36;", "$", -1)
	html2 = strings.Replace(html2, "&#91;", "[", -1)
	html2 = strings.Replace(html2, "&#93;", "]", -1)
	html2 = strings.Replace(html2, "&amp35;", "#", -1)

	if AdminKey == "" {
		c.HTML(http.StatusOK, "view.tmpl", gin.H{
			"Title":     title,
			"WikiName":  RuntimeArgs.WikiName,
			"Body":      template.HTML([]byte(html2)),
			"TotalTime": totalTime.String(),
			"Versions":  versions,
		})
	} else {
		c.HTML(http.StatusOK, "view.tmpl", gin.H{
			"Title":     title,
			"WikiName":  RuntimeArgs.WikiName,
			"Body":      template.HTML([]byte(html2)),
			"Versions":  versions,
			"TotalTime": totalTime.String(),
			"AdminKey":  AdminKey,
		})
	}
}

func reorderList(text string) ([]template.HTML, []string) {
	listItemsString := ""
	for _, lineString := range strings.Split(text, "\n") {
		if len(lineString) > 1 {
			if string(lineString[0]) != "-" {
				listItemsString += "- " + lineString + "\n"
			} else {
				listItemsString += lineString + "\n"
			}
		}
	}

	// get ordering of template.HTML for rendering
	renderedListString := string(blackfriday.MarkdownCommon([]byte(listItemsString)))
	listItems := []template.HTML{}
	endItems := []template.HTML{}
	for _, lineString := range strings.Split(renderedListString, "\n") {
		if len(lineString) > 1 {
			if strings.Contains(lineString, "") || strings.Contains(lineString, "
") {
				endItems = append(endItems, template.HTML(lineString))
			} else {
				listItems = append(listItems, template.HTML(lineString))
			}
		}
	}

	// get ordering of strings for deleting
	listItemsStringArray := []string{}
	endItemsStringArray := []string{}
	for _, lineString := range strings.Split(listItemsString, "\n") {
		if len(lineString) > 1 {
			if strings.Contains(lineString, "~~") {
				endItemsStringArray = append(endItemsStringArray, lineString)
			} else {
				listItemsStringArray = append(listItemsStringArray, lineString)
			}
		}
	}
	return append(listItems, endItems...), append(listItemsStringArray, endItemsStringArray...)
}

func renderList(c *gin.Context, title string) {
	if strings.ToLower(title) == "about" { //}&& strings.Contains(AllowedIPs, c.ClientIP()) != true {
		c.Redirect(302, "/about/view")
	}
	var p WikiData
	err := p.load(strings.ToLower(title))
	if err != nil {
		panic(err)
	}

	listItems, _ := reorderList(p.CurrentText)

	c.HTML(http.StatusOK, "list.tmpl", gin.H{
		"Title":     title,
		"WikiName":  RuntimeArgs.WikiName,
		"ListItems": listItems,
	})
}

func deleteListItem(c *gin.Context) {
	lineNum, err := strconv.Atoi(c.DefaultQuery("lineNum", "None"))
	title := c.Query("title") // shortcut for c.Request.URL.Query().Get("lastname")
	if err == nil {
		var p WikiData
		err := p.load(strings.ToLower(title))
		if err != nil {
			panic(err)
		}

		_, listItems := reorderList(p.CurrentText)
		newText := p.CurrentText
		for i, lineString := range listItems {
			// fmt.Println(i, lineString, lineNum)
			if i+1 == lineNum {
				// fmt.Println("MATCHED")
				if strings.Contains(lineString, "~~") == false {
					// fmt.Println(p.Text, "("+lineString[2:]+"\n"+")", "~~"+lineString[2:]+"~~"+"\n")
					newText = strings.Replace(newText+"\n", lineString[2:]+"\n", "~~"+strings.TrimSpace(lineString[2:])+"~~"+"\n", 1)
				} else {
					newText = strings.Replace(newText+"\n", lineString[2:]+"\n", lineString[4:len(lineString)-2]+"\n", 1)
				}
				p.save(newText)
				break
			}
		}

		c.JSON(200, gin.H{
			"message": "Done.",
		})
	} else {
		c.JSON(404, gin.H{
			"message": "?",
		})
	}
}

func deletePage(c *gin.Context) {
	deleteName := c.DefaultQuery("DeleteName", "None")
	adminKey := c.DefaultQuery("AdminKey", "None")
	if adminKey == RuntimeArgs.AdminKey {
		p := WikiData{deleteName, "", []string{}, []string{}}
		p.save("")
		c.JSON(200, gin.H{
			"message": "Done.",
		})
	} else {
		c.JSON(404, gin.H{
			"message": "?",
		})
	}
}

func listEverything() string {
	everything := `| Title | Current size    | Changes  | Total Size |  |
| --------- |-------------| -----| ------------- | ------------- |
`
	db.View(func(tx *bolt.Tx) error {
		// Assume bucket exists and has keys
		b := tx.Bucket([]byte("datas"))
		c := b.Cursor()
		for k, v := c.First(); k != nil; k, v = c.Next() {
			var p WikiData
			p.load(string(k))
			if len(p.CurrentText) > 1 {
				contentSize := strconv.Itoa(len(p.CurrentText))
				numChanges := strconv.Itoa(len(p.Diffs))
				totalSize := strconv.Itoa(len(v))
				everything += "| [" + p.Title + "](/" + p.Title + "/view) | " + contentSize + " | " + numChanges + " | " + totalSize + ` | <a class="deleteable" id="` + p.Title + `">Delete</a> | ` + "\n"
			}
		}
		return nil
	})
	return everything
}
<commit_msg>Bluemonday onto lists too<commit_after>package main

import (
	"html/template"
	"io/ioutil"
	"net/http"
	"path"
	"regexp"
	"strconv"
	"strings"
	"time"
	"fmt"

	"github.com/boltdb/bolt"
	"github.com/gin-gonic/gin"
	"github.com/microcosm-cc/bluemonday"
	"github.com/russross/blackfriday"
)

func newNote(c *gin.Context) {
	title := randomAlliterateCombo()
	c.Redirect(302, "/"+title)
}

func editNote(c *gin.Context) {
	title := c.Param("title")
	if title == "ws" {
		wshandler(c.Writer, c.Request)
	} else if title == "robots.txt" {
		c.Data(200, "text/plain", []byte(robotsTxt))
	} else if strings.ToLower(title) == "about" { //}&& strings.Contains(AllowedIPs, c.ClientIP()) != true {
		c.Redirect(302, "/about/view")
	} else {
		locked, _ := hasPassword(title)
		if locked {
			c.Redirect(302, "/"+title+"/view")
		} else {
			version := c.DefaultQuery("version", "-1")
			versionNum, _ := strconv.Atoi(version)
			currentText, versions, currentVersion, totalTime := getCurrentText(title, versionNum)
			numRows := len(strings.Split(currentText, "\n")) + 10
			if currentVersion {
				c.HTML(http.StatusOK, "index.tmpl", gin.H{
					"Title":       title,
					"WikiName":    RuntimeArgs.WikiName,
					"ExternalIP":  RuntimeArgs.ExternalIP,
					"CurrentText": currentText,
					"NumRows":     numRows,
					"Versions":    versions,
					"TotalTime":   totalTime,
				})
			} else {
				c.HTML(http.StatusOK, "index.tmpl", gin.H{
					"Title":       title,
					"WikiName":    RuntimeArgs.WikiName,
					"ExternalIP":  RuntimeArgs.ExternalIP,
					"CurrentText": currentText,
					"NumRows":     numRows,
					"Versions":    versions,
					"TotalTime":   totalTime,
					"NoEdit":      true,
				})
			}

		}
	}
}

func everythingElse(c *gin.Context) {
	option := c.Param("option")
	title := c.Param("title")
	if option == "/view" {
		version := c.DefaultQuery("version", "-1")
		versionNum, _ := strconv.Atoi(version)
		if strings.ToLower(title) == "about" {
			versionNum = -1
		}
		currentText, versions, _, totalTime := getCurrentText(title, versionNum)
		renderMarkdown(c, currentText, title, versions, "", totalTime)
	} else if title == "ls" && option == "/"+RuntimeArgs.AdminKey && len(RuntimeArgs.AdminKey) > 1 {
		renderMarkdown(c, listEverything(), "ls", nil, RuntimeArgs.AdminKey, time.Now().Sub(time.Now()))
	} else if option == "/list" {
		renderList(c, title)
	} else if title == "static" {
		serveStaticFile(c, option)
	} else {
		c.Redirect(302, "/"+title)
	}
}

func serveStaticFile(c *gin.Context, option string) {
	staticFile, err := ioutil.ReadFile(path.Join(RuntimeArgs.SourcePath, "static") + option)
	if err != nil {
		c.AbortWithStatus(404)
	} else {
		c.Data(200, contentType(option), []byte(staticFile))
	}
}

func renderMarkdown(c *gin.Context, currentText string, title string, versions []versionsInfo, AdminKey string, totalTime time.Duration) {
	r, _ := regexp.Compile("\\[\\[(.*?)\\]\\]")
	for _, s := range r.FindAllString(currentText, -1) {
		currentText = strings.Replace(currentText, s, "["+s[2:len(s)-2]+"](/"+s[2:len(s)-2]+"/view)", 1)
	}
	unsafe := blackfriday.MarkdownCommon([]byte(currentText))
	pClean := bluemonday.UGCPolicy()
	pClean.AllowElements("img")
	pClean.AllowAttrs("alt").OnElements("img")
	pClean.AllowAttrs("src").OnElements("img")
	pClean.AllowAttrs("class").OnElements("a")
	pClean.AllowAttrs("href").OnElements("a")
	pClean.AllowAttrs("id").OnElements("a")
	pClean.AllowDataURIImages()
	html := pClean.SanitizeBytes(unsafe)
	html2 := string(html)
	r, _ = regexp.Compile("\\$\\$(.*?)\\$\\$")
	for _, s := range r.FindAllString(html2, -1) {
		html2 = strings.Replace(html2, s, "", 1)
	}
	r, _ = regexp.Compile("\\$(.*?)\\$")
	for _, s := range r.FindAllString(html2, -1) {
		html2 = strings.Replace(html2, s, "", 1)
	}

	html2 = strings.Replace(html2, "&#36;", "$", -1)
	html2 = strings.Replace(html2, "&#91;", "[", -1)
	html2 = strings.Replace(html2, "&#93;", "]", -1)
	html2 = strings.Replace(html2, "&amp35;", "#", -1)

	if AdminKey == "" {
		c.HTML(http.StatusOK, "view.tmpl", gin.H{
			"Title":     title,
			"WikiName":  RuntimeArgs.WikiName,
			"Body":      template.HTML([]byte(html2)),
			"TotalTime": totalTime.String(),
			"Versions":  versions,
		})
	} else {
		c.HTML(http.StatusOK, "view.tmpl", gin.H{
			"Title":     title,
			"WikiName":  RuntimeArgs.WikiName,
			"Body":      template.HTML([]byte(html2)),
			"Versions":  versions,
			"TotalTime": totalTime.String(),
			"AdminKey":  AdminKey,
		})
	}
}

func reorderList(text string) ([]template.HTML, []string) {
	listItemsString := ""
	for _, lineString := range strings.Split(text, "\n") {
		if len(lineString) > 1 {
			if string(lineString[0]) != "-" {
				listItemsString += "- " + lineString + "\n"
			} else {
				listItemsString += lineString + "\n"
			}
		}
	}

	// get ordering of template.HTML for rendering
	renderedListString := string(blackfriday.MarkdownCommon([]byte(listItemsString)))
	listItems := []template.HTML{}
	endItems := []template.HTML{}
	for _, lineString := range strings.Split(renderedListString, "\n") {
		if len(lineString) > 1 {
			if strings.Contains(lineString, "") || strings.Contains(lineString, "
") {
				endItems = append(endItems, template.HTML(lineString))
			} else {
				listItems = append(listItems, template.HTML(lineString))
			}
		}
	}

	// get ordering of strings for deleting
	listItemsStringArray := []string{}
	endItemsStringArray := []string{}
	for _, lineString := range strings.Split(listItemsString, "\n") {
		if len(lineString) > 1 {
			if strings.Contains(lineString, "~~") {
				endItemsStringArray = append(endItemsStringArray, lineString)
			} else {
				listItemsStringArray = append(listItemsStringArray, lineString)
			}
		}
	}
	return append(listItems, endItems...), append(listItemsStringArray, endItemsStringArray...)
}

func renderList(c *gin.Context, title string) {
	if strings.ToLower(title) == "about" { //}&& strings.Contains(AllowedIPs, c.ClientIP()) != true {
		c.Redirect(302, "/about/view")
	}
	var p WikiData
	err := p.load(strings.ToLower(title))
	if err != nil {
		panic(err)
	}

	fmt.Println(p.CurrentText)
	pClean := bluemonday.UGCPolicy()
	pClean.AllowElements("img")
	pClean.AllowAttrs("alt").OnElements("img")
	pClean.AllowAttrs("src").OnElements("img")
	pClean.AllowAttrs("class").OnElements("a")
	pClean.AllowAttrs("href").OnElements("a")
	pClean.AllowAttrs("id").OnElements("a")
	pClean.AllowDataURIImages()
	text := pClean.SanitizeBytes([]byte(p.CurrentText))
	listItems, _ := reorderList(string(text))
	fmt.Println(string(text))
	c.HTML(http.StatusOK, "list.tmpl", gin.H{
		"Title":     title,
		"WikiName":  RuntimeArgs.WikiName,
		"ListItems": listItems,
	})
}

func deleteListItem(c *gin.Context) {
	lineNum, err := strconv.Atoi(c.DefaultQuery("lineNum", "None"))
	title := c.Query("title") // shortcut for c.Request.URL.Query().Get("lastname")
	if err == nil {
		var p WikiData
		err := p.load(strings.ToLower(title))
		if err != nil {
			panic(err)
		}

		_, listItems := reorderList(p.CurrentText)
		newText := p.CurrentText
		for i, lineString := range listItems {
			// fmt.Println(i, lineString, lineNum)
			if i+1 == lineNum {
				// fmt.Println("MATCHED")
				if strings.Contains(lineString, "~~") == false {
					// fmt.Println(p.Text, "("+lineString[2:]+"\n"+")", "~~"+lineString[2:]+"~~"+"\n")
					newText = strings.Replace(newText+"\n", lineString[2:]+"\n", "~~"+strings.TrimSpace(lineString[2:])+"~~"+"\n", 1)
				} else {
					newText = strings.Replace(newText+"\n", lineString[2:]+"\n", lineString[4:len(lineString)-2]+"\n", 1)
				}
				p.save(newText)
				break
			}
		}

		c.JSON(200, gin.H{
			"message": "Done.",
		})
	} else {
		c.JSON(404, gin.H{
			"message": "?",
		})
	}
}

func deletePage(c *gin.Context) {
	deleteName := c.DefaultQuery("DeleteName", "None")
	adminKey := c.DefaultQuery("AdminKey", "None")
	if adminKey == RuntimeArgs.AdminKey {
		p := WikiData{deleteName, "", []string{}, []string{}}
		p.save("")
		c.JSON(200, gin.H{
			"message": "Done.",
		})
	} else {
		c.JSON(404, gin.H{
			"message": "?",
		})
	}
}

func listEverything() string {
	everything := `| Title | Current size    | Changes  | Total Size |  |
| --------- |-------------| -----| ------------- | ------------- |
`
	db.View(func(tx *bolt.Tx) error {
		// Assume bucket exists and has keys
		b := tx.Bucket([]byte("datas"))
		c := b.Cursor()
		for k, v := c.First(); k != nil; k, v = c.Next() {
			var p WikiData
			p.load(string(k))
			if len(p.CurrentText) > 1 {
				contentSize := strconv.Itoa(len(p.CurrentText))
				numChanges := strconv.Itoa(len(p.Diffs))
				totalSize := strconv.Itoa(len(v))
				everything += "| [" + p.Title + "](/" + p.Title + "/view) | " + contentSize + " | " + numChanges + " | " + totalSize + ` | <a class="deleteable" id="` + p.Title + `">Delete</a> | ` + "\n"
			}
		}
		return nil
	})
	return everything
}
<|endoftext|>