#include "sharing.h" #include #include #include #include /* I love putting these two together */ #include #include /** * \bug If these routines fail, they print debugging information to stderr, * where it will rarely be seen. They should return it to the caller, * or at least put up a dialogue. Yes, we're cutting a few corners here. */ /** * Keeps all the information in one place about what we want to send. */ typedef struct { /** * The display name of the service we're sending to. */ gchar *service_name; /** * The internal name of the service we're sending to. */ gchar *service; /** * The link we're sending. * Should be a valid URL. */ gchar *link; /** * The title of the link. */ gchar *title; } SharingMessage; /** * Starts sending an email. Any of the parameters can be an empty string, though * none of them should be NULL. This function will just take us to a screen where * people can edit this stuff and then send the email, or cancel it. * * \param to The recipient. * \param cc More recipients. * \param bcc Recipients, but the other recipients won't see their names * \param subject What the email is about. * \param body The text of the email. * \param attachments Comma-delimited set of escaped file:// URLs for attachments. * * \return True iff we forwarded the message upstream correctly. This does not * mean the email was correctly sent. */ static gboolean sharing_send_email (gchar *to, gchar *cc, gchar *bcc, gchar *subject, gchar *body, gchar *attachments) { DBusGConnection *connection; GError *error = NULL; DBusGProxy *proxy; connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); if (connection == NULL) { fprintf (stderr, "ERROR: %s", error->message); g_error_free (error); return FALSE; } proxy = dbus_g_proxy_new_for_name (connection, "com.nokia.modest", "/com/nokia/modest", "com.nokia.modest"); error = NULL; if (!dbus_g_proxy_call (proxy, "ComposeMail", &error, G_TYPE_STRING, to, G_TYPE_STRING, cc, G_TYPE_STRING, bcc, G_TYPE_STRING, subject, G_TYPE_STRING, body, G_TYPE_STRING, attachments, G_TYPE_INVALID, G_TYPE_INVALID)) { fprintf (stderr, "ERROR: %s\n", error->message); g_error_free (error); return FALSE; } return TRUE; } /** * Posts a message on twitter, identica, jaiku, etc. * * \param publisher The publisher, e.g. "Identica" for identi.ca. * \param provider The providing class, e.g. * "org.microfeed.Provider.Twitter" for identi.ca * (because it uses the same backend as Twitter). * \param message What you actually wanted to say. It would be * sensible to keep this below 140 characters, but * it will still work if you don't (Twitter will * show the tweet in full if you look at it; it'll * just appear truncated in people's feeds). * * \return true iff there was no problem communicating the * message upstream. As explained below, this doesn't * mean that the message was posted, unfortunately. * * \bug Currently doesn't check for the signal to say that * the posting *failed*. There is AFAIK no way in libmicrofeed * to know that the posting succeeded other than to wait * an indefinite time and not get a failure signal. * * \bug May print debugging output to stderr if things go wrong * in sending. */ static gboolean sharing_send_tweet (gchar *publisher, gchar *provider, gchar *message) { DBusGConnection *connection; GError *error = NULL; gchar *str; DBusGProxy *proxy; connection = dbus_g_bus_get (DBUS_BUS_SESSION, &error); if (connection == NULL) { fprintf (stderr, "ERROR: %s", error->message); g_error_free (error); return FALSE; } str = g_strdup_printf ("/org/microfeed/publisher/%s", publisher); proxy = dbus_g_proxy_new_for_name (connection, "org.microfeed.Provider.Twitter", str, "org.microfeed.Publisher"); g_free (str); /* get some unique code for this tweet, which isn't important * anyway at the moment because we're being fire-and-forget, * so we'll never * but let's make the code unique on principle */ str = g_strdup_printf ("%d.%d.%ld", getpid (), time (NULL), random ()); error = NULL; if (!dbus_g_proxy_call (proxy, "AddItem", &error, /* Why ":Overview"? The specs seem * to say it should be a URL-like string: * "http://microfeed.org/Feed/Overview". * But that doesn't do anything. * But this is what Mauku does, so * we'll do that. */ G_TYPE_STRING, ":Overview", /* Some unique ID */ G_TYPE_STRING, str, /* This uses the *old* method of * identifying a client, which * isn't open to new registrations. * Therefore we will pretend to * be blt, which was registered before * the rules changed. */ G_TYPE_STRING, ".twitter.source", G_TYPE_STRING, "blt", /* And now, what we actually wanted to say. */ G_TYPE_STRING, "content.text", G_TYPE_STRING, message, G_TYPE_INVALID, G_TYPE_INVALID)) { fprintf (stderr, "ERROR: %s\n", error->message); g_error_free (error); g_free (str); return FALSE; } g_free (str); return TRUE; } /** * Scans one directory for microfeed providers. * Helper function for sharing_scan_for_microfeeds(). * * \param dir_name Name of the directory to scan. * \param options Hash table to stick the results into. */ static void sharing_scan_directory_for_microfeeds (const gchar *dir_name, GHashTable *options) { glob_t globbuf; gchar *target = g_strdup_printf ("%s/*-*", dir_name); GRegex *re = g_regex_new ("/([^/-]*)-(.*)$", 0, 0, NULL); GMatchInfo *found = NULL; gint i; glob (target, 0, NULL, &globbuf); for (i=0; iservice, EMAIL_KEY)==0) { /* Special case for email */ sharing_send_email ("", /* no "to", "cc", or "bcc" */ "", "", message->title, message->link, ""); /* no attachments */ } else { /* One day, we might want to call some sort of * URL shortening service here. */ gchar *str = g_strdup_printf ("%s %s (via raeddit)", message->link, message->title); sharing_send_tweet (message->service_name, message->service, str); g_free (str); } g_free (message->service_name); g_free (message->service); g_free (message->title); g_free (message->link); g_free (message); } void sharing_choice (GtkWindow *window, char *link, char *title) { GtkWidget *dialog; GtkWidget *selector; /** * A map of strings which are display names to strings which are names of * microfeed providers. EMAIL_KEY is a special case that sends email * instead. A more elegant solution would be to map to (function pointer, * arbitrary data pointer) pairs; if we add any more special cases we * should do this. */ GHashTable *options = g_hash_table_new_full (g_str_hash, g_str_equal, g_free, g_free); GList *options_list, *cursor; SharingMessage *message = g_new (SharingMessage, 1); message->link = g_strdup (link); message->title = g_strdup (title); dialog = hildon_picker_dialog_new (GTK_WINDOW (window)); selector = hildon_touch_selector_new_text (); gtk_window_set_title (GTK_WINDOW (dialog), "How would you like to share it?"); /* You always have email available. */ g_hash_table_insert (options, g_strdup (EMAIL_KEY), g_strdup (EMAIL_KEY)); /* And let's see what microfeeds we know about. */ sharing_scan_for_microfeeds (options); options_list = g_hash_table_get_keys (options); for (cursor = options_list; cursor; cursor=cursor->next) { gchar *line = (gchar *) cursor->data; hildon_touch_selector_append_text (HILDON_TOUCH_SELECTOR (selector), line); } g_list_free (options_list); hildon_picker_dialog_set_selector (HILDON_PICKER_DIALOG (dialog), HILDON_TOUCH_SELECTOR (selector)); gtk_widget_show_all (GTK_WIDGET (GTK_DIALOG (dialog)->vbox)); if (gtk_dialog_run (GTK_DIALOG (dialog))!=GTK_RESPONSE_OK) { gtk_widget_destroy (GTK_WIDGET (dialog)); g_hash_table_unref (options); return; } message->service_name = g_strdup (hildon_touch_selector_get_current_text (HILDON_TOUCH_SELECTOR (selector))); message->service = g_strdup ((gchar*) g_hash_table_lookup (options, message->service_name)); if (!g_thread_create(&sharing_send_message_thread, message, FALSE, NULL)) { /* oh dear. */ fprintf (stderr, "Couldn't create the thread. Can't send the message.\n"); } gtk_widget_destroy (GTK_WIDGET (dialog)); g_hash_table_unref (options); return; } /* eof sharing.c */