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