instant_messaging.cpp 7.47 KB
Newer Older
1
/*
2
 *  Copyright (C) 2004, 2005, 2006, 2008, 2009, 2010, 2011 Savoir-Faire Linux Inc.
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
 *  Author: Emmanuel Milou <emmanuel.milou@savoirfairelinux.com>
 *  Author: Alexandre Savard <alexandre.savard@savoirfairelinux.com>
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 3 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *  Additional permission under GNU GPL version 3 section 7:
 *
 *  If you modify this program, or any covered work, by linking or
 *  combining it with the OpenSSL project's OpenSSL library (or a
 *  modified version of that library), containing parts covered by the
 *  terms of the OpenSSL or SSLeay licenses, Savoir-Faire Linux Inc.
 *  grants you additional permission to convey the resulting work.
 *  Corresponding Source for a non-source form of such a combination
 *  shall include the source code for the parts of OpenSSL used as well
 *  as that of the covered work.
 */

32
#include "instant_messaging.h"
33
#include "logger.h"
34
#include "expat.h"
35

36
37
38
namespace {
void XMLCALL
startElementCallback(void *userData, const char *name, const char **atts)
39
{
40
41
    if (strcmp(name, "entry"))
        return;
42

43
    sfl::InstantMessaging::UriEntry entry = sfl::InstantMessaging::UriEntry();
44

45
46
    for (const char **att = atts; *att; att += 2)
        entry.insert(std::pair<std::string, std::string> (*att, *(att+1)));
47

48
    static_cast<sfl::InstantMessaging::UriList *>(userData)->push_back(entry);
49
50
}

51
void XMLCALL endElementCallback(void * /*userData*/, const char * /*name*/)
52
{}
53
} // end anonymous namespace
54

55
56
namespace sfl {
bool InstantMessaging::saveMessage(const std::string &message, const std::string &author, const std::string &id, int mode)
Alexandre Savard's avatar
Alexandre Savard committed
57
58
{
    std::ofstream File;
Rafaël Carré's avatar
Rafaël Carré committed
59
    std::string filename = "im:" + id;
60
    File.open(filename.c_str(), (std::_Ios_Openmode) mode);
61

62
    if (!File.good() || !File.is_open())
Alexandre Savard's avatar
Alexandre Savard committed
63
        return false;
64

Alexandre Savard's avatar
Alexandre Savard committed
65
    File << "[" << author << "] " << message << '\n';
66
    File.close();
67

Alexandre Savard's avatar
Alexandre Savard committed
68
69
    return true;
}
70

71
void InstantMessaging::sip_send(pjsip_inv_session *session, const std::string& id, const std::string& text)
Alexandre Savard's avatar
Alexandre Savard committed
72
73
{
    pjsip_tx_data *tdata;
74

Rafaël Carré's avatar
Rafaël Carré committed
75
    pjsip_dialog* dialog = session->dlg;
76

77
    pjsip_dlg_inc_lock(dialog);
78

Rafaël Carré's avatar
Rafaël Carré committed
79
    pjsip_method msg_method = { PJSIP_OTHER_METHOD, pj_str((char*)"MESSAGE") };
80
81
82
83

    if (pjsip_dlg_create_request(dialog, &msg_method, -1, &tdata) != PJ_SUCCESS) {
        pjsip_dlg_dec_lock(dialog);
        return;
Rafaël Carré's avatar
Rafaël Carré committed
84
    }
85

86
87
    const pj_str_t type =  pj_str((char*) "text");
    const pj_str_t subtype = pj_str((char*) "plain");
88

89
90
91
92
93
94
95
96
    pj_str_t message = pj_str((char*) text.c_str());

    tdata->msg->body = pjsip_msg_body_create(tdata->pool, &type, &subtype, &message);

    pjsip_dlg_send_request(dialog, tdata, -1, NULL);
    pjsip_dlg_dec_lock(dialog);

    saveMessage(text, "Me", id);
Alexandre Savard's avatar
Alexandre Savard committed
97
}
98

99
void InstantMessaging::send_sip_message(pjsip_inv_session *session, const std::string &id, const std::string &message)
Alexandre Savard's avatar
Alexandre Savard committed
100
{
101
    std::vector<std::string> msgs(split_message(message));
102
103
104
105
    std::vector<std::string>::const_iterator iter;

    for (iter = msgs.begin(); iter != msgs.end(); ++iter)
        sip_send(session, id, *iter);
Alexandre Savard's avatar
Alexandre Savard committed
106
}
107

108
#if HAVE_IAX
109
void InstantMessaging::send_iax_message(iax_session *session, const std::string &/* id */, const std::string &message)
110
{
111
    std::vector<std::string> msgs(split_message(message));
112
113
114
115
    std::vector<std::string>::const_iterator iter;

    for (iter = msgs.begin(); iter != msgs.end(); ++iter)
        iax_send_text(session, (*iter).c_str());
116
}
117
#endif
118
119


Rafaël Carré's avatar
Rafaël Carré committed
120
std::vector<std::string> InstantMessaging::split_message(std::string text)
Alexandre Savard's avatar
Alexandre Savard committed
121
122
{
    std::vector<std::string> messages;
123
    size_t len = MAXIMUM_MESSAGE_LENGTH;
Rafaël Carré's avatar
Rafaël Carré committed
124
125

    while (text.length() > len - 2) {
126
        messages.push_back(text.substr(len - 2) + "\n\n");
Rafaël Carré's avatar
Rafaël Carré committed
127
        text = text.substr(len - 2);
Alexandre Savard's avatar
Alexandre Savard committed
128
    }
129

130
    messages.push_back(text);
131

Alexandre Savard's avatar
Alexandre Savard committed
132
133
    return messages;
}
134

135
std::string InstantMessaging::generateXmlUriList(UriList &list)
136
{
Rafaël Carré's avatar
Rafaël Carré committed
137
    std::string xmlbuffer = "<?xml version=\"1.0\" encoding=\"UTF-8\"?>"
138
139
                                  "<resource-lists xmlns=\"urn:ietf:params:xml:ns:resource-lists\" xmlns:cp=\"urn:ietf:params:xml:ns:copycontrol\">"
                                  "<list>";
140

141
142
    for (UriList::iterator iter = list.begin(); iter != list.end(); ++iter)
        xmlbuffer += "<entry uri=" + (*iter)[sfl::IM_XML_URI] + " cp:copyControl=\"to\" />";
143

Rafaël Carré's avatar
Rafaël Carré committed
144
    return xmlbuffer + "</list></resource-lists>";
145
146
147
}


148
149
InstantMessaging::UriList
InstantMessaging::parseXmlUriList(const std::string &urilist)
150
151
152
{
    InstantMessaging::UriList list;

153
154
155
    XML_Parser parser = XML_ParserCreate(NULL);
    XML_SetUserData(parser, &list);
    XML_SetElementHandler(parser, startElementCallback, endElementCallback);
156

157
    if (XML_Parse(parser, urilist.c_str(), urilist.size(), 1) == XML_STATUS_ERROR) {
158
        ERROR("%s at line %lu\n", XML_ErrorString(XML_GetErrorCode(parser)),
159
160
               XML_GetCurrentLineNumber(parser));
        throw InstantMessageException("Error while parsing uri-list xml content");
161
162
163
164
165
    }

    return list;
}

166
std::string InstantMessaging::appendUriList(const std::string &text, UriList& list)
167
{
168
169
170
171
    return "--boundary Content-Type: text/plain" + text +
           "--boundary Content-Type: application/resource-lists+xml" +
           "Content-Disposition: recipient-list" + generateXmlUriList(list) +
           "--boundary--";
172
173
}

174
std::string InstantMessaging::findTextUriList(const std::string &text)
175
{
176
177
178
    const std::string ctype("Content-Type: application/resource-lists+xml");
    const std::string cdispo("Content-Disposition: recipient-list");
    const std::string boundary("--boundary--");
179

180
    // init position pointer
181
182
    size_t pos = 0;

183
    // find the content type
184
185
    if ((pos = text.find(ctype)) == std::string::npos)
        throw InstantMessageException("Could not find Content-Type tag while parsing sip message for recipient-list");
186

187
    // find the content disposition
188
189
    if ((pos = text.find(cdispo, pos)) == std::string::npos)
        throw InstantMessageException("Could not find Content-Disposition tag while parsing sip message for recipient-list");
190

191
    // xml content start after content disposition tag (plus \n\n)
192
    const size_t begin = pos + cdispo.size();
193
194

    // find final boundary
195
    size_t end;
196
197
    if ((end = text.find(boundary, begin)) == std::string::npos)
        throw InstantMessageException("Could not find final \"boundary\" while parsing sip message for recipient-list");
198

199
    return text.substr(begin, end - begin);
200
201
}

202
std::string InstantMessaging::findTextMessage(const std::string &text)
203
204
{
    std::string ctype = "Content-Type: text/plain";
205
    const size_t pos = text.find(ctype);
Rafaël Carré's avatar
Rafaël Carré committed
206
    if (pos == std::string::npos)
207
        throw InstantMessageException("Could not find Content-Type tag while parsing sip message for text");
208

209
    const size_t begin = pos + ctype.size();
210

211
    const size_t end = text.find("--boundary", begin);
Rafaël Carré's avatar
Rafaël Carré committed
212
    if (end == std::string::npos)
213
        throw InstantMessageException("Could not find end of text \"boundary\" while parsing sip message for text");
214

215
    return text.substr(begin, end - begin);
216
217
218
}


219
}