6 # The contents of this file are subject to the terms of the
7 # Common Development and Distribution License (the "License").
8 # You may not use this file except in compliance with the License.
10 # You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
11 # or http://www.opensolaris.org/os/licensing.
12 # See the License for the specific language governing permissions
13 # and limitations under the License.
15 # When distributing Covered Code, include this CDDL HEADER in each
16 # file and include the License file at usr/src/OPENSOLARIS.LICENSE.
17 # If applicable, add the following below this CDDL HEADER, with the
18 # fields enclosed by brackets "[]" replaced with your own identifying
19 # information: Portions Copyright [yyyy] [name of copyright owner]
25 # Copyright (c) 2008, 2010, Oracle and/or its affiliates. All rights reserved.
28 # Solaris needs /usr/xpg6/bin:/usr/xpg4/bin because the tools in /usr/bin are not POSIX-conformant
29 export PATH
=/usr
/xpg
6/bin
:/usr
/xpg
4/bin
:/bin
:/usr
/bin
31 # Make sure all math stuff runs in the "C" locale to avoid problems
32 # with alternative # radix point representations (e.g. ',' instead of
33 # '.' in de_DE.*-locales). This needs to be set _before_ any
34 # floating-point constants are defined in this script).
35 if [[ "${LC_ALL}" != "" ]] ; then
37 LC_MONETARY
="${LC_ALL}" \
38 LC_MESSAGES
="${LC_ALL}" \
39 LC_COLLATE
="${LC_ALL}" \
47 print
-u2 "${progname}: $*"
51 function encode_x_www_form_urlencoded
54 nameref content
="formdata.content"
55 integer numformelements
=${#formdata.form[*]}
60 for (( i
=0 ; i
< numformelements
; i
++ )) ; do
61 nameref element
="formdata.form[${i}]"
62 typeset data
="${element.data}"
63 integer datalen
="${#data}"
66 [[ "$content" != "" ]] && content
+="&"
68 content
+="${element.name}="
70 for ((j
=0 ; j
< datalen
; j
++)) ; do
92 '\') c="%5C" ;; # we need this to avoid the '\'-quoting hell
100 formdata.content_length
=${#content}
105 # parse HTTP return code, cookies etc.
106 function parse_http_response
108 nameref response
="$1"
109 typeset h statuscode statusmsg i
111 # we use '\r' as additional IFS to filter the final '\r'
112 IFS
=$
' \t\r' read -r h statuscode statusmsg
# read HTTP/1.[01] <code>
113 [[ "$h" != ~
(Eil
)HTTP
/.
* ]] && { print
-u2 -f $
"%s: HTTP/ header missing\n" "$0" ; return 1 ; }
114 [[ "$statuscode" != ~
(Elr
)[0-9]* ]] && { print
-u2 -f $
"%s: invalid status code\n" "$0" ; return 1 ; }
115 response.statuscode
="$statuscode"
116 response.statusmsg
="$statusmsg"
118 # skip remaining headers
119 while IFS
='' read -r i
; do
120 [[ "$i" == $
'\r' ]] && break
122 # strip '\r' at the end
126 ~
(Eli
)Content-Type
:.
*)
127 response.content_type
="${i/~(El).*:[[:blank:]]*/}"
129 ~
(Eli
)Content-Length
:[[:blank
:]]*[0-9]*)
130 integer response.content_length
="${i/~(El).*:[[:blank:]]*/}"
132 ~
(Eli
)Transfer-Encoding
:.
*)
133 response.transfer_encoding
="${i/~(El).*:[[:blank:]]*/}"
141 function cat_http_body
144 typeset hexchunksize
="0"
147 if [[ "${emode}" == "chunked" ]] ; then
148 while IFS
=$
'\r' read hexchunksize
&&
149 [[ "${hexchunksize}" == ~
(Elri
)[0-9abcdef]+ ]] &&
150 (( chunksize
=$
( printf "16#%s\n" "${hexchunksize}" ) )) && (( chunksize
> 0 )) ; do
151 dd bs
=1 count
="${chunksize}" 2>/dev
/null
160 function encode_http_basic_auth
168 # ksh93 binary variables use base64 encoding, the same as the
169 # HTTP basic authentification. We only have to read the
170 # plaintext user:passwd string into the binary variable "base64var"
171 # and then print this variable as ASCII.
172 s
="${user}:${passwd}"
174 print
-n "${s}" |
read -N${s_len} base64var
176 print
-- "${base64var}" # print ASCII (base64) representation of binary var
181 function put_twitter_message
183 [[ "$SHTWITTER_USER" == "" ]] && { print
-u2 -f $
"%s: SHTWITTER_USER not set.\n" "$0" ; return 1 ; }
184 [[ "$SHTWITTER_PASSWD" == "" ]] && { print
-u2 -f $
"%s: SHTWITTER_PASSWD not set.\n" "$0" ; return 1 ; }
186 (( $# != 1 )) && { print
-u2 -f $
"%s: Wrong number of arguments.\n" "$0" ; return 1 ; }
189 typeset url_host
="twitter.com"
190 typeset url_path
="/statuses/update.xml"
191 typeset url
="http://${url_host}${url_path}"
192 integer netfd
# http stream number
194 compound httpresponse
# http response
196 # argument for "encode_x_www_form_urlencoded"
200 ( name
="status" data
="${msgtext}" )
204 integer content_length
210 encode_x_www_form_urlencoded urlform
212 content
="${urlform.content}"
214 request
="POST ${url_path} HTTP/1.1\r\n"
215 request
+="Host: ${url_host}\r\n"
216 request
+="Authorization: Basic ${ encode_http_basic_auth "${SHTWITTER_USER}" "${SHTWITTER_PASSWD}" ; }\r\n"
217 request
+="User-Agent: ${http_user_agent}\r\n"
218 request
+="Connection: close\r\n"
219 request
+="Content-Type: application/x-www-form-urlencoded\r\n"
220 request
+="Content-Length: $(( urlform.content_length ))\r\n"
222 redirect
{netfd
}<> "/dev/tcp/${url_host}/80"
223 (( $?
!= 0 )) && { print
-u2 -f "%s: Could not open connection to %s\n." "$0" "${url_host}" ; return 1 ; }
227 print
-n -- "${request}\r\n"
228 print
-n -- "${content}\r\n"
232 parse_http_response httpresponse
<&${netfd}
233 response
="${ cat_http_body "${httpresponse.transfer_encoding}" <&${netfd} ; }"
238 printf $
"twitter response was (%s,%s): %s\n" "${httpresponse.statuscode}" "${httpresponse.statusmsg}" "${response}"
240 if (( httpresponse.statuscode
>= 200 && httpresponse.statuscode
<= 299 )) ; then
249 function verify_twitter_credentials
251 [[ "$SHTWITTER_USER" == "" ]] && { print
-u2 -f $
"%s: SHTWITTER_USER not set.\n" "$0" ; return 1 ; }
252 [[ "$SHTWITTER_PASSWD" == "" ]] && { print
-u2 -f $
"%s: SHTWITTER_PASSWD not set.\n" "$0" ; return 1 ; }
254 (( $# != 0 )) && { print
-u2 -f $
"%s: Wrong number of arguments.\n" "$0" ; return 1 ; }
257 typeset url_host
="twitter.com"
258 typeset url_path
="/account/verify_credentials.xml"
259 typeset url
="http://${url_host}${url_path}"
260 integer netfd
# http stream number
261 compound httpresponse
# http response
265 request
="POST ${url_path} HTTP/1.1\r\n"
266 request
+="Host: ${url_host}\r\n"
267 request
+="Authorization: Basic ${ encode_http_basic_auth "${SHTWITTER_USER}" "${SHTWITTER_PASSWD}" ; }\r\n"
268 request
+="User-Agent: ${http_user_agent}\r\n"
269 request
+="Connection: close\r\n"
270 request
+="Content-Type: application/x-www-form-urlencoded\r\n"
271 request
+="Content-Length: 0\r\n" # dummy
273 redirect
{netfd
}<> "/dev/tcp/${url_host}/80"
274 (( $?
!= 0 )) && { print
-u2 -f $
"%s: Could not open connection to %s.\n" "$0" "${url_host}" ; return 1 ; }
278 print
-n -- "${request}\r\n"
282 parse_http_response httpresponse
<&${netfd}
283 response
="${ cat_http_body "${httpresponse.transfer_encoding}" <&${netfd} ; }"
288 printf $
"twitter response was (%s,%s): %s\n" "${httpresponse.statuscode}" "${httpresponse.statusmsg}" "${response}"
290 if (( httpresponse.statuscode
>= 200 && httpresponse.statuscode
<= 299 )) ; then
302 getopts -a "${progname}" "${shtwitter_usage}" OPT
'-?'
312 typeset progname
="${ basename "${0}" ; }"
314 # HTTP protocol client identifer
315 typeset
-r http_user_agent
="shtwitter/ksh93 (2010-03-27; ${ uname -s -r -p ; })"
317 typeset
-r shtwitter_usage
=$
'+
318 [-?\n@(#)\$Id: shtwitter (Roland Mainz) 2010-03-27 \$\n]
319 [-author?Roland Mainz <roland.mainz@nrubsig.org>]
320 [+NAME?shtwitter - read/write text data to internet clipboards]
321 [+DESCRIPTION?\bshtwitter\b is a small utility which can read and write text
322 to the twitter.com microblogging site.]
323 [+?The first arg \bmethod\b describes one of the methods, "update" posts a
324 text message to the users twitter blog, returning the raw response
325 message from the twitter server.]
326 [+?The second arg \bstring\b contains the string data which should be
327 stored on twitter.com.]
331 [+SEE ALSO?\bksh93\b(1), \brssread\b(1), \bshtinyurl\b(1), http://www.twitter.com]
334 while getopts -a "${progname}" "${shtwitter_usage}" OPT
; do
335 # printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|"
342 # expecting at least one more argument
349 update|blog
) put_twitter_message
"$@" ; exit $?
;;
350 verify_credentials
) verify_twitter_credentials
"$@" ; exit $?
;;
354 fatal_error $
"not reached."