/* SPDX-License-Identifier: LGPL-3.0-or-later */ /* * Copyright (C) 2008 Banco do Brasil S.A. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published * by the Free Software Foundation, either version 3 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 Lesser General Public License * along with this program. If not, see . */ /* * Contatos: * * perry.werneck@gmail.com (Alexandre Perry de Souza Werneck) * erico.mendonca@gmail.com (Erico Mascarenhas Mendonça) * */ /** * @brief Handle connect and disconnect from hosts, and state changes on the host connection. */ #pragma GCC diagnostic ignored "-Wsign-compare" #ifdef HAVE_MALLOC_H #include #endif // HAVE_MALLOC_H #include #include //#include "resources.h" #include "hostc.h" #include "statusc.h" #include "popupsc.h" #include "telnetc.h" #include "trace_dsc.h" #include "utilc.h" #include "xioc.h" #include "screenc.h" #include #include #include #include #include #include #include #include #include /** * @brief Called from timer to attempt an automatic reconnection. */ static int check_for_auto_reconnect(H3270 *hSession, void GNUC_UNUSED(*userdata)) { if(hSession->auto_reconnect_inprogress) { lib3270_write_log(hSession,"3270","Starting auto-reconnect on %s",lib3270_get_url(hSession)); hSession->auto_reconnect_inprogress = 0; // Reset "in-progress" to allow reconnection. if(hSession->cbk.reconnect(hSession,0)) lib3270_write_log(hSession,"3270","Auto-reconnect fails: %s",strerror(errno)); } return 0; } /** * @brief Activate auto-reconnect timer. * * @param hSession TN3270 Session handle. * @param msec Time to reconnect. * * @return 0 if ok or error code if not. * * @retval EBUSY Auto reconnect is already active. */ int lib3270_activate_auto_reconnect(H3270 *hSession, unsigned long msec) { if(hSession->auto_reconnect_inprogress) return EBUSY; hSession->auto_reconnect_inprogress = 1; (void) AddTimer(msec, hSession, check_for_auto_reconnect, NULL); return 0; } LIB3270_EXPORT int lib3270_disconnect(H3270 *h) { debug("%s",__FUNCTION__); return host_disconnect(h,0); } /// @brief Do disconnect. /// @param hSession Session handle. /// @param failed Non zero if it was a failure. int host_disconnect(H3270 *hSession, int failed) { CHECK_SESSION_HANDLE(hSession); debug("%s: connected=%s half connected=%s network=%s", __FUNCTION__, (CONNECTED ? "Yes" : "No"), (HALF_CONNECTED ? "Yes" : "No"), (hSession->network.module->is_connected(hSession) ? "Active" : "Inactive") ); if (CONNECTED || HALF_CONNECTED) { // Disconecting, disable input remove_input_calls(hSession); net_disconnect(hSession); trace("Disconnected (Failed: %d Reconnect: %d in_progress: %d)",failed,lib3270_get_toggle(hSession,LIB3270_TOGGLE_RECONNECT),hSession->auto_reconnect_inprogress); // // Remember a disconnect from ANSI mode, to keep screen tracing in sync. // if (IN_ANSI && lib3270_get_toggle(hSession,LIB3270_TOGGLE_SCREEN_TRACE)) trace_ansi_disc(hSession); lib3270_set_disconnected(hSession); if(hSession->connection.error) { // TODO: Add 'reconnect' option in the popup dialog for optional auto reconnect. lib3270_popup(hSession,hSession->connection.error,!hSession->auto_reconnect_inprogress); lib3270_free(hSession->connection.error); hSession->connection.error = NULL; } if(failed && hSession->connection.retry && lib3270_get_toggle(hSession,LIB3270_TOGGLE_RECONNECT)) lib3270_activate_auto_reconnect(hSession,hSession->connection.retry); return 0; } if(hSession->network.module->is_connected(hSession)) { debug("%s: Disconnecting socket", __FUNCTION__); } return errno = ENOTCONN; } int lib3270_set_cstate(H3270 *hSession, LIB3270_CSTATE cstate) { debug("%s(%s,%d)",__FUNCTION__,lib3270_connection_state_get_name(cstate),(int) cstate); if(hSession->connection.state != cstate) { trace_dsn( hSession, "Connection state changes from %s to %s.\n", lib3270_connection_state_get_name(hSession->connection.state), lib3270_connection_state_get_name(cstate) ); // Salve old states. int connected = lib3270_is_connected(hSession); int disconnected = lib3270_is_disconnected(hSession); // Cstate has changed. hSession->connection.state = cstate; // Do I need to send notifications? if(connected != lib3270_is_connected(hSession)) { // Online state has changed, fire LIB3270_ACTION_GROUP_ONLINE lib3270_action_group_notify(hSession, LIB3270_ACTION_GROUP_ONLINE); } if(disconnected != lib3270_is_disconnected(hSession)) { // Offline state has changed, fire LIB3270_ACTION_GROUP_OFFLINE lib3270_action_group_notify(hSession, LIB3270_ACTION_GROUP_OFFLINE); } return 1; } return 0; } /** * @brief The host has entered 3270 or ANSI mode, or switched between them. */ void host_in3270(H3270 *hSession, LIB3270_CSTATE new_cstate) { Boolean now3270 = (new_cstate == LIB3270_CONNECTED_3270 || new_cstate == LIB3270_CONNECTED_SSCP || new_cstate == LIB3270_CONNECTED_TN3270E); lib3270_set_cstate(hSession,new_cstate); hSession->ever_3270 = now3270; lib3270_st_changed(hSession, LIB3270_STATE_3270_MODE, now3270); } void lib3270_set_connected_initial(H3270 *hSession) { lib3270_set_cstate(hSession,LIB3270_CONNECTED_INITIAL); hSession->starting = 1; // Enable autostart lib3270_st_changed(hSession, LIB3270_STATE_CONNECT, True); if(hSession->cbk.update_connect) hSession->cbk.update_connect(hSession,1); } void lib3270_set_disconnected(H3270 *hSession) { CHECK_SESSION_HANDLE(hSession); lib3270_set_cstate(hSession,LIB3270_NOT_CONNECTED); mcursor_set(hSession,LIB3270_POINTER_LOCKED); hSession->kybdlock = LIB3270_KL_NOT_CONNECTED; hSession->starting = 0; hSession->ssl.state = LIB3270_SSL_UNDEFINED; set_status(hSession,LIB3270_FLAG_UNDERA,False); lib3270_st_changed(hSession,LIB3270_STATE_CONNECT, False); status_changed(hSession,LIB3270_MESSAGE_DISCONNECTED); if(hSession->cbk.update_connect) hSession->cbk.update_connect(hSession,0); hSession->cbk.update_ssl(hSession,hSession->ssl.state); } /** * @brief Signal a state change. */ void lib3270_st_changed(H3270 *hSession, LIB3270_STATE tx, int mode) { struct lib3270_linked_list_node * node; debug("%s(%s,%d)",__FUNCTION__,lib3270_state_get_name(tx),mode); trace_dsn( hSession, "Notifying state %s with mode %d.\n", lib3270_state_get_name(tx), mode ); for(node = hSession->listeners.state[tx].first; node; node = node->next) { ((struct lib3270_state_callback *) node)->func(hSession,mode,node->userdata); } } static void update_url(H3270 *hSession) { char * url = lib3270_strdup_printf( "%s://%s:%s", hSession->network.module->name, hSession->host.current, hSession->host.srvc ); if(hSession->host.url && !strcmp(hSession->host.url,url)) { debug("%s: Same url, ignoring",__FUNCTION__); lib3270_free(url); return; } debug("URL %s -> %s",hSession->host.url,url); lib3270_write_event_trace(hSession,"Host URL was changed\nFrom: %s\nTo: %s\n",hSession->host.url,url); lib3270_free(hSession->host.url); hSession->host.url = url; hSession->cbk.update_url(hSession, hSession->host.url); hSession->network.module->reset(hSession); } LIB3270_EXPORT const char * lib3270_get_associated_luname(const H3270 *hSession) { if(check_online_session(hSession)) return NULL; return hSession->lu.associated; } LIB3270_EXPORT const char * lib3270_get_url(const H3270 *hSession) { if(hSession->host.url) return hSession->host.url; return lib3270_get_default_host(hSession); } LIB3270_EXPORT const char * lib3270_get_default_host(const H3270 GNUC_UNUSED(*hSession)) { #ifdef _WIN32 { lib3270_auto_cleanup(HKEY) hKey = 0; DWORD disp = 0; LSTATUS rc = RegCreateKeyEx( HKEY_LOCAL_MACHINE, "Software\\" LIB3270_STRINGIZE_VALUE_OF(PRODUCT_NAME), 0, NULL, REG_OPTION_NON_VOLATILE, KEY_QUERY_VALUE|KEY_READ, NULL, &hKey, &disp); if(rc == ERROR_SUCCESS) { static char * default_host = NULL; DWORD cbData = 4096; if(!default_host) { default_host = (char *) malloc(cbData+1); } else { default_host = (char *) realloc(default_host,cbData+1); } DWORD dwRet = RegQueryValueEx(hKey,"host",NULL,NULL,(LPBYTE) default_host, &cbData); if(dwRet == ERROR_SUCCESS) { default_host = (char *) realloc(default_host,cbData+1); default_host[cbData] = 0; return default_host; } free(default_host); default_host = NULL; } } #endif // _WIN32 #ifdef LIB3270_DEFAULT_HOST return LIB3270_DEFAULT_HOST; #else return getenv("LIB3270_DEFAULT_HOST"); #endif // LIB3270_DEFAULT_HOST } LIB3270_EXPORT int lib3270_set_url(H3270 *h, const char *n) { FAIL_IF_ONLINE(h); if(!n) n = lib3270_get_default_host(h); if(!n) return errno = ENOENT; lib3270_autoptr(char) str = strdup(n); char * hostname = lib3270_set_network_module_from_url(h,str); const char * srvc; char * ptr; char * query = ""; trace("%s(%s)",__FUNCTION__,str); if(!(hostname && *hostname)) return 0; srvc = h->network.module->service; ptr = strchr(hostname,':'); if(ptr) { *(ptr++) = 0; srvc = ptr; query = strchr(ptr,'?'); } else { srvc = "3270"; query = strchr(hostname,'?'); } if(query) *(query++) = 0; else query = ""; trace("SRVC=[%s]",srvc); Replace(h->host.current,strdup(hostname)); Replace(h->host.srvc,strdup(srvc)); // Verifica parâmetros if(query && *query) { lib3270_autoptr(char) str = strdup(query); char *ptr; #ifdef HAVE_STRTOK_R char *saveptr = NULL; for(ptr = strtok_r(str,"&",&saveptr); ptr; ptr=strtok_r(NULL,"&",&saveptr)) #else for(ptr = strtok(str,"&"); ptr; ptr=strtok(NULL,"&")) #endif { char *var = ptr; char *val = strchr(ptr,'='); if(val) { *(val++) = 0; if(lib3270_set_string_property(h, var, val, 0) == 0) continue; lib3270_write_log(h,"","Can't set attribute \"%s\": %s",var,strerror(errno)); } else { if(lib3270_set_int_property(h,var,1,0)) continue; lib3270_write_log(h,"","Can't set attribute \"%s\": %s",var,strerror(errno)); } } } // Notifica atualização update_url(h); // The "reconnect" action is now available. lib3270_action_group_notify(h, LIB3270_ACTION_GROUP_OFFLINE); return 0; } LIB3270_EXPORT const char * lib3270_get_host(const H3270 *h) { return h->host.url; } LIB3270_EXPORT int lib3270_has_active_script(const H3270 *h) { return (h->oia.flag[LIB3270_FLAG_SCRIPT] != 0); } LIB3270_EXPORT int lib3270_get_typeahead(const H3270 *h) { return (h->oia.flag[LIB3270_FLAG_TYPEAHEAD] != 0); } LIB3270_EXPORT int lib3270_get_undera(const H3270 *h) { return (h->oia.flag[LIB3270_FLAG_UNDERA] != 0); } LIB3270_EXPORT int lib3270_get_oia_box_solid(const H3270 *h) { return (h->oia.flag[LIB3270_FLAG_BOXSOLID] != 0); }