source: skype/skyped.py @ ceee9c7

Last change on this file since ceee9c7 was 1fb0a30, checked in by Miklos Vajna <vmiklos@…>, at 2008-02-23T02:26:04Z

skyped: remove comment, we use skype4py already

  • Property mode set to 100644
File size: 7.2 KB
Line 
1#!/usr/bin/env python
2#
3#   skyped.py
4
5#   Copyright (c) 2007, 2008 by Miklos Vajna <vmiklos@frugalware.org>
6#
7#   This program is free software; you can redistribute it and/or modify
8#   it under the terms of the GNU General Public License as published by
9#   the Free Software Foundation; either version 2 of the License, or
10#   (at your option) any later version.
11#
12#   This program is distributed in the hope that it will be useful,
13#   but WITHOUT ANY WARRANTY; without even the implied warranty of
14#   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15#   GNU General Public License for more details.
16
17#   You should have received a copy of the GNU General Public License
18#   along with this program; if not, write to the Free Software
19#   Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
20#   USA.
21#
22
23import sys
24import os
25import signal
26import locale
27import time
28import gobject
29import socket
30import getopt
31import Skype4Py
32import sha
33from ConfigParser import ConfigParser
34from OpenSSL import SSL
35from traceback import print_exception
36
37__version__ = "0.1.1"
38
39SKYPE_SERVICE = 'com.Skype.API'
40CLIENT_NAME = 'SkypeApiPythonShell'
41
42def eh(type, value, tb):
43        if type != KeyboardInterrupt:
44                print_exception(type, value, tb)
45        gobject.MainLoop().quit()
46        skype.skype.Client.Shutdown()
47        sys.exit("Exiting.")
48
49sys.excepthook = eh
50
51def input_handler(fd, io_condition):
52        global options
53        if options.buf:
54                for i in options.buf:
55                        skype.send(i.strip())
56                options.buf = None
57        else:
58                input = fd.recv(1024)
59                for i in input.split("\n"):
60                        skype.send(i.strip())
61                return True
62
63def idle_handler(skype):
64        try:
65                c = skype.skype.Command("PING", Block=True)
66                skype.skype.SendCommand(c)
67        except Skype4Py.SkypeAPIError, s:
68                dprint("Warning, pinging Skype failed (%s)." % (s))
69                time.sleep(2)
70        return True
71
72def server(host, port):
73        global options
74
75        ctx = SSL.Context(SSL.TLSv1_METHOD)
76        ctx.use_privatekey_file(options.config.sslkey)
77        ctx.use_certificate_file(options.config.sslcert)
78        sock = SSL.Connection(ctx, socket.socket())
79        sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
80        sock.bind((host, port))
81        sock.listen(1)
82        gobject.io_add_watch(sock, gobject.IO_IN, listener)
83
84def listener(sock, *args):
85        global options
86        options.conn, addr = sock.accept()
87        ret = 0
88        line = options.conn.recv(1024)
89        if line.startswith("USERNAME") and line.split(' ')[1].strip() == options.config.username:
90                ret += 1
91        line = options.conn.recv(1024)
92        if line.startswith("PASSWORD") and sha.sha(line.split(' ')[1].strip()).hexdigest() == options.config.password:
93                ret += 1
94        if ret == 2:
95                dprint("Username and password OK.")
96                options.conn.send("PASSWORD OK\n")
97                gobject.io_add_watch(options.conn, gobject.IO_IN, input_handler)
98                return True
99        else:
100                dprint("Username and/or password WRONG.")
101                options.conn.send("PASSWORD KO\n")
102                return False
103
104def dprint(msg):
105        global options
106
107        if options.debug:
108                print msg
109
110class SkypeApi():
111        def __init__(self):
112                self.skype = Skype4Py.Skype()
113                self.skype.OnNotify = self.recv
114                self.skype.Client.Start()
115
116        def recv(self, msg_text):
117                global options
118                if msg_text == "PONG":
119                        return
120                if "\n" in msg_text:
121                        # crappy skype prefixes only the first line for
122                        # multiline messages so we need to do so for the other
123                        # lines, too. this is something like:
124                        # 'CHATMESSAGE id BODY first line\nsecond line' ->
125                        # 'CHATMESSAGE id BODY first line\nCHATMESSAGE id BODY second line'
126                        prefix = " ".join(msg_text.split(" ")[:3])
127                        msg_text = ["%s %s" % (prefix, i) for i in " ".join(msg_text.split(" ")[3:]).split("\n")]
128                else:
129                        msg_text = [msg_text]
130                for i in msg_text:
131                        # use utf-8 here to solve the following problem:
132                        # people use env vars like LC_ALL=en_US (latin1) then
133                        # they complain about why can't they receive latin2
134                        # messages.. so here it is: always use utf-8 then
135                        # everybody will be happy
136                        e = i.encode('UTF-8')
137                        dprint('<< ' + e)
138                        if options.conn:
139                                try:
140                                        options.conn.send(e + "\n")
141                                except IOError, s:
142                                        dprint("Warning, sending '%s' failed (%s)." % (e, s))
143
144        def send(self, msg_text):
145                if not len(msg_text):
146                        return
147                e = msg_text.decode(locale.getdefaultlocale()[1])
148                dprint('>> ' + e)
149                try:
150                        c = self.skype.Command(e, Block=True)
151                        self.skype.SendCommand(c)
152                        self.recv(c.Reply)
153                except Skype4Py.SkypeError:
154                        pass
155                except Skype4Py.SkypeAPIError, s:
156                        dprint("Warning, sending '%s' failed (%s)." % (e, s))
157
158class Options:
159        def __init__(self):
160                self.cfgpath = "/usr/local/etc/skyped/skyped.conf"
161                self.daemon = True
162                self.debug = False
163                self.help = False
164                self.host = "0.0.0.0"
165                self.port = 2727
166                self.version = False
167                # well, this is a bit hackish. we store the socket of the last connected client
168                # here and notify it. maybe later notify all connected clients?
169                self.conn = None
170                # this will be read first by the input handler
171                self.buf = None
172
173
174        def usage(self, ret):
175                print """Usage: skyped [OPTION]...
176
177skyped is a daemon that acts as a tcp server on top of a Skype instance.
178
179Options:
180        -c      --config        path to configuration file (default: %s)
181        -d      --debug         enable debug messages
182        -h      --help          this help
183        -H      --host          set the tcp host (default: %s)
184        -n      --nofork        don't run as daemon in the background
185        -p      --port          set the tcp port (default: %d)
186        -v      --version       display version information""" % (self.cfgpath, self.host, self.port)
187                sys.exit(ret)
188
189if __name__=='__main__':
190        options = Options()
191        try:
192                opts, args = getopt.getopt(sys.argv[1:], "c:dhH:np:v", ["config=", "daemon", "help", "host=", "nofork", "port=", "version"])
193        except getopt.GetoptError:
194                options.usage(1)
195        for opt, arg in opts:
196                if opt in ("-c", "--config"):
197                        options.cfgpath = arg
198                elif opt in ("-d", "--debug"):
199                        options.debug = True
200                elif opt in ("-h", "--help"):
201                        options.help = True
202                elif opt in ("-H", "--host"):
203                        options.host = arg
204                elif opt in ("-n", "--nofork"):
205                        options.daemon = False
206                elif opt in ("-p", "--port"):
207                        options.port = arg
208                elif opt in ("-v", "--version"):
209                        options.version = True
210        if options.help:
211                options.usage(0)
212        elif options.version:
213                print "skyped %s" % __version__
214                sys.exit(0)
215        # parse our config
216        if not os.path.exists(options.cfgpath):
217                print "Can't find configuration file at '%s'." % options.cfgpath
218                print "Use the -c option to specify an alternate one."
219                sys.exit(1)
220        options.config = ConfigParser()
221        options.config.read(options.cfgpath)
222        options.config.username = options.config.get('skyped', 'username').split('#')[0]
223        options.config.password = options.config.get('skyped', 'password').split('#')[0]
224        options.config.sslkey = options.config.get('skyped', 'key').split('#')[0]
225        options.config.sslcert = options.config.get('skyped', 'cert').split('#')[0]
226        dprint("Parsing config file '%s' done, username is '%s'." % (options.cfgpath, options.config.username))
227        if options.daemon:
228                pid = os.fork()
229                if pid == 0:
230                        nullin = file('/dev/null', 'r')
231                        nullout = file('/dev/null', 'w')
232                        os.dup2(nullin.fileno(), sys.stdin.fileno())
233                        os.dup2(nullout.fileno(), sys.stdout.fileno())
234                        os.dup2(nullout.fileno(), sys.stderr.fileno())
235                else:
236                        print 'skyped is started on port %s, pid: %d' % (options.port, pid)
237                        sys.exit(0)
238        server(options.host, options.port)
239        try:
240                skype = SkypeApi()
241        except Skype4Py.SkypeAPIError, s:
242                sys.exit("%s. Are you sure you have started Skype?" % s)
243        gobject.idle_add(idle_handler, skype)
244        gobject.MainLoop().run()
Note: See TracBrowser for help on using the repository browser.