1 /*
2  * This file is part of TelepathyQt
3  *
4  * Copyright (C) 2011 Collabora Ltd. <http://www.collabora.co.uk/>
5  * Copyright (C) 2011 Nokia Corporation
6  *
7  * This library is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU Lesser General Public
9  * License as published by the Free Software Foundation; either
10  * version 2.1 of the License, or (at your option) any later version.
11  *
12  * This library 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 GNU
15  * Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20  */
21 
22 #include "file-sender.h"
23 
24 #include "pending-file-send.h"
25 
26 #include <TelepathyQt/Account>
27 #include <TelepathyQt/AccountFactory>
28 #include <TelepathyQt/AccountManager>
29 #include <TelepathyQt/ChannelClassSpec>
30 #include <TelepathyQt/ChannelClassSpecList>
31 #include <TelepathyQt/ChannelFactory>
32 #include <TelepathyQt/ClientRegistrar>
33 #include <TelepathyQt/Connection>
34 #include <TelepathyQt/ConnectionFactory>
35 #include <TelepathyQt/Contact>
36 #include <TelepathyQt/ContactCapabilities>
37 #include <TelepathyQt/ContactFactory>
38 #include <TelepathyQt/ContactManager>
39 #include <TelepathyQt/Debug>
40 #include <TelepathyQt/OutgoingFileTransferChannel>
41 #include <TelepathyQt/PendingChannel>
42 #include <TelepathyQt/PendingChannelRequest>
43 #include <TelepathyQt/PendingContacts>
44 #include <TelepathyQt/PendingOperation>
45 #include <TelepathyQt/PendingReady>
46 
47 #include <QDebug>
48 
FileSender(const QString & accountName,const QString & receiver,const QString & filePath,QObject * parent)49 FileSender::FileSender(const QString &accountName, const QString &receiver,
50         const QString &filePath, QObject *parent)
51     : QObject(parent),
52       mAccountName(accountName),
53       mReceiver(receiver),
54       mFilePath(filePath),
55       mTransferRequested(false)
56 {
57     qDebug() << "Retrieving account from AccountManager";
58 
59     QDBusConnection bus(QDBusConnection::sessionBus());
60 
61     // Let's not prepare any account feature as we only care about one account, thus no need to
62     // prepare features for all accounts
63     AccountFactoryPtr accountFactory = AccountFactory::create(bus);
64     // We only care about CONNECTED connections, so let's specify that in a Connection Factory
65     ConnectionFactoryPtr connectionFactory = ConnectionFactory::create(bus,
66             Connection::FeatureCore | Connection::FeatureConnected);
67     ChannelFactoryPtr channelFactory = ChannelFactory::create(bus);
68     channelFactory->addCommonFeatures(Channel::FeatureCore);
69     channelFactory->addFeaturesForOutgoingFileTransfers(OutgoingFileTransferChannel::FeatureCore);
70     ContactFactoryPtr contactFactory = ContactFactory::create();
71 
72     mAM = AccountManager::create(bus, accountFactory, connectionFactory,
73             channelFactory, contactFactory);
74     connect(mAM->becomeReady(),
75             SIGNAL(finished(Tp::PendingOperation*)),
76             SLOT(onAMReady(Tp::PendingOperation*)));
77 }
78 
~FileSender()79 FileSender::~FileSender()
80 {
81 }
82 
onAMReady(PendingOperation * op)83 void FileSender::onAMReady(PendingOperation *op)
84 {
85     if (op->isError()) {
86         qWarning() << "AccountManager cannot become ready -" <<
87             op->errorName() << '-' << op->errorMessage();
88         QCoreApplication::exit(1);
89         return;
90     }
91 
92     PendingReady *pr = qobject_cast<PendingReady*>(op);
93     Q_ASSERT(pr != NULL);
94     Q_UNUSED(pr);
95     qDebug() << "AccountManager ready";
96 
97     mAccount = mAM->accountForObjectPath(
98             TP_QT_ACCOUNT_OBJECT_PATH_BASE + QLatin1Char('/') + mAccountName);
99     if (!mAccount) {
100         qWarning() << "The account given does not exist";
101         QCoreApplication::exit(1);
102     }
103     Q_ASSERT(!mAccount->isReady());
104     connect(mAccount->becomeReady(),
105             SIGNAL(finished(Tp::PendingOperation*)),
106             SLOT(onAccountReady(Tp::PendingOperation*)));
107 }
108 
onAccountReady(PendingOperation * op)109 void FileSender::onAccountReady(PendingOperation *op)
110 {
111     if (op->isError()) {
112         qWarning() << "Account cannot become ready -" <<
113             op->errorName() << '-' << op->errorMessage();
114         QCoreApplication::exit(1);
115         return;
116     }
117 
118     PendingReady *pr = qobject_cast<PendingReady*>(op);
119     Q_ASSERT(pr != NULL);
120     Q_UNUSED(pr);
121     qDebug() << "Account ready";
122 
123     qDebug() << "Checking if account is online...";
124     connect(mAccount.data(),
125             SIGNAL(connectionChanged(Tp::ConnectionPtr)),
126             SLOT(onAccountConnectionChanged(Tp::ConnectionPtr)));
127     onAccountConnectionChanged(mAccount->connection());
128 }
129 
onAccountConnectionChanged(const ConnectionPtr & conn)130 void FileSender::onAccountConnectionChanged(const ConnectionPtr &conn)
131 {
132     if (!conn) {
133         qDebug() << "The account given has no connection. "
134             "Please set it online to continue";
135         return;
136     }
137 
138     Q_ASSERT(conn->isValid());
139     Q_ASSERT(conn->status() == ConnectionStatusConnected);
140 
141     qDebug() << "Account online, got a connected connection!";
142     mConnection = conn;
143 
144     qDebug() << "Creating contact object for receiver" << mReceiver;
145     connect(mConnection->contactManager()->contactsForIdentifiers(QStringList() << mReceiver,
146                 Contact::FeatureCapabilities),
147             SIGNAL(finished(Tp::PendingOperation *)),
148             SLOT(onContactRetrieved(Tp::PendingOperation *)));
149 }
150 
onContactRetrieved(PendingOperation * op)151 void FileSender::onContactRetrieved(PendingOperation *op)
152 {
153     if (op->isError()) {
154         qWarning() << "Unable to create contact object for receiver" <<
155             mReceiver << "-" << op->errorName() << "-" << op->errorMessage();
156         QCoreApplication::exit(1);
157         return;
158     }
159 
160     PendingContacts *pc = qobject_cast<PendingContacts*>(op);
161     Q_ASSERT(pc->contacts().size() == 1);
162     mContact = pc->contacts().first();
163 
164     qDebug() << "Checking contact capabilities...";
165     connect(mContact.data(),
166             SIGNAL(capabilitiesChanged(Tp::ContactCapabilities)),
167             SLOT(onContactCapabilitiesChanged()));
168 
169 #if 0
170     qDebug() << "Contact capabilities:";
171     Q_FOREACH (const RequestableChannelClassSpec &spec, mContact->capabilities().allClassSpecs()) {
172         qDebug() << "    fixed:" << spec.fixedProperties();
173         qDebug() << "  allowed:" << spec.allowedProperties();
174     }
175 #endif
176 
177     if (mContact->capabilities().fileTransfers()) {
178         onContactCapabilitiesChanged();
179     } else {
180         qDebug() << "The receiver needs to be online and support file transfers to continue";
181     }
182 }
183 
onContactCapabilitiesChanged()184 void FileSender::onContactCapabilitiesChanged()
185 {
186     if (mTransferRequested) {
187         return;
188     }
189 
190     if (mContact->capabilities().fileTransfers()) {
191         qDebug() << "The remote contact is capable of receiving file transfers. "
192             "Requesting file transfer channel";
193 
194         mTransferRequested = true;
195         FileTransferChannelCreationProperties ftProps(mFilePath,
196                 QLatin1String("application/octet-stream"));
197         connect(mAccount->createAndHandleFileTransfer(mContact, ftProps),
198                 SIGNAL(finished(Tp::PendingOperation*)),
199                 SLOT(onTransferRequestFinished(Tp::PendingOperation*)));
200     }
201 }
202 
onTransferRequestFinished(PendingOperation * op)203 void FileSender::onTransferRequestFinished(PendingOperation *op)
204 {
205     if (op->isError()) {
206         qWarning() << "Unable to request stream tube channel -" <<
207             op->errorName() << ": " << op->errorMessage();
208         QCoreApplication::exit(1);
209         return;
210     }
211 
212     qDebug() << "File transfer channel request finished successfully!";
213 
214     PendingChannel *pc = qobject_cast<PendingChannel*>(op);
215     Q_ASSERT(pc);
216 
217     ChannelPtr chan = pc->channel();
218     if (!chan->isValid()) {
219         qWarning() << "Channel received to handle is invalid, aborting file transfer";
220         QCoreApplication::exit(1);
221         return;
222     }
223 
224     // We should always receive outgoing channels of type FileTransfer, as requested,
225     // otherwise either MC or tp-qt itself is bogus, so let's assert in case they are
226     Q_ASSERT(chan->channelType() == TP_QT_IFACE_CHANNEL_TYPE_FILE_TRANSFER);
227     Q_ASSERT(chan->isRequested());
228 
229     OutgoingFileTransferChannelPtr transferChannel = OutgoingFileTransferChannelPtr::qObjectCast(chan);
230     Q_ASSERT(transferChannel);
231 
232     // We just passed the URI when requesting the channel, so it has to be set
233     Q_ASSERT(!transferChannel->uri().isEmpty());
234 
235     PendingFileSend *sendOperation = new PendingFileSend(transferChannel, SharedPtr<RefCounted>());
236     connect(sendOperation,
237             SIGNAL(finished(Tp::PendingOperation*)),
238             SLOT(onSendFinished(Tp::PendingOperation*)));
239 }
240 
onSendFinished(PendingOperation * op)241 void FileSender::onSendFinished(PendingOperation *op)
242 {
243     PendingFileSend *sendOperation = qobject_cast<PendingFileSend*>(op);
244     qDebug() << "Closing channel";
245     sendOperation->channel()->requestClose();
246 
247     QCoreApplication::exit(0);
248 }
249 
main(int argc,char ** argv)250 int main(int argc, char **argv)
251 {
252     QCoreApplication app(argc, argv);
253 
254     if (argc != 4) {
255         qDebug() << "usage:" << argv[0] << "<account name, as in mc-tool list> <receiver contact ID> <file>";
256         return 1;
257     }
258 
259     QString filePath = QLatin1String(argv[3]);
260     QFileInfo fileInfo(filePath);
261     if (!fileInfo.exists()) {
262         qWarning() << "File" << filePath << "does not exist";
263         return 1;
264     }
265 
266     Tp::registerTypes();
267     Tp::enableDebug(false);
268     Tp::enableWarnings(true);
269 
270     new FileSender(QLatin1String(argv[1]), QLatin1String(argv[2]), filePath, &app);
271 
272     return app.exec();
273 }
274 
275 #include "_gen/file-sender.moc.hpp"
276