InstantMessaging.cpp 7.37 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 "InstantMessaging.h"
33
#include "logger.h"
34
35
#include "expat.h"

Emmanuel Milou's avatar
[#2402]    
Emmanuel Milou committed
36
37
namespace sfl
{
38

39
40
static void XMLCALL startElementCallback (void *userData, const char *name, const char **atts)
{
Rafaël Carré's avatar
Rafaël Carré committed
41
42
    if (strcmp (name, "entry"))
    	return;
43

Rafaël Carré's avatar
Rafaël Carré committed
44
	sfl::InstantMessaging::UriEntry entry = sfl::InstantMessaging::UriEntry();
45

Rafaël Carré's avatar
Rafaël Carré committed
46
47
	for (const char **att = atts; *att; att += 2)
		entry.insert (std::pair<std::string, std::string> (*att, *(att+1)));
48

Rafaël Carré's avatar
Rafaël Carré committed
49
	(static_cast<sfl::InstantMessaging::UriList *> (userData))->push_back(entry);
50
51
}

52
static void XMLCALL endElementCallback (void * /*userData*/, const char * /*name*/)
53
54
55
56
{
}


Rafaël Carré's avatar
Rafaël Carré committed
57
InstantMessaging::InstantMessaging() {}
58
59


Alexandre Savard's avatar
Alexandre Savard committed
60
InstantMessaging::~InstantMessaging() {}
61

62
bool InstantMessaging::saveMessage (const std::string& message, const std::string& author, const std::string& id, int mode)
Alexandre Savard's avatar
Alexandre Savard committed
63
64
{
    std::ofstream File;
Rafaël Carré's avatar
Rafaël Carré committed
65
    std::string filename = "im:" + id;
Alexandre Savard's avatar
Alexandre Savard committed
66
    File.open (filename.c_str (), (std::_Ios_Openmode) mode);
67

Alexandre Savard's avatar
Alexandre Savard committed
68
69
    if (!File.good () || !File.is_open ())
        return false;
70

Alexandre Savard's avatar
Alexandre Savard committed
71
72
    File << "[" << author << "] " << message << '\n';
    File.close ();
73

Alexandre Savard's avatar
Alexandre Savard committed
74
75
    return true;
}
76

Rafaël Carré's avatar
Rafaël Carré committed
77
void InstantMessaging::sip_send (pjsip_inv_session *session, const std::string& id, const std::string& text)
Alexandre Savard's avatar
Alexandre Savard committed
78
79
{
    pjsip_tx_data *tdata;
80

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

Alexandre Savard's avatar
Alexandre Savard committed
83
    pjsip_dlg_inc_lock (dialog);
84

Rafaël Carré's avatar
Rafaël Carré committed
85
86
87
88
89
    pjsip_method msg_method = { PJSIP_OTHER_METHOD, pj_str((char*)"MESSAGE") };
    if (pjsip_dlg_create_request (dialog, &msg_method, -1, &tdata) != PJ_SUCCESS) {
        pjsip_dlg_dec_lock (dialog);
    	return;
    }
90

Rafaël Carré's avatar
Rafaël Carré committed
91
92
93
    const pj_str_t type =  pj_str((char*)"text");
    const pj_str_t subtype = pj_str((char*)"plain");
    pj_str_t message = pj_str ( (char*) text.c_str ());
Alexandre Savard's avatar
Alexandre Savard committed
94
    tdata->msg->body = pjsip_msg_body_create (tdata->pool, &type, &subtype, &message);
95

Rafaël Carré's avatar
Rafaël Carré committed
96
    pjsip_dlg_send_request (dialog, tdata, -1, NULL);
Alexandre Savard's avatar
Alexandre Savard committed
97
    pjsip_dlg_dec_lock (dialog);
98

Rafaël Carré's avatar
Rafaël Carré committed
99
    saveMessage (text, "Me", id);
Alexandre Savard's avatar
Alexandre Savard committed
100
}
101

Rafaël Carré's avatar
Rafaël Carré committed
102
void InstantMessaging::send_sip_message (pjsip_inv_session *session, const std::string& id, const std::string& message)
Alexandre Savard's avatar
Alexandre Savard committed
103
{
Rafaël Carré's avatar
Rafaël Carré committed
104
105
106
107
	std::vector<std::string> msgs = split_message (message);
	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
108
}
109

110

Rafaël Carré's avatar
Rafaël Carré committed
111
void InstantMessaging::send_iax_message (iax_session* session, const std::string& id, const std::string& message)
112
{
Rafaël Carré's avatar
Rafaël Carré committed
113
114
115
	std::vector<std::string> msgs = split_message (message);
	std::vector<std::string>::const_iterator iter;
	for (iter = msgs.begin(); iter != msgs.end(); ++iter)
Rafaël Carré's avatar
Rafaël Carré committed
116
		iax_send_text(session, (*iter).c_str());
117
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;
Rafaël Carré's avatar
Rafaël Carré committed
123
124
125
126
127
    size_t len = getMessageMaximumSize();

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

Rafaël Carré's avatar
Rafaël Carré committed
130
    messages.push_back (text);
131

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

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

Rafaël Carré's avatar
Rafaël Carré committed
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
150
151
152
}


InstantMessaging::UriList InstantMessaging::parseXmlUriList (std::string& urilist)
{
    InstantMessaging::UriList list;

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

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

    return list;
}

165
166
std::string InstantMessaging::appendUriList (std::string text, UriList& list)
{
Rafaël Carré's avatar
Rafaël Carré committed
167
168
169
170
171
172
173
    return
    	"--boundary Content-Type: text/plain" +
    	text +
    	"--boundary Content-Type: application/resource-lists+xml" +
    	"Content-Disposition: recipient-list" +
		generateXmlUriList (list) +
		"--boundary--";
174
175
}

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

182
    // init position pointer
183
    size_t pos = 0;
184
185
    size_t begin = 0;
    size_t end = 0;
186

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

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

195
    // xml content start after content disposition tag (plus \n\n)
196
    begin = pos+cdispo.size();
197
198
199
200
201
202

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

    return text.substr (begin, end-begin);
203
204
}

205
std::string InstantMessaging::findTextMessage (std::string& text)
206
207
{
    std::string ctype = "Content-Type: text/plain";
208

Rafaël Carré's avatar
Rafaël Carré committed
209
210
	size_t pos = text.find (ctype);
    if (pos == std::string::npos)
211
212
        throw InstantMessageException ("Could not find Content-Type tag while parsing sip message for text");

Rafaël Carré's avatar
Rafaël Carré committed
213
    size_t begin = pos+ctype.size();
214

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

219
    return text.substr (begin, end-begin);
220
221
222
}


223
}