#!/usr/bin/ruby -w
#
# pastebin -- a script based on nopaste for pasting to http://(xxx.)pastbin.com.
# nopaste -- quick script in the spirit of eatpaste, to generate nopaste urls.
# See http://www.rafb.net/paste/ for more information.
#
# Copyright 2008 Mihai Moldovan <ionic ionic.de>
# Copyright 2005,2007 Aron Griffis <agriffis n01se.net>
# Released under the GNU General Public License v2
#

require 'cgi'
require 'net/http'
require 'optparse'
require 'ostruct'
require 'uri'

$langs = {
        "text" => "plain text, no syntax highlighting",
        "bash" => "BASH script",
        "c" => "C program",
        "cpp" => "C++ program",
        "html4strict" => "HTML v. 4",
        "java" => "Java",
        "javascript" => "JavaScript",
        "lua" => "Lua script",
        "perl" => "Perl script",
        "php" => "PHP script",
        "python" => "Python script",
        "ruby" => "Ruby script",
        "adap" => "ADAP",
        "actionscript" => "ActionScript",
        "ada" => "ADA program",
        "apache" => "Apache Log File",
        "applescript" => "AppleScript",
        "asm" => "Assembler (NASM based)",
        "asp" => "ASP script",
        "autoit" => "AutoIt",
        "blitzbasic" => "Blitz Basic program",
        "bnf" => "BNF",
        "c_mac" => "C for Macs program",
        "caddcl" => "CAD DCL",
        "cadlisp" => "CAD Lisp program",
        "csharp" => "C# program",
        "cfm" => "ColdFusion",
        "css" => "Cascading Style Sheets",
        "d" => "D program",
        "delphi" => "Delphi program",
        "diff" => "Diff output",
        "dos" => "DOS",
        "eiffel" => "Eiffel program",
        "fortran" => "Fortran (FORTRAN) program",
        "freebasic" => "FreeBASIC program",
        "genero" => "Genero program",
        "gml" => "Game Maker Language program",
        "groovy" => "Groovy script",
        "haskell" => "Haskell program",
        "idl" => "Interactive Data Language program",
        "ini" => "INI file syntax",
        "inno" => "Inno Setup script",
        "latex" => "LaTeX typesetting script",
        "lisp" => "LISP program",
        "matlab" => "MATrix LAboratory program",
        "m68k" => "Motorola 68000 Assembler program",
        "mpasm" => "Microchip Assembler program",
        "mirc" => "mIRC Scripting Language script",
        "mysql" => "MySQL script",
        "nsis" => "Nullsoft Scriptable Install System script",
        "objc" => "Object C program",
        "ocaml" => "Objective Caml script",
        "oobas" => "OpenOffice.org BASIC program",
        "oracle8" => "Oracle 8 script",
        "pascal" => "Pascal program",
        "plswl" => "PL/SQL script",
        "qbasic" => "QuickBASIC program",
        "rails" => "Ruby on Rails script",
        "robots" => "Robots",
        "scheme" => "Scheme program",
        "smalltalk" => "Smalltalk program",
        "smarty" => "Smarty PHP script",
        "sql" => "SQL script",
        "tcl" => "TCL script",
        "vb" => "Visual Basic program",
        "vbnet" => "Visual Basic .NET program",
        "visualfoxpro" => "Microsoft Visual FoxPro program",
        "xml" => "Extensible Markup Language syntax",
        "z80" => "Z80 Assembler program"
}

class CmdLine
  def self.parse(args)
    options = OpenStruct.new
    options.lang = 'Plain Text'

      opts = OptionParser.new do |opts|
      opts.banner = "Usage: #{$0} [options]"
      opts.separator ""
      opts.separator "Options:"

      opts.on("-l", "--language LANG",
              "set language for syntax highlighting (defaults to \"text\",",
              "use -L to get a full list of possible languages)") do |x|
        options.lang = x
      end

      opts.on("-L", "--list-languages",
              "list all known languages") do
        x = 0
	$langs.each { |tmp,unused|
          x = tmp.to_s.length if tmp.to_s.length > x
        }
        $langs.each { |tmp1,tmp2|
          i = ((x.to_i / 8).to_i.ceil - (tmp1.to_s.length / 8).to_i.ceil) + 1
          puts "\t" + tmp1.to_s + "\t" * i + "-\t#{tmp2}"
        }
        exit
      end

      opts.on("-e", "--expire d|m|f", 
              "set the time after which your paste will be deleted (defaults to \"m\")",
              "\"d\" - after one day",
              "\"m\" - after one month",
              "\"f\" - never") do |x|
        options.expire = x
      end

      opts.on("-n", "--nick NICK", 
              "set nick (defaults to your username)") do |x|
        options.nick = x
      end

      opts.on("-p", "--private GROUP",
              "set the private group to paste to") do |x|
        options.private = x
      end

      opts.on("-t", "--txturl",
              "return url for the plain text file") do
        options.txturl = true
      end

      opts.on("-x", "--xcut", 
              "nopaste from X selection (using xclip, xcut or xsel)") do
        options.xcut = true 
      end

      opts.on("-d", "--debug",
              "enable debug output") do
        options.debug = true
      end

      opts.on_tail("-h", "--help", "show this help message") do
        puts opts
        exit 
      end

      opts.on_tail("-V", "--version", "show version information") do 
        puts "pastebin " + $version + " (based on nopaste)"
        exit
      end

      
    end

    begin
      opts.parse!(args)
    rescue OptionParser::InvalidOption => e
      puts "Invalid or unsupported option specified: #{e.args}\n\n"
      puts opts
      exit
    end
    options
  end
end

def e(s)
  return $zero+" error: "+s
end

def nopaste(nick, expire, text, lang = "Plain Text")
  cxn = Net::HTTP::Proxy($proxy.host, $proxy.port, $proxy_user, $proxy_pass
                        ).start($url.host, $url.port) { |cxn|
    response = cxn.request_post($url.path, 
      [ "cvt_tabs=No",
        "format=#{CGI::escape(lang)}",
        "poster=#{CGI::escape(nick)}",
        "expiry=#{CGI::escape(expire)}",
        "code2=#{CGI::escape(text)}",
        "paste=Send",
        "remember=0",
        "regexp=None" ].join('&'),
      { 'Content-Type' => 'application/x-www-form-urlencoded' })

    if $options.debug
      STDERR.puts response.inspect
      response.each_header {|k,v| STDERR.printf "%s: %s\n", k, v}
      STDERR.puts response.body
    end

    case response
    when Net::HTTPRedirection
        u = $url.merge(response['location'])
        u = u.to_s.sub(/\.com\//, '.com/pastebin.php?dl=') if $options.txturl
        return u
    when Net::HTTPSuccess
      # strange
      return e("pastebin.com sent an unexpected HTML response")
    else
      return e("pastebin.com says #{response.code} #{response.message}")
    end
  }
end

$proxy = URI.parse(ENV['http_proxy'] || '')
$proxy_user, $proxy_pass = nil, nil
$proxy_user, $proxy_pass = $proxy.userinfo.split(/:/) if $proxy.userinfo
$version = '$Revision: 2840 $'.split(' ')[1]
$zero = $0.sub(/.*\//, '')
$options = CmdLine.parse(ARGV)
urls = []


if !$options.private then
  $url = URI.parse("http://www.pastebin.com/pastebin.php")
else
  $url = URI.parse("http://#{$options.private}.pastebin.com/pastebin.php")
end

if $options.expire.to_s.empty?
  $options.expire = "m"
end

if $options.nick.to_s.empty?
  $options.nick = ENV['USER'] || 'unknown'
end

begin
  if $options.xcut
    buf = %x{xclip -o 2>/dev/null || xcut -p 2>/dev/null || xsel 2>/dev/null}
    urls << nopaste($options.nick, $options.expire, buf, $options.lang)
  elsif ARGV.empty?
    urls << nopaste($options.nick, $options.expire, gets(nil), $options.lang)
  end

  urls << nopaste($options.nick, $options.expire, gets(nil), $options.lang) until ARGV.empty?
  begin
    IO.popen("xclip >/dev/null 2>&1", "w") {|p| p.print urls.map {|u| u.to_s}.join(' ') }
  rescue Errno::EPIPE
    begin
      IO.popen("xcut 2>/dev/null", "w") {|p| p.print urls.map {|u| u.to_s}.join(' ') }
    rescue Errno::EPIPE
      begin
        IO.popen("xsel -i 2>/dev/null", "w") {|p| p.print urls.map {|u| u.to_s}.join(' ') }
      rescue Errno::EPIPE
      end
    end
  end

rescue
  puts urls # whatever we've accumulated already
  puts e("something bad happened, stack trace follows")
  raise
end

puts urls
puts "Please remember to run dos2unix on the downloaded file!" if $options.txturl

exit 1 if urls.join("\n") =~ /^(?!http:)/
