1 /*
2 gsmlib.cpp - SMS Plugin
3
4 Copyright (c) 2005 by Justin Huff <jjhuff@mspin.net>
5
6 *************************************************************************
7 * *
8 * This program is free software; you can redistribute it and/or modify *
9 * it under the terms of the GNU General Public License as published by *
10 * the Free Software Foundation; either version 2 of the License, or *
11 * (at your option) any later version. *
12 * *
13 *************************************************************************
14 */
15
16 #include "gsmlib.h"
17 #ifdef INCLUDE_SMSGSM
18
19 #include <qcombobox.h>
20 #include <qlayout.h>
21 #include <qapplication.h>
22 #include <qevent.h>
23 #include <qmutex.h>
24 #include <qthread.h>
25 #include <qcheckbox.h>
26 #include <QCustomEvent>
27 #include <Q3GridLayout>
28
29 #include <klocale.h>
30 #include <kurlrequester.h>
31 #include <kmessagebox.h>
32 #include <k3process.h>
33 #include <kdebug.h>
34 #include <kconfigbase.h>
35
36 #include <unistd.h>
37 #include <gsmlib/gsm_me_ta.h>
38 #include <gsmlib/gsm_sms.h>
39 #include <gsmlib/gsm_util.h>
40 #include <gsmlib/gsm_error.h>
41 #include <kconfiggroup.h>
42
43 #include "kopeteaccount.h"
44 #include "kopeteuiglobal.h"
45 #include "kopetemetacontact.h"
46 #include "kopetecontactlist.h"
47 #include "kopetechatsessionmanager.h"
48
49 #include "gsmlibprefs.h"
50 #include "smsprotocol.h"
51 #include "smscontact.h"
52
53 #include "kopete_unix_serial.h"
54
55 /////////////////////////////////////////////////////////////////////
56 #define GSMLIB_EVENT_ID 245
GSMLibEvent(SubType t)57 GSMLibEvent::GSMLibEvent(SubType t) : QCustomEvent(QEvent::User+GSMLIB_EVENT_ID)
58 {
59 setSubType(t);
60 }
61
subType()62 GSMLibEvent::SubType GSMLibEvent::subType()
63 {
64 return m_subType;
65 }
66
setSubType(GSMLibEvent::SubType t)67 void GSMLibEvent::setSubType(GSMLibEvent::SubType t)
68 {
69 m_subType = t;
70 }
71
72 /////////////////////////////////////////////////////////////////////
GSMLibThread(QString dev,GSMLib * parent)73 GSMLibThread::GSMLibThread(QString dev, GSMLib *parent)
74 {
75 m_device = dev;
76 m_parent = parent;
77 m_run = true;
78 m_MeTa = NULL;
79 }
80
~GSMLibThread()81 GSMLibThread::~GSMLibThread()
82 {
83 m_run = false;
84 }
85
stop()86 void GSMLibThread::stop()
87 {
88 m_run = false;
89 kDebug(14160) << "Waiting from GSMLibThread to die";
90 if (wait(4000) == false) {
91 kWarning(14160) << "GSMLibThread didn't exit!";
92 }
93 }
94
run()95 void GSMLibThread::run()
96 {
97 if (doConnect()) {
98 while (m_run)
99 {
100 pollForMessages();
101 sendMessageQueue();
102 }
103 }
104
105 delete m_MeTa;
106 m_MeTa = NULL;
107 QApplication::postEvent(m_parent, new GSMLibEvent(GSMLibEvent::DISCONNECTED));
108 kDebug(14160) << "GSMLibThread exited";
109 }
110
send(const Kopete::Message & msg)111 void GSMLibThread::send(const Kopete::Message &msg)
112 {
113 if (m_MeTa) {
114 m_outMessagesMutex.lock();
115 m_outMessages.push_back(msg);
116 m_outMessagesMutex.unlock();
117 } else {
118 GSMLibEvent *e = new GSMLibEvent(GSMLibEvent::MSG_NOT_SENT);
119 e->Reason = QString("GSMLib: Not Connected");
120 e->Message = msg;
121 QApplication::postEvent(m_parent, e);
122 }
123 }
124
doConnect()125 bool GSMLibThread::doConnect()
126 {
127 // open the port and ME/TA
128 try
129 {
130 kDebug(14160) << "Connecting to: '"<<m_device<<"'";
131
132 gsmlib::Ref<gsmlib::Port> port = new gsmlib::KopeteUnixSerialPort(m_device.toLatin1(), 9600, gsmlib::DEFAULT_INIT_STRING, false);
133
134 kDebug(14160) << "Port created";
135
136 m_MeTa = new gsmlib::MeTa(port);
137 std::string dummy1, dummy2, receiveStoreName;
138 m_MeTa->getSMSStore(dummy1, dummy2, receiveStoreName);
139 m_MeTa->setSMSStore(receiveStoreName, 3);
140
141 m_MeTa->setMessageService(1);
142
143 // switch on SMS routing
144 m_MeTa->setSMSRoutingToTA(true, false, false, true);
145
146 m_MeTa->setEventHandler(this);
147 QApplication::postEvent(m_parent, new GSMLibEvent(GSMLibEvent::CONNECTED));
148 return true;
149 }
150 catch (gsmlib::GsmException &e)
151 {
152 kWarning(14160) << e.what();
153 m_run = false;
154 return false;
155 }
156 }
157
SMSReception(gsmlib::SMSMessageRef newMessage,SMSMessageType messageType)158 void GSMLibThread::SMSReception(gsmlib::SMSMessageRef newMessage, SMSMessageType messageType)
159 {
160 try
161 {
162 IncomingMessage m;
163 m.Type = messageType;
164 m.Message = newMessage;
165
166 m_newMessages.push_back(m);
167 }
168 catch (gsmlib::GsmException &e)
169 {
170 kWarning(14160) << e.what();
171 m_run = false;
172 }
173 }
174
SMSReceptionIndication(std::string storeName,unsigned int index,SMSMessageType messageType)175 void GSMLibThread::SMSReceptionIndication(std::string storeName, unsigned int index, SMSMessageType messageType)
176 {
177 kDebug(14160) << "New Message in store: "<<storeName.c_str();
178
179 try
180 {
181 if (messageType != gsmlib::GsmEvent::NormalSMS) {
182 return;
183 }
184
185 IncomingMessage m;
186 m.Index = index;
187 m.StoreName = storeName.c_str();
188 m.Type = messageType;
189 m_newMessages.push_back(m);
190 }
191 catch (gsmlib::GsmException &e)
192 {
193 kWarning(14160) << e.what();
194 m_run = false;
195 }
196 }
197
pollForMessages()198 void GSMLibThread::pollForMessages()
199 {
200 if (m_MeTa == NULL) {
201 return;
202 }
203
204 try
205 {
206 struct timeval timeoutVal;
207 timeoutVal.tv_sec = 1;
208 timeoutVal.tv_usec = 0;
209 m_MeTa->waitEvent(&timeoutVal);
210
211 MessageList::iterator it;
212 for (it = m_newMessages.begin(); it != m_newMessages.end(); it++) {
213 IncomingMessage m = *it;
214
215 // Do we need to fetch it from the ME?
216 if (m.Message.isnull()) {
217 gsmlib::SMSStoreRef store = m_MeTa->getSMSStore(m.StoreName.toLatin1());
218 store->setCaching(false);
219
220 m.Message = (*store.getptr())[m.Index].message();
221 store->erase(store->begin() + m.Index);
222 }
223
224 GSMLibEvent *e = new GSMLibEvent(GSMLibEvent::NEW_MESSAGE);
225 e->Text = m.Message->userData().c_str();
226 e->Number = m.Message->address().toString().c_str();
227
228 QApplication::postEvent(m_parent, e);
229 }
230 m_newMessages.clear();
231 }
232 catch (gsmlib::GsmException &e)
233 {
234 kWarning(14160) << e.what();
235 m_run = false;
236 }
237 }
238
sendMessageQueue()239 void GSMLibThread::sendMessageQueue()
240 {
241 QMutexLocker _(&m_outMessagesMutex);
242
243 if (m_outMessages.size() == 0) {
244 return;
245 }
246
247 KopeteMessageList::iterator it;
248 for (it = m_outMessages.begin(); it != m_outMessages.end(); it++) {
249 sendMessage(*it);
250 }
251 m_outMessages.clear();
252 }
253
sendMessage(const Kopete::Message & msg)254 void GSMLibThread::sendMessage(const Kopete::Message &msg)
255 {
256 QString reason;
257
258 if (!m_MeTa) {
259 GSMLibEvent *e = new GSMLibEvent(GSMLibEvent::MSG_NOT_SENT);
260 e->Reason = QString("GSMLib: Not Connected");
261 e->Message = msg;
262 QApplication::postEvent(m_parent, e);
263 }
264
265 QString message = msg.plainBody();
266 QString nr = msg.to().first()->contactId();
267
268 // send SMS
269 try
270 {
271 gsmlib::Ref<gsmlib::SMSSubmitMessage> submitSMS = new gsmlib::SMSSubmitMessage();
272 gsmlib::Address destAddr(nr.toLatin1());
273 submitSMS->setDestinationAddress(destAddr);
274 m_MeTa->sendSMSs(submitSMS, message.toLatin1(), true);
275
276 GSMLibEvent *e = new GSMLibEvent(GSMLibEvent::MSG_SENT);
277 e->Message = msg;
278 QApplication::postEvent(m_parent, e);
279 }
280 catch (gsmlib::GsmException &e)
281 {
282 GSMLibEvent *ev = new GSMLibEvent(GSMLibEvent::MSG_NOT_SENT);
283 ev->Reason = QString("GSMLib: ") + e.what();
284 ev->Message = msg;
285 QApplication::postEvent(m_parent, ev);
286 }
287 }
288
289 /////////////////////////////////////////////////////////////////////
290
GSMLib(Kopete::Account * account)291 GSMLib::GSMLib(Kopete::Account *account)
292 : SMSService(account)
293 {
294 prefWidget = 0L;
295 m_thread = NULL;
296
297 loadConfig();
298 }
299
~GSMLib()300 GSMLib::~GSMLib()
301 {
302 disconnect();
303 }
304
saveConfig()305 void GSMLib::saveConfig()
306 {
307 if (m_account != NULL) {
308 KConfigGroup *c = m_account->configGroup();
309
310 c->writeEntry(QString("%1:%2").arg("GSMLib").arg("Device"), m_device);
311 }
312 }
313
loadConfig()314 void GSMLib::loadConfig()
315 {
316 m_device = "/dev/bluetooth/rfcomm0";
317 if (m_account != NULL) {
318 QString temp;
319 KConfigGroup *c = m_account->configGroup();
320
321 temp = c->readEntry(QString("%1:%2").arg("GSMLib").arg("Device"), QString());
322 if (!temp.isEmpty()) {
323 m_device = temp;
324 }
325 }
326 }
327
connect()328 void GSMLib::connect()
329 {
330 m_thread = new GSMLibThread(m_device, this);
331 m_thread->start();
332 }
333
disconnect()334 void GSMLib::disconnect()
335 {
336 kDebug(14160);
337
338 if (m_thread) {
339 m_thread->stop();
340 delete m_thread;
341 m_thread = NULL;
342 emit disconnected();
343 }
344 }
345
setWidgetContainer(QWidget * parent,Q3GridLayout * layout)346 void GSMLib::setWidgetContainer(QWidget *parent, Q3GridLayout *layout)
347 {
348 m_parent = parent;
349 m_layout = layout;
350 QWidget *configWidget = configureWidget(parent);
351 layout->addMultiCellWidget(configWidget, 0, 1, 0, 1);
352 configWidget->show();
353 }
354
send(const Kopete::Message & msg)355 void GSMLib::send(const Kopete::Message &msg)
356 {
357 m_thread->send(msg);
358 }
359
configureWidget(QWidget * parent)360 QWidget *GSMLib::configureWidget(QWidget *parent)
361 {
362 if (prefWidget == 0L) {
363 prefWidget = new GSMLibPrefsUI(parent);
364 }
365
366 loadConfig();
367 prefWidget->device->setUrl(m_device);
368
369 return prefWidget;
370 }
371
savePreferences()372 void GSMLib::savePreferences()
373 {
374 if (prefWidget) {
375 m_device = prefWidget->device->url();
376 }
377 saveConfig();
378 }
379
maxSize()380 int GSMLib::maxSize()
381 {
382 return 160;
383 }
384
customEvent(QCustomEvent * e)385 void GSMLib::customEvent(QCustomEvent *e)
386 {
387 if (e->type() != QEvent::User+GSMLIB_EVENT_ID) {
388 return;
389 }
390
391 if (m_account == NULL) {
392 return;
393 }
394
395 GSMLibEvent *ge = (GSMLibEvent *)e;
396
397 kDebug(14160) << "Got event "<<ge->subType();
398 switch (ge->subType()) {
399 case GSMLibEvent::CONNECTED:
400 emit connected();
401 break;
402 case GSMLibEvent::DISCONNECTED:
403 disconnect();
404 break;
405 case GSMLibEvent::MSG_SENT:
406 emit messageSent(ge->Message);
407 break;
408 case GSMLibEvent::MSG_NOT_SENT:
409 emit messageNotSent(ge->Message, ge->Reason);
410 break;
411 case GSMLibEvent::NEW_MESSAGE:
412 {
413 QString nr = ge->Number;
414 QString text = ge->Text;
415
416 // Locate a contact
417 SMSContact *contact = static_cast<SMSContact *>(m_account->contacts().value(nr));
418 if (contact == NULL) {
419 // No contact found, make a new one
420 Kopete::MetaContact *metaContact = new Kopete::MetaContact();
421 metaContact->setTemporary(true);
422 contact = new SMSContact(m_account, nr, nr, metaContact);
423 Kopete::ContactList::self()->addMetaContact(metaContact);
424 contact->setOnlineStatus(SMSProtocol::protocol()->SMSOnline);
425 }
426
427 // Deliver the msg
428 Kopete::Message msg(contact, m_account->myself(), text, Kopete::Message::Inbound, Kopete::Message::RichText);
429 contact->manager(Kopete::Contact::CanCreate)->appendMessage(msg);
430 break;
431 }
432 }
433 }
434
description()435 const QString &GSMLib::description()
436 {
437 QString url = "http://www.pxh.de/fs/gsmlib/";
438 m_description = i18n("<qt>GSMLib is a library (and utilities) for sending SMS via a GSM device. The program can be found on <a href=\"%1\">%1</a></qt>", url);
439 return m_description;
440 }
441
442 #endif
443 /*
444 * Local variables:
445 * c-indentation-style: k&r
446 * indent-tabs-mode: t
447 * End:
448 */
449