~ubuntu-branches/ubuntu/saucy/sflphone/saucy

« back to all changes in this revision

Viewing changes to sflphone-common/src/im/InstantMessaging.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Francois Marier
  • Date: 2010-12-24 16:33:55 UTC
  • mfrom: (1.1.2 upstream)
  • Revision ID: james.westby@ubuntu.com-20101224163355-tkvvikqxbrbav6up
Tags: 0.9.11-1
* New upstream release
* Add new build dependencies on libwebkit-dev and libyaml-dev

* Bump Standards-Version up to 3.9.1
* Bump debhelper compatibility to 8
* Patch another typo in the upstream code (lintian notice)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
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>
 
5
 *
 
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.
 
10
 *
 
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.
 
15
 *
 
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.
 
19
 *
 
20
 *  Additional permission under GNU GPL version 3 section 7:
 
21
 *
 
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.
 
30
 */
 
31
 
 
32
#include "InstantMessaging.h"
 
33
 
 
34
#include "expat.h"
 
35
 
 
36
namespace sfl
 
37
{
 
38
 
 
39
static inline char* duplicateString (char dst[], const char src[], size_t len)
 
40
{
 
41
    memcpy (dst, src, len);
 
42
    dst[len] = 0;
 
43
    return dst;
 
44
}
 
45
 
 
46
static void XMLCALL startElementCallback (void *userData, const char *name, const char **atts)
 
47
{
 
48
 
 
49
    char attribute[100];
 
50
    char value[100];
 
51
 
 
52
    const char **att;
 
53
 
 
54
    // _debug ("InstantMessaging: StartElement Callback: %s", name);
 
55
 
 
56
    if (strcmp (name, "entry") == 0) {
 
57
 
 
58
        sfl::InstantMessaging::UriList *list = static_cast<sfl::InstantMessaging::UriList *> (userData);
 
59
        sfl::InstantMessaging::UriEntry entry = sfl::InstantMessaging::UriEntry();
 
60
 
 
61
        for (att = atts; *att; att += 2) {
 
62
 
 
63
            const char **val = att+1;
 
64
 
 
65
            duplicateString (attribute, *att, strlen (*att));
 
66
            duplicateString (value, *val, strlen (*val));
 
67
 
 
68
            // _debug ("InstantMessaging: attribute: %s, value: %s", attribute, value);
 
69
 
 
70
            entry.insert (std::pair<std::string, std::string> (std::string (attribute), std::string (value)));
 
71
        }
 
72
 
 
73
        list->push_back (entry);
 
74
    }
 
75
 
 
76
}
 
77
 
 
78
static void XMLCALL endElementCallback (void *userData, const char *name)
 
79
{
 
80
    // std::cout << "endElement " << name << std::endl;
 
81
}
 
82
 
 
83
 
 
84
InstantMessaging::InstantMessaging()
 
85
    : imFiles ()
 
86
    , messageMaxSize (MAXIMUM_MESSAGE_LENGTH) {}
 
87
 
 
88
 
 
89
InstantMessaging::~InstantMessaging() {}
 
90
 
 
91
bool InstantMessaging::init ()
 
92
{
 
93
    return true;
 
94
}
 
95
 
 
96
int InstantMessaging::openArchive (CallID& id)
 
97
{
 
98
 
 
99
    // Create a new file stream
 
100
    std::ofstream File (id.c_str (), std::ios::out | std::ios::app);
 
101
    imFiles[id] = &File;
 
102
 
 
103
    // Attach it to the call ID
 
104
    return (int) imFiles.size ();
 
105
}
 
106
 
 
107
int InstantMessaging::closeArchive (CallID& id)
 
108
{
 
109
 
 
110
    // Erase it from the map
 
111
    imFiles.erase (id);
 
112
    return (int) imFiles.size ();
 
113
}
 
114
 
 
115
bool InstantMessaging::saveMessage (const std::string& message, const std::string& author, CallID& id, int mode)
 
116
{
 
117
 
 
118
    // We need here to write the text message in the right file.
 
119
    // We will use the Call ID
 
120
 
 
121
    std::ofstream File;
 
122
    std::string filename = "im:";
 
123
 
 
124
    filename.append (id);
 
125
    File.open (filename.c_str (), (std::_Ios_Openmode) mode);
 
126
 
 
127
    if (!File.good () || !File.is_open ())
 
128
        return false;
 
129
 
 
130
    File << "[" << author << "] " << message << '\n';
 
131
    File.close ();
 
132
 
 
133
    return true;
 
134
}
 
135
 
 
136
std::string InstantMessaging::receive (const std::string& message, const std::string& author, CallID& id)
 
137
{
 
138
 
 
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
 
141
 
 
142
    _debug ("New message : %s", message.c_str ());
 
143
 
 
144
    // TODO Security check
 
145
    // TODO String cleaning
 
146
 
 
147
    // Archive the message
 
148
    // TODO Deactivate this for the momment, this is an extra feature.
 
149
    // this->saveMessage (message, author, id);
 
150
 
 
151
 
 
152
    return message;
 
153
 
 
154
}
 
155
 
 
156
pj_status_t InstantMessaging::notify (CallID& id)
 
157
{
 
158
    // Notify the clients through a D-Bus signal
 
159
    return PJ_SUCCESS;
 
160
}
 
161
 
 
162
pj_status_t InstantMessaging::sip_send (pjsip_inv_session *session, CallID& id, const std::string& text)
 
163
{
 
164
 
 
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;
 
169
    pj_status_t status;
 
170
    pjsip_dialog* dialog;
 
171
    pj_str_t message;
 
172
 
 
173
    msg_method.id = PJSIP_OTHER_METHOD;
 
174
    msg_method.name = METHOD_NAME;
 
175
 
 
176
    // Get the dialog associated to the call
 
177
    dialog = session->dlg;
 
178
    // Convert the text into a format readable by pjsip
 
179
 
 
180
    message = pj_str ( (char*) text.c_str ());
 
181
 
 
182
    // Must lock dialog
 
183
    pjsip_dlg_inc_lock (dialog);
 
184
 
 
185
    // Create the message request
 
186
    status = pjsip_dlg_create_request (dialog, &msg_method, -1, &tdata);
 
187
    PJ_ASSERT_RETURN (status == PJ_SUCCESS, 1);
 
188
 
 
189
    // Attach "text/plain" body
 
190
    tdata->msg->body = pjsip_msg_body_create (tdata->pool, &type, &subtype, &message);
 
191
 
 
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");
 
196
 
 
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\"");
 
204
 
 
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));
 
209
 
 
210
    // Send the request
 
211
    status = pjsip_dlg_send_request (dialog, tdata, -1, NULL);
 
212
    // PJ_ASSERT_RETURN (status == PJ_SUCCESS, 1);
 
213
 
 
214
    // Done
 
215
    pjsip_dlg_dec_lock (dialog);
 
216
 
 
217
    // Archive the message
 
218
    this->saveMessage (text, "Me", id);
 
219
 
 
220
    printf ("SIPVoIPLink::sendTextMessage %s %s\n", id.c_str(), text.c_str());
 
221
    return PJ_SUCCESS;
 
222
}
 
223
 
 
224
pj_status_t InstantMessaging::send_sip_message (pjsip_inv_session *session, CallID& id, const std::string& message)
 
225
{
 
226
 
 
227
    /* Check the length of the message */
 
228
    if (message.length() < getMessageMaximumSize()) {
 
229
        /* No problem here */
 
230
        sip_send (session, id, message);
 
231
    }
 
232
 
 
233
    else {
 
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();
 
238
        int i = 0;
 
239
 
 
240
        // Maximum is above 1500 character
 
241
        // TODO: Send every messages
 
242
        sip_send (session, id, multiple_messages[i]);
 
243
    }
 
244
 
 
245
    return PJ_SUCCESS;
 
246
}
 
247
 
 
248
 
 
249
bool InstantMessaging::iax_send (iax_session* session, const CallID& id, const std::string& message)
 
250
{
 
251
    if (iax_send_text (session, message.c_str()) != -1)
 
252
        return true;
 
253
    else
 
254
        return false;
 
255
 
 
256
 
 
257
}
 
258
 
 
259
bool InstantMessaging::send_iax_message (iax_session* session, const CallID& id, const std::string& message)
 
260
{
 
261
 
 
262
    bool ret;
 
263
 
 
264
    /* Check the length of the message */
 
265
    if (message.length() < getMessageMaximumSize()) {
 
266
        /* No problem here */
 
267
        ret = iax_send (session, id, message);
 
268
    }
 
269
 
 
270
    else {
 
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();
 
275
        int i = 0;
 
276
 
 
277
        // Maximum is above 1500 character
 
278
        // TODO: Send every messages
 
279
        ret = iax_send (session, id, multiple_messages[i]);
 
280
    }
 
281
 
 
282
    return ret;
 
283
}
 
284
 
 
285
 
 
286
std::vector<std::string> InstantMessaging::split_message (const std::string& text)
 
287
{
 
288
 
 
289
    std::vector<std::string> messages;
 
290
    std::string text_to_split = text;
 
291
 
 
292
    /* Iterate over the message length */
 
293
    while (text_to_split.length() > getMessageMaximumSize()) {
 
294
        /* The remaining string is still too long */
 
295
 
 
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());
 
304
    }
 
305
 
 
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);
 
309
 
 
310
    return messages;
 
311
}
 
312
 
 
313
std::string InstantMessaging::generateXmlUriList (UriList& list)
 
314
{
 
315
 
 
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>");
 
319
 
 
320
    // An iterator over xml attribute
 
321
    UriEntry::iterator iterAttr;
 
322
 
 
323
    // An iterator over list entries
 
324
    UriList::iterator iterEntry = list.begin();
 
325
 
 
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\" />");
 
332
 
 
333
        iterEntry++;
 
334
    }
 
335
 
 
336
    xmlbuffer.append ("</list>");
 
337
    xmlbuffer.append ("</resource-lists>");
 
338
 
 
339
    return xmlbuffer;
 
340
}
 
341
 
 
342
 
 
343
InstantMessaging::UriList InstantMessaging::parseXmlUriList (std::string& urilist)
 
344
{
 
345
    InstantMessaging::UriList list;
 
346
 
 
347
    XML_Parser parser = XML_ParserCreate (NULL);
 
348
    XML_SetUserData (parser, &list);
 
349
    XML_SetElementHandler (parser, startElementCallback, endElementCallback);
 
350
 
 
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");
 
355
    }
 
356
 
 
357
    return list;
 
358
}
 
359
 
 
360
std::string InstantMessaging::appendUriList (std::string text, UriList& list)
 
361
{
 
362
 
 
363
    std::string formatedText = "--boundary Content-Type: text/plain";
 
364
 
 
365
    formatedText.append (text);
 
366
    formatedText.append ("--boundary Content-Type: application/resource-lists+xml");
 
367
    formatedText.append ("Content-Disposition: recipient-list");
 
368
 
 
369
    std::string recipientlist = generateXmlUriList (list);
 
370
 
 
371
    formatedText.append (recipientlist);
 
372
 
 
373
    formatedText.append ("--boundary--");
 
374
 
 
375
    return formatedText;
 
376
}
 
377
 
 
378
std::string InstantMessaging::findTextUriList (std::string& text)
 
379
{
 
380
    std::string ctype = "Content-Type: application/resource-lists+xml";
 
381
    std::string cdispo = "Content-Disposition: recipient-list";
 
382
    std::string boundary = ("--boundary--");
 
383
 
 
384
    // init position pointer
 
385
    size_t pos = 0;
 
386
    size_t begin = 0;
 
387
    size_t end = 0;
 
388
 
 
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");
 
392
 
 
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");
 
396
 
 
397
    // xml content start after content disposition tag (plus \n\n)
 
398
    begin = pos+cdispo.size();
 
399
 
 
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");
 
403
 
 
404
    return text.substr (begin, end-begin);
 
405
}
 
406
 
 
407
std::string InstantMessaging::findTextMessage (std::string& text)
 
408
{
 
409
    std::string ctype = "Content-Type: text/plain";
 
410
    std::string boundary = "--boundary";
 
411
 
 
412
    size_t pos = 0;
 
413
    size_t begin = 0;
 
414
    size_t end = 0;
 
415
 
 
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");
 
419
 
 
420
    // plain text content start after content type tag (plus \n\n)
 
421
    begin = pos+ctype.size();
 
422
 
 
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");
 
426
 
 
427
    return text.substr (begin, end-begin);
 
428
}
 
429
 
 
430
 
 
431
}