#!/usr/bin/env python
# Script to access random.org for random numbers
# Written by Ali Polatel
# Released under the Gnu General Public License v2
# XXX close stdout and open it as logfile in case of logfile is specified
# don't forget to remove coloring when writing to logfile ;)
import os
import sys
def checkversion():
""" Check python version """
too_old = "This program requires python v2.3 or greater\nYour python version :\n %s" % sys.version
try:
version = sys.version_info
if (version[0] < 2) or (version[1] < 3):
print too_old
sys.exit(3)
except AttributeError: # what if we don't have sys.version_info :P
print too_old
sys.exit(3)
checkversion()
import urllib
import urllib2
import optparse
import ConfigParser
import string
import re
import time
from optparse import OptionParser
home = os.environ['HOME']
CONF = {}
def main():
""" Main function """
opts = getopt()
if not opts.verbose: switch(0)
CONF["color"]=opts.color
parser(opts.config) # Parse configuration
if opts.file != "stdout":
sys.stdout.close()
cprint('cyan','o Output redirected to %s ' % opts.file)
if os.path.exists(opts.file):
try:
cprint('green',' [appending]\n')
sys.stdout = open(opts.file,"a")
except:
cprint('red',"o Error opening output file : %s\n" % sys.exc_info()[1])
sys.exit(1)
else:
sys.stdout = open(opts.file,"w")
r = Random()
if opts.mode=="checkbuf" and not opts.verbose: switch(1)
while 1: # Loop until buffer is >=20%
state=r.checkbuf()
if state: break
else:
switch(1)
cprint('red',"Buffer level is less than 20%\n")
cprint('red',"Sleeping for %d minutes before retrying\n" % CONF["wait"])
cprint('red',"Hit ^C to exit\n")
switch(0)
try: time.sleep(CONF["wait"]*60)
except KeyboardInterrupt: sys.exit(3)
if opts.mode == 'randnum': print r.randnum()
elif opts.mode == 'randseq': print r.randseq()
elif opts.mode == 'randbytes': print r.randbyte()
sys.stdout.close()
def switch(state):
""" Open/Close sys.stderr """
if state:
sys.stderr.close()
sys.stderr = open("/dev/stderr","a+")
else:
sys.stderr.close()
sys.stderr = open("/dev/null","a+")
def cprint(color,txt,bold=True):
""" Colorize string
if shell is bash and if --nocolor is not set"""
colors = {'bold': '\x1b[1m',
'normal':'\x1b[0m',
'blue':'\x1b[34m',
'green':'\x1b[32m',
'red':'\x1b[31m',
'cyan':'\x1b[36m'
}
if os.environ['SHELL'] != '/bin/bash' or not CONF["color"]:
sys.stdout.write(txt)
return 0
else:
text = ""
if bold: text += "%(bold)s" % colors
for key in colors:
if key == color: text += colors[key]
text+=txt
text+= "%(normal)s" % colors
sys.stderr.write(text)
return 0
def parser(config):
""" Helper function to parse configurations """
# look if ~/.randomrc exists and parse it
# else write default configuration to ~/.randomrc
if os.path.exists(config):
cprint('cyan',"o Parsing configuration file %s\n" % config)
parseconf(config)
else:
switch(1)
cprint('red',"o Configuration file not found\n")
cprint('red',"o Writing default config to %s\n" % config)
writeconf(config)
cprint('red',"o Adjust it as you want and rerun the script\n")
sys.exit(2)
def writeconf(config):
""" Write default configuration file """
# print "%(bold)s%(red)s[warning]\t%(normal)s%(bold)sConfiguration file ~/.randomrc not found\n" % colors
# print "%(bold)s\t\tWriting default configuration to ~/.randomrc\n" % colors
# print "%(bold)s\t\tEdit it to suit your needs\n" % colors
try:conf = open(config,"w")
except:
cprint('red',"o Error opening config file : %s\n" % sys.exc_info()[1])
sys.exit(1)
conf.write(
"""# random.py configuration file\n# Have a look at http://random.org/http.html
[buffer]
wait= 3 # Minutes to wait if random.org buffer is <20%
[randnum]
num=100 # The number of integers wanted. Currently limited to 10,000. Defaults to 100.
min=1 # The lower bound of the interval (inclusive). This is the minimum value of any number returned by the server.
max=100 # The upper bound of the interval (inclusive).
# This is the maximum value of any number returned by the server.
# Currently restricted to 1,000,000,000 or less. Defaults to 100.
col=5 # The number of columns in which to format the numbers. Defaults to 5.
[randbyte]
nbytes=256 # Number of bytes to request
format=file # avaliable options are file,hex,octal and binary
[randseq]
min=1 # The smallest value in the sequence.
max=100 # The largest value in the sequence.
""")
conf.close()
return 0
def parseconf(conffile):
""" Parse configuration """
# TODO check for configuration file errors
config = ConfigParser.ConfigParser()
config.read(conffile)
CONF["wait"] = config.get("buffer","wait")
CONF["num"] = config.get("randnum","num")
CONF["rnum_min"] = config.get("randnum","min")
CONF["rnum_max"] = config.get("randnum","max")
CONF["col"] = config.get("randnum","col")
CONF["nbytes"] = config.get("randbyte","nbytes")
CONF["format"] = config.get("randbyte","format")
CONF["rseq_min"] = config.get("randseq","min")
CONF["rseq_max"] = config.get("randseq","max")
# check for comments like format=file # <-- hey I'm here
notint=('color','format') # int() all except those
for key in CONF:
try: CONF[key] = re.sub(" ?#.*","",CONF[key])
except TypeError: pass # CONF["color"] is boolean
if key not in notint: CONF[key] = int(CONF[key])
return 0
def getopt():
""" Get options from command line"""
name = "random.py"
usage = "usage: %prog [options]"
desc = "Get random numbers,sequences,bytes from http://random.org"
modes = ["checkbuf","randnum","randseq","randbytes"]
p = OptionParser(usage=usage,version="%prog 0.01",description=desc,prog=name)
p.add_option("-v","--verbose",action="store_true",dest="verbose",default=False,help="be verbose")
p.add_option("-C","--nocolor",action="store_false",dest="color",default=True,help="suppress coloring of output")
p.add_option("-c","--config",default="%s/.randomrc" % home,metavar="FILE",dest="config",help="specify configuration file. [default:~/.randomrc]")
p.add_option("-w","--write",default="stdout",metavar="FILE",dest="file",help="specify output FILE. [default:%default]")
p.add_option("-m","--mode",default="randnum",dest="mode",help="Specify mode: checkbuf,randnum[default],randseq,randbytes")
(opts,args) = p.parse_args()
if opts.mode not in modes: p.error("Invalid mode")
return opts
class Random:
""" Main class including requests to scripts """
def __init__(self):
""" Initialize the class """
self.url="http://www.random.org/cgi-bin"
self.name = "random.py 0.01"
self.mail = "polatel at gmail dot com" # E-mail to put in User-Agent
self.headers = { "User-Agent" : "%s (%s)" % (self.name,self.mail) }
def checkbuf(self):
"""
Check random.org buffer
return 1 if buffer level is >20% and 0 otherwise
"""
cprint('cyan',"o Checking random.org buffer\n")
url = "%s/checkbuf" % self.url
req = urllib2.Request(url,None,self.headers)
handle = urllib2.urlopen(req)
buf = handle.read()
self.buffer = int ( buf.replace("%\n","") )
cprint('cyan',"o Buffer state : ")
if self.buffer >= 20:
cprint('green'," %d%%\n" % self.buffer)
return 1
else:
cprint('red'," %d%%\n" % self.buffer)
return 0
def randnum(self):
"""
Gets a list of integers from randnum script
num : The number of integers wanted. Currently limited to 10,000. Defaults to 100.
min : The lower bound of the interval (inclusive).
This is the minimum value of any number returned by the server.
Currently restricted to -1,000,000,000 or greater.
Defaults to 1.
max : The upper bound of the interval (inclusive).
This is the maximum value of any number returned by the server.
Currently restricted to 1,000,000,000 or less.
Defaults to 100.
col : The number of columns in which to format the numbers. Defaults to 5.
"""
# XXX get these text messages out!
txt='o Requesting %(num)d integers from random.org [min:%(rnum_min)d,max:%(rnum_max)d,columns:%(col)d]\n'% CONF
cprint('cyan',txt)
url = "%s/randnum?num=%d&min=%d&max=%d&col=%d" % (self.url,CONF["num"],CONF["rnum_min"],CONF["rnum_max"],CONF["col"])
req = urllib2.Request(url,None,self.headers)
handle = urllib2.urlopen(req)
return handle.read()
def randbyte(self):
"""
Gets a number of raw bytes from randbyte script
nbytes : The number of bytes requested. Currently restricted to 16,384.
Defaults to 256.
format : f -> the numbers are returned as a binary file of MIME type application/octet-stream.
h -> hexadecimal with MIME type text/plain
o -> octal with MIME type text/plain
b -> binary with MIME type text/plain
"""
txt='o Requesting %(nbytes)d raw bytes with format %(format)s from random.org\n'% CONF
cprint('cyan',txt)
url = "%s/randbyte?nbytes=%d&format=%s" % (self.url,CONF["nbytes"],CONF["format"][0])
req = urllib2.Request(url,None,self.headers)
handle = urllib2.urlopen(req)
return handle.read()
def randseq(self):
"""
Gets a randomized sequence of numbers from randseq script
min : The smallest value in the sequence
max : The largest value in the sequence
"""
txt='o Requesting a random sequence with intervals %(rseq_min)d:%(rseq_max)d.\n' % CONF
cprint('cyan',txt)
url = "%s/randseq?min=%d&max=%d" % (self.url,CONF["rseq_min"],CONF["rseq_max"])
req = urllib2.Request(url,None,self.headers)
handle = urllib2.urlopen(req)
return handle.read()
if __name__ == "__main__":
main()
syntax highlighted by Code2HTML, v. 0.9.1