Package sabayon :: Package sources :: Module mozillasource
[hide private]
[frames] | no frames]

Source Code for Module sabayon.sources.mozillasource

   1  # 
   2  # Copyright (C) 2005 Red Hat, Inc. 
   3  # 
   4  # This program is free software; you can redistribute it and/or modify 
   5  # it under the terms of the GNU General Public License as published by 
   6  # the Free Software Foundation; either version 2 of the License, or 
   7  # (at your option) any later version. 
   8  # 
   9  # This program is distributed in the hope that it will be useful, 
  10  # but WITHOUT ANY WARRANTY; without even the implied warranty of 
  11  # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the 
  12  # GNU General Public License for more details. 
  13  # 
  14  # You should have received a copy of the GNU General Public License 
  15  # along with this program; if not, write to the Free Software 
  16  # Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. 
  17  # 
  18   
  19  import gobject 
  20  import exceptions, sys, os.path, ConfigParser, re, cPickle 
  21  import tempfile, types 
  22  import traceback 
  23  import errno 
  24  import time 
  25  import getpass 
  26  import subprocess 
  27   
  28  try: 
  29      import util 
  30      import config 
  31      import userprofile 
  32      import dirmonitor 
  33      import mozilla_bookmarks 
  34      import debuglog 
  35  except: 
  36      from sabayon import util 
  37      from sabayon import config 
  38      from sabayon import userprofile 
  39      from sabayon import dirmonitor 
  40      from sabayon import mozilla_bookmarks 
  41      from sabayon import debuglog 
  42   
43 -class file_state:
44 (UNKNOWN, 45 VALID, 46 NOT_FOUND, 47 SYS_ERROR, 48 PARSE_ERROR 49 ) = range(5)
50 51 52 firefox_rel_path = ".mozilla/firefox" 53 54 profiles_ini_rel_path = os.path.join(firefox_rel_path, "profiles.ini") 55 56 sabayon_pref_rel_path = os.path.join(firefox_rel_path, "sabayon-prefs.js") 57 sabayon_mandatory_pref_rel_path = os.path.join(firefox_rel_path, "sabayon-mandatory-prefs.js") 58 59 sabayon_bookmark_rel_path = os.path.join(firefox_rel_path, "sabayon-bookmarks.html") 60 sabayon_mandatory_bookmark_rel_path = os.path.join(firefox_rel_path, "sabayon-mandatory-bookmarks.html") 61 62 # FIXME: these should be defined one place; see mozilla_bookmarks.py 63 LOG_OPERATION = 0x00001 64 LOG_CHANGE = 0x00002 65 LOG_IGNORED_CHANGE = 0x00004 66 LOG_APPLY = 0x00008 67 LOG_SYNC = 0x00010 68 LOG_PARSE = 0x00020 69 LOG_PREF = 0x00040 70 LOG_FILE_CONTENTS = 0x00080 71 LOG_DATA = 0x00100 72 LOG_VERBOSE = 0x10000 73
74 -def dprint(mask, fmt, *args):
75 # FIXME: before debuglog was introduced, we could use the mask to filter 76 # which messages to log. Now we don't use it anymore. Is it still useful? 77 # If you change this, synchronize it with mozilla_bookmarks.py 78 debuglog.debug_log (False, debuglog.DEBUG_LOG_DOMAIN_MOZILLA_SOURCE, fmt % args)
79 80 bookmark_exclude_attrs = [re.compile("^id$"), 81 re.compile("^last_"), 82 re.compile("^add_"), 83 #re.compile("^icon$"), 84 ] 85
86 -def filter_attr(attr, exclude):
87 if not exclude: 88 return False 89 90 for regexp in exclude: 91 if regexp.search(attr): 92 return True 93 return False
94
95 -class MozillaChange(userprofile.ProfileChange):
96 ( 97 CREATED, 98 DELETED, 99 CHANGED 100 ) = range(3) 101
102 - def __init__ (self, source, delegate, type, key, value, event):
103 userprofile.ProfileChange.__init__ (self, source, delegate) 104 105 assert event == self.CREATED or \ 106 event == self.DELETED or \ 107 event == self.CHANGED 108 109 self.type = type 110 self.key = key 111 self.value = value 112 self.event = event 113 self.attrs = {}
114
115 - def set_attr(self, attr, value):
116 self.attrs[attr] = value
117
118 - def get_attr(self, attr):
119 return self.attrs[attr]
120
121 - def get_type(self):
122 return self.type
123
124 - def get_key(self):
125 return self.key
126
127 - def get_value(self):
128 return self.value
129
130 - def get_id(self):
131 return self.key
132
133 - def get_short_description(self):
134 if self.event == self.CREATED: 135 return _("Mozilla key '%s' set to '%s'") % (self.key, self.value) 136 elif self.event == self.DELETED: 137 return _("Mozilla key '%s' unset") % self.key 138 elif self.event == self.CHANGED: 139 return _("Mozilla key '%s' changed to '%s'") % (self.key, self.value) 140 else: 141 raise ValueError
142 143 gobject.type_register(MozillaChange) 144
145 -class MozillaDelegate(userprofile.SourceDelegate):
146 - def __init__ (self, source):
147 dprint(LOG_OPERATION, "Delegate construction") 148 userprofile.SourceDelegate.__init__ (self, "Firefox", source, ".mozilla") 149 self.source = source 150 self.delegate = self 151 self.home_dir = util.get_home_dir() 152 self.committed_prefs = {} 153 self.committed_mandatory_prefs = {} 154 self.committed_bookmarks = mozilla_bookmarks.BookmarkFolder("Null", None) 155 self.committed_mandatory_bookmarks = mozilla_bookmarks.BookmarkFolder("Null", None) 156 self.ini_file = None 157 self.SORTPRIORITY = 50
158
159 - def get_full_path(self, path):
160 return os.path.join(self.home_dir, path)
161
162 - def get_profiles_ini_path(self):
164
165 - def get_path_description (self, path):
166 type = get_type_from_path(path) 167 168 if type == FirefoxProfileFile.TYPE_PREFS: 169 return _("Web browser preferences") 170 elif type == FirefoxProfileFile.TYPE_BOOKMARK: 171 return _("Web browser bookmarks") 172 elif type == FirefoxProfileFile.TYPE_PROFILE_INI: 173 return _("Web browser profile list") 174 else: 175 # XXX 176 basename = os.path.basename(path) 177 178 if basename.endswith("prefs.js"): 179 return _("Web browser preferences") 180 elif basename.endswith("bookmarks.html"): 181 return _("Web browser bookmarks") 182 else: 183 return path
184
185 - def load_profiles_ini(self):
186 if not self.ini_file: 187 self.ini_file = FirefoxProfilesIni(self.home_dir, profiles_ini_rel_path) 188 self.ini_file.read()
189
190 - def load_profiles(self):
191 if self.ini_file: 192 self.ini_file.load_profiles()
193
194 - def is_ini_file(self, rel_path):
195 if rel_path == profiles_ini_rel_path: 196 return True 197 else: 198 return False
199
200 - def is_profile_file(self, rel_path):
201 if self.ini_file and self.ini_file.is_valid(): 202 rel_dir = os.path.dirname(rel_path) 203 for profile in self.ini_file.get_profiles(): 204 profile_rel_dir = profile.get_rel_dir() 205 if rel_dir.startswith(profile_rel_dir): 206 return profile 207 return None 208 else: 209 return None
210
211 - def handle_change(self, change):
212 rel_path = change.get_id() 213 dprint (LOG_CHANGE, "handle_change: rel_path='%s'", rel_path) 214 # 215 # INI File 216 # 217 if self.is_ini_file(rel_path): 218 dprint(LOG_CHANGE, "%s ini file: %s", dirmonitor.event_to_string(change.event), rel_path) 219 self.load_profiles_ini() 220 221 # 222 # Profile File 223 # 224 profile = self.is_profile_file(rel_path) 225 if profile: 226 dprint(LOG_CHANGE, "%s profile file: %s", dirmonitor.event_to_string(change.event), rel_path) 227 228 profile_file = profile.add_file(rel_path) 229 profile_file_type = profile_file.get_type() 230 231 if profile_file_type == FirefoxProfileFile.TYPE_PREFS: 232 assert isinstance(profile_file, JavascriptPrefsFile) 233 if change.event == dirmonitor.CREATED or \ 234 change.event == dirmonitor.CHANGED: 235 cat_file(profile_file.get_full_path()) 236 profile_file.read() 237 profile_file.emit_changes(self.source, self.delegate) 238 return True 239 elif change.event == dirmonitor.DELETED: 240 # XXX - what to do here? 241 pass 242 else: 243 raise ValueError 244 elif profile_file_type == FirefoxProfileFile.TYPE_BOOKMARK: 245 assert isinstance(profile_file, BookmarksFile) 246 if change.event == dirmonitor.CREATED or \ 247 change.event == dirmonitor.CHANGED: 248 profile_file.read() 249 profile_file.emit_changes(self.source, self.delegate) 250 return True 251 elif change.event == dirmonitor.DELETED: 252 # XXX - what to do here? 253 pass 254 else: 255 raise ValueError 256 elif profile_file_type != FirefoxProfileFile.TYPE_UNKNOWN: 257 if change.event == dirmonitor.CREATED or \ 258 change.event == dirmonitor.CHANGED: 259 profile.add_file(rel_path) 260 elif change.event == dirmonitor.DELETED: 261 profile.del_file(rel_path) 262 else: 263 raise ValueError 264 else: 265 return True 266 267 # 268 # Ignored Files 269 # 270 dprint(LOG_IGNORED_CHANGE, "IGNORED: %s", rel_path) 271 return True
272
273 - def commit_change(self, change, mandatory = False):
274 if isinstance(change, MozillaChange): 275 dprint(LOG_CHANGE, "Commiting preference (mandatory = %s) key = %s value = %s event = %s", 276 mandatory, change.key, change.value, change.event) 277 278 if mandatory: 279 self.committed_mandatory_prefs[change.get_key()] = \ 280 JavascriptPreference(change.get_type(), change.get_key(), change.get_value()) 281 else: 282 self.committed_prefs[change.get_key()] = \ 283 JavascriptPreference(change.get_type(), change.get_key(), change.get_value()) 284 elif isinstance(change, BookmarkChange): 285 bookmark_path = change.get_bookmark_path() 286 entry = change.get_entry() 287 288 dprint(LOG_CHANGE, "Commiting bookmark (mandatory = %s) path = %s url = %s event = %s", 289 mandatory, entry.path_as_string(), entry.get_url(), change.event) 290 291 if mandatory: 292 self.committed_mandatory_bookmarks.add_path_entry(entry.path(), entry) 293 else: 294 self.committed_bookmarks.add_path_entry(entry.path(), entry)
295 296
297 - def start_monitoring (self):
298 """Start monitoring for configuration changes.""" 299 # Read all files we are going to monitor so that when a change is 300 # reported we have a "before" state to compare to and thus derive 301 # what is different. 302 dprint(LOG_OPERATION, "start_monitoring:") 303 self.load_profiles_ini() 304 self.load_profiles()
305
306 - def stop_monitoring (self):
307 """Stop monitoring for configuration changes.""" 308 dprint(LOG_OPERATION, "stop_monitoring:")
309
310 - def sync_changes(self):
311 """Ensure that all committed changes are saved to disk.""" 312 313 dprint(LOG_SYNC, "sync_changes: home_dir = %s", self.home_dir) 314 ini = self.ini_file 315 if ini.is_valid(): 316 default_profile = ini.get_default_profile() 317 318 self.source.storage.add(ini.get_rel_path(), self.home_dir, self.name, 319 {"file_type" : FirefoxProfileFile.TYPE_PROFILE_INI}) 320 321 if len(self.committed_prefs) > 0: 322 pref_rel_path = sabayon_pref_rel_path 323 dprint(LOG_SYNC, "sync_changes: storing committed_prefs to %s", pref_rel_path) 324 pref_file = JavascriptPrefsFile(self.home_dir, pref_rel_path) 325 pref_file.set_prefs(self.committed_prefs) 326 pref_file.write() 327 self.source.storage.add(pref_rel_path, self.home_dir, self.name, 328 {"file_type" : FirefoxProfileFile.TYPE_PREFS, 329 "mandatory" : False}) 330 331 if len(self.committed_mandatory_prefs) > 0: 332 pref_rel_path = sabayon_mandatory_pref_rel_path 333 dprint(LOG_SYNC, "sync_changes: storing mandatory committed_prefs to %s", pref_rel_path) 334 pref_file = JavascriptPrefsFile(self.home_dir, pref_rel_path) 335 pref_file.set_prefs(self.committed_mandatory_prefs) 336 pref_file.write() 337 self.source.storage.add(pref_rel_path, self.home_dir, self.name, 338 {"file_type" : FirefoxProfileFile.TYPE_PREFS, 339 "mandatory" : True}) 340 341 if len(self.committed_bookmarks.entries) > 0: 342 bookmark_rel_path = sabayon_bookmark_rel_path 343 dprint(LOG_SYNC, "sync_changes: storing committed_bookmarks to %s", bookmark_rel_path) 344 bookmark_file = BookmarksFile(self.home_dir, bookmark_rel_path) 345 bookmark_file.set_root(self.committed_bookmarks) 346 bookmark_file.write(exclude_attrs=bookmark_exclude_attrs) 347 self.source.storage.add(bookmark_rel_path, self.home_dir, self.name, 348 {"file_type" : FirefoxProfileFile.TYPE_BOOKMARK, 349 "mandatory" : False}) 350 351 if len(self.committed_mandatory_bookmarks.entries) > 0: 352 bookmark_rel_path = sabayon_mandatory_bookmark_rel_path 353 dprint(LOG_SYNC, "sync_changes: storing mandatory committed_bookmarks to %s", bookmark_rel_path) 354 bookmark_file = BookmarksFile(self.home_dir, bookmark_rel_path) 355 bookmark_file.set_root(self.committed_mandatory_bookmarks) 356 bookmark_file.write(exclude_attrs=bookmark_exclude_attrs) 357 self.source.storage.add(bookmark_rel_path, self.home_dir, self.name, 358 {"file_type" : FirefoxProfileFile.TYPE_BOOKMARK, 359 "mandatory" : True})
360
361 - def set_enforce_mandatory (self, enforce):
362 # Nothing to do here 363 pass
364
365 - def apply(self, is_sabayon_session):
366 ini_files = [] 367 pref_files = [] 368 bookmark_files = [] 369 other_files = [] 370 371 dprint(LOG_APPLY, "apply: home_dir = %s", self.home_dir) 372 storage_contents = self.source.storage.list(self.name) 373 374 for source, path in storage_contents: 375 attributes = self.source.storage.get_attributes(path) 376 file_type = attributes.get("file_type", None) 377 if file_type != None: 378 file_type = int(file_type) 379 else: 380 file_type = get_type_from_path(path) 381 382 if file_type == FirefoxProfileFile.TYPE_PROFILE_INI: 383 ini_files.append(path) 384 elif file_type == FirefoxProfileFile.TYPE_PREFS: 385 pref_files.append(path) 386 elif file_type == FirefoxProfileFile.TYPE_BOOKMARK: 387 bookmark_files.append(path) 388 elif file_type == FirefoxProfileFile.TYPE_UNKNOWN: 389 other_files.append(path) 390 else: 391 raise ValueError 392 393 dprint(LOG_APPLY, "apply: ini_files=%s pref_files=%s bookmark_files=%s other_files=%s", 394 ini_files, pref_files, bookmark_files, other_files) 395 396 def which_firefox (): 397 path = os.environ['PATH'] 398 paths = path.split(os.pathsep) 399 for p in paths: 400 f = os.path.join (p, "firefox") 401 if os.path.exists (f): 402 return f 403 else: 404 return None
405 406 # Profiles.ini file must be done first, if the target does not 407 # exist then extract it from the profile. 408 # Parse the profiles.ini file to learn the target profiles 409 # If no profile exists in homedir or .zip, create new profile 410 self.load_profiles_ini() 411 if not self.ini_file.is_valid(): 412 dprint(LOG_APPLY, "apply: no valid ini file, extracting %s", profiles_ini_rel_path) 413 if profiles_ini_rel_path in ini_files: 414 self.source.storage.extract(profiles_ini_rel_path, self.home_dir, True) 415 self.load_profiles_ini() 416 else: 417 dprint(LOG_APPLY, "apply: but there isn't an ini file in the profile. Creating new profile.") 418 firefoxpath = which_firefox () 419 if firefoxpath: 420 subprocess.call ([firefoxpath, "-createProfile", getpass.getuser ()]) 421 self.load_profiles_ini() 422 # XXX: Temporary Workaround for Mozilla Bug #333479 423 # Delete bookmarks.html created by firefox -createProfile 424 for profile in self.ini_file.get_profiles(): 425 # There should only be one profile 426 profile_rel_dir = profile.get_rel_dir() 427 bogusbookmarks = os.path.join(profile_rel_dir, "bookmarks.html") 428 if os.path.exists (bogusbookmarks): 429 dprint (LOG_APPLY, "apply: Remove bogus file %s, see Mozilla Bug #333479.", bogusbookmarks) 430 os.remove (bogusbookmarks) 431 432 else: 433 dprint (APPLY, "apply: Firefox not found in PATH. Unable to create new profile.") 434 435 # -------------------- 436 if sabayon_pref_rel_path in pref_files: 437 dprint(LOG_APPLY, "extracting %s", sabayon_pref_rel_path) 438 self.source.storage.extract(sabayon_pref_rel_path, self.home_dir, True) 439 apply_pref = JavascriptPrefsFile(self.home_dir, sabayon_pref_rel_path) 440 apply_pref.read() 441 else: 442 apply_pref = None 443 444 if sabayon_mandatory_pref_rel_path in pref_files: 445 dprint(LOG_APPLY, "extracting %s", sabayon_mandatory_pref_rel_path) 446 self.source.storage.extract(sabayon_mandatory_pref_rel_path, self.home_dir, True) 447 mandatory_apply_pref = JavascriptPrefsFile(self.home_dir, sabayon_mandatory_pref_rel_path) 448 mandatory_apply_pref.read() 449 else: 450 mandatory_apply_pref = None 451 452 # -------------------- 453 454 if sabayon_bookmark_rel_path in bookmark_files: 455 dprint(LOG_APPLY, "extracting %s", sabayon_bookmark_rel_path) 456 self.source.storage.extract(sabayon_bookmark_rel_path, self.home_dir, True) 457 apply_bookmark = BookmarksFile(self.home_dir, sabayon_bookmark_rel_path) 458 apply_bookmark.read() 459 else: 460 apply_bookmark = None 461 462 if sabayon_mandatory_bookmark_rel_path in bookmark_files: 463 dprint(LOG_APPLY, "extracting %s", sabayon_mandatory_bookmark_rel_path) 464 self.source.storage.extract(sabayon_mandatory_bookmark_rel_path, self.home_dir, True) 465 mandatory_apply_bookmark = BookmarksFile(self.home_dir, sabayon_mandatory_bookmark_rel_path) 466 mandatory_apply_bookmark.read() 467 else: 468 mandatory_apply_bookmark = None 469 470 471 # -------------------- 472 473 # Now merge the javascript pref files 474 # XXX - iterate over all target profiles 475 for profile in self.ini_file.get_profiles(): 476 dprint(LOG_APPLY, "apply: applying to profile %s", profile.attrs) 477 profile_rel_dir = profile.get_rel_dir() 478 479 # -------------------- 480 target_pref_rel_path = os.path.join(profile_rel_dir, "prefs.js") 481 target_pref = JavascriptPrefsFile(self.home_dir, target_pref_rel_path) 482 target_pref.read() 483 484 if apply_pref: 485 mandatory = False 486 dprint(LOG_APPLY, "apply: applying src profile %s to target profile %s, mandatory=%s", 487 sabayon_pref_rel_path, target_pref_rel_path, mandatory) 488 target_pref.merge(apply_pref, mandatory) 489 490 if mandatory_apply_pref: 491 mandatory = True 492 dprint(LOG_APPLY, "apply: applying src profile %s to target profile %s, mandatory=%s", 493 sabayon_pref_rel_path, target_pref_rel_path, mandatory) 494 target_pref.merge(mandatory_apply_pref, mandatory) 495 496 if apply_pref or mandatory_apply_pref: 497 target_pref.write() 498 499 # -------------------- 500 target_bookmark_rel_path = os.path.join(profile_rel_dir, "bookmarks.html") 501 target_bookmark = BookmarksFile(self.home_dir, target_bookmark_rel_path) 502 target_bookmark.read() 503 504 if apply_bookmark: 505 mandatory = False 506 dprint(LOG_APPLY, "apply: applying src profile %s to target profile %s, mandatory=%s", 507 sabayon_bookmark_rel_path, target_bookmark_rel_path, mandatory) 508 target_bookmark.merge(apply_bookmark, mandatory) 509 510 if mandatory_apply_bookmark: 511 mandatory = True 512 dprint(LOG_APPLY, "apply: applying src profile %s to target profile %s, mandatory=%s", 513 sabayon_bookmark_rel_path, target_bookmark_rel_path, mandatory) 514 target_bookmark.merge(mandatory_apply_bookmark, mandatory) 515 516 if apply_bookmark or mandatory_apply_bookmark: 517 target_bookmark.write() 518 519 # Finally extract any other file 520 for path in other_files: 521 attributes = self.source.storage.get_attributes(path) 522 mandatory = attributes.get("mandatory", False) 523 dprint(LOG_APPLY, "apply: extracting other file %s, mandatory=%s", path, mandatory) 524 self.source.storage.extract(path, self.home_dir, mandatory) 525 526 dprint(LOG_APPLY, "apply: finished")
527
528 -def get_files_delegate(source):
529 return MozillaDelegate(source)
530 531 #----------------------------------------------------------------------------- 532 533 #----------------------------------------------------------------------------- 534 535 # ------ Globals ------ 536 537 # XXX - Warning: this regular expression is not perfectly robust 538 # the 1st parameter is expected to be a double quoted string without 539 # commas in it nor escaped double quotes. The parsing of the 2nd parameter 540 # should be robust. For our expected input it should be fine. Really 541 # robust parsing would require tokeninzing the expression. 542 pref_re = re.compile("(pref|user_pref|lock_pref)\s*\(\s*\"([^,\"]+)\s*\"\s*,\s*(.+?)\)\s*;\s*$", re.MULTILINE) 543 544 # ------ Excpetions ------ 545
546 -class FileNotFoundError(Exception):
547 - def __init__(self, filename):
548 self.filename = filename
549 - def __str__(self):
550 return _("File Not Found (%s)") % self.filename
551
552 -class BadIniFileError(Exception):
553 - def __init__(self, problem):
554 self.problem = problem
555 - def __str__(self):
556 return self.problem
557 558 559 # ------ Class FirefoxProfileFile ------ 560
561 -class FirefoxProfileFile:
562 (TYPE_UNKNOWN, 563 TYPE_PROFILE_INI, 564 TYPE_PREFS, 565 TYPE_BOOKMARK 566 ) = range(4) 567
568 - def __init__(self, home_dir, rel_path):
569 self.home_dir = home_dir 570 self.rel_path = rel_path 571 self.attrs = {} 572 self.file_type = get_type_from_path(rel_path)
573
574 - def get_full_path(self):
575 return os.path.join(self.home_dir, self.rel_path)
576
577 - def get_rel_path(self):
578 return self.rel_path
579
580 - def type_to_string(self, type):
581 if type == FirefoxProfileFile.TYPE_UNKNOWN: 582 return "UNKNOWN" 583 if type == FirefoxProfileFile.TYPE_PROFILE_INI: 584 return "PROFILE_INI" 585 if type == FirefoxProfileFile.TYPE_PREFS: 586 return "PREFS" 587 if type == FirefoxProfileFile.TYPE_BOOKMARK: 588 return "BOOKMARK" 589 return "?"
590
591 - def get_type(self):
592 return self.file_type
593 594 # ------ Class JavascriptPreference ------ 595
596 -class JavascriptPreference:
597 - def __init__(self, type, key, value):
598 self.type = type 599 self.key = key 600 self.value = value
601
602 - def __eq__(self, other):
603 if self.type == other.type and \ 604 self.key == other.key and \ 605 self.value == other.value: 606 return True 607 else: 608 return False
609
610 - def get_type(self):
611 return self.type
612
613 - def get_key(self):
614 return self.key
615
616 - def get_value(self):
617 return self.value
618 619 # ------ Class JavascriptPrefsFile ------ 620
621 -class JavascriptPrefsFile(FirefoxProfileFile):
622 - def __init__(self, home_dir, rel_path):
623 dprint(LOG_OPERATION, "JavascriptPrefsFile: created (%s)", rel_path) 624 FirefoxProfileFile.__init__(self, home_dir, rel_path) 625 self.file_state = file_state.UNKNOWN 626 self.prefs = {} 627 self.prev_prefs = {}
628
629 - def get_file_state(self):
630 return self.file_state
631
632 - def write(self, full_path=None):
633 if not full_path: 634 full_path = self.get_full_path() 635 dir = os.path.dirname(full_path) 636 if not os.path.exists(dir): 637 os.makedirs(dir) 638 dprint(LOG_OPERATION, "JavascriptPrefsFile: writing file (%s)", full_path) 639 fd = open(full_path, "w") 640 fd.write(''' 641 # Mozilla User Preferences 642 643 /* 644 * Do not edit this file. 645 * Created by %s, version %s 646 */ 647 648 ''' % (config.PACKAGE, config.VERSION)) 649 650 keys = self.prefs.keys() 651 keys.sort() 652 for key in keys: 653 pref = self.prefs[key] 654 fd.write("%s(\"%s\", %s);\n" % 655 (pref.get_type(), pref.get_key(), pref.get_value())) 656 657 fd.close()
658 659
660 - def merge(self, src, mandatory):
661 for src_pref in src.prefs.values(): 662 src_key = src_pref.get_key() 663 if not self.prefs.has_key(src_key) or mandatory: 664 # XXX - should this just be a copy? 665 self.prefs[src_key] = JavascriptPreference(src_pref.get_type(), 666 src_pref.get_key(), 667 src_pref.get_value())
668 669
670 - def read(self, full_path = None):
671 self.prev_prefs = self.get_prefs() 672 673 if not full_path: 674 full_path = self.get_full_path() 675 dprint(LOG_OPERATION, "read profile prefs (%s)", self.get_full_path()) 676 self.file_state = file_state.UNKNOWN 677 try: 678 fd = open(full_path) 679 except IOError, e: 680 if e.errno == errno.ENOENT: 681 self.file_state = file_state.NOT_FOUND 682 return 683 else: 684 self.file_state = file_state.SYS_ERROR 685 raise 686 687 self.filebuf = fd.read() 688 fd.close() 689 self.file_state = file_state.VALID 690 691 self.kill_comments() 692 self.parse()
693 694
695 - def emit_changes(self, source, delegate):
696 cur_prefs = self.get_prefs() 697 698 dc = util.DictCompare(self.prev_prefs, cur_prefs) 699 dc.compare() 700 cs = dc.get_change_set('a', 'b') 701 702 _add = cs['add'] 703 _del = cs['del'] 704 _mod = cs['mod'] 705 706 def emit_changes(items, event): 707 for key, pref in items: 708 source.emit_change( 709 MozillaChange(source, delegate, 710 pref.get_type(), pref.get_key(), pref.get_value(), event))
711 712 emit_changes(_add.items(), MozillaChange.CREATED) 713 emit_changes(_del.items(), MozillaChange.DELETED) 714 emit_changes(_mod.items(), MozillaChange.CHANGED)
715 716 717
718 - def kill_comments(self):
719 def not_in_string (regexp): 720 double = '\\"(?:\\\\.|[^\"\\\\])*\\"' 721 single = "\\'(?:\\\\.|[^\"\\\\])*\\'" 722 return "(" + double + "|" + single + ")(?:" + regexp + ")"
723 def match (matchobj): 724 return matchobj.group(1) 725 726 727 slash_comment_re = re.compile(not_in_string ("//.*$"), re.MULTILINE) 728 hash_comment_re = re.compile(not_in_string ("#.*$"), re.MULTILINE) 729 c_comment_re = re.compile(not_in_string ("/\*.*?\*/"), re.MULTILINE | re.DOTALL) 730 731 self.filebuf = slash_comment_re.sub(match, self.filebuf) 732 self.filebuf = hash_comment_re.sub(match, self.filebuf) 733 self.filebuf = c_comment_re.sub(match, self.filebuf) 734
735 - def parse(self):
736 start = 0; 737 self.prefs = {} 738 739 while 1: 740 match = pref_re.search(self.filebuf, start) 741 if match: 742 type = match.group(1) 743 key = match.group(2) 744 value = match.group(3) 745 dprint(LOG_PARSE, "(%d:%d) key='%s' value='%s'", match.start(), match.end(), key, value) 746 self.prefs[key] = JavascriptPreference(type, key, value) 747 start = match.end() 748 else: 749 break
750
751 - def set_prefs(self, prefs):
752 self.prefs = prefs.copy()
753
754 - def get_prefs(self):
755 return self.prefs.copy()
756
757 - def dump_prefs(self):
758 keys = self.prefs.keys() 759 keys.sort() 760 for key in keys: 761 dprint(LOG_VERBOSE, "%s=%s", key, self.prefs[key])
762 763 764 # ------ Class FirefoxProfile ------ 765
766 -class FirefoxProfile:
767 - def __init__(self, section, home_dir, rel_dir):
768 self.section = section 769 self.home_dir = home_dir 770 self.rel_dir = rel_dir 771 self.attrs = {} 772 self.files = {}
773
774 - def set_attr(self, attr, value):
775 self.attrs[attr] = value
776
777 - def get_attr(self, attr):
778 return self.attrs[attr]
779
780 - def get_name(self):
781 return self.get_attr("name")
782
783 - def get_default(self):
784 return self.get_attr("default")
785
786 - def get_rel_dir(self):
787 return os.path.join(self.rel_dir, self.get_dir())
788
789 - def get_dir(self):
790 return self.get_attr("path")
791
792 - def add_file(self, rel_path):
793 object = self.files.get(rel_path, None) 794 if object: 795 return(object) 796 797 798 file_type = get_type_from_path(rel_path) 799 800 if file_type == FirefoxProfileFile.TYPE_PREFS: 801 object = JavascriptPrefsFile(self.home_dir, rel_path) 802 elif file_type == FirefoxProfileFile.TYPE_BOOKMARK: 803 object = BookmarksFile(self.home_dir, rel_path) 804 else: 805 object = FirefoxProfileFile(self.home_dir, rel_path) 806 self.files[rel_path] = object 807 return object
808
809 - def del_file(self, rel_path):
810 if rel_path in self.files: 811 del self.files[rel_path]
812
813 - def get_files_of_type(self, type):
814 return [ file 815 for file in self.files.values() if file.get_type() == type ]
816 817 818 # ------ Class FirefoxProfilesIni ------ 819
820 -class FirefoxProfilesIni:
821 - def __init__(self, home_dir, rel_path):
822 self.file_state = file_state.UNKNOWN 823 self.default_profile = None 824 self.profiles = {} 825 self.ini = ConfigParser.ConfigParser() 826 self.home_dir = home_dir 827 self.rel_path = rel_path 828 self.rel_dir = os.path.dirname(rel_path)
829
830 - def is_valid(self):
831 return self.file_state == file_state.VALID
832
833 - def get_full_path(self, path):
834 return os.path.join(self.home_dir, path)
835
836 - def get_rel_dir(self):
837 return self.rel_dir
838
839 - def get_rel_path(self):
840 return self.rel_path
841
842 - def get_file_state(self):
843 return self.file_state
844
845 - def load_profiles(self):
846 dprint(LOG_OPERATION, "FirefoxProfilesIni.load_profiles()") 847 for profile in self.get_profiles(): 848 profile_rel_dir = profile.get_rel_dir() 849 850 pref_rel_path = os.path.join(profile_rel_dir, "prefs.js") 851 dprint(LOG_OPERATION, "FirefoxProfilesIni.load_profiles() pref=%s", pref_rel_path) 852 profile_file = profile.add_file(pref_rel_path) 853 profile_file.read() 854 855 bookmark_rel_path = os.path.join(profile_rel_dir, "bookmarks.html") 856 dprint(LOG_OPERATION, "FirefoxProfilesIni.load_profiles() bookmark=%s", bookmark_rel_path) 857 profile_file = profile.add_file(bookmark_rel_path) 858 profile_file.read()
859 860 861
862 - def read(self):
863 dprint(LOG_OPERATION, "FirefoxProfilesIni.read() path = %s", self.get_full_path(self.rel_path)) 864 self.profiles = {} 865 866 try: 867 if self.ini.read(self.get_full_path(self.rel_path)): 868 self.file_state = file_state.VALID 869 else: 870 self.file_state = file_state.NOT_FOUND 871 except ConfigParser.ParsingError, e: 872 self.file_state = file_state.PARSE_ERROR 873 874 875 dprint(LOG_PARSE, "FirefoxProfilesIni: after read, state = %s", self.file_state) 876 if self.file_state != file_state.VALID: 877 self.default_profile = None 878 879 self.parse_sections()
880
881 - def parse_sections(self):
882 profile_re = re.compile("^Profile(\d+)$") 883 884 self.default_profile = None 885 self.profiles = {} 886 last_profile = None 887 888 for section in self.ini.sections(): 889 dprint(LOG_PARSE, "parse_sections() section=%s", section) 890 match = profile_re.match(section) 891 if match: 892 try: 893 default_profile = self.ini.get(section, "default") 894 except ConfigParser.NoOptionError: 895 default_profile = None 896 897 name = self.ini.get(section, "Name") 898 if name in self.profiles: 899 raise BadIniFileError(_("duplicate name(%(name)s) in section %(section)s") % 900 (name, section)) 901 profile = FirefoxProfile(section, self.home_dir, self.rel_dir) 902 self.profiles[name] = profile 903 for (key, value) in self.ini.items(section): 904 profile.set_attr(key, value) 905 906 if default_profile: 907 if self.default_profile: 908 raise BadIniFileError(_("redundant default in section %s") % 909 section) 910 self.default_profile = profile 911 912 last_profile = profile 913 914 if self.default_profile == None and len(self.profiles) == 1: 915 # If there's only one profile, its the default even if it 916 # doesn't have the Default=1 flag 917 # Note: by default Firefox's auto-generated profile doesn't 918 # have the Default= flag) 919 self.default_profile = last_profile 920 dprint(LOG_OPERATION, "defaulting profile to the only choice")
921 922
923 - def get_default_profile(self):
924 if not self.default_profile: 925 raise BadIniFileError(_("no default profile")) 926 return self.default_profile
927
928 - def get_profiles(self, as_rel_dir=False):
929 if as_rel_dir: 930 return [ profile.get_rel_dir() 931 for profile in self.profiles.values() ] 932 else: 933 return self.profiles.values()
934 935 # ------ Class BookmarkChange ------ 936
937 -class BookmarkChange(userprofile.ProfileChange):
938 ( 939 CREATED, 940 DELETED, 941 CHANGED 942 ) = range(3) 943
944 - def __init__ (self, source, delegate, entry, event):
945 userprofile.ProfileChange.__init__ (self, source, delegate) 946 947 assert event == self.CREATED or \ 948 event == self.DELETED or \ 949 event == self.CHANGED 950 951 self.entry = entry 952 self.event = event 953 self.attrs = {}
954
955 - def set_attr(self, attr, value):
956 self.attrs[attr] = value
957
958 - def get_attr(self, attr):
959 return self.attrs[attr]
960
961 - def get_bookmark_path(self):
962 return self.entry.path_as_string()
963
964 - def get_entry(self):
965 return self.entry
966
967 - def get_url(self):
968 return self.entry.get_url()
969
970 - def get_id(self):
971 return self.get_bookmark_path()
972
973 - def get_short_description(self):
974 url = self.get_url() 975 bookmark_path = self.get_bookmark_path() 976 977 if self.event == self.CREATED: 978 # XXX - don't test for url, use type 979 if url: 980 return _("Mozilla bookmark created '%s' -> '%s'") % (bookmark_path, url) 981 else: 982 return _("Mozilla bookmark folder created '%s'") % (bookmark_path) 983 elif self.event == self.DELETED: 984 if url: 985 return _("Mozilla bookmark deleted '%s'") % (bookmark_path) 986 else: 987 return _("Mozilla bookmark folder deleted '%s'") % (bookmark_path) 988 elif self.event == self.CHANGED: 989 if url: 990 return _("Mozilla bookmark changed '%s' '%s'") % (bookmark_path, url) 991 else: 992 return _("Mozilla bookmark folder changed '%s'") % (bookmark_path) 993 994 else: 995 raise ValueError
996 997 gobject.type_register(BookmarkChange) 998 999 # ------ Class BookmarksFile ------ 1000
1001 -class BookmarksFile(FirefoxProfileFile):
1002 - def __init__(self, home_dir, rel_path):
1003 # XXX: It says it is created, but if it doesn't exist it does not. 1004 # Rewrite this when we properly handle sqlite bookmarks. 1005 dprint(LOG_OPERATION, "BookmarksFile: created (%s)", rel_path) 1006 FirefoxProfileFile.__init__(self, home_dir, rel_path) 1007 self.parser = mozilla_bookmarks.BookmarkHTMLParser() 1008 self.root = mozilla_bookmarks.BookmarkFolder("Null", None) 1009 self.parser.set_root(self.root) 1010 self.prev_root = self.parser.get_root() 1011 self.file_state = file_state.UNKNOWN
1012
1013 - def get_full_path(self):
1014 return os.path.join(self.home_dir, self.rel_path)
1015
1016 - def get_rel_path(self):
1017 return self.rel_path
1018
1019 - def get_root(self):
1020 return self.root
1021
1022 - def set_root(self, root):
1023 self.root = root 1024 self.parser.set_root(self.root)
1025
1026 - def read(self):
1027 self.prev_root = self.parser.get_root() 1028 self.root = mozilla_bookmarks.BookmarkFolder("Null", None) 1029 self.parser.set_root(self.root) 1030 self.file_state = file_state.UNKNOWN 1031 full_path = self.get_full_path() 1032 dprint(LOG_OPERATION, "BookmarksFile: read (%s)", full_path) 1033 try: 1034 fd = open(full_path) 1035 except IOError, e: 1036 if e.errno == errno.ENOENT: 1037 self.file_state = file_state.NOT_FOUND 1038 return 1039 else: 1040 self.file_state = file_state.SYS_ERROR 1041 raise 1042 1043 self.file_state = file_state.VALID 1044 self.parser.feed(fd.read()) 1045 self.parser.close() 1046 self.root = self.parser.get_root()
1047
1048 - def write(self, full_path=None, exclude_attrs=None):
1049 if not full_path: 1050 full_path = self.get_full_path() 1051 dir = os.path.dirname(full_path) 1052 if not os.path.exists(dir): 1053 os.makedirs(dir) 1054 dprint(LOG_OPERATION, "MozillaBookmark: writing file (%s)", full_path) 1055 fd = open(full_path, "w") 1056 fd.write(''' 1057 <!DOCTYPE NETSCAPE-Bookmark-file-1> 1058 <!-- This is an automatically generated file. (Created by %s, version %s) 1059 It will be read and overwritten. 1060 DO NOT EDIT! --> 1061 <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=UTF-8"> 1062 <TITLE>Bookmarks</TITLE> 1063 <H1 LAST_MODIFIED="%.0f">Bookmarks</H1> 1064 1065 <DL><p> 1066 ''' % (config.PACKAGE, config.VERSION, time.time())) 1067 1068 def visit(entry, type, path, data): 1069 indent = " " 1070 level = len(path) 1071 1072 if type == mozilla_bookmarks.TYPE_FOLDER: 1073 fd.write("%s<DT><H3" % (indent*level)) 1074 for attr, value in entry.attrs.items(): 1075 if not filter_attr(attr, exclude_attrs): 1076 fd.write(" %s=\"%s\"" % (attr, value)) 1077 fd.write(">%s</H3>\n" % (entry.name)) 1078 fd.write("%s<DL><p>\n" % (indent*level)) 1079 elif type == mozilla_bookmarks.TYPE_BOOKMARK: 1080 fd.write("%s<DT><A" % (indent*level)) 1081 for attr, value in entry.attrs.items(): 1082 if not filter_attr(attr, exclude_attrs): 1083 fd.write(" %s=\"%s\"" % (attr, value)) 1084 fd.write(">%s</A>\n" % (entry.name)) 1085 elif type == mozilla_bookmarks.TYPE_FOLDER_END: 1086 fd.write("%s</DL><p>\n" % (indent*level)) 1087 else: 1088 raise ValueError
1089 1090 self.root.traverse(visit) 1091 fd.close()
1092
1093 - def convert_to_dict(self, root):
1094 result = {} 1095 1096 def visit(entry, type, path, data): 1097 bookmark_path = entry.path_as_string() 1098 result[bookmark_path] = entry
1099 1100 root.traverse(visit) 1101 return result 1102
1103 - def emit_changes(self, source, delegate):
1104 prev_dict = self.convert_to_dict(self.prev_root) 1105 cur_dict = self.convert_to_dict(self.root) 1106 1107 dc = util.DictCompare(prev_dict, cur_dict) 1108 dc.compare() 1109 cs = dc.get_change_set('a', 'b') 1110 1111 _add = cs['add'] 1112 _del = cs['del'] 1113 _mod = cs['mod'] 1114 1115 def emit_changes(items, event): 1116 for bookmark_path, entry in items: 1117 source.emit_change( 1118 BookmarkChange(source, delegate, entry, event))
1119 1120 emit_changes(_add.items(), BookmarkChange.CREATED) 1121 emit_changes(_del.items(), BookmarkChange.DELETED) 1122 emit_changes(_mod.items(), BookmarkChange.CHANGED) 1123 1124
1125 - def merge(self, src, mandatory):
1126 def visit(entry, type, path, data): 1127 if type == mozilla_bookmarks.TYPE_FOLDER_END: 1128 return 1129 dst_entry = self.root.lookup_path(path) 1130 if not dst_entry or mandatory: 1131 self.root.add_path_entry(path, entry)
1132 1133 src.root.traverse(visit) 1134 1135 1136 # ------ Utility Functions ------ 1137
1138 -def get_type_from_path(rel_path):
1139 basename = os.path.basename(rel_path) 1140 1141 if basename == "prefs.js": 1142 return FirefoxProfileFile.TYPE_PREFS 1143 elif basename == "bookmarks.html": 1144 return FirefoxProfileFile.TYPE_BOOKMARK 1145 elif basename == "profiles.ini": 1146 return FirefoxProfileFile.TYPE_PROFILE_INI 1147 else: 1148 return FirefoxProfileFile.TYPE_UNKNOWN
1149
1150 -def cat_file(path):
1151 if os.path.isfile(path): 1152 dprint(LOG_FILE_CONTENTS, "==== %s ====", path) 1153 for line in open(path): 1154 dprint(LOG_FILE_CONTENTS, line.rstrip()) 1155 else: 1156 dprint(LOG_FILE_CONTENTS, "WARNING, does not exist ==== %s ====", path)
1157 1158 1159 1160 #----------------------------------------------------------------------------- 1161 1162 # 1163 # Unit tests 1164 #
1165 -def run_unit_tests():
1166 test_prefs = {'foo':'"bar"', 'uno':'1'} 1167 1168 dprint(LOG_OPERATION, "In mozillaprofile tests")
1169