1 // Copyright (C) 2006-2014 David Sugar, Tycho Softworks.
2 // Copyright (C) 2015 Cherokees of Idaho.
3 //
4 // This program is free software; you can redistribute it and/or modify
5 // it under the terms of the GNU General Public License as published by
6 // the Free Software Foundation; either version 3 of the License, or
7 // (at your option) any later version.
8 //
9 // This program is distributed in the hope that it will be useful,
10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.See the
12 // GNU General Public License for more details.
13 //
14 // You should have received a copy of the GNU General Public License
15 // along with this program. If not, see <http://www.gnu.org/licenses/>.
16
18
19 namespace sipwitch {
20
21 static mutex_t msglock;
22 static unsigned keysize = 177;
23 static unsigned pending = 0;
27 static unsigned volatile allocated = 0;
28 static time_t volatile duration = 900;
29
31 {
34
40 }
41
44 {
45 }
46
48 {
49 shell::log(shell::INFO, "checking messages...");
50 msglock.lock();
51 msglock.release();
52 return true;
53 }
54
56 {
57 linked_pointer<message> mp;
59 unsigned msgcount = 0;
60 time_t now;
61
62 if(!pending)
63 return;
64
65 while(msgcount < keysize) {
66 msglock.lock();
67 time(&now);
68 mp = msgs[msgcount];
69 while(mp) {
70 msgnext = mp->getNext();
71 if(mp->expires < now) {
72 mp->delist(&msgs[msgcount]);
73 mp->enlist(&freelist);
74 }
75 mp = msgnext;
76 }
77 msglock.unlock();
78 ++msgcount;
79 }
80 }
81
83 {
84 assert(cfg != NULL);
85
87 linked_pointer<service::keynode> sp = cfg->
getList(
"messages");
88
89 while(sp) {
90 key = sp->getId();
91 value = sp->getPointer();
92 if(key && value) {
94 keysize = atoi(value);
95 else if(!stricmp(key, "expires"))
96 duration = atoi(value) * 60;
97 }
98 sp.next();
99 }
100
102 return;
103
106 }
107
109 {
110 assert(fp != NULL);
111 fprintf(fp, "Messaging:\n");
112 fprintf(fp, " allocated messages: %d\n", allocated);
113 fprintf(fp, " pending messages: %d\n", pending);
114 }
115
117 {
118 assert(uid == NULL || *uid != 0);
119
120 linked_pointer<message> mp;
122 unsigned path;
123 time_t now;
124 if(!uid || !pending)
125 return;
126
127 path = NamedObject::keyindex(uid, keysize);
128 msglock.lock();
129 time(&now);
130 mp = msgs[path];
131 while(mp) {
132 next = mp->getNext();
133 if(!stricmp(mp->user, uid)) {
134 --pending;
135 mp->delist(&msgs[path]);
136 if(mp->expires < now)
137 mp->enlist(&freelist);
138 else
139 mp->enlist(&sending);
140 }
141 mp = next;
142 }
143 msglock.unlock();
144 }
145
146 int messages::deliver(
const char *to,
const char *reply,
const char *from, caddr_t text,
size_t len,
const char *msgtype,
const char *digest)
147 {
149
150 if(!msgtype)
151 msgtype = "text/plain";
152
153 if(len >
sizeof(msg->
body))
155
156 msglock.lock();
157 msg =
static_cast<message *
>(freelist);
158 if(msg)
159 freelist = msg->getNext();
160 msglock.unlock();
161 if(!msg) {
162 ++allocated;
164 }
166 String::set(msg->
reply,
sizeof(msg->
reply), reply);
167 String::set(msg->
from,
sizeof(msg->
from), from);
168 String::set(msg->
type,
sizeof(msg->
type), msgtype);
169 memset(msg->
body, 0,
sizeof(msg->
body));
170 if(len)
171 memcpy(msg->
body, text, len);
173
174 if(!strchr(to, '@')) {
175 String::set(msg->
user,
sizeof(msg->
user), to);
177 }
178 return remote(to, msg, digest);
179 }
180
182 {
184 const char *scheme;
188
190 scheme = "sips";
191 else
192 scheme = "sip";
193
194 if(!host) {
195 host = "127.0.0.1";
196 #ifdef AF_INET6
198 host = "::1";
199 #endif
200 }
201
202 if(strchr(host, ':'))
203 snprintf(from, sizeof(from), "<%s:%s@[%s]:%u>",
204 scheme, sysid, host, port);
205 else
206 snprintf(from, sizeof(from), "<%s:%s@%s:%u>",
207 scheme, sysid, host, port);
208
209 return deliver(to, sysid, from, (caddr_t)text, strlen(text),
"text/plain");
210 }
211
213 {
214 shell::debug(3,
"instant message delivered to %s from %s", to, msg->
reply);
215
222 const char *schema = NULL;
223
224 if(!ctx)
225 return error;
226
227 if(eq(to, "tcp:", 4)) {
228 schema = "sip";
229 to += 4;
230 }
231 else if(eq(to, "udp:", 4)) {
232 schema = "sip";
233 to += 4;
234 }
235
236 if(schema) {
237 snprintf(rewrite, sizeof(rewrite), "%s:%s", schema, to);
238 to = rewrite;
239 }
240
242 char *authbuf = new char[1024];
243 stringbuf<64> response;
244 stringbuf<64> once;
245 char nounce[64];
246 char *req = NULL;
248 snprintf(authbuf, 1024,
"%s:%s", im->
sip_method, req);
249 Random::uuid(nounce);
250
251 digest_t auth("md5");
252 auth.puts(nounce);
253 once = *auth;
255 auth.puts(authbuf);
256 response = *auth;
257 snprintf(authbuf, 1024, "%s:%s:%s", digest, *once, *response);
258 auth.reset();
259 auth.puts(authbuf);
260 response = *auth;
261 snprintf(authbuf, 1024,
262 "Digest username=\"%s\""
263 ",realm=\"%s\""
264 ",uri=\"%s\""
265 ",response=\"%s\""
266 ",nonce=\"%s\""
267 ",algorithm=%s"
270 delete[] authbuf;
272 }
273 if(im) {
278 }
279 return error;
280 }
281
283 {
284 assert(msg != NULL);
285
286 linked_pointer<registry::target> tp;
289 time_t now;
290 unsigned msgcount = 0;
293
294 time(&now);
295 if(!rr)
297
299 shell::debug(3,
"instant message failed for %s from %s; error=%d", msg->
user, msg->
reply, error);
300 goto final;
301 }
302
303 shell::debug(3,
"instant message delivered to %s from %s", msg->
user, msg->
reply);
305 while(tp) {
306 if(!rr->
expires || tp->expires > now) {
308 to[0] = '<';
309 String::add(to, sizeof(to), ";lr>");
310 im = NULL;
311
314 to[0] = '<';
315 String::add(to, sizeof(to), ">");
319 }
323 ++msgcount;
324 }
325 }
326 tp.next();
327 }
328
329 // as long as we sent to one extension, we are ok...
330 if(msgcount)
332
333 final:
335 msglock.lock();
336 msg->enlist(&freelist);
337 msglock.release();
338 return error;
339 }
340
342 {
344 time_t now;
345
346 for(;;) {
347 msglock.lock();
348 if(sending)
349 time(&now);
350 while(sending) {
352 sending = msg->getNext();
354 break;
355 msg->enlist(&freelist);
356 msg = NULL;
357 }
358 msglock.release();
360 return;
361 }
362 }
363
364 } // end namespace
Structure for SIP Message (REQUEST and RESPONSE).
int osip_uri_to_str(const osip_uri_t *url, char **dest)
Get a string representation of a url element.
void reload(service *cfg)
static void automatic(void)
static void update(const char *userid)
const char *volatile published
void osip_to_free(osip_to_t *header)
Free a To element.
char reply[MAX_USERID_SIZE]
keynode * getList(const char *path)
static bool make_request_message(context_t ctx, const char *method, const char *to, const char *from, msg_t *msg, const char *route=NULL)
union sipwitch::MappedRegistry::@6 source
static int deliver(message *msg)
int osip_message_set_to(osip_message_t *sip, const char *hvalue)
Set the To header.
#define SIP_MESSAGE_TOO_LARGE
static const char * getRealm(void)
osip_uri_t * req_uri
Request-Uri (SIP request only)
struct sipwitch::MappedRegistry::@6::@8 internal
static bool is_configured(void)
static void siplog(voip::msg_t msg)
static void attach(msg_t msg, const char *type, const char *body)
static void detach(mapped *m)
char user[MAX_USERID_SIZE]
static const char * getDigest(void)
static int system(const char *to, const char *message)
voip::context_t route(const char *uri, char *buf, size_t size)
static char * sipAddress(struct sockaddr_internet *addr, char *buf, const char *user=NULL, size_t size=MAX_URI_SIZE)
static void send_request_message(context_t ctx, msg_t msg)
static mapped * access(const char *id)
static int remote(const char *to, message *msg, const char *digest=NULL)
System configuration instance and service functions.
static unsigned short sip_port
char * sip_method
METHOD (SIP request only)
static void header(msg_t msg, const char *key, const char *value)