~ubuntu-branches/ubuntu/wily/sflphone/wily

« back to all changes in this revision

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

  • Committer: Package Import Robot
  • Author(s): Francois Marier
  • Date: 2011-11-25 13:24:12 UTC
  • mfrom: (4.1.10 sid)
  • Revision ID: package-import@ubuntu.com-20111125132412-dc4qvhyosk74cd42
Tags: 1.0.1-4
Don't assume that arch:all packages will get built (closes: #649726)

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 *  Copyright (C) 2004, 2005, 2006, 2009, 2008, 2009, 2010, 2011 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
 
}