├── debian ├── compat ├── docs ├── source │ └── format ├── .gitignore ├── changelog ├── rules ├── control └── copyright ├── AUTHORS ├── mrasender ├── init.lua ├── CMakeLists.txt └── cfunctions.c ├── .gitignore ├── mrasender-scm-1.rockspec ├── rpm └── tarantool-mrasender.spec ├── CMakeLists.txt ├── .travis.yml ├── LICENSE ├── README.md └── FindTarantool.cmake /debian/compat: -------------------------------------------------------------------------------- 1 | 9 2 | -------------------------------------------------------------------------------- /debian/docs: -------------------------------------------------------------------------------- 1 | README.md 2 | AUTHORS 3 | -------------------------------------------------------------------------------- /debian/source/format: -------------------------------------------------------------------------------- 1 | 3.0 (quilt) 2 | -------------------------------------------------------------------------------- /AUTHORS: -------------------------------------------------------------------------------- 1 | Maxim Glekov 2 | -------------------------------------------------------------------------------- /debian/.gitignore: -------------------------------------------------------------------------------- 1 | tarantool-modulekit/ 2 | files 3 | stamp-* 4 | *.substvars 5 | *.log 6 | -------------------------------------------------------------------------------- /mrasender/init.lua: -------------------------------------------------------------------------------- 1 | return require('mrasender.cfunctions') 2 | -- vim: ts=4 sts=4 sw=4 et 3 | -------------------------------------------------------------------------------- /debian/changelog: -------------------------------------------------------------------------------- 1 | tarantool-mrasender (1.0.1) unstable; urgency=low 2 | 3 | * Initial release 4 | 5 | -- Maxim Glekov Thu, 26 Apr 2016 11:30:00 +0300 6 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | CMakeFiles/ 2 | obj-*/ 3 | CMakeCache.txt 4 | Makefile 5 | cmake_*.cmake 6 | install_manifest.txt 7 | *.a 8 | *.cbp 9 | *.d 10 | *.dylib 11 | *.gcno 12 | *.gcda 13 | *.user 14 | *.o 15 | *.reject 16 | *.so 17 | *.snap* 18 | *.xlog* 19 | *~ 20 | .gdb_history 21 | ./build 22 | /build 23 | VERSION 24 | CTestTestfile.cmake 25 | Testing 26 | CTestTestfile.cmake 27 | -------------------------------------------------------------------------------- /debian/rules: -------------------------------------------------------------------------------- 1 | #!/usr/bin/make -f 2 | 3 | DEB_CMAKE_EXTRA_FLAGS := -DCMAKE_BUILD_TYPE=RelWithDebInfo 4 | DEB_CMAKE_EXTRA_FLAGS := -DCMAKE_INSTALL_LIBDIR=lib/$(DEB_HOST_MULTIARCH) \ 5 | -DCMAKE_BUILD_TYPE=RelWithDebInfo 6 | DEB_MAKE_CHECK_TARGET := check 7 | 8 | include /usr/share/cdbs/1/rules/debhelper.mk 9 | include /usr/share/cdbs/1/class/cmake.mk 10 | -------------------------------------------------------------------------------- /mrasender/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | if (APPLE) 2 | set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -undefined suppress -flat_namespace") 3 | endif(APPLE) 4 | 5 | add_library(cfunctions SHARED cfunctions.c) 6 | target_link_libraries(cfunctions ${MSGPUCK_LIBRARIES}) 7 | set_target_properties(cfunctions PROPERTIES PREFIX "" OUTPUT_NAME "cfunctions") 8 | 9 | # Install module 10 | install(FILES init.lua DESTINATION ${TARANTOOL_INSTALL_LUADIR}/${PROJECT_NAME}/) 11 | install(TARGETS cfunctions LIBRARY DESTINATION ${TARANTOOL_INSTALL_LIBDIR}/${PROJECT_NAME}/) 12 | -------------------------------------------------------------------------------- /debian/control: -------------------------------------------------------------------------------- 1 | Source: tarantool-mrasender 2 | Priority: optional 3 | Section: database 4 | Maintainer: Maxim Glekov 5 | # cmake, tarantool-dev, are only needed for Lua/C and C modules 6 | Build-Depends: debhelper (>= 9), cdbs, 7 | cmake (>= 2.8), 8 | tarantool-dev (>= 1.6.8.0), 9 | # For /usr/bin/prove 10 | perl (>= 5.10.0) 11 | Standards-Version: 3.9.6 12 | Homepage: https://github.com/tarantool/mrasender 13 | Vcs-Git: git://github.com/tarantool/mrasender.git 14 | Vcs-Browser: https://github.com/tarantool/mrasender 15 | 16 | Package: tarantool-mrasender 17 | Architecture: i386 amd64 armhf arm64 18 | Depends: tarantool (>= 1.6.8.0), ${shlibs:Depends}, ${misc:Depends} 19 | Pre-Depends: ${misc:Pre-Depends} 20 | Description: Tarantool module for sending messages to Mail.ru Agent 21 | 22 | -------------------------------------------------------------------------------- /mrasender-scm-1.rockspec: -------------------------------------------------------------------------------- 1 | package = 'mrasender' 2 | version = '1.0.1' 3 | 4 | source = { 5 | url = 'git://github.com/tarantool/mrasender.git'; 6 | branch = 'master'; 7 | } 8 | 9 | description = { 10 | summary = "Send message to Mail.Ru Agent from Tarantool"; 11 | detailed = [[ 12 | Send message to Mail.Ru Agent from Tarantool 13 | ]]; 14 | homepage = 'https://github.com/tarantool/mrasender.git'; 15 | license = 'BSD'; 16 | maintainer = "Maxim Glekov "; 17 | } 18 | 19 | dependencies = { 20 | 'lua >= 5.1'; 21 | } 22 | 23 | external_dependencies = { 24 | TARANTOOL = { 25 | header = 'tarantool/module.h' 26 | }; 27 | } 28 | 29 | build = { 30 | type = 'builtin', 31 | 32 | modules = { 33 | ['mrasender'] = 'mrasender/init.lua'; 34 | ['mrasender.functions'] = 'mrasender/functions.lua'; 35 | ['mrasender.cfunctions'] = { 36 | incdirs = { 37 | '$(TARANTOOL_INCDIR)'; 38 | }; 39 | sources = 'mrasender/cfunctions.c'; 40 | } 41 | } 42 | } 43 | -- vim: syntax=lua ts=4 sts=4 sw=4 et 44 | -------------------------------------------------------------------------------- /rpm/tarantool-mrasender.spec: -------------------------------------------------------------------------------- 1 | Name: tarantool-mrasender 2 | Version: 1.0.1 3 | Release: 1%{?dist} 4 | Summary: Tarantool module for sending messages to Mail.ru Agent 5 | Group: Applications/Databases 6 | License: BSD 7 | URL: https://github.com/tarantool/mrasender 8 | Source0: https://github.com/tarantool/%{name}/archive/%{version}/%{name}-%{version}.tar.gz 9 | BuildRequires: cmake >= 2.8 10 | BuildRequires: gcc >= 4.5 11 | BuildRequires: tarantool-devel >= 1.6.8.0 12 | BuildRequires: msgpuck-devel >= 1.0.0 13 | BuildRequires: /usr/bin/prove 14 | Requires: tarantool >= 1.6.8.0 15 | 16 | %description 17 | This package provides send message to Mail.Ru Agent from Tarantool. 18 | 19 | %prep 20 | %setup -q -n %{name}-%{version} 21 | 22 | %build 23 | %cmake . -DCMAKE_BUILD_TYPE=RelWithDebInfo 24 | make %{?_smp_mflags} 25 | 26 | %check 27 | make %{?_smp_mflags} check 28 | 29 | %install 30 | %make_install 31 | 32 | %files 33 | %{_libdir}/tarantool/*/ 34 | %{_datarootdir}/tarantool/*/ 35 | %doc README.md 36 | %{!?_licensedir:%global license %doc} 37 | %license LICENSE AUTHORS 38 | 39 | %changelog 40 | * Thu Apr 26 2016 Maxim Glekov 1.0.1 41 | - Initial version of the RPM spec 42 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 2.8 FATAL_ERROR) 2 | 3 | # project(mrasender C) 4 | project(mrasender C CXX) 5 | 6 | if(NOT CMAKE_BUILD_TYPE) 7 | set(CMAKE_BUILD_TYPE RelWithDebInfo) 8 | endif() 9 | set(CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}" ${CMAKE_MODULE_PATH}) 10 | 11 | # Find Tarantool and Lua dependecies 12 | set(TARANTOOL_FIND_REQUIRED ON) 13 | find_package(Tarantool) 14 | include_directories(${TARANTOOL_INCLUDE_DIRS}) 15 | 16 | # Find other dependecies 17 | 18 | # Set CFLAGS 19 | # set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS}") 20 | set(CMAKE_C_FLAGS_DEBUG "${CMAKE_C_FLAGS_DEBUG} -Wall -Wextra") 21 | # Set CXXFLAGS 22 | # set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 23 | set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -Wall -Wextra") 24 | 25 | # Build module 26 | add_subdirectory(mrasender) 27 | 28 | enable_testing() 29 | 30 | set (LUA_PATH "LUA_PATH=${PROJECT_SOURCE_DIR}/?.lua\\;${PROJECT_SOURCE_DIR}/?/init.lua\\;;") 31 | 32 | # add_test(mrasender ${CMAKE_SOURCE_DIR}/test/mrasender.test.lua) 33 | # set_tests_properties(mrasender PROPERTIES ENVIRONMENT "${LUA_PATH}") 34 | 35 | # Add `make check` 36 | add_custom_target(check 37 | WORKING_DIRECTORY ${PROJECT_BUILD_DIR} 38 | COMMAND ctest -V) 39 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | services: 3 | - docker 4 | 5 | language: python 6 | 7 | env: 8 | global: 9 | - PRODUCT=tarantool-mrasender 10 | 11 | matrix: 12 | - PACK=none 13 | - OS=el DIST=6 PACK=rpm 14 | - OS=el DIST=7 PACK=rpm 15 | - OS=fedora DIST=22 PACK=rpm 16 | - OS=fedora DIST=23 PACK=rpm 17 | - OS=ubuntu DIST=precise PACK=deb 18 | - OS=ubuntu DIST=trusty PACK=deb 19 | - OS=ubuntu DIST=wily PACK=deb 20 | - OS=ubuntu DIST=xenial PACK=deb 21 | - OS=debian DIST=wheezy PACK=deb 22 | - OS=debian DIST=jessie PACK=deb 23 | - OS=debian DIST=stretch PACK=deb 24 | 25 | matrix: 26 | allow_failures: 27 | - env: OS=el DIST=6 PACK=rpm 28 | - env: OS=el DIST=7 PACK=rpm 29 | - env: OS=fedora DIST=22 PACK=rpm 30 | - env: OS=fedora DIST=23 PACK=rpm 31 | - env: OS=ubuntu DIST=precise PACK=deb 32 | - env: OS=ubuntu DIST=trusty PACK=deb 33 | - env: OS=ubuntu DIST=wily PACK=deb 34 | - env: OS=ubuntu DIST=xenial PACK=deb 35 | - env: OS=debian DIST=jessie PACK=deb 36 | - env: OS=debian DIST=wheezy PACK=deb 37 | - env: OS=debian DIST=stretch PACK=deb 38 | 39 | script: 40 | - git clone https://github.com/tarantool/build.git 41 | - ./build/pack/travis.sh 42 | 43 | notifications: 44 | email: true 45 | irc: false 46 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (C) 2010-2015 Tarantool AUTHORS: please see AUTHORS file. 2 | 3 | Redistribution and use in source and binary forms, with or 4 | without modification, are permitted provided that the following 5 | conditions are met: 6 | 7 | 1. Redistributions of source code must retain the above 8 | copyright notice, this list of conditions and the 9 | following disclaimer. 10 | 11 | 2. Redistributions in binary form must reproduce the above 12 | copyright notice, this list of conditions and the following 13 | disclaimer in the documentation and/or other materials 14 | provided with the distribution. 15 | 16 | THIS SOFTWARE IS PROVIDED BY AUTHORS ``AS IS'' AND 17 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 18 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 19 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 20 | AUTHORS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 21 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 22 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 24 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 25 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 27 | THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 28 | SUCH DAMAGE. 29 | 30 | -------------------------------------------------------------------------------- /debian/copyright: -------------------------------------------------------------------------------- 1 | Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Debianized-By: Maxim Glekov 3 | Upstream-Name: tarantool-mrasender 4 | Upstream-Contact: m.glekov@corp.mail.ru 5 | Source: https://github.com/tarantool/mrasender 6 | 7 | Files: * 8 | Copyright: 2016 by Maxim Glekov 9 | License: BSD-2-Clause 10 | Redistribution and use in source and binary forms, with or 11 | without modification, are permitted provided that the following 12 | conditions are met: 13 | . 14 | 1. Redistributions of source code must retain the above 15 | copyright notice, this list of conditions and the 16 | following disclaimer. 17 | . 18 | 2. Redistributions in binary form must reproduce the above 19 | copyright notice, this list of conditions and the following 20 | disclaimer in the documentation and/or other materials 21 | provided with the distribution. 22 | . 23 | THIS SOFTWARE IS PROVIDED BY ``AS IS'' AND 24 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 25 | TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 26 | A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL 27 | OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, 28 | INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 29 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 30 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR 31 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 32 | LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 33 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF 34 | THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 35 | SUCH DAMAGE. 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # mrasender - [Tarantool][] module for sending messages to Mail.Ru Agent 2 | 3 | Warning: recipient must be in your contactlist! 4 | 5 | ## Prerequisites 6 | 7 | * Tarantool 1.6.5+ with header files (tarantool && tarantool-dev packages) 8 | 9 | ## Installation HowTo 10 | * Install [Tarantool] && tarantool-dev packages (see http://tarantool.org/download.html) 11 | * Install mrasender module for [Tarantool] 12 | ``` 13 | git clone https://github.com/agent-0007/mrasender 14 | cd mrasender 15 | cmake . 16 | make 17 | make install 18 | ``` 19 | 20 | * Copy paste LUA script below, save it to file mrasender_test.lua 21 | ```lua 22 | 23 | #!/usr/bin/env tarantool 24 | 25 | box.cfg{ 26 | log_level = 5 27 | } 28 | 29 | local agent = require('mrasender') 30 | local log = require('log') 31 | 32 | local username = 'USERNAME@mail.ru' 33 | local password = 'PASSWORD' 34 | local recipient = 'RECIPIENT@mail.ru' 35 | local msg = 'Test message from tarantool' 36 | 37 | -- send message to recipient from username 38 | local res = agent.send_message_to_mra(username, password, recipient, msg) 39 | 40 | log.info("Send message to:"..recipient..", result:"..res); 41 | 42 | ``` 43 | 44 | * Replace USERNAME@mail.ru, PASSWORD, RECIPIENT@mail.ru whith your sending account and recipient account 45 | * chmod +x mrasender_test.lua 46 | * Run and enjoy: ./mrasender.lua 47 | 48 | ## See Also 49 | 50 | * [Tarantool][] 51 | * [Tarantool Rocks][TarantoolRocks] 52 | * [Tarantool/Lua API Reference][TarantoolLuaReference] 53 | * [Tarantool/C API Reference][TarantoolCReference] 54 | * [Lua/C API Reference][LuaCReference] 55 | 56 | [Tarantool]: http://github.com/tarantool/tarantool 57 | [Download]: http://tarantool.org/download.html 58 | [RockSpec]: https://github.com/keplerproject/luarocks/wiki/Rockspec-format 59 | [LuaCReference]: http://pgl.yoyo.org/luai/i/_ 60 | [TarantoolLuaReference]: http://tarantool.org/doc/reference/index.html 61 | [TarantoolCReference]: http://tarantool.org/doc/reference/capi.html 62 | [TarantoolRocks]: https://github.com/tarantool/rocks 63 | -------------------------------------------------------------------------------- /FindTarantool.cmake: -------------------------------------------------------------------------------- 1 | # Define GNU standard installation directories 2 | include(GNUInstallDirs) 3 | 4 | macro(extract_definition name output input) 5 | string(REGEX MATCH "#define[\t ]+${name}[\t ]+\"([^\"]*)\"" 6 | _t "${input}") 7 | string(REGEX REPLACE "#define[\t ]+${name}[\t ]+\"(.*)\"" "\\1" 8 | ${output} "${_t}") 9 | endmacro() 10 | 11 | find_path(TARANTOOL_INCLUDE_DIR tarantool/module.h 12 | HINTS ENV TARANTOOL_DIR 13 | ) 14 | 15 | if(TARANTOOL_INCLUDE_DIR) 16 | set(_config "-") 17 | file(READ "${TARANTOOL_INCLUDE_DIR}/tarantool/module.h" _config0) 18 | string(REPLACE "\\" "\\\\" _config ${_config0}) 19 | unset(_config0) 20 | extract_definition(PACKAGE_VERSION TARANTOOL_VERSION ${_config}) 21 | extract_definition(INSTALL_PREFIX _install_prefix ${_config}) 22 | unset(_config) 23 | endif() 24 | 25 | include(FindPackageHandleStandardArgs) 26 | find_package_handle_standard_args(TARANTOOL 27 | REQUIRED_VARS TARANTOOL_INCLUDE_DIR VERSION_VAR TARANTOOL_VERSION) 28 | if(TARANTOOL_FOUND) 29 | set(TARANTOOL_INSTALL_LIBDIR "${CMAKE_INSTALL_LIBDIR}/tarantool") 30 | set(TARANTOOL_INSTALL_LUADIR "${CMAKE_INSTALL_DATADIR}/tarantool") 31 | set(TARANTOOL_INCLUDE_DIRS "${TARANTOOL_INCLUDE_DIR}" 32 | "${TARANTOOL_INCLUDE_DIR}/tarantool/") 33 | 34 | if (NOT "${CMAKE_INSTALL_PREFIX}" STREQUAL "/usr/local" AND 35 | NOT "${CMAKE_INSTALL_PREFIX}" STREQUAL "${_install_prefix}") 36 | message(WARNING "Provided CMAKE_INSTALL_PREFIX is different from " 37 | "CMAKE_INSTALL_PREFIX of Tarantool. You might need to set " 38 | "corrent package.path/package.cpath to load this module or " 39 | "change your build prefix:" 40 | "\n" 41 | "cmake . -DCMAKE_INSTALL_PREFIX=${_install_prefix}" 42 | "\n" 43 | ) 44 | endif () 45 | if (NOT TARANTOOL_FIND_QUIETLY AND NOT FIND_TARANTOOL_DETAILS) 46 | set(FIND_TARANTOOL_DETAILS ON CACHE INTERNAL "Details about TARANTOOL") 47 | message(STATUS "Tarantool LUADIR is ${TARANTOOL_INSTALL_LUADIR}") 48 | message(STATUS "Tarantool LIBDIR is ${TARANTOOL_INSTALL_LIBDIR}") 49 | endif () 50 | endif() 51 | mark_as_advanced(TARANTOOL_INCLUDE_DIRS TARANTOOL_INSTALL_LIBDIR 52 | TARANTOOL_INSTALL_LUADIR) 53 | -------------------------------------------------------------------------------- /mrasender/cfunctions.c: -------------------------------------------------------------------------------- 1 | /* A C submodule */ 2 | #include 3 | 4 | #include 5 | #include 6 | #include 7 | 8 | #include 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #define MRA_HOST "mrim.mail.ru" 22 | #define MRA_PORT "2042" 23 | #define VERSION_TXT "tarantool mra sender 1.0.2" 24 | #define MRA_BUF_LEN 65536 25 | #define LPSLENGTH(s) (*((uint32_t *)(s))) 26 | #define LPSSIZE(s) (LPSLENGTH(s) + sizeof(uint32_t)) 27 | #define LPSALLOC(c) ((char *) malloc((c) + sizeof(uint32_t))) 28 | 29 | #define PROTO_VERSION_MAJOR 1 30 | #define PROTO_VERSION_MINOR 8 31 | #define PROTO_VERSION ((((u_long)(PROTO_VERSION_MAJOR))<<16)|(u_long)(PROTO_VERSION_MINOR)) 32 | #define PROTO_MAJOR(p) (((p)&0xFFFF0000)>>16) 33 | #define PROTO_MINOR(p) ((p)&0x0000FFFF) 34 | #define CS_MAGIC 0xDEADBEEF 35 | 36 | #define MRIM_CS_HELLO 0x1001 37 | #define MRIM_CS_HELLO_ACK 0x1002 38 | #define MRIM_CS_LOGIN_ACK 0x1004 39 | #define MRIM_CS_LOGIN_REJ 0x1005 40 | #define MRIM_CS_PING 0x1006 41 | #define MRIM_CS_MESSAGE 0x1008 42 | #define MESSAGE_FLAG_OFFLINE 0x00000001 43 | #define MESSAGE_FLAG_NORECV 0x00000004 44 | #define MESSAGE_FLAG_AUTHORIZE 0x00000008 45 | #define MESSAGE_FLAG_SYSTEM 0x00000040 46 | #define MESSAGE_FLAG_RTF 0x00000080 47 | #define MESSAGE_FLAG_CONTACT 0x00000200 48 | #define MESSAGE_FLAG_NOTIFY 0x00000400 49 | #define MESSAGE_FLAG_MULTICAST 0x00001000 50 | #define MAX_MULTICAST_RECIPIENTS 50 51 | #define MESSAGE_USERFLAGS_MASK 0x000036A8 52 | #define MRIM_CS_MESSAGE_ACK 0x1009 53 | #define MRIM_CS_MESSAGE_RECV 0x1011 54 | #define MRIM_CS_MESSAGE_STATUS 0x1012 55 | #define MESSAGE_DELIVERED 0x0000 56 | #define MESSAGE_REJECTED_NOUSER 0x8001 57 | #define MESSAGE_REJECTED_INTERR 0x8003 58 | #define MESSAGE_REJECTED_LIMIT_EXCEEDED 0x8004 59 | #define MESSAGE_REJECTED_TOO_LARGE 0x8005 60 | #define MESSAGE_REJECTED_DENY_OFFMSG 0x8006 61 | #define MRIM_CS_USER_STATUS 0x100F 62 | #define STATUS_OFFLINE 0x00000000 63 | #define STATUS_ONLINE 0x00000001 64 | #define STATUS_AWAY 0x00000002 65 | #define STATUS_UNDETERMINATED 0x00000003 66 | #define STATUS_FLAG_INVISIBLE 0x80000000 67 | #define MRIM_CS_LOGOUT 0x1013 68 | #define LOGOUT_NO_RELOGIN_FLAG 0x0010 69 | #define MRIM_CS_CONNECTION_PARAMS 0x1014 70 | #define MRIM_CS_USER_INFO 0x1015 71 | #define MRIM_CS_ADD_CONTACT 0x1019 72 | #define CONTACT_FLAG_REMOVED 0x00000001 73 | #define CONTACT_FLAG_GROUP 0x00000002 74 | #define CONTACT_FLAG_INVISIBLE 0x00000004 75 | #define CONTACT_FLAG_VISIBLE 0x00000008 76 | #define CONTACT_FLAG_IGNORE 0x00000010 77 | #define CONTACT_FLAG_SHADOW 0x00000020 78 | #define MRIM_CS_ADD_CONTACT_ACK 0x101A 79 | #define CONTACT_OPER_SUCCESS 0x0000 80 | #define CONTACT_OPER_ERROR 0x0001 81 | #define CONTACT_OPER_INTERR 0x0002 82 | #define CONTACT_OPER_NO_SUCH_USER 0x0003 83 | #define CONTACT_OPER_INVALID_INFO 0x0004 84 | #define CONTACT_OPER_USER_EXISTS 0x0005 85 | #define CONTACT_OPER_GROUP_LIMIT 0x0006 86 | #define MRIM_CS_MODIFY_CONTACT 0x101B 87 | #define MRIM_CS_MODIFY_CONTACT_ACK 0x101C 88 | #define MRIM_CS_OFFLINE_MESSAGE_ACK 0x101D 89 | #define MRIM_CS_DELETE_OFFLINE_MESSAGE 0x101E 90 | #define MRIM_CS_AUTHORIZE 0x1020 91 | #define MRIM_CS_AUTHORIZE_ACK 0x1021 92 | #define MRIM_CS_CHANGE_STATUS 0x1022 93 | #define MRIM_CS_GET_MPOP_SESSION 0x1024 94 | #define MRIM_CS_MPOP_SESSION 0x1025 95 | #define MRIM_GET_SESSION_FAIL 0 96 | #define MRIM_GET_SESSION_SUCCESS 1 97 | #define MRIM_CS_WP_REQUEST 0x1029 98 | #define PARAMS_NUMBER_LIMIT 50 99 | #define PARAM_VALUE_LENGTH_LIMIT 64 100 | #define MRIM_CS_ANKETA_INFO 0x1028 101 | #define MRIM_ANKETA_INFO_STATUS_OK 1 102 | #define MRIM_ANKETA_INFO_STATUS_NOUSER 0 103 | #define MRIM_ANKETA_INFO_STATUS_DBERR 2 104 | #define MRIM_ANKETA_INFO_STATUS_RATELIMERR 3 105 | #define MRIM_CS_MAILBOX_STATUS 0x1033 106 | #define MRIM_CS_CONTACT_LIST2 0x1037 107 | #define GET_CONTACTS_OK 0x0000 108 | #define GET_CONTACTS_ERROR 0x0001 109 | #define GET_CONTACTS_INTERR 0x0002 110 | #define CONTACT_INTFLAG_NOT_AUTHORIZED 0x0001 111 | #define MRIM_CS_LOGIN2 0x1038 112 | #define MAX_CLIENT_DESCRIPTION 256 113 | 114 | enum { 115 | MRIM_CS_WP_REQUEST_PARAM_USER = 0, 116 | MRIM_CS_WP_REQUEST_PARAM_DOMAIN, 117 | MRIM_CS_WP_REQUEST_PARAM_NICKNAME, 118 | MRIM_CS_WP_REQUEST_PARAM_FIRSTNAME, 119 | MRIM_CS_WP_REQUEST_PARAM_LASTNAME, 120 | MRIM_CS_WP_REQUEST_PARAM_SEX, 121 | MRIM_CS_WP_REQUEST_PARAM_BIRTHDAY, 122 | MRIM_CS_WP_REQUEST_PARAM_DATE1, 123 | MRIM_CS_WP_REQUEST_PARAM_DATE2, 124 | MRIM_CS_WP_REQUEST_PARAM_ONLINE, 125 | MRIM_CS_WP_REQUEST_PARAM_STATUS, 126 | MRIM_CS_WP_REQUEST_PARAM_CITY_ID, 127 | MRIM_CS_WP_REQUEST_PARAM_ZODIAC, 128 | MRIM_CS_WP_REQUEST_PARAM_BIRTHDAY_MONTH, 129 | MRIM_CS_WP_REQUEST_PARAM_BIRTHDAY_DAY, 130 | MRIM_CS_WP_REQUEST_PARAM_COUNTRY_ID, 131 | MRIM_CS_WP_REQUEST_PARAM_MAX 132 | }; 133 | 134 | typedef struct mrim_connection_params_t { 135 | unsigned long ping_period; 136 | } mrim_connection_params_t; 137 | 138 | typedef struct _mrim_packet_header_t { 139 | uint32_t magic; // magic 140 | uint32_t proto; // protocol version 141 | uint32_t seq; // sequence number 142 | uint32_t msg; // packet type 143 | uint32_t dlen; // data length 144 | uint32_t from; // sender address 145 | uint32_t fromport; // sender port 146 | u_char reserved[16]; // reserved 147 | } mrim_packet_header_t; 148 | 149 | typedef struct mrasender 150 | { 151 | int mra_socket; // mra socket 152 | char *tx_buf; // TX buffer 153 | unsigned int tx_len; // TX buffer size 154 | char *rx_buf; // RX buffer 155 | unsigned int rx_len; // RX buffer size 156 | unsigned int seq; // Sequence number 157 | int received_hello_ack; // Is 'hello' message received 158 | int received_login_ack; // Is 'login OK' message recievied 159 | int received_login_rej; // Is 'login FAIL' message received 160 | } run_data; 161 | 162 | /* mrim connect */ 163 | int mrim_connect_tcp(char *host, char *port) 164 | { 165 | int s; 166 | struct addrinfo hints, *res; 167 | 168 | if ((s = socket(PF_INET, SOCK_STREAM, 0)) == -1 && errno != EINTR) { 169 | say_crit("cannot create socket: %s", strerror(errno)); 170 | return -1; 171 | } 172 | 173 | memset(&hints, 0, sizeof(hints)); 174 | hints.ai_family = PF_INET; 175 | hints.ai_socktype = SOCK_STREAM; 176 | hints.ai_protocol = 0; 177 | hints.ai_flags = 0; 178 | 179 | if (getaddrinfo(host, port, &hints, &res) != 0 && errno != EINTR) { 180 | say_crit("cannot getaddrinfo for %s:%s: %s", host, port, strerror(errno)); 181 | close(s); 182 | return -1; 183 | } 184 | 185 | if(connect(s, res->ai_addr, res->ai_addrlen) == -1){ 186 | say_crit("cannot connect to %s:%s: %s", host, port, strerror(errno)); 187 | if (res) { 188 | freeaddrinfo(res); 189 | } 190 | close(s); 191 | return -1; 192 | } 193 | 194 | if (res) { 195 | freeaddrinfo(res); 196 | } 197 | 198 | return s; 199 | } 200 | 201 | /* String -> LPS string */ 202 | char *mrim_net_mklps(const char *sz) 203 | { 204 | uint32_t len; 205 | char *lps = LPSALLOC(strlen(sz)); 206 | 207 | len = strlen(sz); 208 | *((uint32_t *)lps) = len; 209 | memcpy(lps + sizeof(uint32_t), sz, strlen(sz)); 210 | return lps; 211 | } 212 | 213 | /* LPS string -> String */ 214 | char *mrim_net_mksz(char *lps) 215 | { 216 | uint32_t len; 217 | char *sz = (char *) malloc(1 + LPSLENGTH(lps)); 218 | 219 | len = *((uint32_t *)lps); 220 | memcpy(sz, lps + sizeof(uint32_t), len); 221 | *(sz + len) = 0; 222 | return sz; 223 | } 224 | 225 | /* Fill mrim packet header */ 226 | void mrim_net_fill_cs_header(mrim_packet_header_t *head, uint32_t seq, uint32_t msg, uint32_t len) 227 | { 228 | head->proto = PROTO_VERSION; 229 | head->magic = CS_MAGIC; 230 | head->seq = seq; 231 | head->msg = msg; 232 | head->dlen = len; 233 | head->from = 0; 234 | head->fromport = 0; 235 | } 236 | 237 | /* Fill RX buffer */ 238 | void mrim_net_send(run_data *work_data, void *data, size_t len) 239 | { 240 | work_data->tx_buf = (char *) realloc(work_data->tx_buf, work_data->tx_len + len); 241 | memcpy(work_data->tx_buf + work_data->tx_len, data, len); 242 | work_data->tx_len += len; 243 | } 244 | 245 | /* Do send RX buffer */ 246 | int mrim_net_send_flush(run_data *work_data) 247 | { 248 | //tx_len + len 249 | 250 | if (write(work_data->mra_socket, work_data->tx_buf, work_data->tx_len) == -1 && errno != EINTR) { 251 | say_crit("cannot write data to socket: %s (%d)", strerror(errno), errno); 252 | return -1; 253 | } else { 254 | memset(work_data->tx_buf, 0, sizeof(work_data->tx_buf)); 255 | work_data->tx_len = 0; 256 | return 0; 257 | } 258 | } 259 | 260 | /* Send 'receive ack' packet */ 261 | int mrim_net_send_receive_ack(run_data *work_data, char *from, uint32_t msg_id) 262 | { 263 | mrim_packet_header_t head; 264 | char *from_lps = mrim_net_mklps(from); 265 | 266 | memset(&head, 0, sizeof(head)); 267 | 268 | mrim_net_fill_cs_header(&head, work_data->seq++, MRIM_CS_MESSAGE_RECV, LPSSIZE(from_lps) + sizeof(msg_id)); 269 | mrim_net_send(work_data, &head, sizeof(head)); 270 | mrim_net_send(work_data, from_lps, LPSSIZE(from_lps)); 271 | mrim_net_send(work_data, &msg_id, sizeof(msg_id)); 272 | free(from_lps); 273 | 274 | if (mrim_net_send_flush(work_data) == -1) { 275 | return -1; 276 | } 277 | return 0; 278 | } 279 | 280 | /* Send 'auth ack' packet */ 281 | int mrim_net_send_auth_request_ack(run_data *work_data, char *email) 282 | { 283 | mrim_packet_header_t head; 284 | char *email_lps = mrim_net_mklps(email); 285 | 286 | memset(&head, 0, sizeof(head)); 287 | 288 | mrim_net_fill_cs_header(&head, work_data->seq++, MRIM_CS_AUTHORIZE, LPSSIZE(email_lps)); 289 | mrim_net_send(work_data, &head, sizeof(head)); 290 | mrim_net_send(work_data, email_lps, LPSSIZE(email_lps)); 291 | free(email_lps); 292 | 293 | if (mrim_net_send_flush(work_data) == -1) { 294 | return -1; 295 | } 296 | return 0; 297 | } 298 | 299 | /* Send 'hello' packet */ 300 | int mrim_send_hello(run_data *work_data) 301 | { 302 | mrim_packet_header_t head; 303 | 304 | memset(&head, 0, sizeof(head)); 305 | 306 | mrim_net_fill_cs_header(&head, work_data->seq++, MRIM_CS_HELLO, 0); 307 | mrim_net_send(work_data, &head, sizeof(head)); 308 | 309 | if (mrim_net_send_flush(work_data) == -1) { 310 | return -1; 311 | } 312 | 313 | while (work_data->received_hello_ack == 0) { 314 | if (mrim_net_read(work_data) == -1) { 315 | return -1; 316 | } 317 | } 318 | 319 | return 0; 320 | } 321 | 322 | /* Send 'auth' packet*/ 323 | int mrim_send_auth(run_data *work_data, const char *username, const char *password, uint32_t status) 324 | { 325 | mrim_packet_header_t head; 326 | char *username_lps; 327 | char *password_lps; 328 | char *desc_lps; 329 | uint32_t dw = 0; 330 | size_t i; 331 | 332 | memset(&head, 0, sizeof(head)); 333 | 334 | // convert username, password and desc to LPS 335 | username_lps = mrim_net_mklps(username); 336 | password_lps = mrim_net_mklps(password); 337 | desc_lps = mrim_net_mklps(VERSION_TXT); 338 | 339 | // send all data 340 | mrim_net_fill_cs_header(&head, work_data->seq++, MRIM_CS_LOGIN2, LPSSIZE(username_lps) + LPSSIZE(password_lps) + LPSSIZE(desc_lps) + sizeof(uint32_t) * 6); 341 | mrim_net_send(work_data, &head, sizeof(head)); 342 | mrim_net_send(work_data, username_lps, LPSSIZE(username_lps)); 343 | mrim_net_send(work_data, password_lps, LPSSIZE(password_lps)); 344 | mrim_net_send(work_data, &status, sizeof(status)); 345 | mrim_net_send(work_data, desc_lps, LPSSIZE(desc_lps)); 346 | 347 | for (i = 0; i < 5; i++) { 348 | mrim_net_send(work_data, &dw, sizeof(dw)); 349 | } 350 | 351 | free(username_lps); 352 | free(password_lps); 353 | free(desc_lps); 354 | 355 | if (mrim_net_send_flush(work_data) == -1) { 356 | return -1; 357 | } 358 | 359 | while (work_data->received_login_ack == 0 && work_data->received_login_rej == 0) { 360 | if (mrim_net_read(work_data) == -1) { 361 | return -1; 362 | } 363 | } 364 | 365 | return 0; 366 | } 367 | 368 | /* Send 'message' packet */ 369 | int mrim_send_message(run_data *work_data, const char *to, const char *message, uint32_t flags) 370 | { 371 | mrim_packet_header_t head; 372 | char *to_lps; 373 | char *message_lps; 374 | char *message_rtf_lps; 375 | int ret; 376 | 377 | memset(&head, 0, sizeof(head)); 378 | 379 | to_lps = mrim_net_mklps(to); 380 | message_lps = mrim_net_mklps(message); 381 | message_rtf_lps = mrim_net_mklps(" "); 382 | 383 | mrim_net_fill_cs_header(&head, work_data->seq++, MRIM_CS_MESSAGE, sizeof(uint32_t) + LPSSIZE(to_lps) + LPSSIZE(message_lps) + LPSSIZE(message_rtf_lps)); 384 | mrim_net_send(work_data, &head, sizeof(head)); 385 | mrim_net_send(work_data, &flags, sizeof(uint32_t)); 386 | mrim_net_send(work_data, to_lps, LPSSIZE(to_lps)); 387 | mrim_net_send(work_data, message_lps, LPSSIZE(message_lps)); 388 | mrim_net_send(work_data, message_rtf_lps, LPSSIZE(message_rtf_lps)); 389 | ret = mrim_net_send_flush(work_data); 390 | 391 | free(to_lps); 392 | free(message_lps); 393 | free(message_rtf_lps); 394 | 395 | return ret; 396 | } 397 | 398 | /* Send 'ping' packet */ 399 | int mrim_send_ping(run_data *work_data) 400 | { 401 | mrim_packet_header_t head; 402 | memset(&head, 0, sizeof(head)); 403 | 404 | say_crit("Send 'PING' packet"); 405 | 406 | mrim_net_fill_cs_header(&head, work_data->seq++, MRIM_CS_PING, 0); 407 | mrim_net_send(work_data, &head, sizeof(head)); 408 | return mrim_net_send_flush(work_data); 409 | } 410 | 411 | /* Read incoming 'message' packet */ 412 | void mrim_read_message(run_data *work_data, char *answer, uint32_t len) 413 | { 414 | uint32_t msg_id; 415 | uint32_t flags; 416 | char *from; 417 | 418 | // parse data 419 | msg_id = *(uint32_t *) answer; 420 | answer += sizeof(uint32_t); 421 | flags = *(uint32_t *) answer; 422 | answer += sizeof(uint32_t); 423 | from = mrim_net_mksz(answer); 424 | 425 | // send receive ack if needed 426 | if (!(flags & MESSAGE_FLAG_NORECV)) { 427 | mrim_net_send_receive_ack(work_data, from, msg_id); 428 | } 429 | 430 | // proceed message 431 | if (flags & MESSAGE_FLAG_AUTHORIZE) { 432 | // authorization request 433 | mrim_net_send_auth_request_ack(work_data, from); 434 | // } else if (flags & MESSAGE_FLAG_SYSTEM) { 435 | // // system message 436 | // } else if (flags & MESSAGE_FLAG_CONTACT) { 437 | // // contacts list 438 | // } else if (flags & MESSAGE_FLAG_NOTIFY) { 439 | // // typing notify 440 | // } else { 441 | // // casual message 442 | } 443 | 444 | free(from); 445 | } 446 | 447 | /* Read and parce incoming message */ 448 | int mrim_net_read_proceed(run_data *work_data) 449 | { 450 | mrim_packet_header_t *head; 451 | size_t packet_len = 0; 452 | char *answer; 453 | char *next_packet; 454 | char *ddata = NULL; 455 | 456 | memset(&head, 0, sizeof(head)); 457 | 458 | if (work_data->rx_len == 0) { 459 | return 0; 460 | } 461 | 462 | if (work_data->rx_len < sizeof(mrim_packet_header_t)) { 463 | return 0; 464 | } 465 | 466 | // detach MRIM packet header from readed data 467 | head = (mrim_packet_header_t *) work_data->rx_buf; 468 | 469 | // check if we have correct magic 470 | if (head->magic != CS_MAGIC) { 471 | say_crit("mrim_net_read_proceed: wrong magic: 0x%08x", (uint32_t) head->magic); 472 | return -1; 473 | } 474 | 475 | packet_len = sizeof(mrim_packet_header_t) + head->dlen; 476 | 477 | 478 | // check if we received full packet 479 | if (work_data->rx_len < packet_len) { 480 | return 0; 481 | } 482 | 483 | // get answer value 484 | answer = work_data->rx_buf + sizeof(mrim_packet_header_t); 485 | 486 | // proceed packet 487 | switch(head->msg) { 488 | case MRIM_CS_HELLO_ACK: 489 | // 'hello' packet 490 | say_crit("received 'MRIM_CS_HELLO_ACK' packet"); 491 | work_data->received_hello_ack = 1; 492 | break; 493 | case MRIM_CS_LOGIN_ACK: 494 | // 'login successful' packet 495 | say_crit("received 'MRIM_CS_LOGIN_ACK' packet"); 496 | work_data->received_login_ack = 1; 497 | break; 498 | case MRIM_CS_LOGIN_REJ: 499 | // 'login failed' packet 500 | say_crit("received 'MRIM_CS_LOGIN_REJ' packet"); 501 | work_data->received_login_rej = 1; 502 | break; 503 | case MRIM_CS_MESSAGE_ACK: 504 | // 'receive message' packet 505 | say_crit("received 'MRIM_CS_MESSAGE_ACK' packet"); 506 | mrim_read_message(work_data, answer, head->dlen); 507 | break; 508 | case MRIM_CS_USER_INFO: 509 | // 'user info' packet 510 | say_crit("received 'MRIM_CS_USER_INFO' packet"); 511 | break; 512 | case MRIM_CS_MESSAGE_STATUS: 513 | // 'message status' packet 514 | say_crit("received 'MRIM_CS_MESSAGE_STATUS' packet"); 515 | break; 516 | case MRIM_CS_CONTACT_LIST2: 517 | // 'contact list' packet 518 | say_crit("received 'MRIM_CS_CONTACT_LIST2' packet"); 519 | break; 520 | default: 521 | // unknown packet 522 | say_crit("unknown packet received: 0x%04x", head->msg); 523 | } 524 | 525 | // if we have more data in incoming buffer 526 | if (work_data->rx_len > packet_len) { 527 | // cut proceeded packet 528 | next_packet = work_data->rx_buf + packet_len; 529 | work_data->rx_len = work_data->rx_len - packet_len; 530 | memmove(work_data->rx_buf, next_packet, work_data->rx_len); 531 | work_data->rx_buf = realloc(work_data->rx_buf, work_data->rx_len); 532 | return 0; 533 | } else { 534 | // else just empty buffer 535 | work_data->rx_len = 0; 536 | work_data->rx_buf = realloc(work_data->rx_buf, MRA_BUF_LEN + 1); 537 | } 538 | return 1; 539 | } 540 | 541 | /* Read data from mail.ru agent server */ 542 | int mrim_net_read(run_data *work_data) 543 | { 544 | int len; 545 | char *buf; 546 | int net_read_try_count = 0; 547 | int res = 0; 548 | 549 | // increase buffer size 550 | work_data->rx_buf = realloc(work_data->rx_buf, work_data->rx_len + MRA_BUF_LEN + 1); 551 | 552 | // read data from socket 553 | buf = work_data->rx_buf + work_data->rx_len; 554 | len = read(work_data->mra_socket, buf, MRA_BUF_LEN); 555 | work_data->rx_len = work_data->rx_len + len; 556 | 557 | if (len < 0 && errno == EAGAIN && errno != EINTR) { 558 | // read more 559 | return 0; 560 | } else if (len < 0) { 561 | say_crit("cannot read data from socket: %s", strerror(errno)); 562 | return -1; 563 | } else if (len == 0) { 564 | // server closed the connection 565 | say_crit("server closed the connection: %s", strerror(errno)); 566 | return -1; 567 | } 568 | 569 | // proceed received data while we can do it =) 570 | while (res == 0) { 571 | res = mrim_net_read_proceed(work_data); 572 | 573 | if (res == 0) { 574 | net_read_try_count++; 575 | } 576 | 577 | if (net_read_try_count > 300) { 578 | break; 579 | } 580 | 581 | } 582 | return 0; 583 | } 584 | 585 | /* Check if data exists */ 586 | int mrim_is_readable(run_data *work_data, int timeout_sec, int timeout_usec) 587 | { 588 | struct timeval tv; 589 | fd_set rset; 590 | int isready; 591 | 592 | FD_ZERO(&rset); 593 | FD_SET(work_data->mra_socket, &rset); 594 | 595 | tv.tv_sec = timeout_sec; 596 | tv.tv_usec = timeout_usec; 597 | 598 | again: 599 | isready = select(work_data->mra_socket + 1, &rset, NULL, NULL, &tv); 600 | if (isready < 0) { 601 | if (errno == EINTR) goto again; 602 | say_crit("error on select socket: %s", strerror(errno)); 603 | return -1; 604 | } 605 | 606 | return isready; 607 | } 608 | 609 | /* split "host:port" to "host" and "port" */ 610 | static void split_host_port(char *login_data, int login_data_size, char *host, int host_size, char *port, int port_size) 611 | { 612 | char *delim_pos = memchr(login_data, ':', login_data_size); 613 | 614 | memset(host, 0, host_size); 615 | memset(port, 0, port_size); 616 | 617 | if (delim_pos){ 618 | *delim_pos='\0'; 619 | strncpy(host, login_data, host_size-1); 620 | strncpy(port, delim_pos+1, port_size-1); 621 | } 622 | } 623 | 624 | /* Connect and login */ 625 | int mrim_connect(run_data *work_data, char *login_host, char *login_port, char *username, char *password) 626 | { 627 | int login_data_size = -1; 628 | char login_data[24]; 629 | char host[16]; 630 | char port[5]; 631 | 632 | say_crit("Start connect to server %s:%s, username: %s, password: %s", login_host, login_port, "***", "***"); 633 | 634 | work_data->received_hello_ack = 0; 635 | work_data->received_login_ack = 0; 636 | work_data->received_login_rej = 0; 637 | 638 | if (work_data->mra_socket > 0) { 639 | close(work_data->mra_socket); 640 | } 641 | // let's get server to connect to 642 | if ((work_data->mra_socket = mrim_connect_tcp(login_host, login_port)) == -1) { 643 | say_crit("cannot connect to %s:%s", login_host, login_port); 644 | return -1; 645 | } 646 | 647 | if ((login_data_size = read(work_data->mra_socket, login_data, sizeof(login_data))) == -1 && errno != EINTR) { 648 | say_crit("cannot read data from socket: %s", strerror(errno)); 649 | return -1; 650 | } 651 | 652 | if ((work_data->mra_socket = close(work_data->mra_socket)) == -1 && errno != EINTR) { 653 | say_crit("cannot close socket: %s", strerror(errno)); 654 | return -1; 655 | } 656 | 657 | 658 | split_host_port(login_data, login_data_size, host, sizeof(host), port, sizeof(port)); 659 | say_crit("Login host: %s", host); 660 | say_crit("Login port: %s", port); 661 | 662 | // let's connect to mrim server 663 | if ((work_data->mra_socket = mrim_connect_tcp(host, port)) == -1) { 664 | say_crit("cannot connect to %s:%s", host, port); 665 | return -1; 666 | } 667 | 668 | // send 'hello' packet 669 | if (mrim_send_hello(work_data) == -1) { 670 | say_crit("cannot send 'hello' packet"); 671 | return -1; 672 | } 673 | 674 | // send 'login' packet 675 | if (mrim_send_auth(work_data, username, password, STATUS_ONLINE) == -1) { 676 | say_crit("cannot send 'login' packet"); 677 | return -1; 678 | } 679 | 680 | if (work_data->received_login_rej == 1) { 681 | say_crit("cannot auth: username or password is wrong"); 682 | return -1; 683 | } 684 | 685 | return 0; 686 | } 687 | 688 | /* Disconnect */ 689 | int mrim_disconnect(run_data *work_data) 690 | { 691 | if (work_data->mra_socket) { 692 | shutdown(work_data->mra_socket, 1); 693 | close(work_data->mra_socket); 694 | } 695 | return 0; 696 | } 697 | 698 | /* login, send message, disconnect */ 699 | int send_mra_message(const char *username, const char *password, const char *recipient, const char *msg) 700 | { 701 | int err = 0; 702 | 703 | char username_in[255]; // username 704 | char password_in[255]; // recipient 705 | char recipient_in[255]; // recipient 706 | char msg_in[1024*1024]; // msg 707 | 708 | run_data work_data; 709 | memset(&work_data, 0, sizeof(work_data)); 710 | 711 | work_data.mra_socket = -1; // mra socket 712 | work_data.tx_buf = NULL; // TX buffer 713 | work_data.tx_len = 0; // TX buffer size 714 | work_data.rx_buf = NULL; // RX buffer 715 | work_data.rx_len = 0; // RX buffer size 716 | work_data.seq = 0; // Sequence number 717 | work_data.received_hello_ack = 0; // Is 'hello' message received 718 | work_data.received_login_ack = 0; // Is 'login OK' message recievied 719 | work_data.received_login_rej = 0; // Is 'login FAIL' message received 720 | 721 | // assign username 722 | if (username) { 723 | snprintf(username_in, sizeof(username_in)-1, "%s", username); 724 | } else { 725 | return -2; 726 | } 727 | 728 | // assign password 729 | if (password) { 730 | snprintf(password_in, sizeof(password_in)-1, "%s", password); 731 | } else { 732 | return -3; 733 | } 734 | 735 | // assign recipient 736 | if (recipient) { 737 | snprintf(recipient_in, sizeof(recipient_in)-1, "%s", recipient); 738 | } 739 | 740 | if (!recipient_in) { 741 | snprintf(recipient_in, sizeof(recipient_in)-1, "%s", username); 742 | } 743 | 744 | if (msg) { 745 | snprintf(msg_in, sizeof(msg_in)-1, "%s", msg); 746 | } 747 | 748 | if (!msg_in) { 749 | snprintf(msg_in, sizeof(msg_in)-1, "%s", "ERROR! No params recipient and msg body!"); 750 | } 751 | 752 | // Connect to mail.ru agent if not connected yet 753 | if (mrim_connect(&work_data, MRA_HOST, MRA_PORT, username_in, password_in) == -1) { 754 | say_crit("%s", "Can't connect to mail.ru agent"); 755 | mrim_disconnect(&work_data); 756 | return -1; 757 | } 758 | 759 | if (mrim_is_readable(&work_data, 0, 500)) { 760 | if(mrim_net_read(&work_data) == -1) { 761 | mrim_disconnect(&work_data); 762 | mrim_connect(&work_data, MRA_HOST, MRA_PORT, username_in, password_in); 763 | } 764 | } 765 | 766 | err = mrim_send_message(&work_data, recipient_in, msg_in, 0); 767 | if(err == -1){ 768 | say_crit("cannot send message to '%s'", recipient); 769 | mrim_disconnect(&work_data); 770 | if (work_data.rx_buf) free(work_data.rx_buf); 771 | if (work_data.tx_buf) free(work_data.tx_buf); 772 | return -4; 773 | } 774 | 775 | fiber_sleep(1); 776 | mrim_disconnect(&work_data); 777 | 778 | if (work_data.rx_buf) free(work_data.rx_buf); 779 | if (work_data.tx_buf) free(work_data.tx_buf); 780 | 781 | return 0; 782 | } 783 | 784 | /** 785 | * Start send message 786 | */ 787 | static int mrasender_func(struct lua_State *L) 788 | { 789 | if (lua_gettop(L) < 4) 790 | luaL_error(L, "Usage: cfunctions.send(username: string, password: string, recipient: string, msg: string)"); 791 | 792 | const char *username = lua_tostring(L, 1); 793 | const char *password = lua_tostring(L, 2); 794 | const char *recipient = lua_tostring(L, 3); 795 | const char *msg = lua_tostring(L, 4); 796 | 797 | lua_pushinteger(L, send_mra_message(username, password, recipient, msg)); 798 | return 1; 799 | } 800 | 801 | LUA_API int luaopen_mrasender_cfunctions(lua_State *L) 802 | { 803 | /* result is returned from require('mrasender.cfunctions') */ 804 | lua_newtable(L); 805 | static const struct luaL_reg meta [] = { 806 | {"send_message_to_mra", mrasender_func}, 807 | {NULL, NULL} 808 | }; 809 | luaL_register(L, NULL, meta); 810 | return 1; 811 | } 812 | --------------------------------------------------------------------------------