summaryrefslogtreecommitdiffstats
path: root/vendor/github.com/sorcix/irc/ctcp/ctcp.go
blob: 7ead788d5991aecb7ada9cd5c55ad70fdfe52c25 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
// Copyright 2014 Vic Demuzere
//
// Use of this source code is governed by the MIT license.

package ctcp

// Sources:
// http://www.irchelp.org/irchelp/rfc/ctcpspec.html
// http://www.kvirc.net/doc/doc_ctcp_handling.html

import (
	"fmt"
	"runtime"
	"strings"
	"time"
)

// Various constants used for formatting CTCP messages.
const (
	delimiter byte = 0x01 // Prefix and suffix for CTCP tagged messages.
	space     byte = 0x20 // Token separator

	empty = "" // The empty string

	timeFormat    = time.RFC1123Z
	versionFormat = "Go v%s (" + runtime.GOOS + ", " + runtime.GOARCH + ")"
)

// Tags extracted from the CTCP spec.
const (
	ACTION     = "ACTION"
	PING       = "PING"
	PONG       = "PONG"
	VERSION    = "VERSION"
	USERINFO   = "USERINFO"
	CLIENTINFO = "CLIENTINFO"
	FINGER     = "FINGER"
	SOURCE     = "SOURCE"
	TIME       = "TIME"
)

// Decode attempts to decode CTCP tagged data inside given message text.
//
// If the message text does not contain tagged data, ok will be false.
//
//    <text>  ::= <delim> <tag> [<SPACE> <message>] <delim>
//    <delim> ::= 0x01
//
func Decode(text string) (tag, message string, ok bool) {

	// Fast path, return if this text does not contain a CTCP message.
	if len(text) < 3 || text[0] != delimiter || text[len(text)-1] != delimiter {
		return empty, empty, false
	}

	s := strings.IndexByte(text, space)

	if s < 0 {

		// Messages may contain only a tag.
		return text[1 : len(text)-1], empty, true
	}

	return text[1:s], text[s+1 : len(text)-1], true
}

// Encode returns the IRC message text for CTCP tagged data.
//
//    <text>  ::= <delim> <tag> [<SPACE> <message>] <delim>
//    <delim> ::= 0x01
//
func Encode(tag, message string) (text string) {

	switch {

	// We can't build a valid CTCP tagged message without at least a tag.
	case len(tag) <= 0:
		return empty

	// Tagged data with a message
	case len(message) > 0:
		return string(delimiter) + tag + string(space) + message + string(delimiter)

	// Tagged data without a message
	default:
		return string(delimiter) + tag + string(delimiter)

	}
}

// Action is a shortcut for Encode(ctcp.ACTION, message).
func Action(message string) string {
	return Encode(ACTION, message)
}

// Ping is a shortcut for Encode(ctcp.PING, message).
func Ping(message string) string {
	return Encode(PING, message)
}

// Pong is a shortcut for Encode(ctcp.PONG, message).
func Pong(message string) string {
	return Encode(PONG, message)
}

// Version is a shortcut for Encode(ctcp.VERSION, message).
func Version(message string) string {
	return Encode(VERSION, message)
}

// VersionReply is a shortcut for ENCODE(ctcp.VERSION, go version info).
func VersionReply() string {
	return Encode(VERSION, fmt.Sprintf(versionFormat, runtime.Version()))
}

// UserInfo is a shortcut for Encode(ctcp.USERINFO, message).
func UserInfo(message string) string {
	return Encode(USERINFO, message)
}

// ClientInfo is a shortcut for Encode(ctcp.CLIENTINFO, message).
func ClientInfo(message string) string {
	return Encode(CLIENTINFO, message)
}

// Finger is a shortcut for Encode(ctcp.FINGER, message).
func Finger(message string) string {
	return Encode(FINGER, message)
}

// Source is a shortcut for Encode(ctcp.SOURCE, message).
func Source(message string) string {
	return Encode(SOURCE, message)
}

// Time is a shortcut for Encode(ctcp.TIME, message).
func Time(message string) string {
	return Encode(TIME, message)
}

// TimeReply is a shortcut for Encode(ctcp.TIME, currenttime).
func TimeReply() string {
	return Encode(TIME, time.Now().Format(timeFormat))
}