2
* Copyright (C) 2004, 2005, 2006, 2009, 2008, 2009, 2010 Savoir-Faire Linux Inc.
3
* Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
4
* Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 3 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20
* Additional permission under GNU GPL version 3 section 7:
22
* If you modify this program, or any covered work, by linking or
23
* combining it with the OpenSSL project's OpenSSL library (or a
24
* modified version of that library), containing parts covered by the
25
* terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
26
* grants you additional permission to convey the resulting work.
27
* Corresponding Source for a non-source form of such a combination
28
* shall include the source code for the parts of OpenSSL used as well
29
* as that of the covered work.
32
#include "InstantMessaging.h"
39
static inline char* duplicateString (char dst[], const char src[], size_t len)
41
memcpy (dst, src, len);
46
static void XMLCALL startElementCallback (void *userData, const char *name, const char **atts)
54
// _debug ("InstantMessaging: StartElement Callback: %s", name);
56
if (strcmp (name, "entry") == 0) {
58
sfl::InstantMessaging::UriList *list = static_cast<sfl::InstantMessaging::UriList *> (userData);
59
sfl::InstantMessaging::UriEntry entry = sfl::InstantMessaging::UriEntry();
61
for (att = atts; *att; att += 2) {
63
const char **val = att+1;
65
duplicateString (attribute, *att, strlen (*att));
66
duplicateString (value, *val, strlen (*val));
68
// _debug ("InstantMessaging: attribute: %s, value: %s", attribute, value);
70
entry.insert (std::pair<std::string, std::string> (std::string (attribute), std::string (value)));
73
list->push_back (entry);
78
static void XMLCALL endElementCallback (void *userData, const char *name)
80
// std::cout << "endElement " << name << std::endl;
84
InstantMessaging::InstantMessaging()
86
, messageMaxSize (MAXIMUM_MESSAGE_LENGTH) {}
89
InstantMessaging::~InstantMessaging() {}
91
bool InstantMessaging::init ()
96
int InstantMessaging::openArchive (CallID& id)
99
// Create a new file stream
100
std::ofstream File (id.c_str (), std::ios::out | std::ios::app);
103
// Attach it to the call ID
104
return (int) imFiles.size ();
107
int InstantMessaging::closeArchive (CallID& id)
110
// Erase it from the map
112
return (int) imFiles.size ();
115
bool InstantMessaging::saveMessage (const std::string& message, const std::string& author, CallID& id, int mode)
118
// We need here to write the text message in the right file.
119
// We will use the Call ID
122
std::string filename = "im:";
124
filename.append (id);
125
File.open (filename.c_str (), (std::_Ios_Openmode) mode);
127
if (!File.good () || !File.is_open ())
130
File << "[" << author << "] " << message << '\n';
136
std::string InstantMessaging::receive (const std::string& message, const std::string& author, CallID& id)
139
// We just receive a TEXT message. Before sent it to the recipient, we must assure that the message is complete.
140
// We should use a queue to push these messages in
142
_debug ("New message : %s", message.c_str ());
144
// TODO Security check
145
// TODO String cleaning
147
// Archive the message
148
// TODO Deactivate this for the momment, this is an extra feature.
149
// this->saveMessage (message, author, id);
156
pj_status_t InstantMessaging::notify (CallID& id)
158
// Notify the clients through a D-Bus signal
162
pj_status_t InstantMessaging::sip_send (pjsip_inv_session *session, CallID& id, const std::string& text)
165
pjsip_method msg_method;
166
const pj_str_t type = STR_TEXT;
167
const pj_str_t subtype = STR_PLAIN;
168
pjsip_tx_data *tdata;
170
pjsip_dialog* dialog;
173
msg_method.id = PJSIP_OTHER_METHOD;
174
msg_method.name = METHOD_NAME;
176
// Get the dialog associated to the call
177
dialog = session->dlg;
178
// Convert the text into a format readable by pjsip
180
message = pj_str ( (char*) text.c_str ());
183
pjsip_dlg_inc_lock (dialog);
185
// Create the message request
186
status = pjsip_dlg_create_request (dialog, &msg_method, -1, &tdata);
187
PJ_ASSERT_RETURN (status == PJ_SUCCESS, 1);
189
// Attach "text/plain" body
190
tdata->msg->body = pjsip_msg_body_create (tdata->pool, &type, &subtype, &message);
192
// Create the Require header to handle recipient-list Content-Disposition type
193
// pjsip_generic_string_hdr reqhdr;
194
// pj_str_t reqhname = pj_str ("Require");
195
// pj_str_t reqhvalue = pj_str ("recipient-list");
197
// Create the Content-Type header to handle multipart/mixed and boundary MIME types
198
// pj_str_t ctype = pj_str ("Content-Type");
199
// pj_str_t sctype = pj_str ("ctype"); // small version of the header name
200
// ctypehdr = pjsip_msg_find_hdr_by_names (tdata->msg, &ctype, &sctype, NULL);
201
// pjsip_generic_string_hdr ctypehdr;
202
// pj_str_t ctypehname = pj_str ("Content-Type");
203
// pj_str_t ctypehvalue = pj_str ("multipart/mixed;boundary=\"boundary\"");
205
// Add headers to the message
206
// pjsip_generic_string_hdr_init2 (&reqhdr, &reqhname, &reqhvalue);
207
// pj_list_push_back (& (tdata->msg->hdr), (pjsip_hdr*) (&reqhdr));
208
// pj_list_push_back (& (tdata->msg->hdr), (pjsip_hdr*) (&ctypehdr));
211
status = pjsip_dlg_send_request (dialog, tdata, -1, NULL);
212
// PJ_ASSERT_RETURN (status == PJ_SUCCESS, 1);
215
pjsip_dlg_dec_lock (dialog);
217
// Archive the message
218
this->saveMessage (text, "Me", id);
220
printf ("SIPVoIPLink::sendTextMessage %s %s\n", id.c_str(), text.c_str());
224
pj_status_t InstantMessaging::send_sip_message (pjsip_inv_session *session, CallID& id, const std::string& message)
227
/* Check the length of the message */
228
if (message.length() < getMessageMaximumSize()) {
229
/* No problem here */
230
sip_send (session, id, message);
234
/* It exceeds the size limit of a SIP MESSAGE (1300 bytes), o plit it and send multiple messages */
235
std::vector<std::string> multiple_messages = split_message (message);
236
/* Send multiple messages */
237
// int size = multiple_messages.size();
240
// Maximum is above 1500 character
241
// TODO: Send every messages
242
sip_send (session, id, multiple_messages[i]);
249
bool InstantMessaging::iax_send (iax_session* session, const CallID& id, const std::string& message)
251
if (iax_send_text (session, message.c_str()) != -1)
259
bool InstantMessaging::send_iax_message (iax_session* session, const CallID& id, const std::string& message)
264
/* Check the length of the message */
265
if (message.length() < getMessageMaximumSize()) {
266
/* No problem here */
267
ret = iax_send (session, id, message);
271
/* It exceeds the size limit of a SIP MESSAGE (1300 bytes), o plit it and send multiple messages */
272
std::vector<std::string> multiple_messages = split_message (message);
273
/* Send multiple messages */
274
// int size = multiple_messages.size();
277
// Maximum is above 1500 character
278
// TODO: Send every messages
279
ret = iax_send (session, id, multiple_messages[i]);
286
std::vector<std::string> InstantMessaging::split_message (const std::string& text)
289
std::vector<std::string> messages;
290
std::string text_to_split = text;
292
/* Iterate over the message length */
293
while (text_to_split.length() > getMessageMaximumSize()) {
294
/* The remaining string is still too long */
296
/* Compute the substring */
297
std::string split_message = text_to_split.substr (0, (size_t) getMessageMaximumSize());
298
/* Append our split character \n\n */
299
split_message.append (DELIMITER_CHAR);
300
/* Append in the vector */
301
messages.push_back (split_message);
302
/* Use the remaining string to not loop forever */
303
text_to_split = text_to_split.substr ( (size_t) getMessageMaximumSize());
306
/* Push the last message */
307
/* If the message length does not exceed the maximum size of a SIP MESSAGE, we go directly here */
308
messages.push_back (text_to_split);
313
std::string InstantMessaging::generateXmlUriList (UriList& list)
316
std::string xmlbuffer = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>";
317
xmlbuffer.append ("<resource-lists xmlns=\"urn:ietf:params:xml:ns:resource-lists\" xmlns:cp=\"urn:ietf:params:xml:ns:copycontrol\">");
318
xmlbuffer.append ("<list>");
320
// An iterator over xml attribute
321
UriEntry::iterator iterAttr;
323
// An iterator over list entries
324
UriList::iterator iterEntry = list.begin();
326
while (iterEntry != list.end()) {
327
xmlbuffer.append ("<entry uri=");
328
UriEntry entry = static_cast<UriEntry> (*iterEntry);
329
iterAttr = entry.find (sfl::IM_XML_URI);
330
xmlbuffer.append (iterAttr->second);
331
xmlbuffer.append (" cp:copyControl=\"to\" />");
336
xmlbuffer.append ("</list>");
337
xmlbuffer.append ("</resource-lists>");
343
InstantMessaging::UriList InstantMessaging::parseXmlUriList (std::string& urilist)
345
InstantMessaging::UriList list;
347
XML_Parser parser = XML_ParserCreate (NULL);
348
XML_SetUserData (parser, &list);
349
XML_SetElementHandler (parser, startElementCallback, endElementCallback);
351
if (XML_Parse (parser, urilist.c_str(), urilist.size(), 1) == XML_STATUS_ERROR) {
352
std::cout << "Error: " << XML_ErrorString (XML_GetErrorCode (parser))
353
<< " at line " << XML_GetCurrentLineNumber (parser) << std::endl;
354
throw InstantMessageException ("Error while parsing uri-list xml content");
360
std::string InstantMessaging::appendUriList (std::string text, UriList& list)
363
std::string formatedText = "--boundary Content-Type: text/plain";
365
formatedText.append (text);
366
formatedText.append ("--boundary Content-Type: application/resource-lists+xml");
367
formatedText.append ("Content-Disposition: recipient-list");
369
std::string recipientlist = generateXmlUriList (list);
371
formatedText.append (recipientlist);
373
formatedText.append ("--boundary--");
378
std::string InstantMessaging::findTextUriList (std::string& text)
380
std::string ctype = "Content-Type: application/resource-lists+xml";
381
std::string cdispo = "Content-Disposition: recipient-list";
382
std::string boundary = ("--boundary--");
384
// init position pointer
389
// find the content type
390
if ( (pos = text.find (ctype)) == std::string::npos)
391
throw InstantMessageException ("Could not find Content-Type tag while parsing sip message for recipient-list");
393
// find the content disposition
394
if ( (pos = text.find (cdispo, pos)) == std::string::npos)
395
throw InstantMessageException ("Could not find Content-Disposition tag while parsing sip message for recipient-list");
397
// xml content start after content disposition tag (plus \n\n)
398
begin = pos+cdispo.size();
400
// find final boundary
401
if ( (end = text.find (boundary, begin)) == std::string::npos)
402
throw InstantMessageException ("Could not find final \"boundary\" while parsing sip message for recipient-list");
404
return text.substr (begin, end-begin);
407
std::string InstantMessaging::findTextMessage (std::string& text)
409
std::string ctype = "Content-Type: text/plain";
410
std::string boundary = "--boundary";
416
// find the content type
417
if ( (pos = text.find (ctype)) == std::string::npos)
418
throw InstantMessageException ("Could not find Content-Type tag while parsing sip message for text");
420
// plain text content start after content type tag (plus \n\n)
421
begin = pos+ctype.size();
423
// retrive end of the text content
424
if ( (end = text.find (boundary, begin)) == std::string::npos)
425
throw InstantMessageException ("Could not find end of text \"boundary\" while parsing sip message for text");
427
return text.substr (begin, end-begin);