main.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502
  1. /*
  2. * Copyright (C) 2009 Mamadou Diop.
  3. *
  4. * Contact: Mamadou Diop <diopmamadou(at)doubango.org>
  5. *
  6. * This file is part of Open Source Doubango Framework.
  7. *
  8. * DOUBANGO is free software: you can redistribute it and/or modify
  9. * it under the terms of the GNU General Public License as published by
  10. * the Free Software Foundation, either version 3 of the License, or
  11. * (at your option) any later version.
  12. *
  13. * DOUBANGO is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with DOUBANGO.
  20. *
  21. */
  22. #include "main.h"
  23. #include "common.h"
  24. #include "invite.h"
  25. #include "message.h"
  26. #include "options.h"
  27. #include "publish.h"
  28. #include "register.h"
  29. #include "subscribe.h"
  30. #include "tinydav.h" /* Doubango Audio/Video Framework */
  31. #include <stdio.h>
  32. #include <string.h>
  33. #include <ctype.h>
  34. #define LINE_DELIM "\n \r\n"
  35. /* === global variables === */
  36. ctx_t* ctx = tsk_null;
  37. const char* trim(const char*);
  38. int insert(char* dest, tsk_size_t index, tsk_size_t dest_size, char* src, tsk_size_t src_size);
  39. int update_param(const char* , const char* );
  40. int execute(const cmd_t* );
  41. /* === entry point === */
  42. int main(int argc, char** argv)
  43. {
  44. char cmdbuf[4096];
  45. tsk_buffer_t* buffer = tsk_null;
  46. cmd_t* cmd = tsk_null;
  47. tsk_bool_t comment = tsk_false;
  48. int ret;
  49. int i, index;
  50. const char* start = tsk_null, *end = tsk_null;
  51. int a = 32 | 1 | 2;
  52. /* Copyright */
  53. printf("Doubango Project (tinyDEMO)\nCopyright (C) 2009 - 2013 Mamadou Diop \n\n");
  54. /* Initialize Network Layer ==> Mandatory */
  55. tnet_startup();
  56. /* Initialize Doubango Audio/Video Framework ==> will register all plugins(codecs and sessions)
  57. * Not mandatory if you have your own plugins*/
  58. tdav_init();
  59. /* Print Usage */
  60. //cmd_print_help();
  61. /* create user's ctx */
  62. if(!(ctx = ctx_create()) || !ctx->stack) {
  63. TSK_DEBUG_ERROR("Failed to create user's ctx.");
  64. goto bail;
  65. }
  66. /* create new buffer */
  67. if(!(buffer = tsk_buffer_create_null())) {
  68. TSK_DEBUG_ERROR("Failed to create new buffer.");
  69. goto bail;
  70. }
  71. /* initial args */
  72. for(i=1 /* index zero contains the exe path */, index=0; i<argc && argv[i]; i++) {
  73. if(index) {
  74. tsk_buffer_append(buffer, " ", 1);
  75. }
  76. tsk_buffer_append(buffer, argv[i], tsk_strlen(argv[i]));
  77. }
  78. /* If initial args ==> parse it now */
  79. if(buffer->size) {
  80. TSK_DEBUG_INFO("Initial command-line: %s", buffer->data);
  81. goto init_buffer;
  82. }
  83. /* always use fgets() instead of gets. gets() is considered to be unsafe.(Android and Mac OS X will warn) */
  84. while(fgets(cmdbuf, sizeof(cmdbuf), stdin)) {
  85. TSK_DEBUG_INFO("Command-Line: %s", cmdbuf);
  86. tsk_buffer_cleanup(buffer); /* cannot read from console while executing scenario */
  87. tsk_buffer_append(buffer, cmdbuf, tsk_strlen(cmdbuf));
  88. init_buffer:
  89. start = buffer->data;
  90. //start = trim(start);
  91. end = start + buffer->size;
  92. if(start >= end) {
  93. TSK_DEBUG_INFO("Empty buffer");
  94. continue;
  95. }
  96. parse_buffer:
  97. TSK_OBJECT_SAFE_FREE(cmd); /* Free old value */
  98. cmd = cmd_parse(start, (end-start), &comment, ctx->params);
  99. if(cmd) {
  100. if(comment || cmd->type == cmd_none) {
  101. goto nex_line;
  102. }
  103. }
  104. else {
  105. continue;
  106. }
  107. /* Load from scenario file? */
  108. if(cmd->type == cmd_scenario) {
  109. FILE* file;
  110. const opt_t* opt;
  111. tsk_size_t read = 0;
  112. tsk_bool_t rm_lf = tsk_false;
  113. if((opt = opt_get_by_type(cmd->opts, opt_path)) && !tsk_strnullORempty(opt->value)) { /* --path option */
  114. if((file = fopen(opt->value, "r"))) {
  115. memset(cmdbuf, '\0', sizeof(cmdbuf)), cmdbuf[0] = '\n';
  116. read = fread(cmdbuf+1, sizeof(uint8_t), sizeof(cmdbuf)-1, file);
  117. fclose(file), file = tsk_null;
  118. if(read == 0) {
  119. TSK_DEBUG_ERROR("[%s] is empty.", opt->value);
  120. goto nex_line;
  121. }
  122. else if(read == sizeof(cmdbuf)-1) {
  123. TSK_DEBUG_ERROR("Buffer too short.");
  124. goto nex_line;
  125. }
  126. read++; /* \n */
  127. /* repplace all '\' with spaces (easier than handling that in the ragel file) */
  128. for(i=0; ((tsk_size_t)i)<read; i++) {
  129. if(cmdbuf[i] == '\\') {
  130. cmdbuf[i] = ' ';
  131. rm_lf = tsk_true;
  132. }
  133. else if(rm_lf && cmdbuf[i] == '\n') {
  134. cmdbuf[i] = ' ';
  135. rm_lf = tsk_false;
  136. }
  137. }
  138. cmdbuf[read] = '\n';
  139. /* insert embedded scenario */
  140. if((index = tsk_strindexOf(start, (end-start), "\n")) == -1) { /* ++sn line */
  141. index = buffer->size;
  142. }
  143. else {
  144. index += (start - ((const char*)buffer->data));
  145. }
  146. if(tsk_buffer_insert(buffer, index, cmdbuf, read)) {
  147. continue;
  148. }
  149. else {
  150. start = ((const char*)buffer->data) + index; // because insert use realloc()
  151. end = (((const char*)buffer->data) + buffer->size);
  152. goto nex_line;
  153. }
  154. }
  155. else {
  156. TSK_DEBUG_ERROR("Failed to open scenario-file [%s].", opt->value);
  157. goto nex_line;
  158. }
  159. continue;
  160. }
  161. else {
  162. TSK_DEBUG_ERROR("++scenario command must have --path option.");
  163. continue;
  164. }
  165. }
  166. /* execute current command */
  167. switch(cmd->type) {
  168. case cmd_exit:
  169. TSK_DEBUG_INFO("Exit/Quit");
  170. goto bail;
  171. default:
  172. ret = execute(cmd);
  173. break;
  174. }
  175. /* next line */
  176. nex_line:
  177. if((index = tsk_strindexOf(start, (end - start), "\n")) !=-1) {
  178. start += index;
  179. while((start < end) && isspace(*start)) {
  180. start ++;
  181. }
  182. if((start + 2/*++*/) < end) {
  183. goto parse_buffer; /* next line */
  184. }
  185. else {
  186. continue; /* wait for new commands */
  187. }
  188. }
  189. } /* while(buffer) */
  190. bail:
  191. /* Free current command */
  192. TSK_OBJECT_SAFE_FREE(cmd);
  193. /* Free buffer */
  194. TSK_OBJECT_SAFE_FREE(buffer);
  195. /* Destroy the user's ctx */
  196. TSK_OBJECT_SAFE_FREE(ctx);
  197. /* Deinitialize Doubango Audio/Video Framework ==> will unregister all plugins(codecs and sessions)
  198. * Not mandatory */
  199. tdav_init();
  200. /* Uninitilize Network Layer */
  201. tnet_cleanup();
  202. #if ANDROID
  203. exit(0);
  204. #endif
  205. return 0;
  206. }
  207. const char* trim(const char* str)
  208. {
  209. while(isspace(*str)) {
  210. str++;
  211. }
  212. return str;
  213. }
  214. int update_param(const char* pname, const char* pvalue)
  215. {
  216. if(ctx->params && pname) {
  217. const tsk_param_t* param;
  218. if((param = tsk_params_get_param_by_name(ctx->params, pname))) {
  219. tsk_strupdate(&TSK_PARAM(param)->value, pvalue);
  220. return 0;
  221. }
  222. else {
  223. TSK_DEBUG_INFO("[%s] parameter does not exist", pname);
  224. }
  225. }
  226. else {
  227. TSK_DEBUG_ERROR("Invalid parameter");
  228. }
  229. return -1;
  230. }
  231. int execute(const cmd_t* cmd)
  232. {
  233. int ret = 0;
  234. tsip_ssession_id_t sid;
  235. tsk_istr_t istr;
  236. if(!cmd) {
  237. TSK_DEBUG_ERROR("Invalid parameter");
  238. return -1;
  239. }
  240. tsk_safeobj_lock(ctx);
  241. switch(cmd->type) {
  242. case cmd_audio:
  243. case cmd_audiovideo: {
  244. TSK_DEBUG_INFO("command=audio/video");
  245. if((sid = invite_handle_cmd(cmd->type, cmd->opts)) != TSIP_SSESSION_INVALID_ID) {
  246. if(cmd->sidparam) {
  247. tsk_itoa(sid, &istr);
  248. update_param(cmd->sidparam, istr);
  249. }
  250. }
  251. break;
  252. }
  253. case cmd_config_session: {
  254. TSK_DEBUG_INFO("command=config-session");
  255. break;
  256. }
  257. case cmd_config_stack: {
  258. TSK_DEBUG_INFO("command=config-satck");
  259. ret = stack_config(cmd->opts);
  260. break;
  261. }
  262. case cmd_dtmf: {
  263. const opt_t* opt;
  264. TSK_DEBUG_INFO("command=dtmf");
  265. if(!(opt = opt_get_by_type(cmd->opts, opt_sid)) || tsk_strnullORempty(opt->value)) { /* --sid option */
  266. TSK_DEBUG_ERROR("++dtmf command need --sid option");
  267. break;
  268. }
  269. if(!(opt = opt_get_by_type(cmd->opts, opt_event)) || tsk_strnullORempty(opt->value)) { /* --event option */
  270. TSK_DEBUG_ERROR("++dtmf command need --event option");
  271. break;
  272. }
  273. invite_handle_cmd(cmd->type, cmd->opts);
  274. break;
  275. }
  276. case cmd_dump: {
  277. TSK_DEBUG_INFO("command=dump");
  278. ret = stack_dump();
  279. break;
  280. }
  281. case cmd_ect: {
  282. const opt_t* opt;
  283. TSK_DEBUG_INFO("command=ect");
  284. if((opt = opt_get_by_type(cmd->opts, opt_sid)) && !tsk_strnullORempty(opt->value)) {
  285. TSK_DEBUG_ERROR("++ect command need --sid option");
  286. ret = -1;
  287. break;
  288. }
  289. if((opt = opt_get_by_type(cmd->opts, opt_to)) && !tsk_strnullORempty(opt->value)) {
  290. TSK_DEBUG_ERROR("++ect command need --to option");
  291. ret = -1;
  292. break;
  293. }
  294. invite_handle_cmd(cmd->type, cmd->opts);
  295. break;
  296. }
  297. case cmd_exit: {
  298. TSK_DEBUG_INFO("command=exit");
  299. goto bail;
  300. break;
  301. }
  302. case cmd_file: {
  303. const opt_t* opt;
  304. TSK_DEBUG_INFO("command=file");
  305. if(!(opt = opt_get_by_type(cmd->opts, opt_path)) || tsk_strnullORempty(opt->value)) {
  306. TSK_DEBUG_ERROR("++file command need --path option");
  307. break;
  308. }
  309. if((sid = invite_handle_cmd(cmd->type, cmd->opts)) != TSIP_SSESSION_INVALID_ID) {
  310. if(cmd->sidparam) {
  311. tsk_itoa(sid, &istr);
  312. update_param(cmd->sidparam, istr);
  313. }
  314. }
  315. break;
  316. }
  317. case cmd_hangup: {
  318. const opt_t* opt;
  319. TSK_DEBUG_INFO("command=hangup");
  320. if((opt = opt_get_by_type(cmd->opts, opt_sid)) && !tsk_strnullORempty(opt->value)) { /* --sid option */
  321. ret = session_hangup(tsk_atoll(opt->value));
  322. }
  323. else {
  324. TSK_DEBUG_ERROR("++hangup command need --sid option");
  325. ret = -1;
  326. }
  327. break;
  328. }
  329. case cmd_help: {
  330. TSK_DEBUG_INFO("command=help");
  331. cmd_print_help();
  332. break;
  333. }
  334. case cmd_hold: {
  335. const opt_t* opt;
  336. TSK_DEBUG_INFO("command=hold");
  337. if((opt = opt_get_by_type(cmd->opts, opt_sid)) && !tsk_strnullORempty(opt->value)) { /* --sid option */
  338. invite_handle_cmd(cmd->type, cmd->opts);
  339. }
  340. else {
  341. TSK_DEBUG_ERROR("++hold command need --sid option");
  342. ret = -1;
  343. }
  344. break;
  345. }
  346. case cmd_message: {
  347. TSK_DEBUG_INFO("command=message");
  348. if((sid = message_handle_cmd(cmd->type, cmd->opts)) != TSIP_SSESSION_INVALID_ID) {
  349. if(cmd->sidparam) {
  350. tsk_itoa(sid, &istr);
  351. update_param(cmd->sidparam, istr);
  352. }
  353. }
  354. break;
  355. }
  356. case cmd_options: {
  357. TSK_DEBUG_INFO("command=options");
  358. if((sid = options_handle_cmd(cmd->type, cmd->opts)) != TSIP_SSESSION_INVALID_ID) {
  359. if(cmd->sidparam) {
  360. tsk_itoa(sid, &istr);
  361. update_param(cmd->sidparam, istr);
  362. }
  363. }
  364. break;
  365. }
  366. case cmd_publish: {
  367. TSK_DEBUG_INFO("command=publish");
  368. if((sid = publish_handle_cmd(cmd->type, cmd->opts)) != TSIP_SSESSION_INVALID_ID) {
  369. if(cmd->sidparam) {
  370. tsk_itoa(sid, &istr);
  371. update_param(cmd->sidparam, istr);
  372. }
  373. }
  374. break;
  375. }
  376. case cmd_register: {
  377. TSK_DEBUG_INFO("command=register");
  378. if((sid = register_handle_cmd(cmd->type, cmd->opts)) != TSIP_SSESSION_INVALID_ID) {
  379. if(cmd->sidparam) {
  380. tsk_itoa(sid, &istr);
  381. update_param(cmd->sidparam, istr);
  382. }
  383. }
  384. break;
  385. }
  386. case cmd_resume: {
  387. const opt_t* opt;
  388. TSK_DEBUG_INFO("command=resume");
  389. if((opt = opt_get_by_type(cmd->opts, opt_sid)) && !tsk_strnullORempty(opt->value)) { /* --sid option */
  390. invite_handle_cmd(cmd->type, cmd->opts);
  391. }
  392. else {
  393. TSK_DEBUG_ERROR("++resume command need --sid option");
  394. ret = -1;
  395. }
  396. break;
  397. }
  398. case cmd_run: {
  399. TSK_DEBUG_INFO("command=run");
  400. ret = stack_run(cmd->opts);
  401. break;
  402. }
  403. case cmd_scenario: {
  404. TSK_DEBUG_INFO("command=scenario");
  405. break;
  406. }
  407. case cmd_sleep: {
  408. const opt_t* opt;
  409. double seconds;
  410. tsk_safeobj_unlock(ctx); /* beacuse of callback function */
  411. if((opt = opt_get_by_type(cmd->opts, opt_sec)) && !tsk_strnullORempty(opt->value)) { /* --sec option */
  412. seconds = strtod(opt->value, tsk_null); /* strtod() is better than atof() */
  413. if(seconds<=0) {
  414. printf("\n==== Press ENTER to continue...\n");
  415. getchar();
  416. }
  417. else {
  418. TSK_DEBUG_INFO("Sleeping %f seconds", seconds);
  419. tsk_thread_sleep((uint64_t)(seconds * 1000));
  420. }
  421. }
  422. else {
  423. TSK_DEBUG_WARN("++sleep need --sec option.");
  424. }
  425. return 0; /* bail: will unlock again */
  426. }
  427. case cmd_sms: {
  428. TSK_DEBUG_INFO("command=sms");
  429. if((sid = message_handle_cmd(cmd->type, cmd->opts)) != TSIP_SSESSION_INVALID_ID) {
  430. if(cmd->sidparam) {
  431. tsk_itoa(sid, &istr);
  432. update_param(cmd->sidparam, istr);
  433. }
  434. }
  435. break;
  436. }
  437. case cmd_stop: {
  438. TSK_DEBUG_INFO("command=stop");
  439. tsip_stack_stop(ctx->stack);
  440. break;
  441. }
  442. case cmd_subscribe: {
  443. TSK_DEBUG_INFO("command=subscribe");
  444. if((sid = subscribe_handle_cmd(cmd->type, cmd->opts)) != TSIP_SSESSION_INVALID_ID) {
  445. if(cmd->sidparam) {
  446. tsk_itoa(sid, &istr);
  447. update_param(cmd->sidparam, istr);
  448. }
  449. }
  450. break;
  451. }
  452. case cmd_video: {
  453. TSK_DEBUG_INFO("command=video");
  454. break;
  455. }
  456. default: {
  457. TSK_DEBUG_ERROR("%d not a valid command.", cmd);
  458. break;
  459. }
  460. }
  461. bail:
  462. tsk_safeobj_unlock(ctx);
  463. return ret;
  464. }