| 1 |
#!/usr/bin/env python2.5 |
| 2 |
# -*- coding: utf-8 -*- |
| 3 |
""" Useful functions that shouldn't be in the UI code |
| 4 |
|
| 5 |
fMMS - MMS for fremantle |
| 6 |
Copyright (C) 2010 Nick Leppänen Larsson <frals@frals.se> |
| 7 |
|
| 8 |
@license: GNU GPLv2, see COPYING file. |
| 9 |
""" |
| 10 |
import logging |
| 11 |
import logging.config |
| 12 |
|
| 13 |
logging.config.fileConfig('/opt/fmms/logger.conf') |
| 14 |
log = logging.getLogger('fmms.%s' % __name__) |
| 15 |
|
| 16 |
import os |
| 17 |
import array |
| 18 |
import re |
| 19 |
import time |
| 20 |
import urlparse |
| 21 |
import subprocess |
| 22 |
import gettext |
| 23 |
import socket |
| 24 |
|
| 25 |
import dbus |
| 26 |
|
| 27 |
import fmms_config as fMMSconf |
| 28 |
import dbhandler as DBHandler |
| 29 |
from mms.message import MMSMessage |
| 30 |
from mms import mms_pdu |
| 31 |
from wappushhandler import MMSSender |
| 32 |
|
| 33 |
#TODO: constants.py? |
| 34 |
MSG_DIRECTION_IN = 0 |
| 35 |
MSG_DIRECTION_OUT = 1 |
| 36 |
MSG_UNREAD = 0 |
| 37 |
MSG_READ = 1 |
| 38 |
|
| 39 |
_ = gettext.gettext |
| 40 |
gettext.bindtextdomain('fmms','/opt/fmms/share/locale/') |
| 41 |
gettext.textdomain('fmms') |
| 42 |
|
| 43 |
class fMMS_controller(): |
| 44 |
|
| 45 |
def __init__(self): |
| 46 |
""" initialize """ |
| 47 |
self.config = fMMSconf.fMMS_config() |
| 48 |
self._mmsdir = self.config.get_mmsdir() |
| 49 |
self._pushdir = self.config.get_pushdir() |
| 50 |
self._outdir = self.config.get_outdir() |
| 51 |
self.store = DBHandler.DatabaseHandler(self) |
| 52 |
self.ui = False |
| 53 |
|
| 54 |
def clean_url(self, url): |
| 55 |
m = re.search(r"http\:\/\/(?i)", url) |
| 56 |
if m: |
| 57 |
url = url.replace(m.group(0), "http://") |
| 58 |
return url |
| 59 |
|
| 60 |
def get_host_from_url(self, url): |
| 61 |
""" gets the hostname from an url """ |
| 62 |
# change HTTP:// etc to lowercase because |
| 63 |
# havoc connector depends on it |
| 64 |
url = self.clean_url(url) |
| 65 |
if not url.startswith("http://"): |
| 66 |
url = "http://%s" % url |
| 67 |
|
| 68 |
ret = urlparse.urlparse(url) |
| 69 |
ret = ret[1].split(":")[0] |
| 70 |
return ret |
| 71 |
|
| 72 |
def convert_to_real_ip(self, indata): |
| 73 |
""" converts a ip with leading zeroes to a real ip """ |
| 74 |
# Check if it looks like an IP |
| 75 |
if re.search(r"\b\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3}\b", indata): |
| 76 |
ip = indata.split(".") |
| 77 |
dot = "." |
| 78 |
final = [] |
| 79 |
for octet in ip: |
| 80 |
final.append(str(int(octet))) |
| 81 |
indata = dot.join(final) |
| 82 |
return indata |
| 83 |
|
| 84 |
def convert_timeformat(self, intime, format, hideToday=False): |
| 85 |
mtime = intime |
| 86 |
try: |
| 87 |
mtime = time.strptime(mtime, "%Y-%m-%d %H:%M:%S") |
| 88 |
except ValueError, e: |
| 89 |
#log.info("timeconversion stage1 failed: %s %s", type(e), e) |
| 90 |
try: |
| 91 |
mtime = time.strptime(mtime) |
| 92 |
except ValueError, e: |
| 93 |
#log.info("timeconversion stage2 failed: %s %s", type(e), e) |
| 94 |
pass |
| 95 |
except Exception, e: |
| 96 |
#log.exception("Could not convert timestamp: %s %s", type(e), e) |
| 97 |
pass |
| 98 |
|
| 99 |
# TODO: check if hideToday == true |
| 100 |
# TODO: remove date if date == today |
| 101 |
try: |
| 102 |
mtime = time.strftime("%Y-%m-%d | %H:%M", mtime) |
| 103 |
except: |
| 104 |
log.info("stftime failed: %s %s", type(e), e) |
| 105 |
mtime = intime |
| 106 |
return mtime |
| 107 |
|
| 108 |
def send_mms(self, to, subject, message, attachment, sender): |
| 109 |
try: |
| 110 |
sender = MMSSender(to, subject, message, attachment, sender, setupConn=True, controller=self) |
| 111 |
except: |
| 112 |
msg = "%s" % (gettext.ldgettext('hildon-common-strings', "sfil_ni_operation_failed")) |
| 113 |
return (-1, msg) |
| 114 |
try: |
| 115 |
(status, reason, output, parsed) = sender.sendMMS() |
| 116 |
|
| 117 |
if parsed == True and "Response-Status" in output: |
| 118 |
if output['Response-Status'] == "Ok": |
| 119 |
log.info("message seems to have sent AOK!") |
| 120 |
return (0, "OK") |
| 121 |
|
| 122 |
errstr = gettext.ldgettext('hildon-common-strings', "sfil_ni_operation_failed") |
| 123 |
message = str(status) + "_" + str(reason) |
| 124 |
reply = str(output) |
| 125 |
msg = "%s\nMMSC: %s\nBODY: %s" % (errstr, message, reply) |
| 126 |
return (-1, msg) |
| 127 |
except socket.error, exc: |
| 128 |
log.exception("sender failed due to connection error") |
| 129 |
errstr = gettext.ldgettext('hildon-common-strings', "sfil_ni_operation_failed") |
| 130 |
errhelp = _("Please check your settings.") |
| 131 |
msg = "%s\n%s\n%s" % (errstr, exc, errhelp) |
| 132 |
return (-1, msg) |
| 133 |
#raise |
| 134 |
except Exception, exc: |
| 135 |
log.exception("Sender failed.") |
| 136 |
msg = "%s\n%s" % (gettext.ldgettext('hildon-common-strings', "sfil_ni_operation_failed"), exc) |
| 137 |
return (-1, msg) |
| 138 |
#raise |
| 139 |
|
| 140 |
def decode_mms_from_push(self, binarydata): |
| 141 |
""" decodes the given mms """ |
| 142 |
decoder = mms_pdu.MMSDecoder() |
| 143 |
wsplist = decoder.decodeCustom(binarydata) |
| 144 |
sndr, url, trans_id = None, None, None |
| 145 |
|
| 146 |
try: |
| 147 |
url = wsplist["Content-Location"] |
| 148 |
log.info("content-location: %s", url) |
| 149 |
trans_id = wsplist["Transaction-Id"] |
| 150 |
trans_id = str(trans_id) |
| 151 |
log.info("transid: %s", trans_id) |
| 152 |
except Exception, e: |
| 153 |
log.exception("no content-location/transid in push; aborting: %s %s", type(e), e) |
| 154 |
bus = dbus.SystemBus() |
| 155 |
proxy = bus.get_object('org.freedesktop.Notifications', '/org/freedesktop/Notifications') |
| 156 |
interface = dbus.Interface(proxy, dbus_interface='org.freedesktop.Notifications') |
| 157 |
interface.SystemNoteInfoprint(gettext.ldgettext('modest', "mail_ni_ui_folder_get_msg_folder_error")) |
| 158 |
raise |
| 159 |
try: |
| 160 |
sndr = wsplist["From"] |
| 161 |
log.info("Sender: %s", sndr) |
| 162 |
except Exception, e: |
| 163 |
log.exception("No sender value defined: %s %s", type(e), e) |
| 164 |
sndr = "Unknown sender" |
| 165 |
|
| 166 |
self.save_binary_push(binarydata, trans_id) |
| 167 |
return (wsplist, sndr, url, trans_id) |
| 168 |
|
| 169 |
def save_binary_push(self, binarydata, transaction): |
| 170 |
""" saves the binary push message """ |
| 171 |
data = array.array('B') |
| 172 |
for b in binarydata: |
| 173 |
data.append(b) |
| 174 |
|
| 175 |
try: |
| 176 |
fp = open(self._pushdir + transaction, 'wb') |
| 177 |
fp.write(data) |
| 178 |
log.info("saved binary push: %s", fp) |
| 179 |
fp.close() |
| 180 |
except Exception, e: |
| 181 |
log.exception("failed to save binary push") |
| 182 |
raise |
| 183 |
|
| 184 |
def save_push_message(self, data): |
| 185 |
""" Gets the decoded data as a list (preferably from decode_mms_from_push) |
| 186 |
""" |
| 187 |
pushid = self.store.insert_push_message(data) |
| 188 |
return pushid |
| 189 |
|
| 190 |
def get_push_list(self, types=None): |
| 191 |
""" gets a list of all push messages """ |
| 192 |
return self.store.get_push_list() |
| 193 |
|
| 194 |
def is_fetched_push_by_transid(self, transactionid): |
| 195 |
return self.store.is_mms_downloaded(transactionid) |
| 196 |
|
| 197 |
def read_push_as_list(self, transactionid): |
| 198 |
return self.store.get_push_message(transactionid) |
| 199 |
|
| 200 |
def save_binary_mms(self, data, transaction): |
| 201 |
dirname = self._mmsdir + transaction |
| 202 |
if not os.path.isdir(dirname): |
| 203 |
os.makedirs(dirname) |
| 204 |
|
| 205 |
fp = open(dirname + "/message", 'wb') |
| 206 |
fp.write(data) |
| 207 |
log.info("saved binary mms %s", fp) |
| 208 |
fp.close() |
| 209 |
return dirname |
| 210 |
|
| 211 |
def save_binary_outgoing_mms(self, data, transaction): |
| 212 |
transaction = str(transaction) |
| 213 |
dirname = self._outdir + transaction |
| 214 |
if not os.path.isdir(dirname): |
| 215 |
os.makedirs(dirname) |
| 216 |
|
| 217 |
fp = open(dirname + "/message", 'wb') |
| 218 |
fp.write(data) |
| 219 |
log.info("saved binary mms %s", fp) |
| 220 |
fp.close() |
| 221 |
return dirname |
| 222 |
|
| 223 |
def decode_binary_mms(self, path): |
| 224 |
""" decodes and saves the binary mms""" |
| 225 |
# Decode the specified file |
| 226 |
# This also creates all the parts as files in path |
| 227 |
log.info("decode_binary_mms running: %s", str(path)) |
| 228 |
try: |
| 229 |
message = MMSMessage.fromFile(path + "/message") |
| 230 |
except Exception, e: |
| 231 |
log.exception("decode binary failed:", type(e), e) |
| 232 |
raise |
| 233 |
log.info("returning message!") |
| 234 |
return message |
| 235 |
|
| 236 |
def get_filepath_for_mms_transid(self, filename): |
| 237 |
return self.store.get_filepath_for_mms_transid(filename).replace("/message", "") |
| 238 |
|
| 239 |
def is_mms_read(self, transactionid): |
| 240 |
return self.store.is_message_read(transactionid) |
| 241 |
|
| 242 |
def mark_mms_read(self, transactionid): |
| 243 |
self.store.mark_message_read(transactionid) |
| 244 |
|
| 245 |
def store_mms_message(self, pushid, message, transactionId=None): |
| 246 |
if transactionId: |
| 247 |
message.headers['Transaction-Id'] = transactionId |
| 248 |
mmsid = self.store.insert_mms_message(pushid, message) |
| 249 |
return mmsid |
| 250 |
|
| 251 |
def store_outgoing_mms(self, message): |
| 252 |
mmsid = self.store.insert_mms_message(0, message, DBHandler.MSG_DIRECTION_OUT) |
| 253 |
return mmsid |
| 254 |
|
| 255 |
def store_outgoing_push(self, wsplist): |
| 256 |
pushid = self.store.insert_push_send(wsplist) |
| 257 |
return pushid |
| 258 |
|
| 259 |
def link_push_mms(self, pushid, mmsid): |
| 260 |
self.store.link_push_mms(pushid, mmsid) |
| 261 |
|
| 262 |
def get_direction_mms(self, transid): |
| 263 |
return self.store.get_direction_mms(transid) |
| 264 |
|
| 265 |
def get_replyuri_from_transid(self, transid): |
| 266 |
uri = self.store.get_replyuri_from_transid(transid) |
| 267 |
try: |
| 268 |
uri = uri.replace("/TYPE=PLMN", "") |
| 269 |
return uri |
| 270 |
except Exception, e: |
| 271 |
log.exception("failed to get replyuri, got: %s (%s)", uri, uri.__class__) |
| 272 |
return "" |
| 273 |
|
| 274 |
def get_mms_from_push(self, transactionid): |
| 275 |
plist = self.store.get_push_message(transactionid) |
| 276 |
#trans_id = plist['Transaction-Id'] |
| 277 |
# lets reuse the transactionid we already got |
| 278 |
trans_id = transactionid |
| 279 |
pushid = plist['PUSHID'] |
| 280 |
url = plist['Content-Location'] |
| 281 |
|
| 282 |
from wappushhandler import PushHandler |
| 283 |
p = PushHandler() |
| 284 |
path = p._get_mms_message(url, trans_id, self) |
| 285 |
log.info("path: %s", path) |
| 286 |
message = self.decode_binary_mms(path) |
| 287 |
log.info("storing mms...%s", trans_id) |
| 288 |
mmsid = self.store_mms_message(pushid, message) |
| 289 |
|
| 290 |
def get_mms_attachments(self, transactionid, allFiles=False): |
| 291 |
return self.store.get_mms_attachments(transactionid, allFiles) |
| 292 |
|
| 293 |
def get_mms_headers(self, transactionid): |
| 294 |
return self.store.get_mms_headers(transactionid) |
| 295 |
|
| 296 |
def delete_mms_message(self, fname): |
| 297 |
fullpath = self.store.get_filepath_for_mms_transid(fname) |
| 298 |
if fullpath: |
| 299 |
fullpath = fullpath.replace("/message", "") |
| 300 |
else: |
| 301 |
fullpath = self._mmsdir + fname |
| 302 |
|
| 303 |
log.info("fullpath: %s", fullpath) |
| 304 |
if os.path.isdir(fullpath): |
| 305 |
log.info("starting deletion of %s", fullpath) |
| 306 |
filelist = os.listdir(fullpath) |
| 307 |
log.info("removing: %s", filelist) |
| 308 |
for fn in filelist: |
| 309 |
try: |
| 310 |
fullfn = fullpath + "/" + fn |
| 311 |
os.remove(fullfn) |
| 312 |
except: |
| 313 |
log.info("failed to remove: %s", fullfn) |
| 314 |
try: |
| 315 |
log.info("trying to remove: %s", fullpath) |
| 316 |
os.rmdir(fullpath) |
| 317 |
except OSError, e: |
| 318 |
log.exception("failed to remove: %s %s", type(e), e) |
| 319 |
raise |
| 320 |
|
| 321 |
self.store.delete_mms_message(fname) |
| 322 |
|
| 323 |
def delete_push_message(self, fname): |
| 324 |
fullpath = self.store.get_filepath_for_push_transid(fname) |
| 325 |
if not fullpath: |
| 326 |
fullpath = self._pushdir + fname |
| 327 |
|
| 328 |
log.info("fullpath: %s", fullpath) |
| 329 |
if os.path.isfile(fullpath): |
| 330 |
log.info("removing: %s", fullpath) |
| 331 |
try: |
| 332 |
os.remove(fullpath) |
| 333 |
except Exception, e: |
| 334 |
log.exception("%s %s", type(e), e) |
| 335 |
raise |
| 336 |
self.store.delete_push_message(fname) |
| 337 |
|
| 338 |
def wipe_message(self, transactionid): |
| 339 |
self.delete_mms_message(transactionid) |
| 340 |
self.delete_push_message(transactionid) |
| 341 |
|
| 342 |
def save_draft(self, rcpt, text, attachment): |
| 343 |
if not rcpt: |
| 344 |
rcpt = "" |
| 345 |
if not text: |
| 346 |
text = "" |
| 347 |
if not attachment: |
| 348 |
attachment = "" |
| 349 |
self.store.save_draft(rcpt, text, attachment) |
| 350 |
|
| 351 |
def get_draft(self): |
| 352 |
return self.store.get_draft() |
| 353 |
|
| 354 |
def validate_phonenumber_email(self, nr): |
| 355 |
nr = str(nr) |
| 356 |
nr = nr.replace("+", "") |
| 357 |
nr = nr.replace(" ", "") |
| 358 |
if re.search(r"(\D)+", nr) == None or "@" in nr or ";" in nr: |
| 359 |
return True |
| 360 |
else: |
| 361 |
return False |
| 362 |
|
| 363 |
def get_mcc_mnc(self): |
| 364 |
""" Gets the SIM cards MMC/MNC """ |
| 365 |
bus = dbus.SystemBus() |
| 366 |
phone = dbus.Interface(bus.get_object("com.nokia.phone.SIM", "/com/nokia/phone/SIM"), "Phone.Sim") |
| 367 |
hplmn = phone.read_hplmn() |
| 368 |
(mcc, thirdbytes, mnc) = hplmn[0] |
| 369 |
# extract 4 lowest bits (as integer) |
| 370 |
mcc1 = int(mcc) & 0xF |
| 371 |
# and 4 highest bits (as integer) |
| 372 |
mcc2 = int(mcc) >> 4 |
| 373 |
# 4 lowest bits of the "united" byte is mcc3 |
| 374 |
mcc3 = int(thirdbytes) & 0xF |
| 375 |
# 4 lowest bits of mnc is mnc1 |
| 376 |
mnc1 = int(mnc) & 0xF |
| 377 |
# 4 highest bits of mnc is mnc2 |
| 378 |
mnc2 = int(mnc) >> 4 |
| 379 |
# if 4 highest bits of "united" byte is |
| 380 |
# 0xf only 2 digits are used for mnc |
| 381 |
mnc3 = int(thirdbytes) >> 4 |
| 382 |
|
| 383 |
if mnc3 == 0xf: |
| 384 |
final_mnc = "%s%s" % (mnc1, mnc2) |
| 385 |
else: |
| 386 |
final_mnc = "%s%s%s" % (mnc1, mnc2, mnc3) |
| 387 |
|
| 388 |
final_mcc = "%s%s%s" % (mcc1, mcc2, mcc3) |
| 389 |
|
| 390 |
return final_mcc, final_mnc |
| 391 |
|
| 392 |
def get_current_connection_iap_id(self): |
| 393 |
bus = dbus.SystemBus() |
| 394 |
icd = dbus.Interface(bus.get_object("com.nokia.icd", "/com/nokia/icd"), "com.nokia.icd") |
| 395 |
(iap, ign, ign, ign, ign, ign, ign) = icd.get_statistics() |
| 396 |
return iap |
| 397 |
|
| 398 |
def disconnect_current_connection(self): |
| 399 |
args = "DISCONNECT" |
| 400 |
retcode = subprocess.call(["/opt/fmms/fmms_magic", args]) |
| 401 |
|
| 402 |
def get_operator_display_name(self): |
| 403 |
bus = dbus.SystemBus() |
| 404 |
phone = dbus.Interface(bus.get_object("com.nokia.phone.SIM", "/com/nokia/phone/SIM"), "Phone.Sim") |
| 405 |
(providername, ign, ign2, err) = phone.get_service_provider_name() |
| 406 |
return providername |
| 407 |
|
| 408 |
def get_settings_from_file(self, mcc, mnc, displayname): |
| 409 |
fn = open("/etc/operator_settings", 'r') |
| 410 |
mcc = str(int(mcc)) |
| 411 |
mnc = str(int(mnc)) |
| 412 |
for line in fn: |
| 413 |
""" |
| 414 |
0 : MCC |
| 415 |
1 : MNC |
| 416 |
2 : SERVICE PROVIDER NAME |
| 417 |
3 : ACCESS TYPE |
| 418 |
4 : IAP NAME |
| 419 |
5 : GPRS ACCESSPOINT NAME |
| 420 |
6 : GPRS AUTOLOGIN (X = YES) |
| 421 |
7 : USERNAME |
| 422 |
8 : PASSWORD |
| 423 |
9 : <empty> |
| 424 |
10 : <empty> |
| 425 |
11 : GPRS MMS/WAP GATEWAY PROXY |
| 426 |
12 : GPRS MMS/WAP GATEWAY PORT |
| 427 |
13 : IP ADDRESS (IF NOT FETCHED FROM SERVER) |
| 428 |
14 : PRIMARY DNS ADDRESS (IF NOT FETCHED FROM SERVER) |
| 429 |
15 : SECONDARY DNS ADDRESS (IF AVAILABLE) |
| 430 |
16 : <empty> |
| 431 |
""" |
| 432 |
row = line.split('\t') |
| 433 |
if row[0] == mcc and row[1] == mnc and row[3] == 'MMS' and displayname.lower() in row[2].lower(): |
| 434 |
settings = {} |
| 435 |
settings['apn'] = row[5] |
| 436 |
settings['user'] = row[7] |
| 437 |
settings['pass'] = row[8] |
| 438 |
settings['proxy'] = row[11] |
| 439 |
settings['proxyport'] = row[12] |
| 440 |
settings['ip'] = row[13] |
| 441 |
settings['pdns'] = row[14] |
| 442 |
settings['sdns'] = row[15] |
| 443 |
settings['mmsc'] = row[17] |
| 444 |
return settings |
| 445 |
return None |
| 446 |
|
| 447 |
def get_apn_settings_automatically(self): |
| 448 |
(mcc, mnc) = self.get_mcc_mnc() |
| 449 |
operatorname = self.get_operator_display_name() |
| 450 |
settings = self.get_settings_from_file(mcc, mnc, operatorname) |
| 451 |
|
| 452 |
if self.are_we_tele2_se(mcc, mnc, operatorname): |
| 453 |
settings = self.are_we_tele2_se(mcc, mnc, operatorname) |
| 454 |
|
| 455 |
log.info("Settings loaded automatically. MCC: %s MNC: %s Operatorname: %s" % (mcc, mnc, operatorname)) |
| 456 |
log.info("Settings loaded automatically: %s" % settings) |
| 457 |
return settings |
| 458 |
|
| 459 |
def are_we_tele2_se(self, mcc, mnc, operatorname): |
| 460 |
mcc = str(int(mcc)) |
| 461 |
mnc = str(int(mnc)) |
| 462 |
if mcc == "240" and mnc == "7": |
| 463 |
if "Tele2" in operatorname: |
| 464 |
settings = {} |
| 465 |
settings['apn'] = "internet.tele2.se" |
| 466 |
settings['user'] = "" |
| 467 |
settings['pass'] = "" |
| 468 |
settings['proxy'] = "130.244.202.30" |
| 469 |
settings['proxyport'] = "8080" |
| 470 |
settings['mmsc'] = "http://mmsc.tele2.se" |
| 471 |
settings['pdns'] = "0.0.0.0" |
| 472 |
settings['sdns'] = "0.0.0.0" |
| 473 |
settings['ip'] = "0.0.0.0" |
| 474 |
return settings |
| 475 |
return False |
| 476 |
|
| 477 |
def reset_all_settings(self): |
| 478 |
self.config.reset_all_settings() |
| 479 |
|
| 480 |
|
| 481 |
class Locker: |
| 482 |
def __init__(self, fn): |
| 483 |
self.fn = fn |
| 484 |
self.fd = None |
| 485 |
self.pid = os.getpid() |
| 486 |
self.failcounter = 0 |
| 487 |
|
| 488 |
def lock(self): |
| 489 |
try: |
| 490 |
self.fd = os.open(self.fn, os.O_CREAT | os.O_EXCL | os.O_RDWR) |
| 491 |
os.write(self.fd, "%d" % self.pid) |
| 492 |
return 1 |
| 493 |
except OSError: |
| 494 |
# we failed to lock |
| 495 |
self.fd = None |
| 496 |
self.failcounter += 1 |
| 497 |
# after 5 unsuccessful locks we check the owner |
| 498 |
# is still alive |
| 499 |
if self.failcounter > 4: |
| 500 |
pid = open(self.fn, 'r').read() |
| 501 |
try: |
| 502 |
os.kill(int(pid), 0) |
| 503 |
except OSError, e: |
| 504 |
# no such process, remove the lock |
| 505 |
os.remove(self.fn) |
| 506 |
return 0 |
| 507 |
|
| 508 |
def unlock(self): |
| 509 |
if not self.fd: |
| 510 |
return 0 |
| 511 |
try: |
| 512 |
os.close(self.fd) |
| 513 |
os.remove(self.fn) |
| 514 |
return 1 |
| 515 |
except OSError: |
| 516 |
return 0 |
| 517 |
|
| 518 |
def __del__(self): |
| 519 |
# deconstructor, make sure lock is released |
| 520 |
self.unlock() |
| 521 |
|
| 522 |
if __name__ == '__main__': |
| 523 |
c = fMMS_controller() |