Files
entropy/libraries/entropy/misc.py
2009-04-19 19:43:41 +02:00

747 lines
24 KiB
Python

# -*- coding: utf-8 -*-
"""
# DESCRIPTION:
# Entropy Object Oriented Interface
Copyright (C) 2007-2009 Fabio Erculiani
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
"""
# pylink ok - doc
from __future__ import with_statement
import os
import sys
import time
import urllib2
import threading
from entropy.const import etpConst, etpUi
from entropy.core import SystemSettings
class Lifo:
def __init__(self):
self.__counter = -1
self.__buf = {}
self.__lock = threading.Lock()
def push(self, item):
with self.__lock:
self.__counter += 1
self.__buf[self.__counter] = item
def clear(self):
with self.__lock:
self.__counter = -1
self.__buf.clear()
def is_filled(self):
if self.__counter == -1:
return False
return True
def pop(self):
with self.__lock:
if self.__counter == -1:
return None
self.__counter -= 1
try:
return self.__buf.pop(self.__counter+1)
except KeyError:
pass
class TimeScheduled(threading.Thread):
def __init__(self, delay, *args, **kwargs):
threading.Thread.__init__(self)
self.__f = args[0]
self.__delay = delay
self.__args = args[1:][:]
self.__kwargs = kwargs.copy()
# never enable this by default
# otherwise kill() and thread
# check will hang until
# time.sleep() is done
self.__accurate = False
self.__delay_before = False
self.__alive = 0
def set_delay(self, delay):
self.__delay = delay
def set_delay_before(self, bool_do):
self.__delay_before = bool(bool_do)
def set_accuracy(self, bool_do):
self.__accurate = bool(bool_do)
def run(self):
self.__alive = 1
while self.__alive:
if self.__delay_before:
do_break = self.__do_delay()
if do_break:
break
if self.__f == None:
break
self.__f(*self.__args, **self.__kwargs)
if not self.__delay_before:
do_break = self.__do_delay()
if do_break:
break
def __do_delay(self):
if not self.__accurate:
if float == None:
return True
mydelay = float(self.__delay)
t_frac = 0.3
while mydelay > 0.0:
if not self.__alive:
return True
if time == None:
return True # shut down?
time.sleep(t_frac)
mydelay -= t_frac
else:
if time == None:
return True # shut down?
time.sleep(self.__delay)
return False
def kill(self):
self.__alive = 0
class ParallelTask(threading.Thread):
def __init__(self, *args, **kwargs):
threading.Thread.__init__(self)
self.__function = args[0]
self.__args = args[1:][:]
self.__kwargs = kwargs.copy()
self.__rc = None
def run(self):
self.__rc = self.__function(*self.__args, **self.__kwargs)
def get_function(self):
return self.__function
def get_rc(self):
return self.__rc
class EmailSender:
def __init__(self):
import smtplib
self.smtplib = smtplib
from email.mime.audio import MIMEAudio
from email.mime.image import MIMEImage
from email.mime.text import MIMEText
from email.mime.base import MIMEBase
from email.mime.multipart import MIMEMultipart
from email import encoders
from email.message import Message
import mimetypes
self.smtpuser = None
self.smtppassword = None
self.smtphost = 'localhost'
self.smtpport = 25
self.text = MIMEText
self.mimefile = MIMEBase
self.audio = MIMEAudio
self.image = MIMEImage
self.multipart = MIMEMultipart
self.default_sender = self.smtp_send
self.mimetypes = mimetypes
self.encoders = encoders
self.message = Message
def smtp_send(self, sender, destinations, message):
s_srv = self.smtplib.SMTP(self.smtphost, self.smtpport)
if self.smtpuser and self.smtppassword:
s_srv.login(self.smtpuser, self.smtppassword)
s_srv.sendmail(sender, destinations, message)
s_srv.quit()
def send_text_email(self, sender_email, destination_emails, subject,
content):
# Create a text/plain message
if isinstance(content, unicode):
content = content.encode('utf-8')
if isinstance(subject, unicode):
subject = subject.encode('utf-8')
msg = self.text(content)
msg['Subject'] = subject
msg['From'] = sender_email
msg['To'] = ', '.join(destination_emails)
return self.default_sender(sender_email, destination_emails,
msg.as_string())
def send_mime_email(self, sender_email, destination_emails, subject,
content, files):
outer = self.multipart()
outer['Subject'] = subject
outer['From'] = sender_email
outer['To'] = ', '.join(destination_emails)
outer.preamble = subject
mymsg = self.text(content)
outer.attach(mymsg)
# attach files
for myfile in files:
if not (os.path.isfile(myfile) and os.access(myfile, os.R_OK)):
continue
ctype, encoding = self.mimetypes.guess_type(myfile)
if ctype is None or encoding is not None:
ctype = 'application/octet-stream'
maintype, subtype = ctype.split('/', 1)
if maintype == 'image':
img_f = open(myfile, 'rb')
msg = self.image(img_f.read(), _subtype = subtype)
img_f.close()
elif maintype == 'audio':
audio_f = open(myfile, 'rb')
msg = self.audio(audio_f.read(), _subtype = subtype)
audio_f.close()
else:
gen_f = open(myfile, 'rb')
msg = self.mimefile(maintype, subtype)
msg.set_payload(gen_f.read())
gen_f.close()
self.encoders.encode_base64(msg)
msg.add_header('Content-Disposition', 'attachment',
filename = os.path.basename(myfile))
outer.attach(msg)
composed = outer.as_string()
return self.default_sender(sender_email, destination_emails, composed)
class EntropyGeoIP:
"""
Entropy geo-tagging interface containing useful
methods to ease metadata management and transfor-
mation.
It's a wrapper over GeoIP at the moment
dev-python/geoip-python required
"""
def __init__(self, geoip_dbfile):
"""
@param1: valid GeoIP (Maxmind) database file (.dat)
"""
import GeoIP
self.__geoip = GeoIP
# http://www.maxmind.com/download/geoip/database/GeoLiteCity.dat.gz
if not (os.path.isfile(geoip_dbfile) and \
os.access(geoip_dbfile, os.R_OK)):
raise AttributeError(
"expecting a valid filepath for geoip_dbfile, got: %s" % (
repr(geoip_dbfile),
)
)
self.__geoip_dbfile = geoip_dbfile
def __get_geo_ip_generic(self):
return self.__geoip.new(self.__geoip.GEOIP_MEMORY_CACHE)
def __get_geo_ip_open(self):
return self.__geoip.open(self.__geoip_dbfile,
self.__geoip.GEOIP_STANDARD)
def get_geoip_country_name_from_ip(self, ip_address):
"""
@return: string or None
@param1: ip address string
"""
gi_a = self.__get_geo_ip_generic()
return gi_a.country_name_by_addr(ip_address)
def get_geoip_country_code_from_ip(self, ip_address):
"""
@return: string or None
@param1: ip address string
"""
gi_a = self.__get_geo_ip_generic()
return gi_a.country_code_by_addr(ip_address)
def get_geoip_record_from_ip(self, ip_address):
"""
@return: dict() or None
@param1: ip address string
dict data:
{
'city': 'Treviso',
'region': '20',
'area_code': 0,
'longitude': 12.244999885559082,
'country_code3': 'ITA',
'latitude': 45.666698455810547,
'postal_code': None,
'dma_code': 0,
'country_code': 'IT',
'country_name': 'Italy'
}
"""
go_a = self.__get_geo_ip_open()
return go_a.record_by_addr(ip_address)
def get_geoip_record_from_hostname(self, hostname):
"""
@return: dict() or None
@param1: hostname
"""
go_a = self.__get_geo_ip_open()
return go_a.record_by_name(hostname)
class rssFeed:
# this is a relative import to avoid circular deps
import tools as entropyTools
def __init__(self, filename, title, description, maxentries = 100):
self.__system_settings = SystemSettings()
self.__feed_title = title
self.__feed_title = self.__feed_title.strip()
self.__feed_description = description
self.__feed_language = "en-EN"
self.__srv_settings_plugin_id = \
etpConst['system_settings_plugins_ids']['server_plugin']
self.__feed_editor = self.__system_settings[self.__srv_settings_plugin_id]['server']['rss']['editor']
self.__feed_copyright = "%s - (C) %s" % (
self.__system_settings['system']['name'],
self.entropyTools.get_year(),
)
self.__file = filename
self.__items = {}
self.__itemscounter = 0
self.__maxentries = maxentries
from xml.dom import minidom
self.minidom = minidom
# sanity check
broken = False
if os.path.isfile(self.__file):
try:
self.xmldoc = self.minidom.parse(self.__file)
except:
broken = True
if not os.path.isfile(self.__file) or broken:
self.__title = self.__feed_title
self.__description = self.__feed_description
self.__language = self.__feed_language
self.__cright = self.__feed_copyright
self.__editor = self.__feed_editor
self.__link = self.__system_settings[self.__srv_settings_plugin_id]['server']['rss']['website_url']
rss_f = open(self.__file, "w")
rss_f.write('')
rss_f.flush()
rss_f.close()
else:
self.__rssdoc = self.xmldoc.getElementsByTagName("rss")[0]
self.__channel = self.__rssdoc.getElementsByTagName("channel")[0]
title_obj = self.__channel.getElementsByTagName("title")[0]
self.__title = title_obj.firstChild.data.strip()
link_obj = self.__channel.getElementsByTagName("link")[0]
self.__link = link_obj.firstChild.data.strip()
desc_obj = self.__channel.getElementsByTagName("description")[0]
description = desc_obj.firstChild
if hasattr(description, "data"):
self.__description = description.data.strip()
else:
self.__description = ''
try:
lang_obj = self.__channel.getElementsByTagName("language")[0]
self.__language = lang_obj.firstChild.data.strip()
except IndexError:
self.__language = 'en'
try:
cright_obj = self.__channel.getElementsByTagName("copyright")[0]
self.__cright = cright_obj.firstChild.data.strip()
except IndexError:
self.__cright = ''
try:
e_obj = self.__channel.getElementsByTagName("managingEditor")[0]
self.__editor = e_obj.firstChild.data.strip()
except IndexError:
self.__editor = ''
entries = self.__channel.getElementsByTagName("item")
self.__itemscounter = len(entries)
if self.__itemscounter > self.__maxentries:
self.__itemscounter = self.__maxentries
mycounter = self.__itemscounter
for item in entries:
if mycounter == 0: # max entries reached
break
mycounter -= 1
self.__items[mycounter] = {}
title_obj = item.getElementsByTagName("title")[0]
self.__items[mycounter]['title'] = \
title_obj.firstChild.data.strip()
desc_obj = item.getElementsByTagName("description")[0]
description = desc_obj.firstChild
if description:
self.__items[mycounter]['description'] = \
description.data.strip()
else:
self.__items[mycounter]['description'] = ""
link = item.getElementsByTagName("link")[0].firstChild
if link:
self.__items[mycounter]['link'] = link.data.strip()
else:
self.__items[mycounter]['link'] = ""
guid_obj = item.getElementsByTagName("guid")[0]
self.__items[mycounter]['guid'] = \
guid_obj.firstChild.data.strip()
pub_date_obj = item.getElementsByTagName("pubDate")[0]
self.__items[mycounter]['pubDate'] = \
pub_date_obj.firstChild.data.strip()
dcs = item.getElementsByTagName("dc:creator")
if dcs:
self.__items[mycounter]['dc:creator'] = \
dcs[0].firstChild.data.strip()
def addItem(self, title, link = '', description = '', pubDate = ''):
self.__itemscounter += 1
self.__items[self.__itemscounter] = {}
self.__items[self.__itemscounter]['title'] = title
if pubDate:
self.__items[self.__itemscounter]['pubDate'] = pubDate
else:
self.__items[self.__itemscounter]['pubDate'] = \
time.strftime("%a, %d %b %Y %X +0000")
self.__items[self.__itemscounter]['description'] = description
self.__items[self.__itemscounter]['link'] = link
if link:
self.__items[self.__itemscounter]['guid'] = link
else:
myguid = self.__system_settings['system']['name'].lower()
myguid = myguid.replace(" ", "")
self.__items[self.__itemscounter]['guid'] = myguid+"~" + \
description + str(self.__itemscounter)
return self.__itemscounter
def removeEntry(self, key):
if key in self.__items:
del self.__items[key]
self.__itemscounter -= 1
return self.__itemscounter
def getEntries(self):
return self.__items, self.__itemscounter
def writeChanges(self, reverse = True):
# filter entries to fit in maxentries
if self.__itemscounter > self.__maxentries:
tobefiltered = self.__itemscounter - self.__maxentries
for index in range(tobefiltered):
try:
del self.__items[index]
except KeyError:
pass
doc = self.minidom.Document()
rss = doc.createElement("rss")
rss.setAttribute("version", "2.0")
rss.setAttribute("xmlns:atom", "http://www.w3.org/2005/Atom")
channel = doc.createElement("channel")
# title
title = doc.createElement("title")
title_text = doc.createTextNode(unicode(self.__title))
title.appendChild(title_text)
channel.appendChild(title)
# link
link = doc.createElement("link")
link_text = doc.createTextNode(unicode(self.__link))
link.appendChild(link_text)
channel.appendChild(link)
# description
description = doc.createElement("description")
desc_text = doc.createTextNode(unicode(self.__description))
description.appendChild(desc_text)
channel.appendChild(description)
# language
language = doc.createElement("language")
lang_text = doc.createTextNode(unicode(self.__language))
language.appendChild(lang_text)
channel.appendChild(language)
# copyright
cright = doc.createElement("copyright")
cr_text = doc.createTextNode(unicode(self.__cright))
cright.appendChild(cr_text)
channel.appendChild(cright)
# managingEditor
managing_editor = doc.createElement("managingEditor")
ed_text = doc.createTextNode(unicode(self.__editor))
managing_editor.appendChild(ed_text)
channel.appendChild(managing_editor)
keys = self.__items.keys()
if reverse:
keys.reverse()
for key in keys:
# sanity check, you never know
if not self.__items.has_key(key):
self.removeEntry(key)
continue
k_error = False
for item in ('title', 'link', 'guid', 'description', 'pubDate',):
if not self.__items[key].has_key(item):
k_error = True
break
if k_error:
self.removeEntry(key)
continue
# item
item = doc.createElement("item")
# title
item_title = doc.createElement("title")
item_title_text = doc.createTextNode(
unicode(self.__items[key]['title']))
item_title.appendChild(item_title_text)
item.appendChild(item_title)
# link
item_link = doc.createElement("link")
item_link_text = doc.createTextNode(
unicode(self.__items[key]['link']))
item_link.appendChild(item_link_text)
item.appendChild(item_link)
# guid
item_guid = doc.createElement("guid")
item_guid.setAttribute("isPermaLink", "true")
item_guid_text = doc.createTextNode(
unicode(self.__items[key]['guid']))
item_guid.appendChild(item_guid_text)
item.appendChild(item_guid)
# description
item_desc = doc.createElement("description")
item_desc_text = doc.createTextNode(
unicode(self.__items[key]['description']))
item_desc.appendChild(item_desc_text)
item.appendChild(item_desc)
# pubdate
item_date = doc.createElement("pubDate")
item_date_text = doc.createTextNode(
unicode(self.__items[key]['pubDate']))
item_date.appendChild(item_date_text)
item.appendChild(item_date)
# add item to channel
channel.appendChild(item)
# add channel to rss
rss.appendChild(channel)
doc.appendChild(rss)
rss_f = open(self.__file, "w")
rss_f.writelines(doc.toprettyxml(indent=" ").encode('utf-8'))
rss_f.flush()
rss_f.close()
class LogFile:
def __init__(self, level = 0, filename = None, header = "[LOG]"):
self.handler = self.default_handler
self.level = level
self.header = header
self.__logfile = None
self.open(filename)
self.__filename = filename
def __del__(self):
self.close()
def close(self):
try:
self.__logfile.close()
except (IOError, OSError,):
pass
def get_fpath(self):
return self.__filename
def flush(self):
self.__logfile.flush()
def fileno(self):
return self.__get_file()
def isatty(self):
return False
def read(self, *args):
return ''
def readline(self):
return ''
def readlines(self):
return []
def seek(self, offset):
return self.__logfile.seek(offset)
def tell(self):
return self.__logfile.tell()
def truncate(self):
return self.__logfile.truncate()
def open(self, file_path = None):
if isinstance(file_path, basestring):
if os.access(file_path, os.W_OK) and os.path.isfile(file_path):
self.__logfile = open(file_path, "aw")
else:
self.__logfile = open("/dev/null", "aw")
elif hasattr(file_path, 'write'):
self.__logfile = file_path
else:
self.__logfile = sys.stderr
def __get_file(self):
return self.__logfile.fileno()
def __call__(self, format, *args):
self.handler (format % args)
def default_handler (self, mystr):
try:
self.__logfile.write ("* %s\n" % (mystr))
except UnicodeEncodeError:
self.__logfile.write ("* %s\n" % (mystr.encode('utf-8'),))
self.__logfile.flush()
def set_loglevel(self, level):
self.level = level
def log(self, messagetype, level, message):
if self.level >= level and not etpUi['nolog']:
self.handler("%s %s %s %s" % (self.__get_header(),
messagetype, self.header, message,))
def write(self, line):
self.handler(line)
def writelines(self, lst):
for line in lst:
self.write(line)
def __get_header(self):
return time.strftime('[%H:%M:%S %d/%m/%Y %Z]')
class Callable:
def __init__(self, anycallable):
self.__call__ = anycallable
class MultipartPostHandler(urllib2.BaseHandler):
handler_order = urllib2.HTTPHandler.handler_order - 10 # needs to run first
def __init__(self):
pass
def http_request(self, request):
import urllib
doseq = 1
data = request.get_data()
if data is not None and type(data) != str:
v_files = []
v_vars = []
try:
for (key, value) in data.items():
if type(value) == file:
v_files.append((key, value))
else:
v_vars.append((key, value))
except TypeError:
raise TypeError, "not a valid non-string sequence" \
" or mapping object"
if len(v_files) == 0:
data = urllib.urlencode(v_vars, doseq)
else:
boundary, data = self.multipart_encode(v_vars, v_files)
contenttype = 'multipart/form-data; boundary=%s' % boundary
request.add_unredirected_header('Content-Type', contenttype)
request.add_data(data)
return request
def multipart_encode(self, myvars, files, boundary = None, buf = None):
from cStringIO import StringIO
import mimetools, mimetypes
#import stat
if boundary is None:
boundary = mimetools.choose_boundary()
if buf is None:
buf = StringIO()
for(key, value) in myvars:
buf.write('--%s\r\n' % boundary)
buf.write('Content-Disposition: form-data; name="%s"' % key)
buf.write('\r\n\r\n' + value + '\r\n')
for(key, fdesc) in files:
#file_size = os.fstat(fdesc.fileno())[stat.ST_SIZE]
filename = fdesc.name.split('/')[-1]
contenttype = mimetypes.guess_type(filename)[0] or \
'application/octet-stream'
buf.write('--%s\r\n' % boundary)
buf.write('Content-Disposition: form-data; name="%s"; ' \
'filename="%s"\r\n' % (key, filename))
buf.write('Content-Type: %s\r\n' % contenttype)
# buffer += 'Content-Length: %s\r\n' % file_size
fdesc.seek(0)
buf.write('\r\n' + fdesc.read() + '\r\n')
buf.write('--' + boundary + '--\r\n\r\n')
buf = buf.getvalue()
return boundary, buf
multipart_encode = Callable(multipart_encode)
https_request = http_request