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