Separated more client classes.
[cnetworkmanager.git] / cnetworkmanager
blob6428b1cb94c05423ae06974407d41c09e951f988
1 #! /usr/bin/python
2 # cnetworkmanager: Command Line Interface for NetworkManager
3 # by: http://en.opensuse.org/User:Mvidner
4 # license: http://www.gnu.org/licenses/gpl-2.0.html or later
6 VERSION = "0.8.4"
7 print "cnetworkmanager %s - Command Line Interface for NetworkManager" % VERSION
9 norpm = False
10 import sys
11 # find other modules in our prefix, if specified
12 if len(sys.argv) > 2 and sys.argv[1] == "--prefix":
13 prefix = sys.argv[2]
14 sys.argv[1:] = sys.argv[3:]
15 sys.path.append(prefix + "/share/cnetworkmanager");
17 import os
18 import string
19 import re
20 import time
21 from optparse import OptionParser
22 try:
23 import dbus
24 import dbus.service
25 import _dbus_bindings
26 except:
27 print "Install python-1-dbus.rpm or or python-dbus.rpm or python-dbus.deb"
28 norpm = True
29 try:
30 import gobject
31 except:
32 # todo - only if loop wanted
33 print "Install python-gobject2.rpm or pygobject2.rpm or python-gobject.deb"
34 norpm = True
35 # python-gnome.rpm has gconf for nm-applet...
36 if norpm:
37 sys.exit(1)
39 from dbus.mainloop.glib import DBusGMainLoop
40 DBusGMainLoop(set_as_default=True)
42 # private modules:
43 from monitor import MonitorBase
44 from configparser_knm import ConfigParserKNM
45 from mkconmap import *
46 from util import *
48 from object import *
49 from manager import *
50 from manager06 import cNM_06
51 from manager07 import cNM_07
52 from device import cDevice
53 from device06 import cDevice_06
54 from device07 import cDevice_07
55 from ap import cAP
56 from ap06 import cAP_06
57 from ap07 import cAP_07
58 from applet import cApplet, SSC, USC, NMIC
59 from applet06 import cApplet_06
61 LOOP = False
63 bus = dbus.SystemBus()
65 # FOOC = connection (service) string
66 # FOOI = interface string
67 # fooo = object
68 # fooi = interface
69 # foopi = property interface
70 def introspect(obj):
71 ii = dbus.Interface(obj, 'org.freedesktop.DBus.Introspectable')
72 print ii.Introspect()
74 def make_nm(opath):
75 "Detects NM version and chooses appropriate class"
77 nmo = bus.get_object(NMC, opath)
78 nmi = dbus.Interface(nmo, NMI)
79 try:
80 dummy = nmi.getDevices()
81 return cNM_06(opath, options)
82 except dbus.exceptions.DBusException, e:
83 if e.get_dbus_name() == 'org.freedesktop.DBus.Error.AccessDenied':
84 raise
85 return cNM_07(opath, options)
87 def opath_validchar(c):
88 # _ is also escaped even though it is valid
89 return \
90 string.ascii_letters.find(c) != -1 or \
91 string.digits.find(c) != -1
93 def opath_escape(s):
94 r = ""
95 for c in s:
96 # TODO find a more elegant way
97 if not opath_validchar(c):
98 # "-" -> "_2d_"
99 c = "_%2x_" % ord(c)
100 r = r + c
101 return r
103 def opath_unescape(s):
104 # "2d" -> "-"
105 unhex = lambda xx: chr(eval("0x"+xx))
106 # all "_2d_" -> "-"
107 return re.sub("_.._", lambda p: unhex(p.group()[1:3]), s)
109 def dump_time(unixtime):
110 return time.asctime(time.localtime(unixtime))
112 # server analog of cApplet
113 class UserSettings(dbus.service.Object):
114 # conmaps is a list
115 def __init__(self, opath, conmaps):
116 dbus.service.Object.__init__(self, bus, opath)
117 #print "CONMAPS:", conmaps
118 self.conns = map(self.newCon, conmaps)
120 def addCon(self, conmap):
121 c = self.newCon(conmap)
122 self.conns.append(c)
123 return c
125 counter = 1
126 def newCon(self, conmap):
127 cpath = "/MyConnection/%d" % self.counter
128 self.counter = self.counter + 1
129 c = Connection(cpath, conmap)
130 self.NewConnection(cpath) # announce it
131 return c
133 @dbus.service.method(dbus_interface='org.freedesktop.NetworkManagerSettings',
134 in_signature='', out_signature='ao')
135 def ListConnections(self):
136 return [c.__dbus_object_path__ for c in self.conns]
138 #this is for EMITTING a signal, not receiving it
139 @dbus.service.signal(dbus_interface='org.freedesktop.NetworkManagerSettings',
140 signature='o')
141 def NewConnection(self, opath):
142 pass
143 #print "signalling newconn:", opath
145 def GetByNet(self, net_name):
146 "Returns connection, or None"
147 for c in self.conns:
148 if c.isNet(net_name):
149 return c
150 return None
153 class UserSettings_06(UserSettings):
154 # conmaps is a list
155 def __init__(self, opath, conmaps):
156 dbus.service.Object.__init__(self, bus, opath)
157 #print "CONMAPS:", conmaps
158 self.conns = map(self.newCon, conmaps)
160 counter = 1
161 def newCon(self, conmap):
162 cpath = "/MyConnection/%d" % self.counter
163 self.counter = self.counter + 1
164 c = Connection_06(cpath, conmap)
165 #self.NewConnection(cpath) # announce it
166 return c
168 @dbus.service.method(dbus_interface="org.freedesktop.NetworkManagerInfo",
169 in_signature="i", out_signature='as')
170 def getNetworks(self, i):
171 # FIXME bytearray to str WHERE?
172 #n = [ssid_str(c.Ssid()) for c in self.conns]
173 n = [c.ID() for c in self.conns]
174 print "getNetworks:", n
175 return n
177 @dbus.service.method(dbus_interface="org.freedesktop.NetworkManagerInfo",
178 in_signature="", out_signature='ao') # out??
179 def getVPNConnections(self):
180 return [] # FIXME
182 @dbus.service.method(dbus_interface="org.freedesktop.NetworkManagerInfo",
183 in_signature="si")
184 #out_signature='sibasi') #varies
185 def getNetworkProperties(self, net, type):
186 print "GNP", net
187 # type is 1, NETWORK_TYPE_ALLOWED
188 c = self.GetByNet(net)
189 if c != None:
190 return c.getNetworkProperties()
191 print "Oops, could not getNetworkProperties for " + net
194 @dbus.service.method(dbus_interface="org.freedesktop.NetworkManagerInfo",
195 in_signature="oosib")
196 #out_signature="isi") varies
197 def getKeyForNetwork(self, dev, net, ssid, attempt, newkey):
198 print "GKFN", dev, net, ssid, attempt, bool(newkey)
199 if newkey:
200 m = "Cannot ask for key"
201 print m
202 raise dbus.exceptions.DBusException(m)
204 snet = opath_unescape(net[net.rfind("/")+1 : ]) # only stuff after /
205 c = self.GetByNet(snet)
206 if c != None:
207 return c.getKeyForNetwork()
208 print "Oops, could not getKeyForNetwork " + net
210 @dbus.service.method(dbus_interface="org.freedesktop.NetworkManagerInfo",
211 out_signature='')
212 #in_signature="sbs isi", varies
213 def updateNetworkInfo(self, ssid, automatic, bssid, *security):
214 print "Connected successfully"
215 return
216 print "UNI"
217 print " ssid:", ssid
218 print " automatic:", bool(automatic)
219 print " bssid:", bssid
220 print " security:", security
223 def GetByNet(self, net_name):
224 "Returns connection, or None"
225 for c in self.conns:
226 if c.isNet(net_name):
227 return c
228 return None
231 # server analog of cConnection
232 class Connection(dbus.service.Object):
233 def __init__(self, opath, conmap):
234 dbus.service.Object.__init__(self, bus, opath)
235 self.settings = cSettings(conmap)
237 @dbus.service.method(dbus_interface='org.freedesktop.NetworkManagerSettings.Connection',
238 sender_keyword='sender',
239 in_signature='', out_signature='a{sa{sv}}')
240 def GetSettings(self, sender):
241 #print "Getting settings:", self. __dbus_object_path__
242 # return self.settings.ConMap()
243 # grr, censoring secrets makes NM complain!?
244 # bnc#479566#c3: Until I figure out how to make it work with
245 # censored secrets, only pass the settings to the same user.
246 sender_uid = bus.get_unix_user(sender)
247 if sender_uid != 0 and sender_uid != os.geteuid():
248 e = "User %u is not permitted to read the settings" % sender_uid
249 print e
250 raise dbus.exceptions.DBusException(e) # could do NM_SETTINGS_ERROR_* instead
251 return self.settings.conmap
253 @dbus.service.method(dbus_interface='org.freedesktop.NetworkManagerSettings.Connection.Secrets',
254 in_signature='sasb', out_signature='a{sa{sv}}')
255 def GetSecrets(self, tag, hints, ask):
256 # FIXME respect args
257 print "Getting secrets:", self.__dbus_object_path__
258 return self.settings.SecMap()
260 @dbus.service.method(dbus_interface='org.freedesktop.NetworkManagerSettings.Connection',
261 in_signature='', out_signature='s')
262 def ID(self):
263 return self.settings.ID()
265 def Ssid(self):
266 return self.settings.Ssid()
268 def isNet(self, net_name):
269 return self.settings.isNet(net_name)
271 class Connection_06(Connection):
272 def __init__(self, opath, conmap):
273 dbus.service.Object.__init__(self, bus, opath)
274 #print "C6", conmap
275 self.settings = cSettings(conmap)
277 # dbus.service.method
278 def getNetworkProperties(self):
279 # essid, timestamp, ?, bssids, we_cipher, ?, ...
280 # we_cipher=16: i wep_auth_algorithm
281 # we_cipher=0: i wpa_psk_key_mgt, i wpa_psk_wpa_version
282 ssid = ssid_str(self.settings.Ssid())
283 time = self.settings.Timestamp() # last sucessfully connected? seen?
284 trusted = self.settings.Trusted()
285 bssids = dbus.Array(self.settings.SeenBssids(), signature="s")
286 r = [ssid, time, trusted, bssids]
287 security = self.getKeyForNetwork("fake key")
288 r.extend(security)
289 return tuple(r)
291 # dbus.service.method
292 def getKeyForNetwork(self, fake="no"):
293 if fake == "no":
294 key = self.settings.Key()
295 else:
296 key = ""
298 # security
299 cip = self.settings.WeCipher()
300 if cip == NM_AUTH_TYPE_NONE:
301 security = tuple([cip])
302 elif cip == NM_AUTH_TYPE_WEP40 or cip == NM_AUTH_TYPE_WEP104:
303 wep_auth_algorithm = self.settings.WepAuthAlgorithm()
304 security = (cip, key, wep_auth_algorithm)
305 elif cip == NM_AUTH_TYPE_WPA_PSK_AUTO or cip == NM_AUTH_TYPE_TKIP or \
306 cip == NM_AUTH_TYPE_CCMP:
307 wpa_psk_key_mgt = self.settings.PskKeyMgt()
308 wpa_psk_wpa_version = self.settings.PskWpaVersion()
309 security = (cip, key, wpa_psk_key_mgt, wpa_psk_wpa_version)
310 elif cip == NM_AUTH_TYPE_WPA_EAP:
311 security = tuple([cip]) # TODO more...
312 elif cip == NM_AUTH_TYPE_LEAP:
313 security = tuple([cip]) # TODO more...
314 return security
318 class Monitor(MonitorBase):
319 def __init__(self, bus):
320 MonitorBase.__init__(self, bus)
322 self.watch(
323 self.propc_h,
324 dbus_interface="org.freedesktop.NetworkManager.Device.Wireless",
325 signal_name="PropertiesChanged")
326 self.watch(
327 self.propc_h,
328 dbus_interface="org.freedesktop.NetworkManager.AccessPoint",
329 signal_name="PropertiesChanged")
331 self.ignore("org.freedesktop.Hal.Device", "PropertyModified")
332 self.ignore("fi.epitest.hostap.WPASupplicant.Interface", "ScanResultsAvailable")
333 self.ignore("com.redhat.PrinterSpooler", "QueueChanged")
334 self.ignore("org.freedesktop.NetworkManager", "StateChange") # deprecated
335 self.watch(self.nm_sc_h, "org.freedesktop.NetworkManager", "StateChanged")
336 self.watch(self.wpas_isc_h, "fi.epitest.hostap.WPASupplicant.Interface", "StateChange")
337 self.watch(self.nmd_sc_h, "org.freedesktop.NetworkManager.Device", "StateChanged")
338 self.watch(self.bus_noc_h, "org.freedesktop.DBus", "NameOwnerChanged")
340 def bus_noc_h(self, *args, **kwargs):
341 (name, old, new) = args
342 if new == "":
343 new = "gone"
344 else:
345 new = "at " + new
346 print "\tBUS NOC\t%s %s" % (name, new)
348 def wpas_isc_h(self, *args, **kwargs):
349 opath = kwargs["path"]
350 (new, old) = args
351 print "\tWPAS %s\t(%s, was %s)" % (new, opath, old.lower())
353 def nmd_sc_h(self, *args, **kwargs):
354 opath = kwargs["path"]
355 (new, old, reason) = args
356 news = cDevice_07.NM_DEVICE_STATE[new]
357 olds = cDevice_07.NM_DEVICE_STATE[old]
358 reasons = ""
359 if reason != 0:
360 reasons = "reason %d" % reason
361 print "\tDevice State %s\t(%s, was %s%s)" % (news, opath, olds.lower(), reasons)
363 def nm_sc_h(self, *args, **kwargs):
364 s = args[0]
365 ss = cNM.NM_STATE[s]
366 print "\tNM State:", ss
368 def propc_h(self, *args, **kwargs):
369 opath = kwargs["path"]
370 props = args[0]
371 for k, v in props.iteritems():
372 if k == "Strength":
373 v = "%u" % v
374 line = "\tPROP\t%s\t%s\t(%s)" % (k, v, opath)
375 print line
377 # main
379 fail = False
381 op = OptionParser(version="%prog " + VERSION)
382 op.add_option("-d", "--dev",
383 action="store_true", default=False,
384 help="list devices")
385 op.add_option("-c", "--actcon",
386 action="store_true", default=False,
387 help="list active connections")
388 op.add_option("-u", "--usrcon",
389 action="store_true", default=False,
390 help="list user connection settings (can CRASH nm-applet)")
391 op.add_option("-s", "--syscon",
392 action="store_true", default=False,
393 help="list system connection settings")
394 op.add_option("-a", "--ap",
395 action="store_true", default=False,
396 help="list found access points")
397 op.add_option("-n", "--nets",
398 action="store_true", default=False,
399 help="list found wireless networks")
400 # TODO http://docs.python.org/lib/optparse-adding-new-types.html
401 op.add_option("-w", "--wifi",
402 choices=["0","1","off","on","no","yes","false","true"],
403 metavar="BOOL",
404 help="enable or disable wireless")
405 op.add_option("-o", "--online",
406 choices=["0","1","off","on","no","yes","false","true"],
407 metavar="BOOL",
408 help="enable or disable network at all")
410 op.add_option("--activate-connection",
411 help="raw API: activate the KIND(user/system) connection CON on device DEV using AP",
412 metavar="[KIND],CON,DEV,[AP]")
413 op.add_option("-C", "--connect",
414 help="connect to a wireless network NET (using knetworkmanagerrc or the key options below)",
415 metavar="NET")
416 op.add_option("--unprotected",
417 action="store_true", default=False,
418 help="network does not require a key")
419 op.add_option("--wep-hex",
420 metavar="KEY",
421 help="use this WEP key of 26 hex digits")
422 op.add_option("--wep-pass",
423 metavar="KEY",
424 help="use this WEP passphrase")
425 op.add_option("--wpa-psk-hex",
426 metavar="KEY",
427 help="use this WPA key of 64 hex digits")
428 op.add_option("--wpa-pass",
429 metavar="KEY",
430 help="use this WPA passphrase")
431 op.add_option("-m", "--monitor",
432 action="store_true", default=False,
433 help="loop to show dbus signals")
436 (options, args) = op.parse_args()
438 if options.ap:
439 options.dev = True
440 if options.monitor:
441 LOOP = True
444 nmp = '/org/freedesktop/NetworkManager'
445 try:
446 nm = make_nm(nmp)
447 except dbus.exceptions.DBusException, e:
448 print "NetworkManager is not running or running as an other user"
449 fail = True
450 if options.dev or options.actcon:
451 nm.Dump()
453 true_choices = ["1", "on", "yes", "true"]
454 if options.wifi != None:
455 nm.SetWifiEnabled(options.wifi in true_choices)
456 if options.online != None:
457 nm.SetOnline(options.online in true_choices)
459 if options.nets:
460 nm.ListNets()
462 if options.syscon:
463 print "SYSTEM Connections"
464 if nm.Api() == "06":
465 print "Cannot do that with NM 0.6"
466 fail = True
467 else:
468 ss = cApplet(SSC, '/org/freedesktop/NetworkManagerSettings')
469 ss.Dump()
471 if options.usrcon:
472 print "USER Connections"
473 try:
474 if nm.Api() == "06":
475 us = cApplet_06(NMIC, "/org/freedesktop/NetworkManagerInfo")
476 else:
477 us = cApplet(USC, '/org/freedesktop/NetworkManagerSettings')
478 us.Dump()
479 except dbus.exceptions.DBusException, e:
480 print e
481 #if e.get_dbus_name() == "org.freedesktop.DBus.Error.ServiceUnknown":
482 print "Applet is not running"
483 fail = True
485 nmo = bus.get_object(NMC, nmp)
486 nmi = dbus.Interface(nmo, NMI)
488 def service_pid(name):
489 DBS = 'org.freedesktop.DBus'
490 DBI = DBS
491 dbo = bus.get_object(DBS, '/')
492 dbi = dbus.Interface(dbo, DBI)
493 owner = dbi.GetNameOwner(name)
494 pid = dbi.GetConnectionUnixProcessID(owner)
495 return pid
497 # TODO UserSettings_06
498 if options.connect != None:
499 if nm.Api() == "06":
500 name = NMIC
501 else:
502 name = USC
503 brn = bus.request_name(name, _dbus_bindings.NAME_FLAG_DO_NOT_QUEUE)
504 if brn == _dbus_bindings.REQUEST_NAME_REPLY_EXISTS:
505 print "Could not provide settings service, another applet is running (pid %s)" % service_pid(name)
506 sys.exit(1)
507 cfg = ConfigParserKNM()
508 if nm.Api() == "06":
509 us = UserSettings_06("/org/freedesktop/NetworkManagerInfo",
510 cfg.ConMaps())
511 else:
512 us = UserSettings("/org/freedesktop/NetworkManagerSettings",
513 cfg.ConMaps())
515 def Connect(wanted_net): # any. or take arg. net is config name or ssid name
516 # ... in general, look for string in all config data. ssid for wifi, whatever for dialup
517 # TODO also respect autoconnect
519 # ActivateConn wants setting device ap; can find device from ap? ap is "specific" for wifi devices
520 #print "Connection wanted to", wanted_net
521 found_con = found_ap = found_dev = None
522 for dev in nm.Devices():
523 for ap in dev.APs():
524 if wanted_net == ap.Ssid():
525 found_ap = ap
526 found_dev = dev
527 break # FIXME both loops
528 found_con = us.GetByNet(wanted_net)
529 if found_ap == None:
530 print "No AP found with SSID", wanted_net
531 return False
532 if found_con == None:
533 print "No settings for net %s, assuming no key is needed" % wanted_net
534 c = mkconmap_wifi(wanted_net)
535 found_con = us.addCon(c)
536 nm.ActivateConnection(found_con, found_dev, found_ap) # TODO async
537 # TODO run loop, exit it when we have serviced the required calls
538 return True
540 if options.connect != None:
541 if options.unprotected:
542 c = mkconmap_wifi(options.connect)
543 us.addCon(c)
544 if options.wep_hex != None:
545 c = mkconmap_wep(options.connect, options.wep_hex)
546 us.addCon(c)
547 if options.wep_pass != None:
548 c = mkconmap_wep_pass(options.connect, options.wep_pass)
549 us.addCon(c)
550 if options.wpa_psk_hex != None:
551 c = mkconmap_psk(options.connect, options.wpa_psk_hex)
552 us.addCon(c)
553 if options.wpa_pass != None:
554 c = mkconmap_psk(options.connect, options.wpa_pass)
555 us.addCon(c)
556 nm.WatchState()
557 if Connect(options.connect):
558 LOOP = True
559 else:
560 fail = True
562 if options.activate_connection != None:
563 (svc, conpath, devpath, appath) = options.activate_connection.split(',')
564 if svc == "" or svc == "user":
565 svc = USC
566 elif svc == "system":
567 svc = SSC
569 if devpath == "":
570 TODO
571 if appath == "":
572 appath = "/"
573 nm.WatchState()
574 nm.nmi.ActivateConnection(svc, conpath, devpath, appath,
575 reply_handler=nm.silent_handler,
576 error_handler=nm.err_handler,
578 LOOP = True
580 if options.monitor:
581 m = Monitor(bus)
583 def loop():
584 loop = gobject.MainLoop()
585 try:
586 loop.run()
587 except:
588 print "Loop exited"
590 if LOOP:
591 loop()
593 if fail:
594 sys.exit(1)