/* Copyright (C) 2005-2009 Michel de Boer This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "msg_session.h" #include #include #include "im_iscomposing_body.h" #include "log.h" #include "phone.h" #include "translator.h" #include "parser/media_type.h" #include "utils/file_utils.h" #define COMPOSING_LOCAL_IDLE_TIMEOUT 15 #define COMPOSING_LOCAL_REFRESH_TIMEOUT 90 extern t_phone *phone; using namespace im; using namespace utils; t_composing_state im::string2composing_state(const string &state_name) { if (state_name == IM_ISCOMPOSING_STATE_ACTIVE) { return COMPOSING_STATE_ACTIVE; } return COMPOSING_STATE_IDLE; } string im::composing_state2string(t_composing_state state) { switch (state) { case COMPOSING_STATE_IDLE: return "idle"; case COMPOSING_STATE_ACTIVE: return "active"; default: assert(false); } return "idle"; } // class t_msg t_msg::t_msg() : has_attachment(false) { struct timeval t; gettimeofday(&t, NULL); timestamp = t.tv_sec; } t_msg::t_msg(const string &msg, t_direction dir, t_text_format fmt) : message(msg), direction(dir), format(fmt), has_attachment(false) { struct timeval t; gettimeofday(&t, NULL); timestamp = t.tv_sec; } void t_msg::set_attachment(const string &filename, const t_media &media, const string &save_as) { attachment_filename = filename; attachment_media = media; attachment_save_as_name = save_as; has_attachment = true; } // class t_msg_session t_msg_session::t_msg_session(t_user *u) : user_config(u), new_message_added(false), error_recvd(false), delivery_notification_recvd(false), msg_in_flight(false), send_composing_state(u->get_im_send_iscomposing()), local_composing_state(COMPOSING_STATE_IDLE), local_idle_timeout(0), local_refresh_timeout(0), remote_composing_state(COMPOSING_STATE_IDLE), remote_idle_timeout(0) {} t_msg_session::t_msg_session(t_user *u, t_display_url _remote_party) : user_config(u), remote_party(_remote_party), new_message_added(false), error_recvd(false), delivery_notification_recvd(false), msg_in_flight(false), send_composing_state(u->get_im_send_iscomposing()), local_composing_state(COMPOSING_STATE_IDLE), local_idle_timeout(0), local_refresh_timeout(0), remote_composing_state(COMPOSING_STATE_IDLE), remote_idle_timeout(0) {} t_msg_session::~t_msg_session() { // Remove temporary files. for (list::iterator it = messages.begin(); it != messages.end(); ++it) { // Temporary files are created for incoming messages only. if (it->has_attachment && it->direction == MSG_DIR_IN) { // Defensive check to make sure we are deleting tmp files only. if (sys_config->is_tmpfile(it->attachment_filename)) { log_file->write_header("t_msg_session::~t_msg_session"); log_file->write_raw("Remove tmp file "); log_file->write_raw(it->attachment_filename); log_file->write_endl(); log_file->write_footer(); unlink(it->attachment_filename.c_str()); } } } } t_user *t_msg_session::get_user(void) const { return user_config; } t_display_url t_msg_session::get_remote_party(void) const { return remote_party; } t_composing_state t_msg_session::get_remote_composing_state(void) const { return remote_composing_state; } void t_msg_session::set_user(t_user *u) { user_config = u; } void t_msg_session::set_remote_party(const t_display_url &du) { remote_party = du; } void t_msg_session::set_send_composing_state(bool enable) { send_composing_state = enable; } t_msg t_msg_session::get_last_message(void) { new_message_added = false; if (messages.empty()) { throw empty_list_exception(); } return messages.back(); } bool t_msg_session::is_new_message_added(void) const { return new_message_added; } void t_msg_session::set_display_if_empty(const string &display) { if (remote_party.display.empty()) { remote_party.display = display; } } const list &t_msg_session::get_messages(void) const { return messages; } void t_msg_session::recv_msg(const t_msg &msg) { // RFC 3994 3.3 // The composing state of the remote party transitions to idle // when a message is received. remote_composing_state = COMPOSING_STATE_IDLE; remote_idle_timeout = 0; messages.push_back(msg); new_message_added = true; notify(); } void t_msg_session::send_msg(const string &message, t_text_format format) { // RFC 3994 3.2 // If a content message is sent before the idle threshold expires, no // "idle" state indication is needed. // The local state is set to idle without sending an indication to the // remote party. local_composing_state = COMPOSING_STATE_IDLE; local_idle_timeout = 0; local_refresh_timeout = 0; t_msg msg(message, im::MSG_DIR_OUT, format); messages.push_back(msg); new_message_added = true; bool ret = phone->pub_send_message(user_config, remote_party.url, remote_party.display, msg); if (ret) { msg_in_flight = true; } else { set_error(TRANSLATE("Failed to send message.")); } notify(); } void t_msg_session::send_file(const string &filename, const t_media &media, const string &subject) { // RFC 3994 3.2 // If a content message is sent before the idle threshold expires, no // "idle" state indication is needed. // The local state is set to idle without sending an indication to the // remote party. local_composing_state = COMPOSING_STATE_IDLE; local_idle_timeout = 0; local_refresh_timeout = 0; t_msg msg; msg.set_attachment(filename, media, strip_path_from_filename(filename)); msg.subject = subject; msg.direction = MSG_DIR_OUT; messages.push_back(msg); new_message_added = true; bool ret = phone->pub_send_message(user_config, remote_party.url, remote_party.display, msg); if (ret) { msg_in_flight = true; notify(); } else { // Notify user interface about the sent message before // setting the error. notify(); set_error(TRANSLATE("Failed to send message.")); } } void t_msg_session::set_error(const string &message) { error_msg = message; error_recvd = true; notify(); } bool t_msg_session::error_received(void) const { return error_recvd; } string t_msg_session::take_error(void) { if (!error_recvd) return ""; error_recvd = false; return error_msg; } void t_msg_session::set_delivery_notification(const string ¬ification) { delivery_notification = notification; delivery_notification_recvd = true; notify(); } bool t_msg_session::delivery_notification_received(void) const { return delivery_notification_recvd; } string t_msg_session::take_delivery_notification(void) { if (!delivery_notification_recvd) return ""; delivery_notification_recvd = false; return delivery_notification; } bool t_msg_session::match(t_user *user, t_url _remote_party) { return user == user_config && _remote_party == remote_party.url; } void t_msg_session::set_msg_in_flight(bool in_flight) { msg_in_flight = in_flight; notify(); } bool t_msg_session::is_msg_in_flight(void) const { return msg_in_flight; } void t_msg_session::set_local_composing_state(t_composing_state state) { if (!remote_party.is_valid()) { // The session is not yet established return; } switch (local_composing_state) { case COMPOSING_STATE_IDLE: switch (state) { case COMPOSING_STATE_IDLE: break; case COMPOSING_STATE_ACTIVE: local_composing_state = state; local_idle_timeout = COMPOSING_LOCAL_IDLE_TIMEOUT; local_refresh_timeout = COMPOSING_LOCAL_REFRESH_TIMEOUT - 10; if (send_composing_state) { (void)phone->pub_send_im_iscomposing( user_config, remote_party.url, remote_party.display, IM_ISCOMPOSING_STATE_ACTIVE, COMPOSING_LOCAL_REFRESH_TIMEOUT); } break; default: assert(false); } break; case COMPOSING_STATE_ACTIVE: switch (state) { case COMPOSING_STATE_IDLE: local_composing_state = state; local_idle_timeout = 0; local_refresh_timeout = 0; if (send_composing_state) { (void)phone->pub_send_im_iscomposing( user_config, remote_party.url, remote_party.display, IM_ISCOMPOSING_STATE_IDLE, COMPOSING_LOCAL_REFRESH_TIMEOUT); } break; case COMPOSING_STATE_ACTIVE: local_idle_timeout = COMPOSING_LOCAL_IDLE_TIMEOUT; break; default: assert(false); } break; default: assert(false); } } void t_msg_session::set_remote_composing_state(t_composing_state state, time_t idle_timeout) { switch (remote_composing_state) { case COMPOSING_STATE_IDLE: switch (state) { case COMPOSING_STATE_IDLE: break; case COMPOSING_STATE_ACTIVE: remote_composing_state = state; remote_idle_timeout = idle_timeout; notify(); break; default: assert(false); } break; case COMPOSING_STATE_ACTIVE: switch (state) { case COMPOSING_STATE_IDLE: remote_composing_state = state; remote_idle_timeout = 0; notify(); break; case COMPOSING_STATE_ACTIVE: remote_idle_timeout = idle_timeout; break; } break; default: assert(false); } } void t_msg_session::dec_local_composing_timeout(void) { if (local_composing_state == COMPOSING_STATE_IDLE) return; local_idle_timeout--; if (local_idle_timeout == 0) { set_local_composing_state(COMPOSING_STATE_IDLE); } else { local_refresh_timeout--; if (local_refresh_timeout == 0) { local_refresh_timeout = COMPOSING_LOCAL_REFRESH_TIMEOUT - 10; if (send_composing_state) { (void)phone->pub_send_im_iscomposing( user_config, remote_party.url, remote_party.display, IM_ISCOMPOSING_STATE_ACTIVE, COMPOSING_LOCAL_REFRESH_TIMEOUT); } } } } void t_msg_session::dec_remote_composing_timeout(void) { if (remote_composing_state == COMPOSING_STATE_IDLE) return; remote_idle_timeout--; if (remote_idle_timeout == 0) { set_remote_composing_state(COMPOSING_STATE_IDLE); } }