Created
September 10, 2010 05:13
-
-
Save sebnow/573141 to your computer and use it in GitHub Desktop.
Onet Czat API
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package onet | |
import ( | |
"xml" | |
"io" | |
"fmt" | |
"http" | |
"os" | |
"net" | |
"bufio" | |
"strings" | |
) | |
const APIUrl = "http://czat.onet.pl/include/ajaxapi.xml.php3" | |
const APIVersion = "1.1(20090619-1228 - R)" | |
type responseError struct { | |
XMLName xml.Name "error" | |
Err_code string "attr" | |
Err_text string "attr" | |
} | |
type responseUOKey struct { | |
XMLName xml.Name "root" | |
UoKey string | |
ZuoUsername string | |
Error responseError | |
} | |
type Error struct { | |
code int | |
text string | |
} | |
func (err Error) String() string { | |
return err.text | |
} | |
// A sample (successful) response looks like this: | |
// | |
// <?xml version="1.0" encoding="ISO-8859-2"?> | |
// <root> | |
// <uoKey>WRpTCGUHQZH_W4Qxumky34XNoXBMiDfx</uoKey> | |
// <zuoUsername>PossiblyDifferentUsername</zuoUsername> | |
// <error err_code="TRUE" err_text="wartość prawdziwa"></error> | |
// </root> | |
// | |
// This function unmarshals the response to a responseUOKey structure. | |
func getUOKeyResponse(source io.Reader) (r *responseUOKey, err os.Error) { | |
response := new(responseUOKey) | |
err = xml.Unmarshal(source, response) | |
return response, err | |
} | |
// Encode a value using the Onet Czat API encoding. | |
// | |
// The type of param will be checked to determine how to encode the | |
// data. If the type is a container, such as a map, the algorithm will | |
// recurse to encode the contained values as well. | |
// | |
// The syntax of the encoding in BNF is: | |
// | |
// <encoded> ::= <map> | <string> | <integer> | |
// <length> ::= DIGIT | |
// <number> ::= "i:" DIGITS | |
// <string> ::= "s:" <length> ':"' CHARACTERS '"' | |
// <map> ::= "a:" <length> <mapping> | |
// <mapping> ::= <association> | <association> <mapping> | |
// <association> ::= <encoded> ';' <encoded> ';' | |
// | |
// The value of <length> is the length of data structure. In the case | |
// of a string the <length> is the amount of characters in the | |
// string. In the case of a mapping, it is the amount of associations. | |
// | |
// Boolean types do not exist in this encoding. Booleans are converted | |
// to an integer, with 1 representing true, and 0 representing false. | |
func encodeParameter(param interface{}) (encoded string) { | |
switch value := param.(type) { | |
case string: | |
encoded = fmt.Sprintf("s:%d:\"%s\"", len(value), value) | |
case int: | |
encoded = fmt.Sprintf("i:%d", value) | |
case bool: | |
as_int := 0 | |
if value { | |
as_int = 1 | |
} | |
return encodeParameter(as_int) | |
case map[string]string: | |
encoded = fmt.Sprintf("a:%d:{", len(value)) | |
for k, v := range value { | |
encoded += encodeParameter(k) + ";" | |
encoded += encodeParameter(v) + ";" | |
} | |
encoded += "}" | |
case map[string]int: | |
encoded = fmt.Sprintf("a:%d:{", len(value)) | |
for k, v := range value { | |
encoded += encodeParameter(k) + ";" | |
encoded += encodeParameter(v) + ";" | |
} | |
encoded += "}" | |
case map[string]interface{}: | |
encoded = fmt.Sprintf("a:%d:{", len(value)) | |
for k, v := range value { | |
encoded += encodeParameter(k) + ";" | |
encoded += encodeParameter(v) + ";" | |
} | |
encoded += "}" | |
} | |
return | |
} | |
type nopCloser struct { | |
io.Reader | |
} | |
func (nopCloser) Close() (err os.Error) { return } | |
func sendPostRequest(url, contentType, body string) (resp *http.Response, err os.Error) { | |
// The standard http.Post() function unfortunately always | |
// sends the request as chunked, which isn't supported by the | |
// Onet servers. We have to duplicate most of the code from the | |
// function, just to specify the Content-Length. | |
var headers = map[string]string { | |
"Content-Type": contentType, | |
"Accept": "text/html, application/xml", | |
} | |
var req = http.Request { | |
Method: "POST", | |
ContentLength: int64(len(body)), | |
Close: true, | |
Header: headers, | |
Body: nopCloser{strings.NewReader(body)}, | |
ProtoMajor: 1, | |
ProtoMinor: 1, | |
} | |
req.URL, err = http.ParseURL(url) | |
addr := req.URL.Host | |
if strings.LastIndex(addr, ":") > strings.LastIndex(addr, "]") { | |
addr += "http" | |
} | |
conn, err := net.Dial("tcp", "", "czat.onet.pl:http") | |
if err != nil { | |
return | |
} | |
err = req.Write(conn) | |
if err != nil { | |
return | |
} | |
reader := bufio.NewReader(conn) | |
resp, err = http.ReadResponse(reader, req.Method) | |
if err != nil { | |
conn.Close() | |
return nil, err | |
} | |
return | |
} | |
// Request a UO for the sepified nick and password. | |
// | |
// If a password is not supplied the user will be logged in as a | |
// temporary user, and the new nick will be returned as newNick | |
// (typically nick with "~" prepended). If an error occurs during the | |
// request, it will be returned as err. | |
func GetUOKey(nick, password string) (uokey, newNick string, err os.Error) { | |
var args = map[string]interface{} { | |
"nick": nick, | |
"tempNick": true, | |
"version": APIVersion, | |
} | |
if password != "" { | |
args["tempNick"] = false | |
} | |
call := "api_function=getUoKey¶ms=" + encodeParameter(args) | |
resp, err := sendPostRequest(APIUrl, "application/x-www-form-urlencoded", call) | |
if err != nil { | |
return | |
} | |
response, err := getUOKeyResponse(resp.Body) | |
if err != nil { | |
return | |
} | |
uokey = response.UoKey | |
newNick = response.ZuoUsername | |
if response.Error.Err_code != "TRUE" { | |
err = os.ErrorString(response.Error.Err_text) | |
return | |
} | |
return uokey, newNick, nil | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment