1 /*
2  * Music messaging plugin for Purple
3  *
4  * Copyright (C) 2005 Christian Muise.
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU General Public License as
8  * published by the Free Software Foundation; either version 2 of the
9  * License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful, but
12  * WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19  * 02111-1301, USA.
20  */
21 
22 #include "internal.h"
23 #include "pidgin.h"
24 
25 #include "conversation.h"
26 
27 #include "gtkconv.h"
28 #include "gtkplugin.h"
29 #include "gtkutils.h"
30 
31 #include "notify.h"
32 #include "version.h"
33 #include "debug.h"
34 
35 #define DBUS_API_SUBJECT_TO_CHANGE
36 #include <dbus/dbus.h>
37 #include "dbus-maybe.h"
38 #include "dbus-bindings.h"
39 #include "dbus-server.h"
40 #include "dbus-purple.h"
41 
42 #define MUSICMESSAGING_PLUGIN_ID "gtk-hazure-musicmessaging"
43 #define MUSICMESSAGING_PREFIX "##MM##"
44 #define MUSICMESSAGING_START_MSG _("A music messaging session has been requested. Please click the MM icon to accept.")
45 #define MUSICMESSAGING_CONFIRM_MSG _("Music messaging session confirmed.")
46 
47 typedef struct {
48 	PurpleConversation *conv; /* pointer to the conversation */
49 	GtkWidget *seperator; /* seperator in the conversation */
50 	GtkWidget *button; /* button in the conversation */
51 	GPid pid; /* the pid of the score editor */
52 
53 	gboolean started; /* session has started and editor run */
54 	gboolean originator; /* started the mm session */
55 	gboolean requested; /* received a request to start a session */
56 
57 } MMConversation;
58 
59 static gboolean start_session(MMConversation *mmconv);
60 static void run_editor(MMConversation *mmconv);
61 static void kill_editor(MMConversation *mmconv);
62 static void add_button (MMConversation *mmconv);
63 static void remove_widget (GtkWidget *button);
64 static void init_conversation (PurpleConversation *conv);
65 static void conv_destroyed(PurpleConversation *conv);
66 static gboolean intercept_sent(PurpleAccount *account, const char *who, char **message, void* pData);
67 static gboolean intercept_received(PurpleAccount *account, char **sender, char **message, PurpleConversation *conv, int *flags);
68 static gboolean send_change_request (const int session, const char *id, const char *command, const char *parameters);
69 static gboolean send_change_confirmed (const int session, const char *command, const char *parameters);
70 static void session_end (MMConversation *mmconv);
71 
72 /* Globals */
73 /* List of sessions */
74 static GList *conversations;
75 
76 /* Pointer to this plugin */
77 static PurplePlugin *plugin_pointer;
78 
79 /* Define types needed for DBus */
80 DBusGConnection *connection;
81 DBusGProxy *proxy;
82 #define DBUS_SERVICE_GSCORE "org.gscore.GScoreService"
83 #define DBUS_PATH_GSCORE "/org/gscore/GScoreObject"
84 #define DBUS_INTERFACE_GSCORE "org.gscore.GScoreInterface"
85 
86 /* Define the functions to export for use with DBus */
87 DBUS_EXPORT void music_messaging_change_request (const int session, const char *command, const char *parameters);
88 DBUS_EXPORT void music_messaging_change_confirmed (const int session, const char *command, const char *parameters);
89 DBUS_EXPORT void music_messaging_change_failed (const int session, const char *id, const char *command, const char *parameters);
90 DBUS_EXPORT void music_messaging_done_session (const int session);
91 
92 /* This file has been generated by the #dbus-analize-functions.py
93    script.  It contains dbus wrappers for the four functions declared
94    above. */
95 #include "music-messaging-bindings.c"
96 
97 /* Exported functions */
music_messaging_change_request(const int session,const char * command,const char * parameters)98 void music_messaging_change_request(const int session, const char *command, const char *parameters)
99 {
100 
101 	MMConversation *mmconv = (MMConversation *)g_list_nth_data(conversations, session);
102 
103 	if (mmconv->started)
104 	{
105 		if (mmconv->originator)
106 		{
107 			char *name = (mmconv->conv)->name;
108 			send_change_request (session, name, command, parameters);
109 		} else
110 		{
111 			GString *to_send = g_string_new("");
112 			g_string_append_printf(to_send, "##MM## request %s %s##MM##", command, parameters);
113 
114 			purple_conv_im_send(PURPLE_CONV_IM(mmconv->conv), to_send->str);
115 
116 			purple_debug_misc("musicmessaging", "Sent request: %s\n", to_send->str);
117 		}
118 	}
119 
120 }
121 
music_messaging_change_confirmed(const int session,const char * command,const char * parameters)122 void music_messaging_change_confirmed(const int session, const char *command, const char *parameters)
123 {
124 
125 	MMConversation *mmconv = (MMConversation *)g_list_nth_data(conversations, session);
126 
127 	if (mmconv->started)
128 	{
129 		if (mmconv->originator)
130 		{
131 			GString *to_send = g_string_new("");
132 			g_string_append_printf(to_send, "##MM## confirm %s %s##MM##", command, parameters);
133 
134 			purple_conv_im_send(PURPLE_CONV_IM(mmconv->conv), to_send->str);
135 		} else
136 		{
137 			/* Do nothing. If they aren't the originator, then they can't confirm. */
138 		}
139 	}
140 
141 }
142 
music_messaging_change_failed(const int session,const char * id,const char * command,const char * parameters)143 void music_messaging_change_failed(const int session, const char *id, const char *command, const char *parameters)
144 {
145 	MMConversation *mmconv = (MMConversation *)g_list_nth_data(conversations, session);
146 
147 	purple_notify_message(plugin_pointer, PURPLE_NOTIFY_MSG_INFO, command,
148                         parameters, NULL, NULL, NULL);
149 
150 	if (mmconv->started)
151 	{
152 		if (mmconv->originator)
153 		{
154 			GString *to_send = g_string_new("");
155 			g_string_append_printf(to_send, "##MM## failed %s %s %s##MM##", id, command, parameters);
156 
157 			purple_conv_im_send(PURPLE_CONV_IM(mmconv->conv), to_send->str);
158 		} else
159 		{
160 			/* Do nothing. If they aren't the originator, then they can't confirm. */
161 		}
162 	}
163 }
164 
music_messaging_done_session(const int session)165 void music_messaging_done_session(const int session)
166 {
167 	MMConversation *mmconv = (MMConversation *)g_list_nth_data(conversations, session);
168 
169 	purple_notify_message(plugin_pointer, PURPLE_NOTIFY_MSG_INFO, "Session",
170 						"Session Complete", NULL, NULL, NULL);
171 
172 	session_end(mmconv);
173 }
174 
175 
176 /* DBus commands that can be sent to the editor */
177 G_BEGIN_DECLS
178 DBusConnection *purple_dbus_get_connection(void);
179 G_END_DECLS
180 
send_change_request(const int session,const char * id,const char * command,const char * parameters)181 static gboolean send_change_request (const int session, const char *id, const char *command, const char *parameters)
182 {
183 	DBusMessage *message;
184 
185 	/* Create the signal we need */
186 	message = dbus_message_new_signal (DBUS_PATH_PURPLE, DBUS_INTERFACE_PURPLE, "GscoreChangeRequest");
187 
188 	/* Append the string "Ping!" to the signal */
189 	dbus_message_append_args (message,
190 							DBUS_TYPE_INT32, &session,
191 							DBUS_TYPE_STRING, &id,
192 							DBUS_TYPE_STRING, &command,
193 							DBUS_TYPE_STRING, &parameters,
194 							DBUS_TYPE_INVALID);
195 
196 	/* Send the signal */
197 	dbus_connection_send (purple_dbus_get_connection(), message, NULL);
198 
199 	/* Free the signal now we have finished with it */
200 	dbus_message_unref (message);
201 
202 	/* Tell the user we sent a signal */
203 	g_printerr("Sent change request signal: %d %s %s %s\n", session, id, command, parameters);
204 
205 	return TRUE;
206 }
207 
send_change_confirmed(const int session,const char * command,const char * parameters)208 static gboolean send_change_confirmed (const int session, const char *command, const char *parameters)
209 {
210 	DBusMessage *message;
211 
212 	/* Create the signal we need */
213 	message = dbus_message_new_signal (DBUS_PATH_PURPLE, DBUS_INTERFACE_PURPLE, "GscoreChangeConfirmed");
214 
215 	/* Append the string "Ping!" to the signal */
216 	dbus_message_append_args (message,
217 							DBUS_TYPE_INT32, &session,
218 							DBUS_TYPE_STRING, &command,
219 							DBUS_TYPE_STRING, &parameters,
220 							DBUS_TYPE_INVALID);
221 
222 	/* Send the signal */
223 	dbus_connection_send (purple_dbus_get_connection(), message, NULL);
224 
225 	/* Free the signal now we have finished with it */
226 	dbus_message_unref (message);
227 
228 	/* Tell the user we sent a signal */
229 	g_printerr("Sent change confirmed signal.\n");
230 
231 	return TRUE;
232 }
233 
234 
235 static int
mmconv_from_conv_loc(PurpleConversation * conv)236 mmconv_from_conv_loc(PurpleConversation *conv)
237 {
238 	GList *l;
239 	MMConversation *mmconv_current = NULL;
240 	guint i;
241 
242 	i = 0;
243 	for (l = conversations; l != NULL; l = l->next)
244 	{
245 		mmconv_current = l->data;
246 		if (conv == mmconv_current->conv)
247 		{
248 			return i;
249 		}
250 		i++;
251 	}
252 	return -1;
253 }
254 
255 static MMConversation*
mmconv_from_conv(PurpleConversation * conv)256 mmconv_from_conv(PurpleConversation *conv)
257 {
258 	return (MMConversation *)g_list_nth_data(conversations, mmconv_from_conv_loc(conv));
259 }
260 
261 static gboolean
plugin_load(PurplePlugin * plugin)262 plugin_load(PurplePlugin *plugin) {
263 	void *conv_list_handle;
264 
265 	PURPLE_DBUS_RETURN_FALSE_IF_DISABLED(plugin);
266 
267     /* First, we have to register our four exported functions with the
268        main purple dbus loop.  Without this statement, the purple dbus
269        code wouldn't know about our functions. */
270     PURPLE_DBUS_REGISTER_BINDINGS(plugin);
271 
272 	/* Keep the plugin for reference (needed for notify's) */
273 	plugin_pointer = plugin;
274 
275 	/* Add the button to all the current conversations */
276 	purple_conversation_foreach (init_conversation);
277 
278 	/* Listen for any new conversations */
279 	conv_list_handle = purple_conversations_get_handle();
280 
281 	purple_signal_connect(conv_list_handle, "conversation-created",
282 					plugin, PURPLE_CALLBACK(init_conversation), NULL);
283 
284 	/* Listen for conversations that are ending */
285 	purple_signal_connect(conv_list_handle, "deleting-conversation",
286 					plugin, PURPLE_CALLBACK(conv_destroyed), NULL);
287 
288 	/* Listen for sending/receiving messages to replace tags */
289 	purple_signal_connect(conv_list_handle, "sending-im-msg",
290 					plugin, PURPLE_CALLBACK(intercept_sent), NULL);
291 	purple_signal_connect(conv_list_handle, "receiving-im-msg",
292 					plugin, PURPLE_CALLBACK(intercept_received), NULL);
293 
294 	return TRUE;
295 }
296 
297 static gboolean
plugin_unload(PurplePlugin * plugin)298 plugin_unload(PurplePlugin *plugin) {
299 	MMConversation *mmconv = NULL;
300 
301 	while (conversations != NULL)
302 	{
303 		mmconv = conversations->data;
304 		conv_destroyed(mmconv->conv);
305 	}
306 	return TRUE;
307 }
308 
309 
310 
311 static gboolean
intercept_sent(PurpleAccount * account,const char * who,char ** message,void * pData)312 intercept_sent(PurpleAccount *account, const char *who, char **message, void* pData)
313 {
314 	if (message == NULL || *message == NULL || **message == '\0')
315 		return FALSE;
316 
317 	if (0 == strncmp(*message, MUSICMESSAGING_PREFIX, strlen(MUSICMESSAGING_PREFIX)))
318 	{
319 		purple_debug_misc("purple-musicmessaging", "Sent MM Message: %s\n", *message);
320 		message = 0;
321 	}
322 	else if (0 == strncmp(*message, MUSICMESSAGING_START_MSG, strlen(MUSICMESSAGING_START_MSG)))
323 	{
324 		purple_debug_misc("purple-musicmessaging", "Sent MM request.\n");
325 		return FALSE;
326 	}
327 	else if (0 == strncmp(*message, MUSICMESSAGING_CONFIRM_MSG, strlen(MUSICMESSAGING_CONFIRM_MSG)))
328 	{
329 		purple_debug_misc("purple-musicmessaging", "Sent MM confirm.\n");
330 		return FALSE;
331 	}
332 	else if (0 == strncmp(*message, "test1", strlen("test1")))
333 	{
334 		purple_debug_misc("purple-musicmessaging", "\n\nTEST 1\n\n");
335 		send_change_request(0, "test-id", "test-command", "test-parameters");
336 		return FALSE;
337 	}
338 	else if (0 == strncmp(*message, "test2", strlen("test2")))
339 	{
340 		purple_debug_misc("purple-musicmessaging", "\n\nTEST 2\n\n");
341 		send_change_confirmed(1, "test-command", "test-parameters");
342 		return FALSE;
343 	}
344 	else
345 	{
346 		return FALSE;
347 		/* Do nothing...procceed as normal */
348 	}
349 	return TRUE;
350 }
351 
352 static gboolean
intercept_received(PurpleAccount * account,char ** sender,char ** message,PurpleConversation * conv,int * flags)353 intercept_received(PurpleAccount *account, char **sender, char **message, PurpleConversation *conv, int *flags)
354 {
355 	MMConversation *mmconv;
356 
357 	if (conv == NULL) {
358 		/* XXX: This is just to avoid a crash (#2726).
359 		 *      We may want to create the conversation instead of returning from here
360 		 */
361 		return FALSE;
362 	}
363 
364 	mmconv = mmconv_from_conv(conv);
365 
366 	purple_debug_misc("purple-musicmessaging", "Intercepted: %s\n", *message);
367 	if (strstr(*message, MUSICMESSAGING_PREFIX))
368 	{
369 		char *parsed_message = strtok(strstr(*message, MUSICMESSAGING_PREFIX), "<");
370 		purple_debug_misc("purple-musicmessaging", "Received an MM Message: %s\n", parsed_message);
371 
372 		if (mmconv->started)
373 		{
374 			if (strstr(parsed_message, "request"))
375 			{
376 				if (mmconv->originator)
377 				{
378 					int session = mmconv_from_conv_loc(conv);
379 					char *id = (mmconv->conv)->name;
380 					char *command;
381 					char *parameters;
382 
383 					purple_debug_misc("purple-musicmessaging", "Sending request to gscore.\n");
384 
385 					/* Get past the first two terms - '##MM##' and 'request' */
386 					strtok(parsed_message, " "); /* '##MM##' */
387 					strtok(NULL, " "); /* 'request' */
388 
389 					command = strtok(NULL, " ");
390 					parameters = strtok(NULL, "#");
391 
392 					send_change_request (session, id, command, parameters);
393 
394 				}
395 			} else if (strstr(parsed_message, "confirm"))
396 			{
397 				if (!mmconv->originator)
398 				{
399 					int session = mmconv_from_conv_loc(conv);
400 					char *command;
401 					char *parameters;
402 
403 					purple_debug_misc("purple-musicmessaging", "Sending confirmation to gscore.\n");
404 
405 					/* Get past the first two terms - '##MM##' and 'confirm' */
406 					strtok(parsed_message, " "); /* '##MM##' */
407 					strtok(NULL, " "); /* 'confirm' */
408 
409 					command = strtok(NULL, " ");
410 					parameters = strtok(NULL, "#");
411 
412 					send_change_confirmed (session, command, parameters);
413 				}
414 			} else if (strstr(parsed_message, "failed"))
415 			{
416 				char *id;
417 				char *command;
418 
419 				/* Get past the first two terms - '##MM##' and 'confirm' */
420 				strtok(parsed_message, " "); /* '##MM##' */
421 				strtok(NULL, " "); /* 'failed' */
422 
423 				id = strtok(NULL, " ");
424 				command = strtok(NULL, " ");
425 				/* char *parameters = strtok(NULL, "#"); DONT NEED PARAMETERS */
426 
427 				if ((mmconv->conv)->name == id)
428 				{
429 					purple_notify_message(plugin_pointer, PURPLE_NOTIFY_MSG_ERROR,
430 							    _("Music Messaging"),
431 							    _("There was a conflict in running the command:"), command, NULL, NULL);
432 				}
433 			}
434 		}
435 
436 		message = 0;
437 	}
438 	else if (strstr(*message, MUSICMESSAGING_START_MSG))
439 	{
440 		purple_debug_misc("purple-musicmessaging", "Received MM request.\n");
441 		if (!(mmconv->originator))
442 		{
443 			mmconv->requested = TRUE;
444 			return FALSE;
445 		}
446 
447 	}
448 	else if (strstr(*message, MUSICMESSAGING_CONFIRM_MSG))
449 	{
450 		purple_debug_misc("purple-musicmessagin", "Received MM confirm.\n");
451 
452 		if (mmconv->originator)
453 		{
454 			start_session(mmconv);
455 			return FALSE;
456 		}
457 	}
458 	else
459 	{
460 		return FALSE;
461 		/* Do nothing. */
462 	}
463 	return TRUE;
464 }
465 
send_request(MMConversation * mmconv)466 static void send_request(MMConversation *mmconv)
467 {
468 	PurpleConnection *connection = purple_conversation_get_gc(mmconv->conv);
469 	const char *convName = purple_conversation_get_name(mmconv->conv);
470 	serv_send_im(connection, convName, MUSICMESSAGING_START_MSG, PURPLE_MESSAGE_SEND);
471 }
472 
send_request_confirmed(MMConversation * mmconv)473 static void send_request_confirmed(MMConversation *mmconv)
474 {
475 	PurpleConnection *connection = purple_conversation_get_gc(mmconv->conv);
476 	const char *convName = purple_conversation_get_name(mmconv->conv);
477 	serv_send_im(connection, convName, MUSICMESSAGING_CONFIRM_MSG, PURPLE_MESSAGE_SEND);
478 }
479 
480 
481 static gboolean
start_session(MMConversation * mmconv)482 start_session(MMConversation *mmconv)
483 {
484 	run_editor(mmconv);
485 	return TRUE;
486 }
487 
session_end(MMConversation * mmconv)488 static void session_end (MMConversation *mmconv)
489 {
490 	mmconv->started = FALSE;
491 	mmconv->originator = FALSE;
492 	mmconv->requested = FALSE;
493 	kill_editor(mmconv);
494 }
495 
music_button_toggled(GtkWidget * widget,gpointer data)496 static void music_button_toggled (GtkWidget *widget, gpointer data)
497 {
498 	MMConversation *mmconv = mmconv_from_conv(((MMConversation *) data)->conv);
499 	if (gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (widget)))
500     {
501 		if (((MMConversation *) data)->requested)
502 		{
503 			start_session(mmconv);
504 			send_request_confirmed(mmconv);
505 		}
506 		else
507 		{
508 			((MMConversation *) data)->originator = TRUE;
509 			send_request((MMConversation *) data);
510 		}
511     } else {
512 		session_end((MMConversation *)data);
513     }
514 }
515 
set_editor_path(GtkWidget * button,GtkWidget * text_field)516 static void set_editor_path (GtkWidget *button, GtkWidget *text_field)
517 {
518 	const char * path = gtk_entry_get_text((GtkEntry*)text_field);
519 	purple_prefs_set_string("/plugins/gtk/musicmessaging/editor_path", path);
520 
521 }
522 
run_editor(MMConversation * mmconv)523 static void run_editor (MMConversation *mmconv)
524 {
525 	GError *spawn_error = NULL;
526 	GString *session_id;
527 	gchar * args[4];
528 	args[0] = (gchar *)purple_prefs_get_string("/plugins/gtk/musicmessaging/editor_path");
529 
530 	args[1] = "-session_id";
531 	session_id = g_string_new("");
532 	g_string_append_printf(session_id, "%d", mmconv_from_conv_loc(mmconv->conv));
533 	args[2] = session_id->str;
534 
535 	args[3] = NULL;
536 
537 	if (!(g_spawn_async (".", args, NULL, 4, NULL, NULL, &(mmconv->pid), &spawn_error)))
538 	{
539 		purple_notify_error(plugin_pointer, _("Error Running Editor"),
540 				  _("The following error has occurred:"), spawn_error->message);
541 		mmconv->started = FALSE;
542 	}
543 	else
544 	{
545 		mmconv->started = TRUE;
546 	}
547 }
548 
kill_editor(MMConversation * mmconv)549 static void kill_editor (MMConversation *mmconv)
550 {
551 	if (mmconv->pid)
552 	{
553 		kill(mmconv->pid, SIGINT);
554 		mmconv->pid = 0;
555 	}
556 }
557 
init_conversation(PurpleConversation * conv)558 static void init_conversation (PurpleConversation *conv)
559 {
560 	MMConversation *mmconv;
561 	mmconv = g_malloc(sizeof(MMConversation));
562 
563 	mmconv->conv = conv;
564 	mmconv->started = FALSE;
565 	mmconv->originator = FALSE;
566 	mmconv->requested = FALSE;
567 
568 	add_button(mmconv);
569 
570 	conversations = g_list_append(conversations, mmconv);
571 }
572 
conv_destroyed(PurpleConversation * conv)573 static void conv_destroyed (PurpleConversation *conv)
574 {
575 	MMConversation *mmconv = mmconv_from_conv(conv);
576 
577 	remove_widget(mmconv->button);
578 	remove_widget(mmconv->seperator);
579 	if (mmconv->started)
580 	{
581 		kill_editor(mmconv);
582 	}
583 	conversations = g_list_remove(conversations, mmconv);
584 }
585 
add_button(MMConversation * mmconv)586 static void add_button (MMConversation *mmconv)
587 {
588 	PurpleConversation *conv = mmconv->conv;
589 
590 	GtkWidget *button, *image, *sep;
591 	gchar *file_path;
592 
593 	button = gtk_toggle_button_new();
594 	gtk_button_set_relief(GTK_BUTTON(button), GTK_RELIEF_NONE);
595 
596 	g_signal_connect(G_OBJECT(button), "toggled", G_CALLBACK(music_button_toggled), mmconv);
597 
598 	file_path = g_build_filename(DATADIR, "pixmaps", "purple", "buttons",
599 										"music.png", NULL);
600 	image = gtk_image_new_from_file(file_path);
601 	g_free(file_path);
602 
603 	gtk_container_add((GtkContainer *)button, image);
604 
605 	sep = gtk_vseparator_new();
606 
607 	mmconv->seperator = sep;
608 	mmconv->button = button;
609 
610 	gtk_widget_show(sep);
611 	gtk_widget_show(image);
612 	gtk_widget_show(button);
613 
614 	gtk_box_pack_start(GTK_BOX(PIDGIN_CONVERSATION(conv)->toolbar), sep, FALSE, FALSE, 0);
615 	gtk_box_pack_start(GTK_BOX(PIDGIN_CONVERSATION(conv)->toolbar), button, FALSE, FALSE, 0);
616 }
617 
remove_widget(GtkWidget * button)618 static void remove_widget (GtkWidget *button)
619 {
620 	gtk_widget_hide(button);
621 	gtk_widget_destroy(button);
622 }
623 
624 static GtkWidget *
get_config_frame(PurplePlugin * plugin)625 get_config_frame(PurplePlugin *plugin)
626 {
627 	GtkWidget *ret;
628 	GtkWidget *vbox;
629 
630 	GtkWidget *editor_path;
631 	GtkWidget *editor_path_label;
632 	GtkWidget *editor_path_button;
633 
634 	/* Outside container */
635 	ret = gtk_vbox_new(FALSE, 18);
636 	gtk_container_set_border_width(GTK_CONTAINER(ret), 10);
637 
638 	/* Configuration frame */
639 	vbox = pidgin_make_frame(ret, _("Music Messaging Configuration"));
640 
641 	/* Path to the score editor */
642 	editor_path = gtk_entry_new();
643 	editor_path_label = gtk_label_new(_("Score Editor Path"));
644 	editor_path_button = gtk_button_new_with_mnemonic(_("_Apply"));
645 
646 	gtk_entry_set_text((GtkEntry*)editor_path, "/usr/local/bin/gscore");
647 
648 	g_signal_connect(G_OBJECT(editor_path_button), "clicked",
649 					 G_CALLBACK(set_editor_path), editor_path);
650 
651 	gtk_box_pack_start(GTK_BOX(vbox), editor_path_label, FALSE, FALSE, 0);
652 	gtk_box_pack_start(GTK_BOX(vbox), editor_path, FALSE, FALSE, 0);
653 	gtk_box_pack_start(GTK_BOX(vbox), editor_path_button, FALSE, FALSE, 0);
654 
655 	gtk_widget_show_all(ret);
656 
657 	return ret;
658 }
659 
660 static PidginPluginUiInfo ui_info =
661 {
662 	get_config_frame,
663 	0, /* page_num (reserved) */
664 
665 	/* padding */
666 	NULL,
667 	NULL,
668 	NULL,
669 	NULL
670 };
671 
672 static PurplePluginInfo info = {
673     PURPLE_PLUGIN_MAGIC,
674     PURPLE_MAJOR_VERSION,
675     PURPLE_MINOR_VERSION,
676     PURPLE_PLUGIN_STANDARD,                                /**< type           */
677     PIDGIN_PLUGIN_TYPE,                                /**< ui_requirement */
678     0,                                                   /**< flags          */
679     NULL,                                                /**< dependencies   */
680     PURPLE_PRIORITY_DEFAULT,                               /**< priority       */
681 
682     MUSICMESSAGING_PLUGIN_ID,                            /**< id             */
683     "Music Messaging",	                                 /**< name           */
684     DISPLAY_VERSION,                                     /**< version        */
685     N_("Music Messaging Plugin for collaborative composition."),
686                                                          /**  summary        */
687     N_("The Music Messaging Plugin allows a number of users to simultaneously "
688        "work on a piece of music by editing a common score in real-time."),
689 	                                                 /**  description    */
690     "Christian Muise <christian.muise@gmail.com>",       /**< author         */
691     PURPLE_WEBSITE,                                        /**< homepage       */
692     plugin_load,                                         /**< load           */
693     plugin_unload,                                       /**< unload         */
694     NULL,                                                /**< destroy        */
695     &ui_info,                                            /**< ui_info        */
696     NULL,                                                /**< extra_info     */
697     NULL,
698     NULL,
699 
700 	/* padding */
701 	NULL,
702 	NULL,
703 	NULL,
704 	NULL
705 };
706 
707 static void
init_plugin(PurplePlugin * plugin)708 init_plugin(PurplePlugin *plugin) {
709 	purple_prefs_add_none("/plugins/gtk/musicmessaging");
710 	purple_prefs_add_string("/plugins/gtk/musicmessaging/editor_path", "/usr/bin/gscore");
711 }
712 
713 PURPLE_INIT_PLUGIN(musicmessaging, init_plugin, info);
714