ignore groupchat invitations from contacts that are not in roster if ignore_unknown_c...
[gajim.git] / src / gajim.py
blob8ac395f77c49b326fe46130f417d2519813aff6d
1 # -*- coding:utf-8 -*-
2 ## src/gajim.py
3 ##
4 ## Copyright (C) 2003-2010 Yann Leboulanger <asterix AT lagaule.org>
5 ## Copyright (C) 2004-2005 Vincent Hanquez <tab AT snarc.org>
6 ## Copyright (C) 2005 Alex Podaras <bigpod AT gmail.com>
7 ## Norman Rasmussen <norman AT rasmussen.co.za>
8 ## Stéphan Kochen <stephan AT kochen.nl>
9 ## Copyright (C) 2005-2006 Dimitur Kirov <dkirov AT gmail.com>
10 ## Alex Mauer <hawke AT hawkesnest.net>
11 ## Copyright (C) 2005-2007 Travis Shirk <travis AT pobox.com>
12 ## Nikos Kouremenos <kourem AT gmail.com>
13 ## Copyright (C) 2006 Junglecow J <junglecow AT gmail.com>
14 ## Stefan Bethge <stefan AT lanpartei.de>
15 ## Copyright (C) 2006-2008 Jean-Marie Traissard <jim AT lapin.org>
16 ## Copyright (C) 2007 Lukas Petrovicky <lukas AT petrovicky.net>
17 ## James Newton <redshodan AT gmail.com>
18 ## Copyright (C) 2007-2008 Brendan Taylor <whateley AT gmail.com>
19 ## Julien Pivotto <roidelapluie AT gmail.com>
20 ## Stephan Erb <steve-e AT h3c.de>
21 ## Copyright (C) 2008 Jonathan Schleifer <js-gajim AT webkeks.org>
23 ## This file is part of Gajim.
25 ## Gajim is free software; you can redistribute it and/or modify
26 ## it under the terms of the GNU General Public License as published
27 ## by the Free Software Foundation; version 3 only.
29 ## Gajim is distributed in the hope that it will be useful,
30 ## but WITHOUT ANY WARRANTY; without even the implied warranty of
31 ## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32 ## GNU General Public License for more details.
34 ## You should have received a copy of the GNU General Public License
35 ## along with Gajim. If not, see <http://www.gnu.org/licenses/>.
38 import os
39 import sys
41 if os.name == 'nt':
42 import locale
43 import gettext
44 APP = 'gajim'
45 DIR = '../po'
46 lang, enc = locale.getdefaultlocale()
47 os.environ['LANG'] = lang
48 gettext.bindtextdomain(APP, DIR)
49 gettext.textdomain(APP)
50 gettext.install(APP, DIR, unicode=True)
52 locale.setlocale(locale.LC_ALL, '')
53 import ctypes
54 import ctypes.util
55 libintl_path = ctypes.util.find_library('intl')
56 if libintl_path == None:
57 local_intl = os.path.join('gtk', 'bin', 'intl.dll')
58 if os.path.exists(local_intl):
59 libintl_path = local_intl
60 if libintl_path == None:
61 raise ImportError('intl.dll library not found')
62 libintl = ctypes.cdll.LoadLibrary(libintl_path)
63 libintl.bindtextdomain(APP, DIR)
65 import warnings
67 if os.name == 'nt':
68 log_path = os.path.join(os.environ['APPDATA'], 'Gajim')
69 if not os.path.exists(log_path):
70 os.mkdir(log_path, 0700)
71 log_file = os.path.join(log_path, 'gajim.log')
72 fout = open(log_file, 'a')
73 sys.stdout = fout
74 sys.stderr = fout
76 warnings.filterwarnings(action='ignore')
78 if os.path.isdir('gtk'):
79 # Used to create windows installer with GTK included
80 paths = os.environ['PATH']
81 list_ = paths.split(';')
82 new_list = []
83 for p in list_:
84 if p.find('gtk') < 0 and p.find('GTK') < 0:
85 new_list.append(p)
86 new_list.insert(0, 'gtk/lib')
87 new_list.insert(0, 'gtk/bin')
88 os.environ['PATH'] = ';'.join(new_list)
89 os.environ['GTK_BASEPATH'] = 'gtk'
91 if os.name == 'nt':
92 # needed for docutils
93 sys.path.append('.')
95 from common import logging_helpers
96 logging_helpers.init('TERM' in os.environ)
98 import logging
99 # gajim.gui or gajim.gtk more appropriate ?
100 log = logging.getLogger('gajim.gajim')
102 import getopt
103 from common import i18n
105 def parseOpts():
106 profile_ = ''
107 config_path_ = None
109 try:
110 shortargs = 'hqvl:p:c:'
111 longargs = 'help quiet verbose loglevel= profile= config_path='
112 opts = getopt.getopt(sys.argv[1:], shortargs, longargs.split())[0]
113 except getopt.error, msg1:
114 print msg1
115 print 'for help use --help'
116 sys.exit(2)
117 for o, a in opts:
118 if o in ('-h', '--help'):
119 print 'gajim [--help] [--quiet] [--verbose] ' + \
120 '[--loglevel subsystem=level[,subsystem=level[...]]] ' + \
121 '[--profile name] [--config-path]'
122 sys.exit()
123 elif o in ('-q', '--quiet'):
124 logging_helpers.set_quiet()
125 elif o in ('-v', '--verbose'):
126 logging_helpers.set_verbose()
127 elif o in ('-p', '--profile'): # gajim --profile name
128 profile_ = a
129 elif o in ('-l', '--loglevel'):
130 logging_helpers.set_loglevels(a)
131 elif o in ('-c', '--config-path'):
132 config_path_ = a
133 return profile_, config_path_
135 profile, config_path = parseOpts()
136 del parseOpts
138 import locale
139 profile = unicode(profile, locale.getpreferredencoding())
141 import common.configpaths
142 common.configpaths.gajimpaths.init(config_path)
143 del config_path
144 common.configpaths.gajimpaths.init_profile(profile)
145 del profile
147 if os.name == 'nt':
148 class MyStderr(object):
149 _file = None
150 _error = None
151 def write(self, text):
152 fname = os.path.join(common.configpaths.gajimpaths.cache_root,
153 os.path.split(sys.executable)[1]+'.log')
154 if self._file is None and self._error is None:
155 try:
156 self._file = open(fname, 'a')
157 except Exception, details:
158 self._error = details
159 if self._file is not None:
160 self._file.write(text)
161 self._file.flush()
162 def flush(self):
163 if self._file is not None:
164 self._file.flush()
166 sys.stderr = MyStderr()
168 # PyGTK2.10+ only throws a warning
169 warnings.filterwarnings('error', module='gtk')
170 try:
171 import gobject
172 import gtk
173 except Warning, msg2:
174 if str(msg2) == 'could not open display':
175 print >> sys.stderr, _('Gajim needs X server to run. Quiting...')
176 else:
177 print >> sys.stderr, _('importing PyGTK failed: %s') % str(msg2)
178 sys.exit()
179 warnings.resetwarnings()
181 gobject.set_prgname('gajim')
183 if os.name == 'nt':
184 warnings.filterwarnings(action='ignore')
186 pritext = ''
188 from common import exceptions
189 try:
190 from common import gajim
191 except exceptions.DatabaseMalformed:
192 pritext = _('Database Error')
193 sectext = _('The database file (%s) cannot be read. Try to repair it (see '
194 'http://trac.gajim.org/wiki/DatabaseBackup) or remove it (all history '
195 'will be lost).') % common.logger.LOG_DB_PATH
196 else:
197 from common import dbus_support
198 if dbus_support.supported:
199 from music_track_listener import MusicTrackListener
201 from ctypes import CDLL
202 from ctypes.util import find_library
203 import platform
205 sysname = platform.system()
206 if sysname in ('Linux', 'FreeBSD', 'OpenBSD', 'NetBSD'):
207 libc = CDLL(find_library('c'))
209 # The constant defined in <linux/prctl.h> which is used to set the name
210 # of the process.
211 PR_SET_NAME = 15
213 if sysname == 'Linux':
214 libc.prctl(PR_SET_NAME, 'gajim')
215 elif sysname in ('FreeBSD', 'OpenBSD', 'NetBSD'):
216 libc.setproctitle('gajim')
218 if gtk.pygtk_version < (2, 16, 0):
219 pritext = _('Gajim needs PyGTK 2.16 or above')
220 sectext = _('Gajim needs PyGTK 2.16 or above to run. Quiting...')
221 elif gtk.gtk_version < (2, 16, 0):
222 pritext = _('Gajim needs GTK 2.16 or above')
223 sectext = _('Gajim needs GTK 2.16 or above to run. Quiting...')
225 from common import check_paths
227 if os.name == 'nt':
228 try:
229 import winsound # windows-only built-in module for playing wav
230 import win32api # do NOT remove. we req this module
231 except Exception:
232 pritext = _('Gajim needs pywin32 to run')
233 sectext = _('Please make sure that Pywin32 is installed on your '
234 'system. You can get it at %s') % \
235 'http://sourceforge.net/project/showfiles.php?group_id=78018'
237 if pritext:
238 dlg = gtk.MessageDialog(None,
239 gtk.DIALOG_DESTROY_WITH_PARENT | gtk.DIALOG_MODAL,
240 gtk.MESSAGE_ERROR, gtk.BUTTONS_OK, message_format = pritext)
242 dlg.format_secondary_text(sectext)
243 dlg.run()
244 dlg.destroy()
245 sys.exit()
247 del pritext
249 import gtkexcepthook
251 import signal
252 import gtkgui_helpers
254 gajimpaths = common.configpaths.gajimpaths
256 pid_filename = gajimpaths['PID_FILE']
257 config_filename = gajimpaths['CONFIG_FILE']
259 import traceback
260 import errno
261 import dialogs
263 def pid_alive():
264 try:
265 pf = open(pid_filename)
266 except IOError:
267 # probably file not found
268 return False
270 try:
271 pid = int(pf.read().strip())
272 pf.close()
273 except Exception:
274 traceback.print_exc()
275 # PID file exists, but something happened trying to read PID
276 # Could be 0.10 style empty PID file, so assume Gajim is running
277 return True
279 if os.name == 'nt':
280 try:
281 from ctypes import (windll, c_ulong, c_int, Structure, c_char)
282 from ctypes import (POINTER, pointer, sizeof)
283 except Exception:
284 return True
286 class PROCESSENTRY32(Structure):
287 _fields_ = [
288 ('dwSize', c_ulong, ),
289 ('cntUsage', c_ulong, ),
290 ('th32ProcessID', c_ulong, ),
291 ('th32DefaultHeapID', c_ulong, ),
292 ('th32ModuleID', c_ulong, ),
293 ('cntThreads', c_ulong, ),
294 ('th32ParentProcessID', c_ulong, ),
295 ('pcPriClassBase', c_ulong, ),
296 ('dwFlags', c_ulong, ),
297 ('szExeFile', c_char*512, ),
300 kernel = windll.kernel32
301 kernel.CreateToolhelp32Snapshot.argtypes = c_ulong, c_ulong,
302 kernel.CreateToolhelp32Snapshot.restype = c_int
303 kernel.Process32First.argtypes = c_int, POINTER(PROCESSENTRY32),
304 kernel.Process32First.restype = c_int
305 kernel.Process32Next.argtypes = c_int, POINTER(PROCESSENTRY32),
306 kernel.Process32Next.restype = c_int
308 def get_p(pid_):
309 TH32CS_SNAPPROCESS = 2
310 CreateToolhelp32Snapshot = kernel.CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0)
311 assert CreateToolhelp32Snapshot > 0, 'CreateToolhelp32Snapshot failed'
312 pe32 = PROCESSENTRY32()
313 pe32.dwSize = sizeof( PROCESSENTRY32 )
314 f3 = kernel.Process32First(CreateToolhelp32Snapshot, pointer(pe32))
315 while f3:
316 if pe32.th32ProcessID == pid_:
317 return pe32.szExeFile
318 f3 = kernel.Process32Next(CreateToolhelp32Snapshot, pointer(pe32))
320 if get_p(pid) in ('python.exe', 'gajim.exe'):
321 return True
322 return False
323 try:
324 if not os.path.exists('/proc'):
325 return True # no /proc, assume Gajim is running
327 try:
328 f1 = open('/proc/%d/cmdline'% pid)
329 except IOError, e1:
330 if e1.errno == errno.ENOENT:
331 return False # file/pid does not exist
332 raise
334 n = f1.read().lower()
335 f1.close()
336 if n.find('gajim') < 0:
337 return False
338 return True # Running Gajim found at pid
339 except Exception:
340 traceback.print_exc()
342 # If we are here, pidfile exists, but some unexpected error occured.
343 # Assume Gajim is running.
344 return True
346 if pid_alive():
347 pix = gtkgui_helpers.get_icon_pixmap('gajim', 48)
348 gtk.window_set_default_icon(pix) # set the icon to all newly opened wind
349 pritext = _('Gajim is already running')
350 sectext = _('Another instance of Gajim seems to be running\nRun anyway?')
351 dialog = dialogs.YesNoDialog(pritext, sectext)
352 dialog.popup()
353 if dialog.run() != gtk.RESPONSE_YES:
354 sys.exit(3)
355 dialog.destroy()
356 # run anyway, delete pid and useless global vars
357 if os.path.exists(pid_filename):
358 os.remove(pid_filename)
359 del pix
360 del pritext
361 del sectext
362 dialog.destroy()
364 # Create .gajim dir
365 pid_dir = os.path.dirname(pid_filename)
366 if not os.path.exists(pid_dir):
367 check_paths.create_path(pid_dir)
368 # Create pid file
369 try:
370 f2 = open(pid_filename, 'w')
371 f2.write(str(os.getpid()))
372 f2.close()
373 except IOError, e2:
374 dlg = dialogs.ErrorDialog(_('Disk Write Error'), str(e2))
375 dlg.run()
376 dlg.destroy()
377 sys.exit()
378 del pid_dir
380 def on_exit():
381 # delete pid file on normal exit
382 if os.path.exists(pid_filename):
383 os.remove(pid_filename)
384 # Shutdown GUI and save config
385 if hasattr(gajim.interface, 'roster') and gajim.interface.roster:
386 gajim.interface.roster.prepare_quit()
388 import atexit
389 atexit.register(on_exit)
391 from gui_interface import Interface
393 if __name__ == '__main__':
394 def sigint_cb(num, stack):
395 sys.exit(5)
396 # ^C exits the application normally to delete pid file
397 signal.signal(signal.SIGINT, sigint_cb)
399 log.info("Encodings: d:%s, fs:%s, p:%s", sys.getdefaultencoding(), \
400 sys.getfilesystemencoding(), locale.getpreferredencoding())
402 if os.name != 'nt':
403 # Session Management support
404 try:
405 import gnome.ui
406 raise ImportError
407 except ImportError:
408 pass
409 else:
410 def die_cb(dummy):
411 gajim.interface.roster.quit_gtkgui_interface()
412 gnome.program_init('gajim', gajim.version)
413 cli = gnome.ui.master_client()
414 cli.connect('die', die_cb)
416 path_to_gajim_script = gtkgui_helpers.get_abspath_for_script(
417 'gajim')
419 if path_to_gajim_script:
420 argv = [path_to_gajim_script]
421 try:
422 cli.set_restart_command(argv)
423 except TypeError:
424 # Fedora systems have a broken gnome-python wrapper for this
425 # function.
426 cli.set_restart_command(len(argv), argv)
428 check_paths.check_and_possibly_create_paths()
430 interface = Interface()
431 interface.run()
433 try:
434 if os.name != 'nt':
435 # This makes Gajim unusable under windows, and threads are used only
436 # for GPG, so not under windows
437 gtk.gdk.threads_init()
438 gtk.main()
439 except KeyboardInterrupt:
440 print >> sys.stderr, 'KeyboardInterrupt'