1 /*
2     This file is part of Akonadi Contact.
3 
4     SPDX-FileCopyrightText: 2009 Tobias Koenig <tokoe@kde.org>
5 
6     SPDX-License-Identifier: LGPL-2.0-or-later
7 */
8 
9 #include "qskypedialer.h"
10 
11 #include <KLocalizedString>
12 #include <QDBusConnection>
13 #include <QDBusConnectionInterface>
14 #include <QDBusInterface>
15 #include <QDBusReply>
16 #include <QProcess>
17 #include <QStandardPaths>
18 
19 #if !defined(Q_OS_WIN)
20 #include <unistd.h>
21 #else
22 #include <windows.h>
23 #endif
24 
isSkypeServiceRegistered()25 static bool isSkypeServiceRegistered()
26 {
27     const QString service(QStringLiteral("com.Skype.API"));
28 
29     QDBusConnectionInterface *interface = QDBusConnection::sessionBus().interface();
30     if (interface->isServiceRegistered(service)) {
31         return true;
32     }
33 
34     interface = QDBusConnection::sessionBus().interface();
35     if (interface->isServiceRegistered(service)) {
36         return true;
37     }
38 
39     return false;
40 }
41 
searchSkypeDBusInterface()42 static QDBusInterface *searchSkypeDBusInterface()
43 {
44     const QString service(QStringLiteral("com.Skype.API"));
45     const QString path(QStringLiteral("/com/Skype"));
46 
47     auto interface = new QDBusInterface(service, path, QString(), QDBusConnection::sessionBus());
48     if (!interface->isValid()) {
49         delete interface;
50         interface = new QDBusInterface(service, path, QString(), QDBusConnection::sessionBus());
51     }
52 
53     return interface;
54 }
55 
QSkypeDialer(const QString & applicationName)56 QSkypeDialer::QSkypeDialer(const QString &applicationName)
57     : QDialer(applicationName)
58 {
59 }
60 
~QSkypeDialer()61 QSkypeDialer::~QSkypeDialer()
62 {
63     delete mInterface;
64 }
65 
initializeSkype()66 bool QSkypeDialer::initializeSkype()
67 {
68     if (mInterface && mInterface->isValid()) {
69         return true;
70     }
71 
72     // first check whether dbus interface is available yet
73     if (!isSkypeServiceRegistered()) {
74         // it could be skype is not running yet, so start it now
75         const QString progFullPath = QStandardPaths::findExecutable(QStringLiteral("skype"));
76         if (progFullPath.isEmpty() || !QProcess::startDetached(QStringLiteral("skype"), QStringList())) {
77             mErrorMessage = i18n("Unable to start skype process, check that skype executable is in your PATH variable.");
78             return false;
79         }
80 
81         const int runs = 100;
82         for (int i = 0; i < runs; ++i) {
83             if (!isSkypeServiceRegistered()) {
84 #if !defined(Q_OS_WIN)
85                 ::sleep(2);
86 #else
87                 Sleep(2000);
88 #endif
89             } else {
90                 break;
91             }
92         }
93     }
94 
95     // check again for the dbus interface
96     mInterface = searchSkypeDBusInterface();
97 
98     if (!mInterface->isValid()) {
99         delete mInterface;
100         mInterface = nullptr;
101 
102         mErrorMessage = i18n("Skype Public API (D-Bus) seems to be disabled.");
103         return false;
104     }
105 
106     QDBusReply<QString> reply = mInterface->call(QStringLiteral("Invoke"), QStringLiteral("NAME %1").arg(mApplicationName));
107     if (reply.value() != QLatin1String("OK")) {
108         delete mInterface;
109         mInterface = nullptr;
110 
111         mErrorMessage = i18n("Skype registration failed.");
112         return false;
113     }
114 
115     reply = mInterface->call(QStringLiteral("Invoke"), QStringLiteral("PROTOCOL 1"));
116     if (reply.value() != QLatin1String("PROTOCOL 1")) {
117         delete mInterface;
118         mInterface = nullptr;
119 
120         mErrorMessage = i18n("Protocol mismatch.");
121         return false;
122     }
123 
124     return true;
125 }
126 
dialNumber(const QString & number)127 bool QSkypeDialer::dialNumber(const QString &number)
128 {
129     if (!initializeSkype()) {
130         return false;
131     }
132 
133     QDBusReply<QString> reply = mInterface->call(QStringLiteral("Invoke"), QStringLiteral("CALL %1").arg(number));
134 
135     return true;
136 }
137 
sendSms(const QString & number,const QString & text)138 bool QSkypeDialer::sendSms(const QString &number, const QString &text)
139 {
140     if (!initializeSkype()) {
141         return false;
142     }
143 
144     // First we create a new SMS object that gets an ID. We need that ID later...
145     QDBusReply<QString> reply = mInterface->call(QStringLiteral("Invoke"), QStringLiteral("CREATE SMS OUTGOING %1").arg(number));
146     const QString messageId = reply.value().section(QLatin1Char(' '), 1, 1);
147 
148     // Set the SMS text
149     reply = mInterface->call(QStringLiteral("Invoke"), QStringLiteral("SET SMS %1 BODY %2").arg(messageId, text));
150 
151     // Send the SMS
152     reply = mInterface->call(QStringLiteral("Invoke"), QStringLiteral("ALTER SMS %1 SEND").arg(messageId));
153     if (reply.value().contains(QLatin1String("ERROR"))) {
154         mErrorMessage = reply.value();
155         // As sending the message failed (not enough Skype credit), lets delete the message
156         reply = mInterface->call(QStringLiteral("Invoke"), QStringLiteral("DELETE SMS %1").arg(messageId));
157         return false;
158     }
159 
160     return true;
161 }
162