diff --git a/Makefile.in b/Makefile.in index e9f0144..e1989ab 100644 --- a/Makefile.in +++ b/Makefile.in @@ -19,7 +19,7 @@ DIR_LIST=${DEP} ${AUTO} ${EXE} ${OBJ} ${LIB} ${DEP}/auto ${OBJ}/auto EXE_LIST=${EXE}/telegram-cli -TG_OBJECTS=${OBJ}/main.o ${OBJ}/loop.o ${OBJ}/interface.o ${OBJ}/lua-tg.o +TG_OBJECTS=${OBJ}/main.o ${OBJ}/loop.o ${OBJ}/interface.o ${OBJ}/lua-tg.o ${OBJ}/json-tg.o INCLUDE=-I. -I${srcdir} -I${srcdir}/tgl CC=@CC@ diff --git a/config.h.in b/config.h.in index 56902f3..38bec97 100644 --- a/config.h.in +++ b/config.h.in @@ -36,6 +36,9 @@ /* Define to 1 if you have the `event' library (-levent). */ #undef HAVE_LIBEVENT +/* Define to 1 if you have the `jansson' library (-ljansson). */ +#undef HAVE_LIBJANSSON + /* Define to 1 if you have the `m' library (-lm). */ #undef HAVE_LIBM @@ -155,6 +158,9 @@ /* Define to 1 if you have the ANSI C header files. */ #undef STDC_HEADERS +/* use json */ +#undef USE_JSON + /* use lua */ #undef USE_LUA diff --git a/configure b/configure index 288d320..ea6dc0f 100755 --- a/configure +++ b/configure @@ -694,6 +694,7 @@ with_zlib enable_libconfig enable_extf enable_liblua +enable_json with_progname enable_valgrind ' @@ -1321,6 +1322,7 @@ Optional Features: --enable-libconfig/--disable-libconfig --enable-extf/--disable-extf --enable-liblua/--disable-liblua +--enable-json/--disable-json --enable-valgrind/--disable-valgrind Optional Packages: @@ -5865,7 +5867,130 @@ fi fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking for libjansson" >&5 +$as_echo_n "checking for libjansson... " >&6; } +# Check whether --enable-json was given. +if test "${enable_json+set}" = set; then : + enableval=$enable_json; + if test "x$enableval" = "xno" ; then + { $as_echo "$as_me:${as_lineno-$LINENO}: result: disabled" >&5 +$as_echo "disabled" >&6; } + else + { $as_echo "$as_me:${as_lineno-$LINENO}: result: enabled" >&5 +$as_echo "enabled" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for json_array_set_new in -ljansson" >&5 +$as_echo_n "checking for json_array_set_new in -ljansson... " >&6; } +if ${ac_cv_lib_jansson_json_array_set_new+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ljansson $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char json_array_set_new (); +int +main () +{ +return json_array_set_new (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_jansson_json_array_set_new=yes +else + ac_cv_lib_jansson_json_array_set_new=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_jansson_json_array_set_new" >&5 +$as_echo "$ac_cv_lib_jansson_json_array_set_new" >&6; } +if test "x$ac_cv_lib_jansson_json_array_set_new" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBJANSSON 1 +_ACEOF + + LIBS="-ljansson $LIBS" + +else + as_fn_error $? "No libjansson found. Try --disable-json" "$LINENO" 5 +fi + + +$as_echo "#define USE_JSON 1" >>confdefs.h + + fi + +else + + { $as_echo "$as_me:${as_lineno-$LINENO}: result: enabled" >&5 +$as_echo "enabled" >&6; } + { $as_echo "$as_me:${as_lineno-$LINENO}: checking for json_array_set_new in -ljansson" >&5 +$as_echo_n "checking for json_array_set_new in -ljansson... " >&6; } +if ${ac_cv_lib_jansson_json_array_set_new+:} false; then : + $as_echo_n "(cached) " >&6 +else + ac_check_lib_save_LIBS=$LIBS +LIBS="-ljansson $LIBS" +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char json_array_set_new (); +int +main () +{ +return json_array_set_new (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO"; then : + ac_cv_lib_jansson_json_array_set_new=yes +else + ac_cv_lib_jansson_json_array_set_new=no +fi +rm -f core conftest.err conftest.$ac_objext \ + conftest$ac_exeext conftest.$ac_ext +LIBS=$ac_check_lib_save_LIBS +fi +{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $ac_cv_lib_jansson_json_array_set_new" >&5 +$as_echo "$ac_cv_lib_jansson_json_array_set_new" >&6; } +if test "x$ac_cv_lib_jansson_json_array_set_new" = xyes; then : + cat >>confdefs.h <<_ACEOF +#define HAVE_LIBJANSSON 1 +_ACEOF + + LIBS="-ljansson $LIBS" + +else + as_fn_error $? "No libjansson found. Try --disable-json" "$LINENO" 5 +fi + + +$as_echo "#define USE_JSON 1" >>confdefs.h + + +fi + + #check for custom prog name +{ $as_echo "$as_me:${as_lineno-$LINENO}: checking progname" >&5 +$as_echo_n "checking progname... " >&6; } # Check whether --with-progname was given. if test "${with_progname+set}" = set; then : diff --git a/configure.ac b/configure.ac index 44a6767..3e33cc2 100644 --- a/configure.ac +++ b/configure.ac @@ -94,7 +94,24 @@ AC_ARG_ENABLE(liblua,[--enable-liblua/--disable-liblua], ]) ]) +AC_MSG_CHECKING([for libjansson]) +AC_ARG_ENABLE(json,[--enable-json/--disable-json], + [ + if test "x$enableval" = "xno" ; then + AC_MSG_RESULT([disabled]) + else + AC_MSG_RESULT([enabled]) + AC_CHECK_LIB([jansson],[json_array_set_new],[],AC_MSG_ERROR([No libjansson found. Try --disable-json])) + AC_DEFINE(USE_JSON,1,[use json]) + fi + ],[ + AC_MSG_RESULT([enabled]) + AC_CHECK_LIB([jansson],[json_array_set_new],[],AC_MSG_ERROR([No libjansson found. Try --disable-json])) + AC_DEFINE(USE_JSON,1,[use json]) + ]) + #check for custom prog name +AC_MSG_CHECKING([progname]) AC_ARG_WITH(progname,[--with-progname=], [ if test "x$with_progname" = "xno" ; then diff --git a/interface.c b/interface.c index 7f15711..f93fc74 100644 --- a/interface.c +++ b/interface.c @@ -74,6 +74,11 @@ #define OPEN_BIN "xdg-open %s" #endif +#ifdef USE_JSON +# include "jansson.h" +# include "json-tg.h" +#endif + #define ALLOW_MULT 1 char *default_prompt = "> "; @@ -82,7 +87,7 @@ extern char one_string[]; extern int one_string_len; extern char *one_string_prompt; extern int one_string_flags; - +extern int enable_json; int disable_auto_accept; int msg_num_mode; int disable_colors; @@ -1599,17 +1604,40 @@ void work_modifier (const char *s, int l) { void print_fail (struct in_ev *ev) { mprint_start (ev); - mprintf (ev, "FAIL\n"); + if (!enable_json) { + mprintf (ev, "FAIL\n"); + } else { + #ifdef USE_JSON + json_t *res = json_object (); + assert (json_object_set (res, "result", json_string ("FAIL")) >= 0); + char *s = json_dumps (res, 0); + mprintf (ev, "%s\n", s); + json_decref (res); + free (s); + #endif + } mprint_end (ev); } void print_success (struct in_ev *ev) { - if (ev) { + if (ev || enable_json) { mprint_start (ev); - mprintf (ev, "SUCCESS\n"); + if (!enable_json) { + mprintf (ev, "SUCCESS\n"); + } else { + #ifdef USE_JSON + json_t *res = json_object (); + assert (json_object_set (res, "result", json_string ("SUCCESS")) >= 0); + char *s = json_dumps (res, 0); + mprintf (ev, "%s\n", s); + json_decref (res); + free (s); + #endif + } mprint_end (ev); } } + void print_success_gw (struct tgl_state *TLSR, void *extra, int success) { assert (TLS == TLSR); struct in_ev *ev = extra; @@ -1644,10 +1672,26 @@ void print_msg_list_gw (struct tgl_state *TLSR, void *extra, int success, int nu return; } if (!success) { print_fail (ev); return; } + mprint_start (ev); - int i; - for (i = num - 1; i >= 0; i--) { - print_message (ev, ML[i]); + if (!enable_json) { + int i; + for (i = num - 1; i >= 0; i--) { + print_message (ev, ML[i]); + } + } else { + #ifdef USE_JSON + json_t *res = json_array (); + int i; + for (i = num - 1; i >= 0; i--) { + json_t *a = json_pack_message (ML[i]); + assert (json_array_append (res, a) >= 0); + } + char *s = json_dumps (res, 0); + mprintf (ev, "%s\n", s); + json_decref (res); + free (s); + #endif } mprint_end (ev); } @@ -1661,7 +1705,17 @@ void print_msg_gw (struct tgl_state *TLSR, void *extra, int success, struct tgl_ } if (!success) { print_fail (ev); return; } mprint_start (ev); - print_message (ev, M); + if (!enable_json) { + print_message (ev, M); + } else { + #ifdef USE_JSON + json_t *res = json_pack_message (M); + char *s = json_dumps (res, 0); + mprintf (ev, "%s\n", s); + json_decref (res); + free (s); + #endif + } mprint_end (ev); } @@ -1674,10 +1728,25 @@ void print_user_list_gw (struct tgl_state *TLSR, void *extra, int success, int n } if (!success) { print_fail (ev); return; } mprint_start (ev); - int i; - for (i = num - 1; i >= 0; i--) { - print_user_name (ev, UL[i]->id, (void *)UL[i]); - mprintf (ev, "\n"); + if (!enable_json) { + int i; + for (i = num - 1; i >= 0; i--) { + print_user_name (ev, UL[i]->id, (void *)UL[i]); + mprintf (ev, "\n"); + } + } else { + #ifdef USE_JSON + json_t *res = json_array (); + int i; + for (i = num - 1; i >= 0; i--) { + json_t *a = json_pack_peer (UL[i]->id, (void *)UL[i]); + assert (json_array_append (res, a) >= 0); + } + char *s = json_dumps (res, 0); + mprintf (ev, "%s\n", s); + json_decref (res); + free (s); + #endif } mprint_end (ev); } @@ -1691,8 +1760,18 @@ void print_user_gw (struct tgl_state *TLSR, void *extra, int success, struct tgl } if (!success) { print_fail (ev); return; } mprint_start (ev); - print_user_name (ev, U->id, (void *)U); - mprintf (ev, "\n"); + if (!enable_json) { + print_user_name (ev, U->id, (void *)U); + mprintf (ev, "\n"); + } else { + #ifdef USE_JSON + json_t *res = json_pack_peer (U->id, (void *)U); + char *s = json_dumps (res, 0); + mprintf (ev, "%s\n", s); + json_decref (res); + free (s); + #endif + } mprint_end (ev); } @@ -1705,7 +1784,18 @@ void print_filename_gw (struct tgl_state *TLSR, void *extra, int success, char * } if (!success) { print_fail (ev); return; } mprint_start (ev); - mprintf (ev, "Saved to %s\n", name); + if (!enable_json) { + mprintf (ev, "Saved to %s\n", name); + } else { + #ifdef USE_JSON + json_t *res = json_object (); + assert (json_object_set (res, "result", json_string (name)) >= 0); + char *s = json_dumps (res, 0); + mprintf (ev, "%s\n", s); + json_decref (res); + free (s); + #endif + } mprint_end (ev); } @@ -1718,7 +1808,19 @@ void print_string_gw (struct tgl_state *TLSR, void *extra, int success, const ch } if (!success) { print_fail (ev); return; } mprint_start (ev); - mprintf (ev, "%s\n", name); + mprint_start (ev); + if (!enable_json) { + mprintf (ev, "%s\n", name); + } else { + #ifdef USE_JSON + json_t *res = json_object (); + assert (json_object_set (res, "result", json_string (name)) >= 0); + char *s = json_dumps (res, 0); + mprintf (ev, "%s\n", s); + json_decref (res); + free (s); + #endif + } mprint_end (ev); } @@ -1752,30 +1854,42 @@ void print_chat_info_gw (struct tgl_state *TLSR, void *extra, int success, struc } if (!success) { print_fail (ev); return; } mprint_start (ev); - - tgl_peer_t *U = (void *)C; - mpush_color (ev, COLOR_YELLOW); - mprintf (ev, "Chat "); - print_chat_name (ev, U->id, U); - mprintf (ev, " (id %d) members:\n", tgl_get_peer_id (U->id)); - int i; - for (i = 0; i < C->user_list_size; i++) { - mprintf (ev, "\t\t"); - print_user_name (ev, TGL_MK_USER (C->user_list[i].user_id), tgl_peer_get (TLS, TGL_MK_USER (C->user_list[i].user_id))); - mprintf (ev, " invited by "); - print_user_name (ev, TGL_MK_USER (C->user_list[i].inviter_id), tgl_peer_get (TLS, TGL_MK_USER (C->user_list[i].inviter_id))); - mprintf (ev, " at "); - print_date_full (ev, C->user_list[i].date); - if (C->user_list[i].user_id == C->admin_id) { - mprintf (ev, " admin"); + + if (!enable_json) { + tgl_peer_t *U = (void *)C; + mpush_color (ev, COLOR_YELLOW); + mprintf (ev, "Chat "); + print_chat_name (ev, U->id, U); + mprintf (ev, " (id %d) members:\n", tgl_get_peer_id (U->id)); + int i; + for (i = 0; i < C->user_list_size; i++) { + mprintf (ev, "\t\t"); + print_user_name (ev, TGL_MK_USER (C->user_list[i].user_id), tgl_peer_get (TLS, TGL_MK_USER (C->user_list[i].user_id))); + mprintf (ev, " invited by "); + print_user_name (ev, TGL_MK_USER (C->user_list[i].inviter_id), tgl_peer_get (TLS, TGL_MK_USER (C->user_list[i].inviter_id))); + mprintf (ev, " at "); + print_date_full (ev, C->user_list[i].date); + if (C->user_list[i].user_id == C->admin_id) { + mprintf (ev, " admin"); + } + mprintf (ev, "\n"); } - mprintf (ev, "\n"); + mpop_color (ev); + } else { + #ifdef USE_JSON + json_t *res = json_pack_peer (C->id, (void *)C); + char *s = json_dumps (res, 0); + mprintf (ev, "%s\n", s); + json_decref (res); + free (s); + #endif } - mpop_color (ev); + mprint_end (ev); } void print_user_status (struct tgl_user_status *S, struct in_ev *ev) { + if (enable_json) { return; } if (S->online > 0) { mprintf (ev, "online (was online "); print_date_full (ev, S->when); @@ -1805,21 +1919,31 @@ void print_user_info_gw (struct tgl_state *TLSR, void *extra, int success, struc return; } if (!success) { print_fail (ev); return; } - tgl_peer_t *C = (void *)U; mprint_start (ev); - mpush_color (ev, COLOR_YELLOW); - mprintf (ev, "User "); - print_user_name (ev, U->id, C); - if (U->username) { - mprintf (ev, " @%s", U->username); + tgl_peer_t *C = (void *)U; + if (!enable_json) { + mpush_color (ev, COLOR_YELLOW); + mprintf (ev, "User "); + print_user_name (ev, U->id, C); + if (U->username) { + mprintf (ev, " @%s", U->username); + } + mprintf (ev, " (#%d):\n", tgl_get_peer_id (U->id)); + mprintf (ev, "\treal name: %s %s\n", U->real_first_name, U->real_last_name); + mprintf (ev, "\tphone: %s\n", U->phone); + mprintf (ev, "\t"); + print_user_status (&U->status, ev); + mprintf (ev, "\n"); + mpop_color (ev); + } else { + #ifdef USE_JSON + json_t *res = json_pack_peer (U->id, (void *)U); + char *s = json_dumps (res, 0); + mprintf (ev, "%s\n", s); + json_decref (res); + free (s); + #endif } - mprintf (ev, " (#%d):\n", tgl_get_peer_id (U->id)); - mprintf (ev, "\treal name: %s %s\n", U->real_first_name, U->real_last_name); - mprintf (ev, "\tphone: %s\n", U->phone); - mprintf (ev, "\t"); - print_user_status (&U->status, ev); - mprintf (ev, "\n"); - mpop_color (ev); mprint_end (ev); } @@ -1832,11 +1956,21 @@ void print_secret_chat_gw (struct tgl_state *TLSR, void *extra, int success, str } if (!success) { print_fail (ev); return; } mprint_start (ev); - mpush_color (ev, COLOR_YELLOW); - mprintf (ev, " Encrypted chat "); - print_encr_chat_name (ev, E->id, (void *)E); - mprintf (ev, " is now in wait state\n"); - mpop_color (ev); + if (!enable_json) { + mpush_color (ev, COLOR_YELLOW); + mprintf (ev, " Encrypted chat "); + print_encr_chat_name (ev, E->id, (void *)E); + mprintf (ev, " is now in wait state\n"); + mpop_color (ev); + } else { + #ifdef USE_JSON + json_t *res = json_pack_peer (E->id, (void *)E); + char *s = json_dumps (res, 0); + mprintf (ev, "%s\n", s); + json_decref (res); + free (s); + #endif + } mprint_end (ev); } @@ -1849,26 +1983,41 @@ void print_dialog_list_gw (struct tgl_state *TLSR, void *extra, int success, int } if (!success) { print_fail (ev); return; } mprint_start (ev); - mpush_color (ev, COLOR_YELLOW); - int i; - for (i = size - 1; i >= 0; i--) { - tgl_peer_t *UC; - switch (tgl_get_peer_type (peers[i])) { - case TGL_PEER_USER: - UC = tgl_peer_get (TLS, peers[i]); - mprintf (ev, "User "); - print_user_name (ev, peers[i], UC); - mprintf (ev, ": %d unread\n", unread_count[i]); - break; - case TGL_PEER_CHAT: - UC = tgl_peer_get (TLS, peers[i]); - mprintf (ev, "Chat "); - print_chat_name (ev, peers[i], UC); - mprintf (ev, ": %d unread\n", unread_count[i]); - break; + if (!enable_json) { + mpush_color (ev, COLOR_YELLOW); + int i; + for (i = size - 1; i >= 0; i--) { + tgl_peer_t *UC; + switch (tgl_get_peer_type (peers[i])) { + case TGL_PEER_USER: + UC = tgl_peer_get (TLS, peers[i]); + mprintf (ev, "User "); + print_user_name (ev, peers[i], UC); + mprintf (ev, ": %d unread\n", unread_count[i]); + break; + case TGL_PEER_CHAT: + UC = tgl_peer_get (TLS, peers[i]); + mprintf (ev, "Chat "); + print_chat_name (ev, peers[i], UC); + mprintf (ev, ": %d unread\n", unread_count[i]); + break; + } } + mpop_color (ev); + } else { + #ifdef USE_JSON + json_t *res = json_array (); + int i; + for (i = size - 1; i >= 0; i--) { + json_t *a = json_pack_peer (peers[i], tgl_peer_get (TLS, peers[i])); + assert (json_array_append (res, a) >= 0); + } + char *s = json_dumps (res, 0); + mprintf (ev, "%s\n", s); + json_decref (res); + free (s); + #endif } - mpop_color (ev); mprint_end (ev); } @@ -2029,6 +2178,7 @@ void print_typing (struct in_ev *ev, enum tgl_typing_status status) { void type_notification_upd (struct tgl_state *TLSR, struct tgl_user *U, enum tgl_typing_status status) { assert (TLSR == TLS); if (log_level < 2 || (disable_output && !notify_ev)) { return; } + if (enable_json) { return; } struct in_ev *ev = notify_ev; mprint_start (ev); mpush_color (ev, COLOR_YELLOW); @@ -2044,6 +2194,7 @@ void type_notification_upd (struct tgl_state *TLSR, struct tgl_user *U, enum tgl void type_in_chat_notification_upd (struct tgl_state *TLSR, struct tgl_user *U, struct tgl_chat *C, enum tgl_typing_status status) { assert (TLSR == TLS); if (log_level < 2 || (disable_output && !notify_ev)) { return; } + if (enable_json) { return; } struct in_ev *ev = notify_ev; mprint_start (ev); mpush_color (ev, COLOR_YELLOW); @@ -2074,7 +2225,17 @@ void print_message_gw (struct tgl_state *TLSR, struct tgl_message *M) { if (disable_output && !notify_ev) { return; } struct in_ev *ev = notify_ev; mprint_start (ev); - print_message (ev, M); + if (!enable_json) { + print_message (ev, M); + } else { + #ifdef USE_JSON + json_t *res = json_pack_message (M); + char *s = json_dumps (res, 0); + mprintf (ev, "%s\n", s); + json_decref (res); + free (s); + #endif + } mprint_end (ev); } @@ -2130,6 +2291,18 @@ void print_peer_updates (struct in_ev *ev, int flags) { } } +void json_peer_update (struct in_ev *ev, tgl_peer_t *P, unsigned flags) { + #ifdef USE_JSON + json_t *res = json_object (); + assert (json_object_set (res, "peer", json_pack_peer (P->id, P)) >= 0); + assert (json_object_set (res, "updates", json_pack_updates (flags)) >= 0); + char *s = json_dumps (res, 0); + mprintf (ev, "%s\n", s); + json_decref (res); + free (s); + #endif +} + void user_update_gw (struct tgl_state *TLSR, struct tgl_user *U, unsigned flags) { assert (TLSR == TLS); #ifdef USE_LUA @@ -2142,17 +2315,21 @@ void user_update_gw (struct tgl_state *TLSR, struct tgl_user *U, unsigned flags) if (!(flags & TGL_UPDATE_CREATED)) { mprint_start (ev); - mpush_color (ev, COLOR_YELLOW); - mprintf (ev, "User "); - print_user_name (ev, U->id, (void *)U); - if (!(flags & TGL_UPDATE_DELETED)) { - mprintf (ev, " updated"); - print_peer_updates (ev, flags); + if (!enable_json) { + mpush_color (ev, COLOR_YELLOW); + mprintf (ev, "User "); + print_user_name (ev, U->id, (void *)U); + if (!(flags & TGL_UPDATE_DELETED)) { + mprintf (ev, " updated"); + print_peer_updates (ev, flags); + } else { + mprintf (ev, " deleted"); + } + mprintf (ev, "\n"); + mpop_color (ev); } else { - mprintf (ev, " deleted"); + json_peer_update (ev, (void *)U, flags); } - mprintf (ev, "\n"); - mpop_color (ev); mprint_end (ev); } } @@ -2169,17 +2346,21 @@ void chat_update_gw (struct tgl_state *TLSR, struct tgl_chat *U, unsigned flags) if (!(flags & TGL_UPDATE_CREATED)) { mprint_start (ev); - mpush_color (ev, COLOR_YELLOW); - mprintf (ev, "Chat "); - print_chat_name (ev, U->id, (void *)U); - if (!(flags & TGL_UPDATE_DELETED)) { - mprintf (ev, " updated"); - print_peer_updates (ev, flags); + if (!enable_json) { + mpush_color (ev, COLOR_YELLOW); + mprintf (ev, "Chat "); + print_chat_name (ev, U->id, (void *)U); + if (!(flags & TGL_UPDATE_DELETED)) { + mprintf (ev, " updated"); + print_peer_updates (ev, flags); + } else { + mprintf (ev, " deleted"); + } + mprintf (ev, "\n"); + mpop_color (ev); } else { - mprintf (ev, " deleted"); + json_peer_update (ev, (void *)U, flags); } - mprintf (ev, "\n"); - mpop_color (ev); mprint_end (ev); } } @@ -2207,17 +2388,21 @@ void secret_chat_update_gw (struct tgl_state *TLSR, struct tgl_secret_chat *U, u if (!(flags & TGL_UPDATE_CREATED)) { mprint_start (ev); - mpush_color (ev, COLOR_YELLOW); - mprintf (ev, "Secret chat "); - print_encr_chat_name (ev, U->id, (void *)U); - if (!(flags & TGL_UPDATE_DELETED)) { - mprintf (ev, " updated"); - print_peer_updates (ev, flags); + if (!enable_json) { + mpush_color (ev, COLOR_YELLOW); + mprintf (ev, "Secret chat "); + print_encr_chat_name (ev, U->id, (void *)U); + if (!(flags & TGL_UPDATE_DELETED)) { + mprintf (ev, " updated"); + print_peer_updates (ev, flags); + } else { + mprintf (ev, " deleted"); + } + mprintf (ev, "\n"); + mpop_color (ev); } else { - mprintf (ev, " deleted"); + json_peer_update (ev, (void *)U, flags); } - mprintf (ev, "\n"); - mpop_color (ev); mprint_end (ev); } } @@ -2231,10 +2416,27 @@ void print_card_gw (struct tgl_state *TLSR, void *extra, int success, int size, } if (!success) { print_fail (ev); return; } mprint_start (ev); - mprintf (ev, "Card: "); - int i; - for (i = 0; i < size; i++) { - mprintf (ev, "%08x%c", card[i], i == size - 1 ? '\n' : ':'); + if (!enable_json) { + mprintf (ev, "Card: "); + int i; + for (i = 0; i < size; i++) { + mprintf (ev, "%08x%c", card[i], i == size - 1 ? '\n' : ':'); + } + } else { + #ifdef USE_JSON + static char q[1000]; + int pos = 0; + int i; + for (i = 0; i < size; i++) { + pos += sprintf (q + pos, "%08x%s", card[i], i == size - 1 ? "" : ":"); + } + json_t *res = json_object (); + assert (json_object_set (res, "result", json_string (q)) >= 0); + char *s = json_dumps (res, 0); + mprintf (ev, "%s\n", s); + json_decref (res); + free (s); + #endif } mprint_end (ev); } @@ -2247,7 +2449,18 @@ void callback_extf (struct tgl_state *TLS, void *extra, int success, char *buf) } if (!success) { print_fail (ev); return; } mprint_start (ev); - mprintf (ev, "%s\n", buf); + if (!enable_json) { + mprintf (ev, "%s\n", buf); + } else { + #ifdef USE_JSON + json_t *res = json_object (); + assert (json_object_set (res, "result", json_string (buf)) >= 0); + char *s = json_dumps (res, 0); + mprintf (ev, "%s\n", s); + json_decref (res); + free (s); + #endif + } mprint_end (ev); } @@ -2255,6 +2468,7 @@ void user_status_upd (struct tgl_state *TLS, struct tgl_user *U) { if (disable_output && !notify_ev) { return; } if (!binlog_read) { return; } if (log_level < 3) { return; } + if (enable_json) { return; } struct in_ev *ev = notify_ev; mprint_start (ev); mpush_color (ev, COLOR_YELLOW); diff --git a/json-tg.c b/json-tg.c new file mode 100644 index 0000000..87e4c76 --- /dev/null +++ b/json-tg.c @@ -0,0 +1,369 @@ +#include "config.h" +#ifdef USE_JSON + +#include +#include "json-tg.h" +#include +#include +#include + +extern struct tgl_state *TLS; + +void json_pack_peer_type (json_t *res, tgl_peer_id_t id) { + int x = tgl_get_peer_type (id); + switch (x) { + case TGL_PEER_USER: + assert (json_object_set (res, "type", json_string ("user")) >= 0); + break; + case TGL_PEER_CHAT: + assert (json_object_set (res, "type", json_string ("chat")) >= 0); + break; + case TGL_PEER_ENCR_CHAT: + assert (json_object_set (res, "type", json_string ("encr_chat")) >= 0); + break; + default: + assert (0); + } +} + + +void json_pack_user (json_t *res, tgl_peer_t *P) { + if (P->user.first_name) { + assert (json_object_set (res, "first_name", json_string (P->user.first_name)) >= 0); + } + if (P->user.last_name) { + assert (json_object_set (res, "last_name", json_string (P->user.last_name)) >= 0); + } + if (P->user.real_first_name) { + assert (json_object_set (res, "real_first_name", json_string (P->user.real_first_name)) >= 0); + } + if (P->user.real_last_name) { + assert (json_object_set (res, "real_last_name", json_string (P->user.real_last_name)) >= 0); + } + if (P->user.phone) { + assert (json_object_set (res, "phone", json_string (P->user.phone)) >= 0); + } + if (P->user.username) { + assert (json_object_set (res, "username", json_string (P->user.username)) >= 0); + } +} + +void json_pack_chat (json_t *res, tgl_peer_t *P) { + assert (P->chat.title); + assert (json_object_set (res, "title", json_string (P->chat.title)) >= 0); + assert (json_object_set (res, "members_num", json_integer (P->chat.users_num)) >= 0); + + if (P->chat.user_list) { + json_t *m = json_array (); + assert (m); + + int i; + for (i = 0; i < P->chat.users_num; i++) { + tgl_peer_id_t id = TGL_MK_USER (P->chat.user_list[i].user_id); + assert (json_array_append (m, json_pack_peer (id, tgl_peer_get (TLS, id))) >= 0); + } + + assert (json_object_set (res, "members", m) >= 0); + } +} + + +void json_pack_encr_chat (json_t *res, tgl_peer_t *P) { + assert (json_object_set (res, "user", json_pack_peer (TGL_MK_USER (P->encr_chat.user_id), tgl_peer_get (TLS, TGL_MK_USER (P->encr_chat.user_id)))) >= 0); +} + +json_t *json_pack_peer (tgl_peer_id_t id, tgl_peer_t *P) { + json_t *res = json_object (); + assert (json_object_set (res, "id", json_integer (tgl_get_peer_id (id))) >= 0); + + json_pack_peer_type (res, id); + + assert (res); + + if (!P || !(P->flags & TGLPF_CREATED)) { + static char s[100]; + switch (tgl_get_peer_type (id)) { + case TGL_PEER_USER: + sprintf (s, "user#%d", tgl_get_peer_id (id)); + break; + case TGL_PEER_CHAT: + sprintf (s, "chat#%d", tgl_get_peer_id (id)); + break; + case TGL_PEER_ENCR_CHAT: + sprintf (s, "encr_chat#%d", tgl_get_peer_id (id)); + break; + default: + assert (0); + } + + assert (json_object_set (res, "print_name", json_string (s)) >= 0); + return res; + } + + assert (json_object_set (res, "print_name", json_string (P->print_name)) >= 0); + assert (json_object_set (res, "flags", json_integer (P->flags)) >= 0); + + switch (tgl_get_peer_type (id)) { + case TGL_PEER_USER: + json_pack_user (res, P); + break; + case TGL_PEER_CHAT: + json_pack_chat (res, P); + break; + case TGL_PEER_ENCR_CHAT: + json_pack_encr_chat (res, P); + break; + default: + assert (0); + } + return res; +} + +json_t *json_pack_updates (unsigned flags) { + json_t *a = json_array (); + + if (flags & TGL_UPDATE_CREATED) { + assert (json_array_append (a, json_string ("created")) >= 0); + } + if (flags & TGL_UPDATE_DELETED) { + assert (json_array_append (a, json_string ("deleted")) >= 0); + } + if (flags & TGL_UPDATE_PHONE) { + assert (json_array_append (a, json_string ("phone")) >= 0); + } + if (flags & TGL_UPDATE_CONTACT) { + assert (json_array_append (a, json_string ("contact")) >= 0); + } + if (flags & TGL_UPDATE_PHOTO) { + assert (json_array_append (a, json_string ("photo")) >= 0); + } + if (flags & TGL_UPDATE_BLOCKED) { + assert (json_array_append (a, json_string ("blocked")) >= 0); + } + if (flags & TGL_UPDATE_REAL_NAME) { + assert (json_array_append (a, json_string ("real_name")) >= 0); + } + if (flags & TGL_UPDATE_NAME) { + assert (json_array_append (a, json_string ("name")) >= 0); + } + if (flags & TGL_UPDATE_REQUESTED) { + assert (json_array_append (a, json_string ("requested")) >= 0); + } + if (flags & TGL_UPDATE_WORKING) { + assert (json_array_append (a, json_string ("working")) >= 0); + } + if (flags & TGL_UPDATE_FLAGS) { + assert (json_array_append (a, json_string ("flags")) >= 0); + } + if (flags & TGL_UPDATE_TITLE) { + assert (json_array_append (a, json_string ("title")) >= 0); + } + if (flags & TGL_UPDATE_ADMIN) { + assert (json_array_append (a, json_string ("admin")) >= 0); + } + if (flags & TGL_UPDATE_MEMBERS) { + assert (json_array_append (a, json_string ("members")) >= 0); + } + if (flags & TGL_UPDATE_USERNAME) { + assert (json_array_append (a, json_string ("username")) >= 0); + } + + return a; +} + + +json_t *json_pack_media (struct tgl_message_media *M) { + json_t *res = json_object (); + + switch (M->type) { + case tgl_message_media_photo: + assert (json_object_set (res, "type", json_string ("photo")) >= 0); + if (M->caption) { + assert (json_object_set (res, "caption", json_string (M->caption)) >= 0); + } + break; + case tgl_message_media_document: + case tgl_message_media_document_encr: + assert (json_object_set (res, "type", json_string ("document")) >= 0); + break; + case tgl_message_media_unsupported: + assert (json_object_set (res, "type", json_string ("unsupported")) >= 0); + break; + case tgl_message_media_geo: + assert (json_object_set (res, "type", json_string ("geo")) >= 0); + assert (json_object_set (res, "longitude", json_real (M->geo.longitude)) >= 0); + assert (json_object_set (res, "latitude", json_real (M->geo.latitude)) >= 0); + break; + case tgl_message_media_contact: + assert (json_object_set (res, "type", json_string ("contact")) >= 0); + assert (json_object_set (res, "phone", json_string (M->phone)) >= 0); + assert (json_object_set (res, "first_name", json_string (M->first_name)) >= 0); + assert (json_object_set (res, "last_name", json_string (M->last_name)) >= 0); + assert (json_object_set (res, "user_id", json_integer (M->user_id)) >= 0); + break; + case tgl_message_media_webpage: + assert (json_object_set (res, "type", json_string ("webpage")) >= 0); + if (M->webpage->url) { + assert (json_object_set (res, "url", json_string (M->webpage->url)) >= 0); + } + if (M->webpage->title) { + assert (json_object_set (res, "title", json_string (M->webpage->title)) >= 0); + } + if (M->webpage->description) { + assert (json_object_set (res, "description", json_string (M->webpage->description)) >= 0); + } + if (M->webpage->author) { + assert (json_object_set (res, "author", json_string (M->webpage->author)) >= 0); + } + break; + case tgl_message_media_venue: + assert (json_object_set (res, "type", json_string ("venue")) >= 0); + assert (json_object_set (res, "longitude", json_real (M->venue.geo.longitude)) >= 0); + assert (json_object_set (res, "latitude", json_real (M->venue.geo.latitude)) >= 0); + if (M->venue.title) { + assert (json_object_set (res, "type", json_string (M->venue.title)) >= 0); + } + if (M->venue.address) { + assert (json_object_set (res, "address", json_string (M->venue.address)) >= 0); + } + if (M->venue.provider) { + assert (json_object_set (res, "provider", json_string (M->venue.provider)) >= 0); + } + if (M->venue.venue_id) { + assert (json_object_set (res, "venue_id", json_string (M->venue.venue_id)) >= 0); + } + break; + default: + assert (json_object_set (res, "type", json_string ("???")) >= 0); + } + return res; +} + +json_t *json_pack_service (struct tgl_message *M) { + json_t *res = json_object (); + switch (M->action.type) { + case tgl_message_action_geo_chat_create: + assert (json_object_set (res, "type", json_string ("geo_created")) >= 0); + break; + case tgl_message_action_geo_chat_checkin: + assert (json_object_set (res, "type", json_string ("geo_checkin")) >= 0); + break; + case tgl_message_action_chat_create: + assert (json_object_set (res, "type", json_string ("chat_created")) >= 0); + assert (json_object_set (res, "title", json_string (M->action.title)) >= 0); + break; + case tgl_message_action_chat_edit_title: + assert (json_object_set (res, "type", json_string ("chat_rename")) >= 0); + assert (json_object_set (res, "title", json_string (M->action.title)) >= 0); + break; + case tgl_message_action_chat_edit_photo: + assert (json_object_set (res, "type", json_string ("chat_change_photo")) >= 0); + break; + case tgl_message_action_chat_delete_photo: + assert (json_object_set (res, "type", json_string ("chat_delete_photo")) >= 0); + break; + case tgl_message_action_chat_add_user: + assert (json_object_set (res, "type", json_string ("chat_add_user")) >= 0); + assert (json_object_set (res, "user", json_pack_peer (tgl_set_peer_id (TGL_PEER_USER, M->action.user), tgl_peer_get (TLS, tgl_set_peer_id (TGL_PEER_USER, M->action.user)))) >= 0); + break; + case tgl_message_action_chat_add_user_by_link: + assert (json_object_set (res, "type", json_string ("chat_add_user_link")) >= 0); + assert (json_object_set (res, "user", json_pack_peer (tgl_set_peer_id (TGL_PEER_USER, M->action.user), tgl_peer_get (TLS, tgl_set_peer_id (TGL_PEER_USER, M->action.user)))) >= 0); + break; + case tgl_message_action_chat_delete_user: + assert (json_object_set (res, "type", json_string ("chat_del_user")) >= 0); + assert (json_object_set (res, "user", json_pack_peer (tgl_set_peer_id (TGL_PEER_USER, M->action.user), tgl_peer_get (TLS, tgl_set_peer_id (TGL_PEER_USER, M->action.user)))) >= 0); + break; + case tgl_message_action_set_message_ttl: + assert (json_object_set (res, "type", json_string ("set_ttl")) >= 0); + assert (json_object_set (res, "ttl", json_integer (M->action.ttl)) >= 0); + break; + case tgl_message_action_read_messages: + assert (json_object_set (res, "type", json_string ("read")) >= 0); + assert (json_object_set (res, "count", json_integer (M->action.read_cnt)) >= 0); + break; + case tgl_message_action_delete_messages: + assert (json_object_set (res, "type", json_string ("delete")) >= 0); + assert (json_object_set (res, "count", json_integer (M->action.delete_cnt)) >= 0); + break; + case tgl_message_action_screenshot_messages: + assert (json_object_set (res, "type", json_string ("screenshot")) >= 0); + assert (json_object_set (res, "count", json_integer (M->action.screenshot_cnt)) >= 0); + break; + case tgl_message_action_flush_history: + assert (json_object_set (res, "type", json_string ("flush")) >= 0); + break; + case tgl_message_action_resend: + assert (json_object_set (res, "type", json_string ("resend")) >= 0); + break; + case tgl_message_action_notify_layer: + assert (json_object_set (res, "type", json_string ("notify_layer")) >= 0); + assert (json_object_set (res, "layer", json_integer (M->action.layer)) >= 0); + break; + case tgl_message_action_typing: + assert (json_object_set (res, "type", json_string ("typing")) >= 0); + break; + case tgl_message_action_noop: + assert (json_object_set (res, "type", json_string ("noop")) >= 0); + break; + case tgl_message_action_request_key: + assert (json_object_set (res, "type", json_string ("request_key")) >= 0); + break; + case tgl_message_action_accept_key: + assert (json_object_set (res, "type", json_string ("accept_key")) >= 0); + break; + case tgl_message_action_commit_key: + assert (json_object_set (res, "type", json_string ("commit_key")) >= 0); + break; + case tgl_message_action_abort_key: + assert (json_object_set (res, "type", json_string ("abort_key")) >= 0); + break; + default: + assert (json_object_set (res, "type", json_string ("???")) >= 0); + break; + } + return res; +} + +json_t *json_pack_message (struct tgl_message *M) { + json_t *res = json_object (); + + assert (json_object_set (res, "id", json_integer (M->id)) >= 0); + if (!(M->flags & TGLMF_CREATED)) { return res; } + + assert (json_object_set (res, "id", json_integer (M->flags)) >= 0); + + if (tgl_get_peer_type (M->fwd_from_id)) { + assert (json_object_set (res, "fwd_from", json_pack_peer (M->fwd_from_id, tgl_peer_get (TLS, M->fwd_from_id))) >= 0); + assert (json_object_set (res, "fwd_date", json_integer (M->fwd_date)) >= 0); + } + + if (M->reply_id) { + assert (json_object_set (res, "reply_id", json_integer (M->reply_id)) >= 0); + } + + if (M->flags & TGLMF_MENTION) { + assert (json_object_set (res, "mention", json_true ()) >= 0); + } + + assert (json_object_set (res, "from", json_pack_peer (M->from_id, tgl_peer_get (TLS, M->from_id))) >= 0); + assert (json_object_set (res, "to", json_pack_peer (M->to_id, tgl_peer_get (TLS, M->to_id))) >= 0); + + assert (json_object_set (res, "out", json_boolean (M->flags & TGLMF_OUT)) >= 0); + assert (json_object_set (res, "unread", json_boolean (M->flags & TGLMF_UNREAD)) >= 0); + assert (json_object_set (res, "service", json_boolean (M->flags & TGLMF_SERVICE)) >= 0); + assert (json_object_set (res, "date", json_integer (M->date)) >= 0); + + if (!(M->flags & TGLMF_SERVICE)) { + if (M->message_len && M->message) { + assert (json_object_set (res, "text", json_string (M->message)) >= 0); + } + if (M->media.type && M->media.type != tgl_message_media_none) { + assert (json_object_set (res, "media", json_pack_media (&M->media)) >= 0); + } + } else { + assert (json_object_set (res, "action", json_pack_service (M)) >= 0); + } + return res; +} +#endif diff --git a/json-tg.h b/json-tg.h new file mode 100644 index 0000000..a692109 --- /dev/null +++ b/json-tg.h @@ -0,0 +1,12 @@ +#ifndef __JSON_TG_H__ +#define __JSON_TG_H__ +#include "config.h" +#ifdef USE_JSON +#include +#include +#include +json_t *json_pack_message (struct tgl_message *M); +json_t *json_pack_updates (unsigned flags); +json_t *json_pack_peer (tgl_peer_id_t id, tgl_peer_t *P); +#endif +#endif diff --git a/main.c b/main.c index a94f8b2..888eece 100644 --- a/main.c +++ b/main.c @@ -120,6 +120,7 @@ int use_ids; int ipv6_enabled; char *start_command; int disable_link_preview; +int enable_json; struct tgl_state *TLS; @@ -480,7 +481,9 @@ void usage (void) { printf (" --help/-h prints this help\n"); printf (" --accept-any-tcp accepts tcp connections from any src (only loopback by default)\n"); printf (" --disable-link-preview disables server-side previews to links\n"); - + #ifdef USE_JSON + printf (" --json prints answers and values in json format\n"); + #endif exit (1); } @@ -625,6 +628,7 @@ void args_parse (int argc, char **argv) { {"help", no_argument, 0, 'h'}, {"accept-any-tcp", no_argument, 0, 1001}, {"disable-link-preview", no_argument, 0, 1002}, + {"json", no_argument, 0, 1003}, {0, 0, 0, 0 } }; @@ -738,6 +742,9 @@ void args_parse (int argc, char **argv) { case 1002: disable_link_preview = 2; break; + case 1003: + enable_json = 1; + break; case 'h': default: usage ();