notify.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613
  1. /*
  2. * Presence Agent, notifications
  3. *
  4. * $Id$
  5. *
  6. * Copyright (C) 2001-2003 FhG Fokus
  7. *
  8. * This file is part of ser, a free SIP server.
  9. *
  10. * ser is free software; you can redistribute it and/or modify
  11. * it under the terms of the GNU General Public License as published by
  12. * the Free Software Foundation; either version 2 of the License, or
  13. * (at your option) any later version
  14. *
  15. * For a license to use the ser software under conditions
  16. * other than those described here, or to purchase support for this
  17. * software, please contact iptel.org by e-mail at the following addresses:
  18. * [email protected]
  19. *
  20. * ser is distributed in the hope that it will be useful,
  21. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  23. * GNU General Public License for more details.
  24. *
  25. * You should have received a copy of the GNU General Public License
  26. * along with this program; if not, write to the Free Software
  27. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  28. *
  29. * History:
  30. * --------
  31. * 2003-02-28 protocolization of t_uac_dlg completed (jiri)
  32. */
  33. #include "../../str.h"
  34. #include "../../dprint.h"
  35. #include "../../trim.h"
  36. #include "../../parser/parse_event.h"
  37. #include "pa_mod.h"
  38. #include "presentity.h"
  39. #include "paerrno.h"
  40. #include "notify.h"
  41. #include "watcher.h"
  42. #include "qsa_interface.h"
  43. #include <presence/pidf.h>
  44. #include <presence/xpidf.h>
  45. #include <presence/lpidf.h>
  46. #include "winfo_doc.h"
  47. #include "dlist.h"
  48. static str notify = STR_STATIC_INIT("NOTIFY");
  49. /* TM callback processing */
  50. typedef struct {
  51. dlg_id_t id;
  52. str uid;
  53. struct pdomain *domain; /* replace with domain name for safe stop */
  54. char buf[1];
  55. } pa_notify_cb_param_t;
  56. static pa_notify_cb_param_t *create_notify_cb_param(presentity_t *p, watcher_t *w)
  57. {
  58. pa_notify_cb_param_t *cbd;
  59. int size;
  60. if ((!p) || (!w)) return NULL;
  61. if (!w->dialog) return NULL;
  62. size = sizeof(*cbd) + p->uuid.len +
  63. w->dialog->id.call_id.len +
  64. w->dialog->id.rem_tag.len +
  65. w->dialog->id.loc_tag.len;
  66. cbd = (pa_notify_cb_param_t*)mem_alloc(size);
  67. if (!cbd) {
  68. ERR("can't allocate memory (%d bytes)\n", size);
  69. return NULL;
  70. }
  71. cbd->domain = p->pdomain;
  72. cbd->uid.s = cbd->buf;
  73. cbd->uid.len = p->uuid.len;
  74. cbd->id.call_id.s = cbd->uid.s + cbd->uid.len;
  75. cbd->id.call_id.len = w->dialog->id.call_id.len;
  76. cbd->id.rem_tag.s = cbd->id.call_id.s + cbd->id.call_id.len;
  77. cbd->id.rem_tag.len = w->dialog->id.rem_tag.len;
  78. cbd->id.loc_tag.s = cbd->id.rem_tag.s + cbd->id.rem_tag.len;
  79. cbd->id.loc_tag.len = w->dialog->id.loc_tag.len;
  80. /* copy data */
  81. if (p->uuid.s) memcpy(cbd->uid.s, p->uuid.s, p->uuid.len);
  82. if (w->dialog->id.call_id.s) memcpy(cbd->id.call_id.s,
  83. w->dialog->id.call_id.s, w->dialog->id.call_id.len);
  84. if (w->dialog->id.rem_tag.s) memcpy(cbd->id.rem_tag.s,
  85. w->dialog->id.rem_tag.s, w->dialog->id.rem_tag.len);
  86. if (w->dialog->id.loc_tag.s) memcpy(cbd->id.loc_tag.s,
  87. w->dialog->id.loc_tag.s, w->dialog->id.loc_tag.len);
  88. return cbd;
  89. }
  90. static int get_watcher(pa_notify_cb_param_t *cbd,
  91. watcher_t **w, presentity_t **p)
  92. {
  93. int et = EVENT_PRESENCE;
  94. if (find_presentity_uid(cbd->domain, &cbd->uid, p) != 0) {
  95. return -1;
  96. }
  97. if (find_watcher_dlg(*p, &cbd->id, et, w) != 0) {
  98. /* presence watcher NOT found */
  99. et = EVENT_PRESENCE_WINFO;
  100. if (find_watcher_dlg(*p, &cbd->id, et, w) != 0) {
  101. /* presence.winfo watcher NOT found */
  102. return -1;
  103. }
  104. }
  105. return 0;
  106. }
  107. static void destroy_subscription(pa_notify_cb_param_t *cbd)
  108. {
  109. presentity_t *p = NULL;
  110. watcher_t *w = NULL;
  111. /* if (find_pdomain(cbd->domain, &domain) != 0) {
  112. ERR("can't find PA domain\n");
  113. return;
  114. } */
  115. lock_pdomain(cbd->domain);
  116. if (get_watcher(cbd, &w, &p) != 0) {
  117. unlock_pdomain(cbd->domain);
  118. return;
  119. }
  120. remove_watcher(p, w);
  121. free_watcher(w);
  122. unlock_pdomain(cbd->domain);
  123. }
  124. static void refresh_dialog(pa_notify_cb_param_t *cbd, struct sip_msg *m)
  125. {
  126. watcher_t *w;
  127. presentity_t *p;
  128. lock_pdomain(cbd->domain);
  129. if (get_watcher(cbd, &w, &p) >= 0)
  130. tmb.dlg_response_uac(w->dialog, m, notify_is_refresh != 0 ? IS_TARGET_REFRESH : IS_NOT_TARGET_REFRESH);
  131. unlock_pdomain(cbd->domain);
  132. }
  133. static void pa_notify_cb(struct cell* t, int type, struct tmcb_params* params)
  134. {
  135. pa_notify_cb_param_t *cbd = NULL;
  136. if (!params) return;
  137. /* Possible problems - see subscribe_cb in presence_b2b/euac_funcs.c */
  138. if (params->param) cbd = (pa_notify_cb_param_t *)*(params->param);
  139. if (!cbd) {
  140. ERR("BUG empty cbd parameter given to callback function\n");
  141. return;
  142. }
  143. if ((params->code >= 200) && (params->code < 300)) {
  144. if (params->rpl && (params->rpl != FAKED_REPLY))
  145. refresh_dialog(cbd, params->rpl);
  146. }
  147. if ((params->code >= 300)) {
  148. int ignore = 0;
  149. switch (params->code) {
  150. case 408:
  151. if (ignore_408_on_notify) ignore = 1;
  152. /* due to eyeBeam's problems with processing more NOTIFY
  153. * requests sent consequently without big delay */
  154. break;
  155. }
  156. if (!ignore) {
  157. WARN("destroying subscription from callback due to %d response on NOTIFY\n", params->code);
  158. destroy_subscription(cbd);
  159. TRACE("subscription destroyed!!!\n");
  160. }
  161. }
  162. shm_free(cbd);
  163. }
  164. /* helper functions */
  165. static inline int add_event_hf(dstring_t *buf, int event_package)
  166. {
  167. dstr_append_zt(buf, "Event: ");
  168. dstr_append_zt(buf, event_package2str(event_package));
  169. dstr_append_zt(buf, "\r\n");
  170. return 0;
  171. }
  172. static inline int add_cont_type_hf(dstring_t *buf, str *content_type)
  173. {
  174. /* content types can have dynamical parameters (multipart/related)
  175. * => don't generate them "staticaly"; use values created in the
  176. * time of document creation */
  177. if (is_str_empty(content_type)) return 0; /* documents without body doesn't need it */
  178. dstr_append_zt(buf, "Content-Type: ");
  179. dstr_append_str(buf, content_type);
  180. dstr_append_zt(buf, "\r\n");
  181. return 0;
  182. }
  183. static inline int add_subs_state_hf(dstring_t *buf, watcher_status_t _s, time_t _e)
  184. {
  185. char* num;
  186. int len;
  187. str s = STR_NULL;
  188. static str timeout = STR_STATIC_INIT("timeout");
  189. static str rejected = STR_STATIC_INIT("rejected");
  190. switch(_s) {
  191. case WS_ACTIVE: ;
  192. s = watcher_status_names[WS_ACTIVE];
  193. break;
  194. case WS_REJECTED:
  195. case WS_PENDING_TERMINATED:
  196. case WS_TERMINATED:
  197. s = watcher_status_names[WS_TERMINATED];
  198. break;
  199. case WS_PENDING:
  200. s = watcher_status_names[WS_PENDING];
  201. break;
  202. }
  203. dstr_append_zt(buf, "Subscription-State: ");
  204. dstr_append_str(buf, &s);
  205. switch(_s) {
  206. case WS_PENDING:;
  207. case WS_ACTIVE:
  208. dstr_append_zt(buf, ";expires=");
  209. num = int2str((unsigned int)_e, &len);
  210. dstr_append(buf, num, len);
  211. break;
  212. case WS_REJECTED:
  213. case WS_PENDING_TERMINATED:
  214. case WS_TERMINATED:
  215. dstr_append_zt(buf, ";reason=");
  216. if (_e <= 0) dstr_append_str(buf, &timeout);
  217. else dstr_append_str(buf, &rejected);
  218. break;
  219. }
  220. dstr_append_zt(buf, "\r\n");
  221. return 0;
  222. }
  223. static inline int create_headers(struct watcher* _w, str *dst, str *content_type)
  224. {
  225. dstring_t buf;
  226. time_t t;
  227. int err = 0;
  228. dstr_init(&buf, 256);
  229. str_clear(dst);
  230. /* required by RFC 3261 */
  231. dstr_append_zt(&buf, "Max-Forwards: 70\r\n");
  232. /* Event header */
  233. dstr_append_zt(&buf, "Event: ");
  234. dstr_append_zt(&buf, event_package2str(_w->event_package));
  235. dstr_append_zt(&buf, "\r\n");
  236. /* Content-Type header */
  237. /* content types can have dynamical parameters (multipart/related)
  238. * => don't generate them "staticaly"; use values created in the
  239. * time of document creation */
  240. if (!is_str_empty(content_type)) { /* documents without body doesn't need it */
  241. dstr_append_zt(&buf, "Content-Type: ");
  242. dstr_append_str(&buf, content_type);
  243. dstr_append_zt(&buf, "\r\n");
  244. }
  245. /* Contact header */
  246. if (is_str_empty(&_w->server_contact)) {
  247. LOG(L_WARN, "add_contact_hf(): Can't add empty contact to NOTIFY.\n");
  248. }
  249. else {
  250. dstr_append_zt(&buf, "Contact: ");
  251. dstr_append_str(&buf, &_w->server_contact);
  252. dstr_append_zt(&buf, "\r\n");
  253. }
  254. /* Subscription-State header */
  255. if (_w->expires) t = _w->expires - time(0);
  256. else t = 0;
  257. if (add_subs_state_hf(&buf, _w->status, t) < 0) {
  258. LOG(L_ERR, "create_headers(): Error while adding Subscription-State\n");
  259. dstr_destroy(&buf);
  260. return -3;
  261. }
  262. err = dstr_get_str(&buf, dst);
  263. dstr_destroy(&buf);
  264. return err;
  265. }
  266. /* NOTIFY creation functions for specific events/state */
  267. static int prepare_presence_notify(struct retr_buf **dst,
  268. struct presentity* _p, struct watcher* _w,
  269. pa_notify_cb_param_t *cbd)
  270. {
  271. /* Send a notify, saved Contact will be put in
  272. * Request-URI, To will be put in from and new tag
  273. * will be generated, callid will be callid,
  274. * from will be put in to including tag
  275. */
  276. str doc = STR_NULL;
  277. str content_type = STR_NULL;
  278. str headers = STR_NULL;
  279. str body = STR_STATIC_INIT("");
  280. int res = 0;
  281. uac_req_t uac_r;
  282. switch(_w->preferred_mimetype) {
  283. case DOC_XPIDF:
  284. res = create_xpidf_document(&_p->data, &doc, &content_type);
  285. break;
  286. case DOC_LPIDF:
  287. res = create_lpidf_document(&_p->data, &doc, &content_type);
  288. break;
  289. case DOC_CPIM_PIDF:
  290. res = create_cpim_pidf_document(&_p->data, &doc, &content_type);
  291. break;
  292. case DOC_MSRTC_PIDF:
  293. case DOC_PIDF:
  294. default:
  295. res = create_pidf_document(&_p->data, &doc, &content_type);
  296. }
  297. if (res != 0) {
  298. LOG(L_ERR, "can't create presence document (%d)\n", _w->preferred_mimetype);
  299. return -2;
  300. }
  301. if (create_headers(_w, &headers, &content_type) < 0) {
  302. LOG(L_ERR, "send_presence_notify(): Error while adding headers\n");
  303. str_free_content(&doc);
  304. str_free_content(&content_type);
  305. return -7;
  306. }
  307. if (!is_str_empty(&doc)) body = doc;
  308. /* res = tmb.t_request_within(&notify, &headers, &body,
  309. _w->dialog, pa_notify_cb, cbd);*/
  310. set_uac_req(&uac_r,
  311. &notify,
  312. &headers,
  313. &body,
  314. _w->dialog,
  315. TMCB_LOCAL_COMPLETED,
  316. pa_notify_cb,
  317. cbd
  318. );
  319. res = tmb.prepare_request_within(&uac_r, dst);
  320. if (res < 0) {
  321. ERR("Can't send NOTIFY (%d) in dlg %.*s, %.*s, %.*s\n", res,
  322. FMT_STR(_w->dialog->id.call_id),
  323. FMT_STR(_w->dialog->id.rem_tag),
  324. FMT_STR(_w->dialog->id.loc_tag));
  325. }
  326. str_free_content(&doc);
  327. str_free_content(&headers);
  328. str_free_content(&content_type);
  329. return res;
  330. }
  331. static int prepare_winfo_notify(struct retr_buf **dst,
  332. struct presentity* _p, struct watcher* _w,
  333. pa_notify_cb_param_t *cbd)
  334. {
  335. str doc = STR_NULL;
  336. str content_type = STR_NULL;
  337. str headers = STR_NULL;
  338. int res = 0;
  339. str body = STR_STATIC_INIT("");
  340. uac_req_t uac_r;
  341. switch (_w->preferred_mimetype) {
  342. case DOC_WINFO:
  343. create_winfo_document(_p, _w, &doc, &content_type);
  344. DEBUG("winfo document created\n");
  345. break;
  346. /* other formats ? */
  347. default:
  348. ERR("unknow doctype\n");
  349. return -1;
  350. }
  351. if (create_headers(_w, &headers, &content_type) < 0) {
  352. ERR("Error while adding headers\n");
  353. str_free_content(&doc);
  354. str_free_content(&content_type);
  355. return -7;
  356. }
  357. if (!is_str_empty(&doc)) body = doc;
  358. /* res = tmb.t_request_within(&notify, &headers, &body, _w->dialog, 0, 0); */
  359. set_uac_req(&uac_r,
  360. &notify,
  361. &headers,
  362. &body,
  363. _w->dialog,
  364. TMCB_LOCAL_COMPLETED,
  365. pa_notify_cb,
  366. cbd
  367. );
  368. res = tmb.prepare_request_within(&uac_r, dst);
  369. if (res < 0) {
  370. ERR("Can't send watcherinfo notification (%d)\n", res);
  371. }
  372. else {
  373. _w->document_index++; /* increment index for next document */
  374. }
  375. str_free_content(&doc);
  376. str_free_content(&headers);
  377. str_free_content(&content_type);
  378. return res;
  379. }
  380. int prepare_unauthorized_notify(struct retr_buf **dst,
  381. struct presentity* _p, struct watcher* _w,
  382. pa_notify_cb_param_t *cbd)
  383. {
  384. str headers = STR_NULL;
  385. str body = STR_STATIC_INIT("");
  386. int res;
  387. unc_req_t uac_r;
  388. /* send notifications to unauthorized (pending) watchers */
  389. if (create_headers(_w, &headers, NULL) < 0) {
  390. LOG(L_ERR, "notify_unauthorized_watcher(): Error while adding headers\n");
  391. return -7;
  392. }
  393. set_uac_req(&uac_r,
  394. &notify,
  395. &headers,
  396. &body,
  397. _w->dialog,
  398. TMCB_LOCAL_COMPLETED,
  399. pa_notify_cb,
  400. cbd
  401. );
  402. res = tmb.prepare_request_within(&uac_r, dst);
  403. if (res < 0) {
  404. ERR("Can't send NOTIFY (%d) in dlg %.*s, %.*s, %.*s\n", res,
  405. FMT_STR(_w->dialog->id.call_id),
  406. FMT_STR(_w->dialog->id.rem_tag),
  407. FMT_STR(_w->dialog->id.loc_tag));
  408. }
  409. str_free_content(&headers);
  410. return res;
  411. }
  412. int prepare_notify(struct retr_buf **dst,
  413. struct presentity* _p, struct watcher* _w)
  414. {
  415. int rc = 0;
  416. pa_notify_cb_param_t *cbd = NULL;
  417. /* alloc data for callback */
  418. cbd = create_notify_cb_param(_p, _w);
  419. if (!cbd) {
  420. ERR("can't allocate data for callback\n");
  421. /* FIXME: destroy subscription? */
  422. return -1;
  423. }
  424. LOG(L_DBG, "notifying %.*s _p->flags=%x _w->event_package=%d _w->preferred_mimetype=%d _w->status=%d\n",
  425. _w->uri.len, _w->uri.s, _p->flags, _w->event_package, _w->preferred_mimetype, _w->status);
  426. if ((_w->status == WS_PENDING) ||
  427. (_w->status == WS_PENDING_TERMINATED) ||
  428. (_w->status == WS_REJECTED)) {
  429. rc = prepare_unauthorized_notify(dst, _p, _w, cbd);
  430. }
  431. else {
  432. switch (_w->event_package) {
  433. case EVENT_PRESENCE:
  434. rc = prepare_presence_notify(dst, _p, _w, cbd);
  435. break;
  436. case EVENT_PRESENCE_WINFO:
  437. rc = prepare_winfo_notify(dst, _p, _w, cbd);
  438. break;
  439. default:
  440. LOG(L_ERR, "sending notify for unknow package\n");
  441. rc = -1;
  442. }
  443. }
  444. if ((rc < 0) && cbd) shm_free(cbd); /* ??? or not ??? */
  445. else {
  446. /* At least dialog has changed (sometimes more - for example
  447. * version counter for winfo)!
  448. * Ignore errors there because the watcher need NOT to be in DB
  449. * (polling). */
  450. if (use_db) db_update_watcher(_p, _w);
  451. /* if (use_db && (!is_watcher_terminated(_w)))
  452. db_update_watcher(_p, _w); */
  453. }
  454. return rc;
  455. }
  456. int send_notify(struct presentity* _p, struct watcher* _w)
  457. {
  458. struct retr_buf *request;
  459. int res = prepare_notify(&request, _p, _w);
  460. if (res < 0) return res;
  461. tmb.send_prepared_request(request);
  462. return res;
  463. }
  464. int send_winfo_notify_offline(struct presentity* _p,
  465. struct watcher* _w,
  466. offline_winfo_t *info,
  467. transaction_cb completion_cb, void* cbp)
  468. {
  469. str doc = STR_NULL;
  470. str content_type = STR_NULL;
  471. str headers = STR_NULL;
  472. str body = STR_STATIC_INIT("");
  473. uac_req_t uac_r;
  474. switch (_w->preferred_mimetype) {
  475. case DOC_WINFO:
  476. create_winfo_document_offline(_p, _w, info, &doc, &content_type);
  477. break;
  478. /* other formats ? */
  479. default:
  480. ERR("send_winfo_notify: unknow doctype\n");
  481. return -1;
  482. }
  483. if (create_headers(_w, &headers, &content_type) < 0) {
  484. ERR("send_winfo_notify(): Error while adding headers\n");
  485. str_free_content(&doc);
  486. str_free_content(&content_type);
  487. return -7;
  488. }
  489. if (!is_str_empty(&doc)) body = doc;
  490. set_uac_req(&uac_r,
  491. &notify,
  492. &headers,
  493. &body,
  494. _w->dialog,
  495. TMCB_LOCAL_COMPLETED,
  496. completion_cb,
  497. cbp
  498. );
  499. tmb.t_request_within(&uac_r);
  500. str_free_content(&doc);
  501. str_free_content(&headers);
  502. str_free_content(&content_type);
  503. _w->document_index++; /* increment index for next document */
  504. if (use_db) db_update_watcher(_p, _w); /* dialog and index have changed */
  505. return 0;
  506. }