2 * Irreco Telnet backend
3 * Copyright (C) 2008 Sampo Savola (samposav@paju.oulu.fi)
4 * This backend has some parts from Jami Pekkanen's original mythtv backend
5 * (jami.pekkanen at tkk.fi)
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 #define IRRECO_DEBUG_PREFIX "TELNET"
23 #include <irreco_util.h>
24 #include <irreco_backend_api.h>
29 #include <sys/types.h>
30 #include <sys/socket.h>
31 #include <netinet/in.h>
33 #include <arpa/inet.h>
38 #include <hildon/hildon-number-editor.h>
40 /* This is used elsewhere too */
41 #define _(String) (String)
48 IrrecoKeyFile
*keyfile
;
58 GtkCellRenderer
*renderer
;
59 GtkTreeViewColumn
*column
;
66 TELNET_BACKEND_ERROR_CONFIG_READ
= 1,
67 TELNET_BACKEND_ERROR_CONFIG_WRITE
,
68 TELNET_BACKEND_ERROR_CONNECT
,
69 TELNET_BACKEND_ERROR_CON_WRITE
,
70 TELNET_BACKEND_ERROR_COMMANDS_READ
,
84 void telnet_backend_edit_commands(TelnetBackend
*self
, GtkWindow
*parent
);
85 void telnet_command_edited_callback(GtkCellRendererText
*renderer
, gchar
86 *path_string
,gchar
*new_text
,
88 void telnet_backend_add_device(TelnetBackend
*self
);
89 void remove_command(TelnetBackend
*self
);
90 void add_command(TelnetBackend
*self
);
91 void telnet_backend_store_commands(TelnetBackend
*self
);
92 const char *telnet_backend_get_error_msg(TelnetBackend
*self
,
93 IrrecoBackendStatus code
)
97 case TELNET_BACKEND_ERROR_CONFIG_READ
:
98 return _("Couldn't read configuration");
99 case TELNET_BACKEND_ERROR_CONFIG_WRITE
:
100 return _("Couldn't write configuration");
101 case TELNET_BACKEND_ERROR_CONNECT
:
102 return _("Couldn't connect to remote system");
103 case TELNET_BACKEND_ERROR_CON_WRITE
:
104 return _("Error while sending data to remote system");
105 case TELNET_BACKEND_ERROR_COMMANDS_READ
:
106 return _("Error while reading commands file");
110 return _("Unknown error");
113 void *telnet_backend_create()
117 self
= g_slice_new0(TelnetBackend
);
118 self
->host
= g_string_new("localhost");
124 self
->keyfile
= irreco_keyfile_create
125 ("", TELNET_DATA_DIR
"/telnet.conf",
127 self
->groups
= g_key_file_get_groups(self
->keyfile
->keyfile
,
128 &self
->length_groups
);
130 /**create list store **/
132 self
->store
= gtk_list_store_new (NUM_COLS
,
133 G_TYPE_STRING
,G_TYPE_STRING
);
137 self
->model
= GTK_TREE_MODEL (self
->store
);
144 void telnet_backend_disconnect(TelnetBackend
*self
)
148 g_io_channel_shutdown(self
->con
, TRUE
, NULL
);
149 g_io_channel_unref(self
->con
);
154 void telnet_backend_destroy(TelnetBackend
*self
)
156 telnet_backend_disconnect(self
);
157 g_string_free(self
->host
, TRUE
);
158 g_slice_free(TelnetBackend
, self
);
161 #define TELNET_BACKEND_CONFIG_GROUP "telnet"
164 telnet_backend_read_from_conf(TelnetBackend
*self
,
165 const char *config_file
)
168 GKeyFile
*avain
= NULL
;
172 gchar
*device
= NULL
;
174 IrrecoKeyFile
*keyfile
= NULL
;
177 gsize commands_length
;
183 keyfile
= irreco_keyfile_create(
184 g_path_get_dirname(config_file
),
186 TELNET_BACKEND_CONFIG_GROUP
);
188 retval
= TELNET_BACKEND_ERROR_CONFIG_READ
;
190 if(keyfile
== NULL
) goto cleanup
;
191 if(!irreco_keyfile_get_int(keyfile
, "port", &port
)) goto cleanup
;
192 if(!irreco_keyfile_get_str(keyfile
, "host", &host
)) goto cleanup
;
193 if(!irreco_keyfile_get_str(keyfile
, "type", &device
)) goto cleanup
;
197 if(irreco_keyfile_get_gkeyfile(keyfile
, &avain
)){}
200 commands
= g_key_file_get_keys(avain
,
201 "commands",&commands_length
,NULL
);
205 /** read commands from instance conf to model **/
206 if(commands
!= NULL
){
207 for(j
=0;j
<commands_length
;j
++){
210 gtk_list_store_append (GTK_LIST_STORE(self
->store
), &iter
);
212 gtk_list_store_set (GTK_LIST_STORE(self
->store
), &iter
,
213 COL_NAME
, commands
[j
],COL_CMD
,
214 g_key_file_get_string(avain
,"commands",
215 commands
[j
],NULL
),-1);
220 g_string_set_size(self
->host
, 0);
221 g_string_append(self
->host
, host
);
222 self
->device
= device
;
223 retval
= IRRECO_BACKEND_OK
;
227 irreco_keyfile_destroy(keyfile
);
234 telnet_backend_save_to_conf(TelnetBackend
*self
,
235 const char *config_file
)
239 GKeyFile
*keyfile
= NULL
;
240 gchar
*grp
= TELNET_BACKEND_CONFIG_GROUP
;
242 keyfile
= g_key_file_new();
243 g_key_file_set_string(keyfile
, grp
, "host", self
->host
->str
);
244 g_key_file_set_integer(keyfile
, grp
, "port", self
->port
);
245 g_key_file_set_string(keyfile
, grp
, "type", self
->device
);
246 valid
= gtk_tree_model_get_iter_first (self
->model
, &self
->iter
);
252 gtk_tree_model_get (self
->model
, &self
->iter
, COL_NAME
, &name
,
254 g_key_file_set_string(keyfile
, "commands",name
,cmd
);
255 valid
= gtk_tree_model_iter_next (self
->model
, &self
->iter
);
261 if(!irreco_write_keyfile(keyfile
, config_file
))
262 return TELNET_BACKEND_ERROR_CONFIG_WRITE
;
264 return IRRECO_BACKEND_OK
;
268 telnet_backend_get_devices(TelnetBackend
*self
,
269 IrrecoGetDeviceCallback callback
)
272 if(self
->groups
!= NULL
)
274 callback(self
->device
, NULL
);
277 return IRRECO_BACKEND_OK
;
281 telnet_backend_get_commands(TelnetBackend
*self
,
282 const char *device_name
,
283 gpointer device_context
,
284 IrrecoGetCommandCallback callback
)
287 valid
= gtk_tree_model_get_iter_first (self
->model
, &self
->iter
);
295 gtk_tree_model_get (self
->model
, &self
->iter
, COL_NAME
, &name
,
299 valid
= gtk_tree_model_iter_next (self
->model
, &self
->iter
);
302 return IRRECO_BACKEND_OK
;
306 telnet_backend_connection_error(TelnetBackend
*self
, GError
*error
)
308 /* TODO: See if connection has died? */
309 IRRECO_PRINTF("Connection error occured, Killing connection");
310 telnet_backend_disconnect(self
);
313 void telnet_backend_connection_error_callback(GIOChannel
*source
,
314 GIOCondition cond
, TelnetBackend
*self
)
316 IRRECO_PRINTF("Connection error by callback");
317 /* TODO: Can GError be digged from the object? */
318 telnet_backend_connection_error(self
, NULL
);
323 telnet_backend_connect(TelnetBackend
*self
)
331 struct sockaddr_in addr
;
332 struct hostent
*host
;
334 IRRECO_PRINTF("Connecting to %s:%d \n", self
->host
->str
, self
->port
);
336 memset(&addr
, '\0', sizeof(addr
));
338 sock
= socket(AF_INET
, SOCK_STREAM
, 0);
339 arg
= fcntl(sock
, F_GETFL
, NULL
);
341 fcntl(sock
, F_SETFL
, arg
);
343 addr
.sin_family
= AF_INET
;
344 addr
.sin_port
= htons(self
->port
);
346 if(inet_aton(self
->host
->str
, &addr
.sin_addr
))
348 IRRECO_PRINTF("Address is IP\n");
350 else if((host
= gethostbyname(self
->host
->str
)))
352 IRRECO_PRINTF("Address is valid hostname");
353 memcpy((void *) &addr
.sin_addr
, (void *) host
->h_addr_list
[0],
354 (size_t) host
->h_length
);
356 res
= connect(sock
, (struct sockaddr
*)&addr
, sizeof(addr
));
359 if (errno
== EINPROGRESS
) {
363 FD_SET(sock
, &myset
);
364 if (select(sock
+1, NULL
, &myset
, NULL
, &tv
) > 0)
367 getsockopt(sock
, SOL_SOCKET
, SO_ERROR
, (void*)
371 IRRECO_PRINTF("Error in connection: %s\n"
373 return TELNET_BACKEND_ERROR_CONNECT
;
378 IRRECO_PRINTF("Connection timed out: %s\n",
380 return TELNET_BACKEND_ERROR_CONNECT
;
386 IRRECO_PRINTF("Couldn't resolve address: %s",
388 return TELNET_BACKEND_ERROR_CONNECT
;
393 arg
= fcntl(sock
, F_GETFL
, NULL
);
394 arg
&= (~O_NONBLOCK
);
395 fcntl(sock
, F_SETFL
, arg
);
397 self
->con
= g_io_channel_unix_new(sock
);
399 g_io_add_watch(self
->con
, G_IO_ERR
,
400 (GIOFunc
) telnet_backend_connection_error_callback
,
403 /* TODO: Should wait until the server responds */
405 return 0; /* IRRECO_BACKEND_OK */
410 telnet_backend_ensure_connection(TelnetBackend
*self
)
414 /* TODO: Better checking? */
415 return 0; /* IRRECO_BACKEND_OK */
419 return telnet_backend_connect(self
);
424 telnet_backend_send_command(TelnetBackend
*self
,
425 const char *device_name
,
426 void *device_context
,
427 const char *command_name
,
428 void *command_context
)
434 GError
*error
= NULL
;
437 command
= g_string_new((gchar
*)command_context
);
438 g_string_append(command
, "\r\n");
440 IRRECO_PRINTF("In telnet_backend_send_command\n");
442 if((constatus
= telnet_backend_ensure_connection(self
)))
447 IRRECO_PRINTF("Connection ensured, starting write\n");
449 /* TODO: Should read incoming stuff */
451 for(total_written
= 0; total_written
< command
->len
;
452 total_written
+= written
)
454 /* TODO: Better error reporting */
455 status
= g_io_channel_write_chars(self
->con
,
456 &(command
->str
[total_written
]), -1, &written
, &error
);
457 if(status
== G_IO_STATUS_ERROR
)
459 IRRECO_PRINTF("Failed writing to socket: %s \n",
461 telnet_backend_connection_error(self
, error
);
462 return TELNET_BACKEND_ERROR_CON_WRITE
;
466 IRRECO_PRINTF("Command written. Flushing\n");
468 status
= g_io_channel_flush(self
->con
, NULL
);
470 /* TODO: Better handling for G_IO_STATUS_AGAIN ? */
473 case G_IO_STATUS_ERROR
:
474 case G_IO_STATUS_AGAIN
:
475 telnet_backend_connection_error(self
, error
);
476 return TELNET_BACKEND_ERROR_CON_WRITE
;
480 IRRECO_PRINTF("Command sent successfully\n");
482 g_string_free(command
, TRUE
);
484 return IRRECO_BACKEND_OK
;
488 telnet_backend_configure(TelnetBackend
*self
,
493 GtkEntry
*host_widget
;
494 GtkWidget
* port_editor
;
495 const gchar
*new_host
;
506 device
= GTK_COMBO_BOX(gtk_combo_box_new_text());
508 /** get devices to be controlled from telnet.conf **/
509 if(self
->groups
!= NULL
){
510 for(i
=0;i
<self
->length_groups
;i
++)
512 gtk_combo_box_append_text(GTK_COMBO_BOX(device
),self
->groups
[i
]);
514 if(self
->device
!= NULL
)
516 if(g_utf8_collate(self
->groups
[i
],self
->device
) == 0)
518 gtk_combo_box_set_active(GTK_COMBO_BOX(device
), i
);
523 gtk_combo_box_set_active(GTK_COMBO_BOX(device
), 0);
531 dialog
= GTK_DIALOG(gtk_dialog_new_with_buttons(
532 "Telnet configuration",
534 GTK_DIALOG_MODAL
| GTK_DIALOG_DESTROY_WITH_PARENT
,
535 GTK_STOCK_CANCEL
, GTK_RESPONSE_REJECT
,
536 GTK_STOCK_OK
, GTK_RESPONSE_ACCEPT
, NULL
));
538 table
= GTK_TABLE(gtk_table_new(3, 2, FALSE
));
540 host_widget
= GTK_ENTRY(gtk_entry_new());
541 gtk_entry_set_text(host_widget
, self
->host
->str
);
543 port_editor
= hildon_number_editor_new(min_port
, max_port
);
546 hildon_number_editor_set_value(HILDON_NUMBER_EDITOR(port_editor
), self
->port
);
548 gtk_table_attach_defaults(table
,
549 gtk_label_new(_("Host")), 0, 1, 0, 1);
550 gtk_table_attach_defaults(table
,
551 GTK_WIDGET(host_widget
), 1, 2, 0, 1);
552 gtk_table_attach_defaults(table
,
553 gtk_label_new(_("Port")), 0, 1, 1, 2);
554 gtk_table_attach_defaults(table
,
555 GTK_WIDGET(port_editor
), 1, 2, 1, 2);
556 gtk_table_attach_defaults(table
,
557 gtk_label_new(_("Type")), 0, 1, 2, 3);
558 gtk_table_attach_defaults(table
,
559 GTK_WIDGET(device
), 1, 2, 2, 3);
561 gtk_container_add(GTK_CONTAINER(dialog
->vbox
), GTK_WIDGET(table
));
563 gtk_widget_show_all(GTK_WIDGET(dialog
));
567 response
= gtk_dialog_run(GTK_DIALOG(dialog
));
570 case GTK_RESPONSE_REJECT
:
575 case GTK_RESPONSE_ACCEPT
:
577 new_host
= gtk_entry_get_text(host_widget
);
579 hildon_number_editor_get_value(
580 HILDON_NUMBER_EDITOR(port_editor
));
582 if(gtk_combo_box_get_active_text(
583 GTK_COMBO_BOX(device
))!= NULL
)
586 gtk_combo_box_get_active_text(
587 GTK_COMBO_BOX(device
));
588 self
->port
= new_port
;
589 g_string_assign(self
->host
, new_host
);
590 telnet_backend_store_commands(self
);
593 telnet_backend_disconnect(self
);
594 telnet_backend_connect(self
);
602 gtk_widget_destroy(GTK_WIDGET(dialog
));
604 return IRRECO_BACKEND_OK
;
606 gboolean
telnet_backend_is_device_editable(TelnetBackend
*self
,
607 const gchar
* device_name
,
608 gpointer device_contex
)
613 IrrecoBackendStatus
telnet_backend_edit_device(TelnetBackend
*self
,
614 const char *device_name
,
615 gpointer device_contex
,
618 telnet_backend_edit_commands(self
, parent
);
619 return IRRECO_BACKEND_OK
;
622 void telnet_backend_store_commands(TelnetBackend
*self
){
631 self
->keys
= g_key_file_get_keys(self
->keyfile
->keyfile
,
632 self
->device
,&self
->length_keys
,NULL
);
634 /** first clear old entries **/
635 gtk_list_store_clear(GTK_LIST_STORE(self
->store
));
638 /** add data to the list store **/
639 for(j
=0;j
<self
->length_keys
;j
++){
641 gtk_list_store_append (GTK_LIST_STORE(self
->store
), &iter
);
642 gtk_list_store_set (GTK_LIST_STORE(self
->store
), &iter
,
643 COL_NAME
, self
->keys
[j
],COL_CMD
,
644 g_key_file_get_string(
645 self
->keyfile
->keyfile
,
646 self
->device
,self
->keys
[j
],NULL
),-1);
647 /*g_print(self->keys[j]);
649 g_print(g_key_file_get_string(self->keyfile->keyfile,
650 self->device,self->keys[j],NULL));*/
656 valid = gtk_tree_model_get_iter_first (self->model, &self->iter);
664 gtk_tree_model_get (self->model, &self->iter, COL_NAME, &name,
669 g_print("%s %s \n", name,cmd);
670 valid = gtk_tree_model_iter_next (self->model, &self->iter);
675 void telnet_backend_edit_commands(TelnetBackend
*self
, GtkWindow
*parent
)
680 GtkWidget
*scrollbar
;
688 self
->treeview
= gtk_tree_view_new_with_model(self
->model
);
690 gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (self
->treeview
), TRUE
);
694 self
->model
= gtk_tree_view_get_model(GTK_TREE_VIEW(self
->treeview
));
695 gtk_tree_view_set_headers_visible(GTK_TREE_VIEW(self
->treeview
),TRUE
);
697 /** column for Name **/
698 self
->renderer
= gtk_cell_renderer_text_new ();
699 g_object_set (self
->renderer
,
700 "editable", TRUE
, NULL
);
702 g_signal_connect (self
->renderer
, "edited",
703 G_CALLBACK (telnet_command_edited_callback
), self
);
705 g_object_set_data (G_OBJECT (self
->renderer
), "column",
706 GINT_TO_POINTER (COL_NAME
));
708 self
->column
= gtk_tree_view_column_new_with_attributes ("Name ",
709 self
->renderer
, "text",COL_NAME
,NULL
);
711 gtk_tree_view_append_column(GTK_TREE_VIEW(self
->treeview
), self
->column
);
712 /** column for Command **/
713 self
->renderer
= gtk_cell_renderer_text_new ();
714 self
->column
= gtk_tree_view_column_new_with_attributes ("Command \
716 self
->renderer
,"text",COL_CMD
,NULL
);
718 g_object_set (self
->renderer
, "editable", TRUE
, NULL
);
720 g_signal_connect (self
->renderer
, "edited",
721 G_CALLBACK (telnet_command_edited_callback
), self
);
722 g_object_set_data (G_OBJECT (self
->renderer
), "column",
723 GINT_TO_POINTER(COL_CMD
));
725 gtk_tree_view_append_column(GTK_TREE_VIEW(self
->treeview
), self
->column
);
727 /** Create dialog **/
729 dialog
= GTK_DIALOG(gtk_dialog_new_with_buttons("Edit Commands",parent
,
730 GTK_DIALOG_MODAL
, GTK_STOCK_ADD
,
731 ADD_COMMAND
,GTK_STOCK_REMOVE
,
732 REMOVE_COMMAND
,GTK_STOCK_CANCEL
,
740 scrollbar
= gtk_vscrollbar_new(gtk_tree_view_get_vadjustment(
741 GTK_TREE_VIEW(self
->treeview
)));
745 hbox
= gtk_hbox_new (FALSE
, 8);
746 gtk_container_add (GTK_CONTAINER (dialog
->vbox
), hbox
);
748 gtk_box_pack_start (GTK_BOX (hbox
), self
->treeview
, FALSE
, FALSE
, 0);
749 gtk_box_pack_start (GTK_BOX (hbox
), scrollbar
, FALSE
, FALSE
, 0);
751 gtk_widget_show_all(GTK_WIDGET(dialog
));
757 response
= gtk_dialog_run(GTK_DIALOG(dialog
));
760 case GTK_RESPONSE_REJECT
:
763 case GTK_RESPONSE_ACCEPT
:
767 remove_command(self
);
779 gtk_widget_destroy(GTK_WIDGET(dialog
));
782 void telnet_command_edited_callback(GtkCellRendererText
*renderer
,
783 gchar
*path_string
,gchar
*new_text
,
790 path
= gtk_tree_path_new_from_string (path_string
);
792 column
= GPOINTER_TO_INT(g_object_get_data(G_OBJECT(renderer
),"column"));
793 path
= gtk_tree_path_new_from_string (path_string
);
794 gtk_tree_model_get_iter (self
->model
, &self
->iter
, path
);
802 gtk_tree_model_get (self
->model
, &self
->iter
,
803 column
, &old_text
, -1);
805 i
= gtk_tree_path_get_indices (path
)[0];
806 gtk_list_store_set (GTK_LIST_STORE (self
->model
),
807 &self
->iter
, column
,new_text
, -1);
816 gtk_tree_model_get (self
->model
, &self
->iter
, column
,
819 i
= gtk_tree_path_get_indices (path
)[0];
820 gtk_list_store_set (GTK_LIST_STORE (self
->model
),
821 &self
->iter
, column
,new_text
, -1);
828 valid
= gtk_tree_model_get_iter_first (self
->model
, &self
->iter
);
831 void remove_command(TelnetBackend
*self
)
835 GtkTreeSelection
*selection
;
838 selection
= gtk_tree_view_get_selection(GTK_TREE_VIEW(self
->treeview
));
839 if (gtk_tree_selection_get_selected (selection
, NULL
, &iter
))
841 gtk_list_store_remove (GTK_LIST_STORE (self
->model
), &iter
);
847 void add_command(TelnetBackend
*self
)
849 gtk_list_store_append(GTK_LIST_STORE(self
->store
), &self
->iter
);
850 gtk_list_store_set (GTK_LIST_STORE(self
->store
), &self
->iter
,
851 COL_NAME
, "name",COL_CMD
,"command",-1);
856 gchar
*telnet_backend_get_description(gpointer instance_context
)
861 IrrecoBackendStatus
telnet_backend_create_device(gpointer instance_context
,
866 return IRRECO_BACKEND_OK
;
868 IrrecoBackendStatus
telnet_backend_delete_device(gpointer instance_context
,
869 const gchar
* device_name
,
870 gpointer device_contex
,
874 return IRRECO_BACKEND_OK
;
877 IrrecoBackendFunctionTable telnet_backend_function_table
=
879 IRRECO_BACKEND_API_VERSION
,
880 IRRECO_BACKEND_EDITABLE_DEVICES
,
882 (IrrecoBackendGetErrorMsg
) telnet_backend_get_error_msg
,
883 (IrrecoBackendCreate
) telnet_backend_create
,
884 (IrrecoBackendDestroy
) telnet_backend_destroy
,
885 (IrrecoBackendReadFromConf
) telnet_backend_read_from_conf
,
886 (IrrecoBackendSaveToConf
) telnet_backend_save_to_conf
,
887 (IrrecoBackendGetDevices
) telnet_backend_get_devices
,
888 (IrrecoBackendGetCommands
) telnet_backend_get_commands
,
889 (IrrecoBackendSendCommand
) telnet_backend_send_command
,
890 (IrrecoBackendConfigure
) telnet_backend_configure
,
891 (IrrecoBackendGetDescription
) telnet_backend_get_description
,
892 (IrrecoBackendCreateDevice
) telnet_backend_create_device
,
893 (IrrecoBackendIsDeviceEditable
)telnet_backend_is_device_editable
,
894 (IrrecoBackendEditDevice
) telnet_backend_edit_device
,
895 (IrrecoBackendDeleteDevice
) telnet_backend_delete_device
,NULL
,NULL
,NULL
898 IrrecoBackendFunctionTable
*get_irreco_backend_function_table()
900 return &telnet_backend_function_table
;