Files
cameradar/vendor/github.com/andelf/go-curl/easy.go
T
Brendan LE GLAUNEC 5849898283 Cameradar 3.0.0: Uses ullaakut/nmap, runs faster, removed legacy code (#188)
Unit tests functional and coverage back to 100%

Add more routes to dictionary, add more credentials, add default port 5554, rename cameradar logs ENV variable, improve unit test readability, remove tmp file
2019-01-22 21:16:16 +01:00

528 lines
15 KiB
Go

package curl
/*
#include <stdlib.h>
#include <curl/curl.h>
#include "callback.h"
#include "compat.h"
static CURLcode curl_easy_setopt_long(CURL *handle, CURLoption option, long parameter) {
return curl_easy_setopt(handle, option, parameter);
}
static CURLcode curl_easy_setopt_string(CURL *handle, CURLoption option, char *parameter) {
return curl_easy_setopt(handle, option, parameter);
}
static CURLcode curl_easy_setopt_slist(CURL *handle, CURLoption option, struct curl_slist *parameter) {
return curl_easy_setopt(handle, option, parameter);
}
static CURLcode curl_easy_setopt_pointer(CURL *handle, CURLoption option, void *parameter) {
return curl_easy_setopt(handle, option, parameter);
}
static CURLcode curl_easy_setopt_off_t(CURL *handle, CURLoption option, off_t parameter) {
return curl_easy_setopt(handle, option, parameter);
}
static CURLcode curl_easy_getinfo_string(CURL *curl, CURLINFO info, char **p) {
return curl_easy_getinfo(curl, info, p);
}
static CURLcode curl_easy_getinfo_long(CURL *curl, CURLINFO info, long *p) {
return curl_easy_getinfo(curl, info, p);
}
static CURLcode curl_easy_getinfo_double(CURL *curl, CURLINFO info, double *p) {
return curl_easy_getinfo(curl, info, p);
}
static CURLcode curl_easy_getinfo_slist(CURL *curl, CURLINFO info, struct curl_slist **p) {
return curl_easy_getinfo(curl, info, p);
}
static CURLFORMcode curl_formadd_name_content_length(
struct curl_httppost **httppost, struct curl_httppost **last_post, char *name, char *content, int length) {
return curl_formadd(httppost, last_post,
CURLFORM_COPYNAME, name,
CURLFORM_COPYCONTENTS, content,
CURLFORM_CONTENTSLENGTH, length, CURLFORM_END);
}
static CURLFORMcode curl_formadd_name_content_length_type(
struct curl_httppost **httppost, struct curl_httppost **last_post, char *name, char *content, int length, char *type) {
return curl_formadd(httppost, last_post,
CURLFORM_COPYNAME, name,
CURLFORM_COPYCONTENTS, content,
CURLFORM_CONTENTSLENGTH, length,
CURLFORM_CONTENTTYPE, type, CURLFORM_END);
}
static CURLFORMcode curl_formadd_name_file_type(
struct curl_httppost **httppost, struct curl_httppost **last_post, char *name, char *filename, char *type) {
return curl_formadd(httppost, last_post,
CURLFORM_COPYNAME, name,
CURLFORM_FILE, filename,
CURLFORM_CONTENTTYPE, type, CURLFORM_END);
}
// TODO: support multi file
*/
import "C"
import (
"fmt"
"mime"
"path"
"unsafe"
"sync"
)
type CurlInfo C.CURLINFO
type CurlError C.CURLcode
type CurlString *C.char
func NewCurlString(s string) CurlString {
return CurlString(unsafe.Pointer(C.CString(s)))
}
func FreeCurlString(s CurlString) {
C.free(unsafe.Pointer(s))
}
func (e CurlError) Error() string {
// ret is const char*, no need to free
ret := C.curl_easy_strerror(C.CURLcode(e))
return fmt.Sprintf("curl: %s", C.GoString(ret))
}
func newCurlError(errno C.CURLcode) error {
if errno == C.CURLE_OK { // if nothing wrong
return nil
}
return CurlError(errno)
}
// curl_easy interface
type CURL struct {
handle unsafe.Pointer
// callback functions, bool ret means ok or not
headerFunction, writeFunction *func([]byte, interface{}) bool
readFunction *func([]byte, interface{}) int // return num of bytes writed to buf
progressFunction *func(float64, float64, float64, float64, interface{}) bool
fnmatchFunction *func(string, string, interface{}) int
// callback datas
headerData, writeData, readData, progressData, fnmatchData interface{}
// list of C allocs
mallocAllocs []*C.char
}
// concurrent safe context map
type contextMap struct {
items map[uintptr]*CURL
sync.RWMutex
}
func (c *contextMap) Set(k uintptr, v *CURL) {
c.Lock()
defer c.Unlock()
c.items[k] = v
}
func (c *contextMap) Get(k uintptr) *CURL {
c.RLock()
defer c.RUnlock()
return c.items[k]
}
func (c *contextMap) Delete(k uintptr) {
c.Lock()
defer c.Unlock()
delete(c.items, k)
}
var context_map = &contextMap {
items: make(map[uintptr]*CURL),
}
// curl_easy_init - Start a libcurl easy session
func EasyInit() *CURL {
p := C.curl_easy_init()
c := &CURL{handle: p, mallocAllocs: make([]*C.char, 0)} // other field defaults to nil
context_map.Set(uintptr(p), c)
return c
}
// curl_easy_duphandle - Clone a libcurl session handle
func (curl *CURL) Duphandle() *CURL {
p := C.curl_easy_duphandle(curl.handle)
c := &CURL{handle: p}
context_map.Set(uintptr(p), c)
return c
}
// curl_easy_cleanup - End a libcurl easy session
func (curl *CURL) Cleanup() {
p := curl.handle
C.curl_easy_cleanup(p)
curl.MallocFreeAfter(0)
context_map.Delete(uintptr(p))
}
// curl_easy_setopt - set options for a curl easy handle
// WARNING: a function pointer is &fun, but function addr is reflect.ValueOf(fun).Pointer()
func (curl *CURL) Setopt(opt int, param interface{}) error {
p := curl.handle
if param == nil {
// NOTE: some option will crash program when got a nil param
return newCurlError(C.curl_easy_setopt_pointer(p, C.CURLoption(opt), nil))
}
switch {
// not really set
case opt == OPT_READDATA: // OPT_INFILE
curl.readData = param
return nil
case opt == OPT_PROGRESSDATA:
curl.progressData = param
return nil
case opt == OPT_HEADERDATA: // also known as OPT_WRITEHEADER
curl.headerData = param
return nil
case opt == OPT_WRITEDATA: // OPT_FILE
curl.writeData = param
return nil
case opt == OPT_READFUNCTION:
fun := param.(func([]byte, interface{}) int)
curl.readFunction = &fun
ptr := C.return_read_function()
if err := newCurlError(C.curl_easy_setopt_pointer(p, C.CURLoption(opt), ptr)); err == nil {
return newCurlError(C.curl_easy_setopt_pointer(p, OPT_READDATA, unsafe.Pointer(curl.handle)))
} else {
return err
}
case opt == OPT_PROGRESSFUNCTION:
fun := param.(func(float64, float64, float64, float64, interface{}) bool)
curl.progressFunction = &fun
ptr := C.return_progress_function()
if err := newCurlError(C.curl_easy_setopt_pointer(p, C.CURLoption(opt), ptr)); err == nil {
return newCurlError(C.curl_easy_setopt_pointer(p, OPT_PROGRESSDATA, unsafe.Pointer(curl.handle)))
} else {
return err
}
case opt == OPT_HEADERFUNCTION:
fun := param.(func([]byte, interface{}) bool)
curl.headerFunction = &fun
ptr := C.return_header_function()
if err := newCurlError(C.curl_easy_setopt_pointer(p, C.CURLoption(opt), ptr)); err == nil {
return newCurlError(C.curl_easy_setopt_pointer(p, OPT_HEADERDATA, unsafe.Pointer(curl.handle)))
} else {
return err
}
case opt == OPT_WRITEFUNCTION:
fun := param.(func([]byte, interface{}) bool)
curl.writeFunction = &fun
ptr := C.return_write_function()
if err := newCurlError(C.curl_easy_setopt_pointer(p, C.CURLoption(opt), ptr)); err == nil {
return newCurlError(C.curl_easy_setopt_pointer(p, OPT_WRITEDATA, unsafe.Pointer(curl.handle)))
} else {
return err
}
// for OPT_HTTPPOST, use struct Form
case opt == OPT_HTTPPOST:
post := param.(*Form)
ptr := post.head
return newCurlError(C.curl_easy_setopt_pointer(p, C.CURLoption(opt), unsafe.Pointer(ptr)))
case opt >= C.CURLOPTTYPE_OFF_T:
val := C.off_t(0)
switch t := param.(type) {
case int:
val = C.off_t(t)
case uint64:
val = C.off_t(t)
default:
panic("OFF_T conversion not supported")
}
return newCurlError(C.curl_easy_setopt_off_t(p, C.CURLoption(opt), val))
case opt >= C.CURLOPTTYPE_FUNCTIONPOINT:
// function pointer
panic("function pointer not implemented yet!")
case opt >= C.CURLOPTTYPE_OBJECTPOINT:
switch t := param.(type) {
case string:
ptr := C.CString(t)
curl.mallocAddPtr(ptr)
return newCurlError(C.curl_easy_setopt_string(p, C.CURLoption(opt), ptr))
case CurlString:
ptr := (*C.char)(t)
return newCurlError(C.curl_easy_setopt_string(p, C.CURLoption(opt), ptr))
case []string:
if len(t) > 0 {
ptr := C.CString(t[0])
curl.mallocAddPtr(ptr)
a_slist := C.curl_slist_append(nil, ptr)
for _, s := range t[1:] {
ptr := C.CString(s)
curl.mallocAddPtr(ptr)
a_slist = C.curl_slist_append(a_slist, ptr)
}
return newCurlError(C.curl_easy_setopt_slist(p, C.CURLoption(opt), a_slist))
} else {
return newCurlError(C.curl_easy_setopt_slist(p, C.CURLoption(opt), nil))
}
case []CurlString:
if len(t) > 0 {
ptr := (*C.char)(t[0])
a_slist := C.curl_slist_append(nil, ptr)
for _, s := range t[1:] {
ptr := (*C.char)(s)
a_slist = C.curl_slist_append(a_slist, ptr)
}
return newCurlError(C.curl_easy_setopt_slist(p, C.CURLoption(opt), a_slist))
} else {
return newCurlError(C.curl_easy_setopt_slist(p, C.CURLoption(opt), nil))
}
default:
// It panics if v's Kind is not Chan, Func, Map, Ptr, Slice, or UnsafePointer.
// val := reflect.ValueOf(param)
//fmt.Printf("DEBUG(Setopt): param=%x\n", val.Pointer())
//println("DEBUG can addr =", val.Pointer(), "opt=", opt)
// pass a pointer to GoInterface
return newCurlError(C.curl_easy_setopt_pointer(p, C.CURLoption(opt),
unsafe.Pointer(&param)))
}
case opt >= C.CURLOPTTYPE_LONG:
val := C.long(0)
switch t := param.(type) {
case int:
val = C.long(t)
case bool:
if t {
val = 1
}
case int64:
val = C.long(t)
case int32:
val = C.long(t)
default:
panic("not supported converstion to c long")
}
return newCurlError(C.curl_easy_setopt_long(p, C.CURLoption(opt), val))
}
panic("opt param error!")
}
// curl_easy_send - sends raw data over an "easy" connection
func (curl *CURL) Send(buffer []byte) (int, error) {
p := curl.handle
buflen := len(buffer)
n := C.size_t(0)
ret := C.curl_easy_send(p, unsafe.Pointer(&buffer[0]), C.size_t(buflen), &n)
return int(n), newCurlError(ret)
}
// curl_easy_recv - receives raw data on an "easy" connection
func (curl *CURL) Recv(buffer []byte) (int, error) {
p := curl.handle
buflen := len(buffer)
buf := C.CString(string(buffer))
n := C.size_t(0)
ret := C.curl_easy_recv(p, unsafe.Pointer(buf), C.size_t(buflen), &n)
return copy(buffer, C.GoStringN(buf, C.int(n))), newCurlError(ret)
}
// curl_easy_perform - Perform a file transfer
func (curl *CURL) Perform() error {
p := curl.handle
return newCurlError(C.curl_easy_perform(p))
}
// curl_easy_pause - pause and unpause a connection
func (curl *CURL) Pause(bitmask int) error {
p := curl.handle
return newCurlError(C.curl_easy_pause(p, C.int(bitmask)))
}
// curl_easy_reset - reset all options of a libcurl session handle
func (curl *CURL) Reset() {
p := curl.handle
C.curl_easy_reset(p)
}
// curl_easy_escape - URL encodes the given string
func (curl *CURL) Escape(url string) string {
p := curl.handle
oldUrl := C.CString(url)
defer C.free(unsafe.Pointer(oldUrl))
newUrl := C.curl_easy_escape(p, oldUrl, 0)
defer C.curl_free(unsafe.Pointer(newUrl))
return C.GoString(newUrl)
}
// curl_easy_unescape - URL decodes the given string
func (curl *CURL) Unescape(url string) string {
p := curl.handle
oldUrl := C.CString(url)
outlength := C.int(0)
defer C.free(unsafe.Pointer(oldUrl))
// If outlength is non-NULL, the function will write the length of the
// returned string in the integer it points to. This allows an
// escaped string containing %00 to still get used properly after unescaping.
newUrl := C.curl_easy_unescape(p, oldUrl, 0, &outlength)
defer C.curl_free(unsafe.Pointer(newUrl))
return C.GoStringN(newUrl, outlength)
}
// curl_easy_getinfo - extract information from a curl handle
func (curl *CURL) Getinfo(info CurlInfo) (ret interface{}, err error) {
p := curl.handle
cInfo := C.CURLINFO(info)
switch cInfo & C.CURLINFO_TYPEMASK {
case C.CURLINFO_STRING:
a_string := C.CString("")
defer C.free(unsafe.Pointer(a_string))
err := newCurlError(C.curl_easy_getinfo_string(p, cInfo, &a_string))
ret := C.GoString(a_string)
debugf("Getinfo %s", ret)
return ret, err
case C.CURLINFO_LONG:
a_long := C.long(-1)
err := newCurlError(C.curl_easy_getinfo_long(p, cInfo, &a_long))
ret := int(a_long)
debugf("Getinfo %s", ret)
return ret, err
case C.CURLINFO_DOUBLE:
a_double := C.double(0.0)
err := newCurlError(C.curl_easy_getinfo_double(p, cInfo, &a_double))
ret := float64(a_double)
debugf("Getinfo %s", ret)
return ret, err
case C.CURLINFO_SLIST:
a_ptr_slist := new(_Ctype_struct_curl_slist)
err := newCurlError(C.curl_easy_getinfo_slist(p, cInfo, &a_ptr_slist))
ret := []string{}
for a_ptr_slist != nil {
debugf("Getinfo %s %v", C.GoString(a_ptr_slist.data), a_ptr_slist.next)
ret = append(ret, C.GoString(a_ptr_slist.data))
a_ptr_slist = a_ptr_slist.next
}
return ret, err
default:
panic("error calling Getinfo\n")
}
panic("not implemented yet!")
return nil, nil
}
func (curl *CURL) GetHandle() unsafe.Pointer {
return curl.handle
}
func (curl *CURL) MallocGetPos() int {
return len(curl.mallocAllocs)
}
func (curl *CURL) MallocFreeAfter(from int) {
l := len(curl.mallocAllocs)
for idx := from; idx < l; idx++ {
C.free(unsafe.Pointer(curl.mallocAllocs[idx]))
curl.mallocAllocs[idx] = nil
}
curl.mallocAllocs = curl.mallocAllocs[0:from]
}
func (curl *CURL) mallocAddPtr(ptr *C.char) {
curl.mallocAllocs = append(curl.mallocAllocs, ptr)
}
// A multipart/formdata HTTP POST form
type Form struct {
head, last *C.struct_curl_httppost
}
func NewForm() *Form {
return &Form{}
}
func (form *Form) Add(name string, content interface{}) error {
head, last := form.head, form.last
namestr := C.CString(name)
defer C.free(unsafe.Pointer(namestr))
var (
buffer *C.char
length C.int
)
switch t := content.(type) {
case string:
buffer = C.CString(t)
length = C.int(len(t))
case []byte:
buffer = C.CString(string(t))
length = C.int(len(t))
default:
panic("not implemented")
}
defer C.free(unsafe.Pointer(buffer))
C.curl_formadd_name_content_length(&head, &last, namestr, buffer, length)
form.head, form.last = head, last
return nil
}
func (form *Form) AddWithType(name string, content interface{}, content_type string) error {
head, last := form.head, form.last
namestr := C.CString(name)
typestr := C.CString(content_type)
defer C.free(unsafe.Pointer(namestr))
defer C.free(unsafe.Pointer(typestr))
var (
buffer *C.char
length C.int
)
switch t := content.(type) {
case string:
buffer = C.CString(t)
length = C.int(len(t))
case []byte:
buffer = C.CString(string(t))
length = C.int(len(t))
default:
panic("not implemented")
}
defer C.free(unsafe.Pointer(buffer))
C.curl_formadd_name_content_length_type(&head, &last, namestr, buffer, length, typestr)
form.head, form.last = head, last
return nil
}
func (form *Form) AddFile(name, filename string) error {
head, last := form.head, form.last
namestr := C.CString(name)
pathstr := C.CString(filename)
typestr := C.CString(guessType(filename))
defer C.free(unsafe.Pointer(namestr))
defer C.free(unsafe.Pointer(pathstr))
defer C.free(unsafe.Pointer(typestr))
C.curl_formadd_name_file_type(&head, &last, namestr, pathstr, typestr)
form.head, form.last = head, last
return nil
}
func (form *Form) AddFromFile(name, filename string) {
}
func guessType(filename string) string {
ext := path.Ext(filename)
file_type := mime.TypeByExtension(ext)
if file_type == "" {
return "application/octet-stream"
}
return file_type
}