Hi!

----

[The users in irc://irc.freenode.org/#opensolaris already know it but
AFAIK it was never announced to the general public]
I've set-up an OpenSolaris-specific version of the pastebin.ca internet
clipboard, e.g. a public place to paste larger texts via browser (or the
command-line tool below) which couldn't be transfered easily via other
methods (except email) - this is AFAIK quite usefull during IRC
conversations, phone meetings and maybe things like the upcoming
opensolaris summit (e.g. genereal-purpose scripts/drafts/etc. dumping
ground).

For those who don't have a browser around to paste things like logs and
other stuff to opensolaris.pastebin.ca I wrote a small script (attached
as "shnote.ksh.txt" ; note that this script needs Solaris 11/Nevada B72
or higher) which does this without requiring a browser, e.g. install the
script with...
-- snip --
# cp shnote.ksh.txt /usr/bin/shnote
# chmod a+rx,a-w /usr/bin/shnote
-- snip --
...and then use it like this:
-- snip --
$ shnote put "demo #8932001"
SUCCESS: http://opensolaris.pastebin.ca/702814
-- snip --
... will write the string "demo #8932001" to
http://opensolaris.pastebin.ca and returns the URL where the text can be
accessed from a browser.

Sending command output works similary, e.g.
-- snip --
$ shnote put "$(ls -l)" 
SUCCESS: http://opensolaris.pastebin.ca/702822
-- snip --
... will send the output of "ls -l" to http://opensolaris.pastebin.ca
and return the access URL.

Additionally the script maintains a small history containing the access
URL, title (usually the users name) and the date, e.g.
-- snip --
$ shnote hist    
# <url>                               <title> <date>
...
...
http://opensolaris.pastebin.ca/702814 gisburn Sep 19 04:51:21 CET 2007
...
http://opensolaris.pastebin.ca/702822 gisburn Sep 19 05:02:52 CET 2007
-- snip --

Finally you can get strings/data/text/etc. from
http://opensolaris.pastebin.ca using...
-- snip --
$ shnote get http://opensolaris.pastebin.ca/702814
# Record name is '702814'
demo #8932001
-- snip --
...or just short:
-- snip --
$ shnote get 702814
# Record name is '702814'
demo #8932001
-- snip --

Suggestions/comments/rants/etc. welcome... :-)

----

Bye,
Roland

-- 
  __ .  . __
 (o.\ \/ /.o) [EMAIL PROTECTED]
  \__\/\/__/  MPEG specialist, C&&JAVA&&Sun&&Unix programmer
  /O /==\ O\  TEL +49 641 7950090
 (;O/ \/ \O;)
#!/bin/ksh93

#
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
# Common Development and Distribution License (the "License").
# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
# See the License for the specific language governing permissions
# and limitations under the License.
#
# When distributing Covered Code, include this CDDL HEADER in each
# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
# If applicable, add the following below this CDDL HEADER, with the
# fields enclosed by brackets "[]" replaced with your own identifying
# information: Portions Copyright [yyyy] [name of copyright owner]
#
# CDDL HEADER END
#

#
# Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I%     %E% SMI"
#

# Solaris needs /usr/xpg4/bin/ because the tools in /usr/bin are not 
POSIX-conformant
export PATH=/usr/xpg4/bin:/bin:/usr/bin

# Make sure all math stuff runs in the "C" locale to avoid problems with 
alternative
# radix point representations (e.g. ',' instead of '.' in de_DE.*-locales). This
# needs to be set _before_ any floating-point constants are defined in this 
script)
if [[ "${LC_ALL}" != "" ]] ; then
    export \
        LC_MONETARY="${LC_ALL}" \
        LC_MESSAGES="${LC_ALL}" \
        LC_COLLATE="${LC_ALL}" \
        LC_CTYPE="${LC_ALL}"
        unset LC_ALL
fi
export LC_NUMERIC=C

function fatal_error
{
    print -u 2 "${progname}: $@"
    exit 1
}

# fixme: use "stat" builtin instead of "ls"
function get_filesize
{
    typeset filename="$1"
    typeset dummy # dummy string
    integer filesize=-1

    if [[ -f "${filename}" ]] ; then
        ls -lb "${filename}" | read dummy dummy dummy dummy filesize dummy 
    fi
    
    print -- ${filesize}
    
    return 0
}

function encode_multipart_form_data
{
#set -o xtrace
    nameref formdata=$1
    nameref content="${1}.content" # ToDO: should be 
|content="formdata.content"|
    integer numformelements=${#formdata.form[*]}
    integer i
    
    content=""
    
    # todo: add support to upload files
    for (( i=0 ; i < numformelements ; i++ )) ; do
        nameref element="${1}.form[${i}]" # ToDo: should be 
|element="formdata.form[${i}]"|

        content+="--${formdata.boundary}\n"
        content+="Content-Disposition: form-data; name=\"${element.name}\"\n"
        content+="\n"
        content+="${element.data}\n" # fixme: may need encoding for non-ASCII 
data
    done
    
    formdata.content_length=${#content}

    # add content tail (which MUST not be added to the content length)
    content+="--${formdata.boundary}--\n"

    return 0
}

# parse return code, cookies etc.
function parse_http_response
{
    nameref response=$1
    typeset h c i
    
    IFS=$' \t' read -r h c # read HTTP/1.[01] <code>
    [[ "$h" != ~(Eil)HTTP/.* ]] && { print -u2 "$0: HTTP/ header missing" ; 
return 1 ; }
    response.httpcode="$c"
    
    # skip remaining headers
    while IFS='' read -r i ; do 
        [[ "$i" = $'\r' ]] && break
    done

    return 0
}


function history_write_record
{
    # rec: history record:
    #     rec.title
    #     rec.description
    #     rec.provider
    #     rec.providertoken
    #     rec.url
    nameref rec="$1"
    integer histfd

    mkdir -p "${HOME}/.shnote"
    
    {
        # write a single-line record which can be read
        # as a compound variable back into the shell
        printf "title=%q description=%q date=%q provider=%q providertoken=%q 
url=%q\n" \
            "${rec.title}" \
            "${rec.description}" \
            "$(date)" \
            "${rec.provider}" \
            "${rec.providertoken}" \
            "${rec.url}"            
    } >>"${history_file}"
    return $?
}

function print_history
{
    integer histfd # http stream number
    typeset line

    # default output format is:
    # <access url>/<title> <date> <access url>
    [[ "$1" = "-l" ]] || printf "# %s\t\t\t\t\t%s\t%s\n" "<url>" "<title>" 
"<date>"

    # no history file ?     
    if [[ ! -f "${history_file}" ]] ; then
        return 0
    fi

    # open history file
    exec {histfd}<>"${history_file}"
    
    while read -u${histfd} line ; do
        typeset rec=()
        
        # This is a bit messy - "eval" should be avoided but there is currently
        # no better way to read a serialised compound variable from a file back
        # into the shell.
        eval "rec=( $line )"
        
        if [[ "$1" = "-l" ]] ; then 
            print -- "${rec}"
        else
            printf "%s\t%s\t%s\n" "${rec.url}" "${rec.title}" "${rec.date}"
        fi
        
        unset rec
    done
    
    # close history file
    exec {histfd}<&-
}

function put_note_pastebin_ca
{
#set -o xtrace  

    # key to autheticate this script against pastebin.ca
    typeset -r pastebin_ca_key="9CFXFyeNC3iga/vthok75kTBu5kSSLPD"
    # site setup
    typeset url_host="opensolaris.pastebin.ca"
    typeset url_path="/quiet-paste.php?api=${pastebin_ca_key}"
    typeset url="http://${url_host}${url_path}";
    integer netfd # http stream number

    # argument for "encode_multipart_form_data"
    typeset mimeform=(
        # input
        typeset boundary
        typeset -a form
        # output
        typeset content
        integer content_length
    )
     
    typeset request=""
    typeset content=""

    typeset -r 
boundary="--------shnote_${RANDOM}_Xfish_${RANDOM}_Yeats_${RANDOM}_Zchicken_${RANDOM}monster_--------"

    # ToDo: Use "mimeform.form+=(" once ksh93 has been fixed.
    mimeform.boundary="${boundary}"
    mimeform.form[0]=( name="name"        data="${LOGNAME}" )
    mimeform.form[1]=( name="expiry"      data="Never" )
    mimeform.form[2]=( name="type"        data="1" )
    mimeform.form[3]=( name="description" 
data="logname=${LOGNAME};hostname=$(hostname);date=$(date)" )
    mimeform.form[4]=( name="content"     data="$1" )
  
    encode_multipart_form_data mimeform
          
    content="${mimeform.content}"

    request="POST ${url_path} HTTP/1.1\n"
    request+="Host: ${url_host}\n"
    request+="User-Agent: ${http_user_agent}\n"
    request+="Connection: close\n"
    request+="Content-Type: multipart/form-data; boundary=${boundary}\n"
    request+="Content-Length: $(( ${mimeform.content_length} ))\n"

    exec {netfd}<>"/dev/tcp/${url_host}/80" 
    (( $? != 0 )) && { print -u2 "Couldn't open connection to ${url_host}." ;  
return 1 ; }

    # send http post
    {
        print -- "${request}"
        print -- "${content}"
    }  >&${netfd}

    # process reply
    parse_http_response httpresponse <&${netfd}
    response="$(cat <&${netfd})"

    # close connection
    exec {netfd}<&-
    
    if [[ "${response}" = ~(E).*SUCCESS.* ]] ; then
        typeset response_token="${response/~(E).*SUCCESS:/}"

        print "SUCCESS: http://opensolaris.pastebin.ca/${response_token}";
        
        # write history entry
        typeset histrec=(
            title="${mimeform.form[0].data}"
            description="${mimeform.form[3].data}"
            providertoken="${response_token}"
            provider="opensolaris.pastebin.ca"
            url="http://opensolaris.pastebin.ca/${response_token}";
        )       

        history_write_record histrec
    else
        print "ERROR: ${response}"
        return 1
    fi
    
    return 0
}

function get_note_pastebin_ca
{
    typeset recordname="$1"
    integer netfd # http stream number
    
    case "${recordname}" in
        ~(Elr)[0-9][0-9]*)
            # pass-through
            ;;
        ~(Elr)http://opensolaris.pastebin.ca/raw/[0-9]*)
            
recordname="${recordname/~(El)http:\/\/opensolaris.pastebin.ca\/raw\//}"
            ;;
        ~(Elr)http://opensolaris.pastebin.ca/[0-9]*)
            recordname="${recordname/~(El)http:\/\/opensolaris.pastebin.ca\//}"
            ;;
           *)
           fatal_error "Unsupported record name ${recordname}."
    esac
    
    print -u2 -f "# Record name is '%s'\n" "${recordname}"

    typeset url_host="opensolaris.pastebin.ca"
    typeset url_path="/raw/${recordname}"
    typeset url="http://${url_host}${url_path}";
    # I hereby curse Solaris for not having an entry for "http" in /etc/services

    # open TCP channel
    exec {netfd}<>"/dev/tcp/${url_host}/80"
    (( $? != 0 )) && { print -u2 "Couldn't open connection to ${url_host}." ; 
return 1 ; }

    # send HTTP request    
    request="GET ${url_path} HTTP/1.1\n"
    request+="Host: ${url_host}\n"
    request+="User-Agent: ${http_user_agent}\n"
    request+="Connection: close\n"
    print -u${netfd} -- "${request}\n"
    
    # collect response and send it to stdout
    parse_http_response httpresponse <&${netfd}
    cat <&${netfd}
    
    # close connection
    exec {netfd}<&-
    
    print # add newline
    
    return 0
}

function usage
{
    OPTIND=0
    getopts -a "${progname}" "${USAGE}" OPT '-?'
    exit 2
}

# program start
builtin basename
builtin cat
builtin date
builtin uname

typeset progname="$(basename "${0}")"

# HTTP protocol client identifer
typeset -r http_user_agent="shnote/ksh93 (2007-09-18; $(uname -s -r -p)"

# name of history log (the number after "history" is some kind of version
# counter to handle incompatible changes to the history file format)
typeset -r history_file="${HOME}/.shnote/history0.txt"

USAGE=$'
[-?\n@(#)\$Id: shnote (Roland Mainz) 2007-09-18 \$\n]
[-author?Roland Mainz <[EMAIL PROTECTED]
[+NAME?shnote - read/write text data to internet clipboards]
[+DESCRIPTION?\bshnote\b is a small utilty which can read and write text
        data to internet "clipboards" such as opensolaris.pastebin.ca.]
[+?The first arg \bmethod\b describes one of the methods, "put" saves a string
        to the internet clipboard, returning an identifer and the full URL
        where the data are stored. The method "get" retrives the raw
        information using the identifer from the previous "put" action.]
[+?The second arg \bstring\b contains either the string data which should be
        stored on the clipboard using the "put" method, the "get" method uses
        this information as identifer to retrive the raw data from the 
clipboard.]

method [ string ]

[+SEE ALSO?\bksh93\b(1), \brssread\b(1), http://opensolaris.pastebin.ca]
'

while getopts -a "${progname}" "${USAGE}" OPT ; do 
#    printmsg "## OPT=|${OPT}|, OPTARG=|${OPTARG}|"
    case ${OPT} in
        *)    usage ;;
    esac
done
shift $((OPTIND-1))

typeset method="$1"
shift

# todo: "history" mode
case "${method}" in
    put)   put_note_pastebin_ca "$@" ; exit $? ;;
    get)   get_note_pastebin_ca "$@" ; exit $? ;;
    hist)  print_history "$@"        ; exit $? ;;
    *)   usage ;;
esac

fatal_error "not reached."
# EOF.
_______________________________________________
opensolaris-discuss mailing list
opensolaris-discuss@opensolaris.org

Reply via email to